1use glium;
2use glium::glutin::surface::WindowSurface;
3use glium::implement_vertex;
4use glium::Frame;
5use glium::Surface;
6use parley::fontique::Collection;
7use parley::FontContext;
8use parley::LayoutContext;
9use primitives::Texture;
10use swash::scale::ScaleContext;
11pub use winit;
13use winit::event_loop::EventLoop;
14pub mod primitives;
16pub mod shaders;
18use shaders::*;
19pub mod drawable_object;
22use drawable_object::*;
23use rustc_hash::FxHashMap;
24pub mod animation;
25pub mod blending;
26pub mod lights;
29pub mod text;
30
31const HANDLE_STRING_ID: &str = "wdAYG8&DWtyiwDhukhjwda";
32
33#[doc = include_str!("../lumenpyx wiki/Home.md")]
35#[doc = include_str!("../lumenpyx wiki/Common-problems-and-their-solutions.md")]
36#[doc = include_str!("../lumenpyx wiki/Creating-custom-drawable-objects.md")]
37#[doc = include_str!("../lumenpyx wiki/Creating-Custom-Lights.md")]
38#[doc = include_str!("../lumenpyx wiki/Creating-Custom-Renderables.md")]
39#[doc = include_str!("../lumenpyx wiki/Rendering-a-Sprite.md")]
40#[doc = include_str!("../lumenpyx wiki/Technical-Documentation.md")]
41
42pub(crate) const DEFAULT_BEHAVIOR: glium::uniforms::SamplerBehavior =
43 glium::uniforms::SamplerBehavior {
44 minify_filter: glium::uniforms::MinifySamplerFilter::Nearest,
45 magnify_filter: glium::uniforms::MagnifySamplerFilter::Nearest,
46 max_anisotropy: 1,
47 wrap_function: (
48 glium::uniforms::SamplerWrapFunction::Mirror,
49 glium::uniforms::SamplerWrapFunction::Mirror,
50 glium::uniforms::SamplerWrapFunction::Mirror,
51 ),
52 depth_texture_comparison: None,
53 };
54
55pub enum DebugOption {
57 None,
59 Albedo,
61 Height,
63 Roughness,
65 Normal,
67 ShadowStrength,
69}
70
71impl Default for DebugOption {
72 fn default() -> Self {
73 DebugOption::None
74 }
75}
76
77#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
78pub struct TextureHandle {
79 id: u32,
80}
81
82pub struct LumenpyxProgram {
84 pub window: winit::window::Window,
86 pub display: glium::Display<WindowSurface>,
88 pub indices: glium::index::NoIndices,
90 shaders: FxHashMap<String, glium::Program>,
91 cache: LumenpyxCache,
92 next_texture_id: u32,
93 dimensions: [u32; 2],
94 pub debug: DebugOption,
95 pub render_settings: RenderSettings,
96 font_context: Option<FontContext>,
97 scale_context: Option<ScaleContext>,
98 layout_context: Option<LayoutContext>,
99}
100
101impl LumenpyxProgram {
102 pub fn new(resolution: [u32; 2], name: &str) -> (LumenpyxProgram, EventLoop<()>) {
104 let (event_loop, window, display, indices) = setup_program();
105 event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
106
107 let mut program = LumenpyxProgram {
108 window,
109 display,
110 indices,
111 shaders: FxHashMap::default(),
112 next_texture_id: 0,
113 cache: LumenpyxCache::default(),
114 dimensions: resolution,
115 debug: DebugOption::None,
116 render_settings: RenderSettings {
117 shadows: true,
118 reflections: true,
119 render_resolution: None,
120 blur_reflections: false,
121 blur_strength: 0.01,
122 },
123 font_context: None,
124 scale_context: None,
125 layout_context: None,
126 };
127
128 program.set_name(name);
129
130 shaders::load_all_system_shaders(&mut program);
131
132 (program, event_loop)
133 }
134
135 pub fn add_shader(&mut self, program: glium::Program, name: &str) {
137 self.shaders.insert(name.to_string(), program);
138 }
139
140 pub fn get_shader(&self, name: &str) -> Option<&glium::Program> {
142 self.shaders.get(name)
143 }
144
145 pub fn add_texture(&mut self, texture: glium::texture::Texture2d, name: &str) {
147 self.cache.insert(name.to_string(), texture);
148 }
149
150 pub fn get_texture(&self, name: &str) -> Option<&glium::texture::Texture2d> {
152 self.cache.get_texture(name)
153 }
154
155 pub fn get_texture_from_handle(
157 &self,
158 handle: &TextureHandle,
159 ) -> Option<&glium::texture::Texture2d> {
160 self.cache
161 .get_texture(&format!("{}_{}", HANDLE_STRING_ID, handle.id))
162 }
163
164 pub fn remove_shader(&mut self, name: &str) {
166 self.shaders.remove(name);
167 }
168
169 pub fn set_name(&mut self, name: &str) {
171 self.window.set_title(name);
172 }
173
174 pub fn set_debug(&mut self, debug: DebugOption) {
176 self.debug = debug;
177 }
178
179 pub fn set_render_settings(&mut self, settings: RenderSettings) {
181 self.render_settings = settings;
182 }
183
184 pub fn set_resolution(&mut self, resolution: [u32; 2]) {
186 self.dimensions = resolution;
187 }
188
189 pub fn get_resolution(&self) -> [u32; 2] {
191 self.dimensions
192 }
193
194 pub fn run<F>(&mut self, event_loop: EventLoop<()>, mut update: F)
196 where
197 F: FnMut(&mut Self),
198 {
199 event_loop
200 .run(move |ev, window_target| match ev {
201 winit::event::Event::WindowEvent { event, .. } => match event {
202 winit::event::WindowEvent::CloseRequested => {
203 window_target.exit();
204 }
205 winit::event::WindowEvent::Resized(physical_size) => {
206 self.display.resize(physical_size.into());
207 }
208 winit::event::WindowEvent::RedrawRequested => {
209 update(self);
210 }
211 _ => (),
212 },
213 winit::event::Event::AboutToWait => {
214 self.window.request_redraw();
217 }
218 _ => (),
219 })
220 .expect("Failed to run event loop");
221 }
222
223 pub fn get_dimensions(&self) -> [u32; 2] {
224 self.dimensions
225 }
226
227 pub(crate) fn get_render_resolution(&self) -> [u32; 2] {
228 self.render_settings
229 .render_resolution
230 .unwrap_or(self.dimensions)
231 }
232
233 pub(crate) fn adjust_transform_for_drawable(
234 &self,
235 mut transform: &Transform,
236 camera: &Camera,
237 ) -> Transform {
238 let render_resolution = self.get_render_resolution();
239 let mut new_transform = transform.clone();
240
241 let mut scale = transform.get_scale();
242 if render_resolution[0] > render_resolution[1] {
244 scale[0] *= render_resolution[1] as f32 / render_resolution[0] as f32;
245 } else {
246 scale[1] *= render_resolution[0] as f32 / render_resolution[1] as f32;
247 }
248 new_transform.set_scale(scale[0], scale[1], scale[2]);
249
250 let (mut x, mut y, z) = (transform.get_x(), transform.get_y(), transform.get_z());
251 x -= camera.position[0];
253 y -= camera.position[1];
254
255 x /= render_resolution[0] as f32;
256 y /= render_resolution[1] as f32;
257
258 x *= 2.0;
260 y *= 2.0;
261
262 new_transform.translate(x, y, z);
263
264 new_transform
265 }
266
267 pub fn add_not_named_texture(&mut self, texture: glium::texture::Texture2d) -> TextureHandle {
269 let id = self.next_texture_id;
270 self.next_texture_id += 1;
271
272 self.cache
273 .insert(format!("{}_{}", HANDLE_STRING_ID, id), texture);
274
275 TextureHandle { id }
276 }
277
278 pub fn remove_texture(&mut self, handle: &TextureHandle) {
279 self.cache
280 .hashmap
281 .remove(&format!("{}_{}", HANDLE_STRING_ID, handle.id));
282 }
283
284 pub fn set_font_collection(
285 &mut self,
286 font_collection: Collection,
287 lumenpyx_program: &mut crate::LumenpyxProgram,
288 ) {
289 if let Some(font_context) = &mut lumenpyx_program.font_context {
290 font_context.collection = font_collection;
291 } else {
292 let mut new_font_context = FontContext::default();
293 new_font_context.collection = font_collection;
294 lumenpyx_program.font_context = Some(new_font_context);
295 }
296 }
297
298 pub fn add_font_to_collection(&mut self, font: Vec<u8>) {
299 if let Some(font_context) = &mut self.font_context {
300 font_context.collection.register_fonts(font);
301 } else {
302 let mut new_font_context = FontContext::default();
303 new_font_context.collection.register_fonts(font);
304 self.font_context = Some(new_font_context);
305 }
306 }
307
308 pub fn get_font_collection(&self) -> Option<&Collection> {
309 if let Some(font_context) = &self.font_context {
310 Some(&font_context.collection)
311 } else {
312 None
313 }
314 }
315
316 pub fn get_font_context(&self) -> Option<&FontContext> {
317 self.font_context.as_ref()
318 }
319
320 pub fn get_font_context_mut(&mut self) -> Option<&mut FontContext> {
321 self.font_context.as_mut()
322 }
323}
324
325struct LumenpyxCache {
326 hashmap: FxHashMap<String, glium::Texture2d>,
327}
328
329impl Default for LumenpyxCache {
330 fn default() -> Self {
331 LumenpyxCache {
332 hashmap: FxHashMap::default(),
333 }
334 }
335}
336
337impl LumenpyxCache {
338 fn insert(&mut self, name: String, texture: glium::Texture2d) {
339 self.hashmap.insert(name, texture);
340 }
341
342 fn get_texture(&self, name: &str) -> Option<&glium::Texture2d> {
343 let texture = self.hashmap.get(name);
344
345 if let Some(texture) = texture {
346 Some(texture)
347 } else {
348 None
349 }
350 }
351}
352
353#[derive(Copy, Clone)]
355pub struct Transform {
356 matrix: [[f32; 4]; 4],
357 rotation: f32,
358}
359
360impl Default for Transform {
361 fn default() -> Self {
362 Transform::new([0.0, 0.0, 0.0])
363 }
364}
365
366impl Transform {
367 pub fn from_matrix(matrix: [[f32; 4]; 4]) -> Transform {
368 Transform {
369 matrix,
370 rotation: 0.0,
371 }
372 }
373
374 pub fn new(pos: [f32; 3]) -> Transform {
375 Transform {
376 matrix: [
377 [1.0, 0.0, 0.0, 0.0],
378 [0.0, 1.0, 0.0, 0.0],
379 [0.0, 0.0, 1.0, 0.0],
380 [pos[0], pos[1], pos[2], 1.0],
381 ],
382 rotation: 0.0,
383 }
384 }
385
386 pub fn get_matrix(&self) -> [[f32; 4]; 4] {
388 let rotation_matrix = [
389 [self.rotation.cos(), -self.rotation.sin(), 0.0, 0.0],
390 [self.rotation.sin(), self.rotation.cos(), 0.0, 0.0],
391 [0.0, 0.0, 1.0, 0.0],
392 [0.0, 0.0, 0.0, 1.0],
393 ];
394
395 let mut self_matrix = self.matrix;
396 self_matrix[3][2] = 0.0;
397
398 multiply_matrix(rotation_matrix, self_matrix)
399 }
400
401 pub fn translate(&mut self, x: f32, y: f32, z: f32) {
403 self.matrix[3][0] = x;
404 self.matrix[3][1] = y;
405 self.matrix[3][2] = z;
406 }
407
408 pub fn set_scale(&mut self, x: f32, y: f32, z: f32) {
410 self.matrix[0][0] = x;
411 self.matrix[1][1] = y;
412 self.matrix[2][2] = z;
413 }
414
415 pub fn get_scale(&self) -> [f32; 3] {
417 [self.matrix[0][0], self.matrix[1][1], self.matrix[2][2]]
418 }
419
420 pub fn set_x(&mut self, x: f32) {
422 self.matrix[3][0] = x;
423 }
424
425 pub fn get_x(&self) -> f32 {
427 self.matrix[3][0]
428 }
429
430 pub fn set_y(&mut self, y: f32) {
432 self.matrix[3][1] = y;
433 }
434
435 pub fn get_y(&self) -> f32 {
437 self.matrix[3][1]
438 }
439
440 pub fn set_z(&mut self, z: f32) {
442 self.matrix[3][2] = z;
443 }
444
445 pub fn get_z(&self) -> f32 {
447 self.matrix[3][2]
448 }
449
450 pub fn set_rotation(&mut self, angle: f32) {
452 self.rotation = angle;
453 }
454
455 pub fn get_rotation(&self) -> f32 {
456 self.rotation
457 }
458
459 pub fn add_parent(&self, parent: &Transform) -> Transform {
460 let mut new_transform = Transform::new([0.0, 0.0, 0.0]);
461
462 new_transform.set_x(self.get_x() + parent.get_x());
463 new_transform.set_y(self.get_y() + parent.get_y());
464 new_transform.set_z(self.get_z() + parent.get_z());
465
466 new_transform.set_rotation(self.get_rotation() + parent.get_rotation());
467
468 new_transform.set_scale(
469 self.get_scale()[0] * parent.get_scale()[0],
470 self.get_scale()[1] * parent.get_scale()[1],
471 self.get_scale()[2] * parent.get_scale()[2],
472 );
473
474 new_transform
475 }
476}
477
478fn multiply_matrix(matrix1: [[f32; 4]; 4], matrix2: [[f32; 4]; 4]) -> [[f32; 4]; 4] {
479 let mut new_matrix = [[0.0; 4]; 4];
480
481 for i in 0..4 {
482 for j in 0..4 {
483 for k in 0..4 {
484 new_matrix[i][j] = new_matrix[i][j] + matrix1[i][k] * matrix2[k][j];
485 }
486 }
487 }
488
489 new_matrix
490}
491
492impl<'a, 'b> std::ops::Add<&'b Transform> for &'a Transform {
494 type Output = Transform;
495
496 fn add(self, other: &'b Transform) -> Transform {
497 let mut new_transform = Transform::new([0.0, 0.0, 0.0]);
498
499 new_transform.translate(
500 self.get_x() + other.get_x(),
501 self.get_y() + other.get_y(),
502 self.get_z() + other.get_z(),
503 );
504
505 new_transform.set_rotation(self.get_rotation() + other.get_rotation());
506
507 new_transform.set_scale(
508 self.get_x() * other.get_x(),
509 self.get_y() * other.get_y(),
510 self.get_z() * other.get_z(),
511 );
512
513 new_transform
514 }
515}
516
517#[derive(Copy, Clone)]
521pub struct Vertex {
522 position: [f32; 2],
523 tex_coords: [f32; 2],
524}
525implement_vertex!(Vertex, position, tex_coords);
526
527pub(crate) fn setup_program() -> (
529 EventLoop<()>,
530 winit::window::Window,
531 glium::Display<WindowSurface>,
532 glium::index::NoIndices,
533) {
534 let (event_loop, display, window, indices) = setup_window();
536
537 (event_loop, window, display, indices)
538}
539
540fn load_image(path: &str) -> glium::texture::RawImage2d<f32> {
541 let img = image::open(path).expect(format!("Failed to load image at path {}", path,).as_str());
542 img.flipv();
543 let path = format!("{}", path);
544 let image = image::load(
545 std::io::Cursor::new(std::fs::read(path).expect("Failed to read image")),
546 image::ImageFormat::Png,
547 )
548 .expect("Failed to load image")
549 .to_rgba32f();
550 let image_dimensions = image.dimensions();
551 let image = glium::texture::RawImage2d::from_raw_rgba_reversed(&image, image_dimensions);
552 image
553}
554
555fn setup_window() -> (
556 EventLoop<()>,
557 glium::Display<WindowSurface>,
558 winit::window::Window,
559 glium::index::NoIndices,
560) {
561 let event_loop = winit::event_loop::EventLoopBuilder::new().build();
563
564 match event_loop {
565 Ok(event_loop) => {
566 let (window, display) =
568 glium::backend::glutin::SimpleWindowBuilder::new().build(&event_loop);
569
570 let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
571
572 (event_loop, display, window, indices)
573 }
574 Err(error) => panic!("Failed to create event loop: {}", error),
575 }
576}
577
578#[derive(Copy, Clone)]
580pub struct Camera {
581 pub position: [f32; 3],
582}
583
584impl Camera {
585 pub fn new(position: [f32; 3]) -> Camera {
587 Camera { position }
588 }
589}
590
591pub struct RenderSettings {
592 shadows: bool,
593 reflections: bool,
594 render_resolution: Option<[u32; 2]>,
599 blur_reflections: bool,
600 blur_strength: f32,
601}
602
603impl Default for RenderSettings {
604 fn default() -> Self {
605 RenderSettings {
606 shadows: true,
607 reflections: true,
608 render_resolution: None,
609 blur_reflections: false,
610 blur_strength: 0.01,
611 }
612 }
613}
614
615impl RenderSettings {
616 pub fn with_shadows(mut self, shadows: bool) -> Self {
617 self.shadows = shadows;
618 self
619 }
620
621 pub fn with_reflections(mut self, reflections: bool) -> Self {
622 self.reflections = reflections;
623 self
624 }
625
626 pub fn with_render_resolution(mut self, resolution: [u32; 2]) -> Self {
627 self.render_resolution = Some(resolution);
628 self
629 }
630
631 pub fn with_blur_reflections(mut self, blur: bool) -> Self {
632 self.blur_reflections = blur;
633 self
634 }
635
636 pub fn with_blur_strength(mut self, strength: f32) -> Self {
638 self.blur_strength = strength;
639 self
640 }
641}
642
643pub fn draw_all(
645 lights: Vec<&dyn lights::LightDrawable>,
646 drawables: Vec<&dyn Drawable>,
647 program: &mut LumenpyxProgram,
648 camera: &Camera,
649) {
650 for drawable in &drawables {
652 drawable.try_load_shaders(program);
653 }
654 for light in &lights {
655 light.try_load_shaders(program);
656 }
657 load_all_textures(program);
658
659 let reflected_texture = program
679 .get_texture("reflected_texture")
680 .expect("Failed to get reflected texture");
681
682 let (
683 albedo_texture,
684 normal_texture,
685 height_texture,
686 roughness_texture,
687 shadow_strength_texture,
688 ) = draw_all_no_post(drawables, program, camera);
689
690 let lit_texture = draw_lighting(
691 lights,
692 program,
693 camera,
694 &albedo_texture,
695 &height_texture,
696 &roughness_texture,
697 &shadow_strength_texture,
698 );
699
700 let display = &program.display;
701 let debug = &program.debug;
702 let render_settings = &program.render_settings;
703 let render_resolution = render_settings
704 .render_resolution
705 .unwrap_or(program.dimensions);
706 if render_resolution < program.dimensions {
707 panic!("Render resolution must be greater than or equal to the window resolution");
708 }
709 if render_settings.reflections {
710 let roughness = glium::uniforms::Sampler(roughness_texture, DEFAULT_BEHAVIOR);
711 let height = glium::uniforms::Sampler(height_texture, DEFAULT_BEHAVIOR);
712 let normal = glium::uniforms::Sampler(normal_texture, DEFAULT_BEHAVIOR);
713 let lit_sampler = if render_settings.shadows {
714 glium::uniforms::Sampler(lit_texture, DEFAULT_BEHAVIOR)
715 } else {
716 glium::uniforms::Sampler(albedo_texture, DEFAULT_BEHAVIOR)
717 };
718
719 let mut reflected_framebuffer =
720 glium::framebuffer::SimpleFrameBuffer::new(display, reflected_texture)
721 .expect("Failed to create reflected frame buffer");
722
723 draw_reflections(
724 camera,
725 lit_sampler,
726 height,
727 roughness,
728 normal,
729 &mut reflected_framebuffer,
730 &program,
731 );
732 }
733
734 {
735 let reflected_texture = if render_settings.reflections {
736 reflected_texture
737 } else if render_settings.shadows {
738 &lit_texture
739 } else {
740 &albedo_texture
741 };
742
743 let finished_texture = match debug {
744 DebugOption::None => glium::uniforms::Sampler(reflected_texture, DEFAULT_BEHAVIOR),
745 DebugOption::Albedo => glium::uniforms::Sampler(albedo_texture, DEFAULT_BEHAVIOR),
746 DebugOption::Height => glium::uniforms::Sampler(height_texture, DEFAULT_BEHAVIOR),
747 DebugOption::Roughness => glium::uniforms::Sampler(roughness_texture, DEFAULT_BEHAVIOR),
748 DebugOption::Normal => glium::uniforms::Sampler(normal_texture, DEFAULT_BEHAVIOR),
749 DebugOption::ShadowStrength => {
750 glium::uniforms::Sampler(shadow_strength_texture, DEFAULT_BEHAVIOR)
751 }
752 };
753
754 draw_upscale(finished_texture, &program, program.dimensions);
755 }
756}
757
758fn load_all_textures(program: &mut LumenpyxProgram) {
759 let display = &program.display;
760 let render_settings = &program.render_settings;
761 let render_resolution = render_settings
762 .render_resolution
763 .unwrap_or(program.dimensions);
764 if render_resolution < program.dimensions {
765 panic!("Render resolution must be greater than or equal to the window resolution");
766 }
767
768 let albedo_texture = program.cache.get_texture("albedo_texture");
769 if albedo_texture.is_none() {
770 let albedo_texture_owned = glium::texture::Texture2d::empty_with_format(
771 display,
772 glium::texture::UncompressedFloatFormat::U8U8U8U8,
773 glium::texture::MipmapsOption::NoMipmap,
774 render_resolution[0],
775 render_resolution[1],
776 )
777 .expect("Failed to create albedo texture");
778
779 program
780 .cache
781 .insert("albedo_texture".to_string(), albedo_texture_owned);
782 }
783
784 let height_texture = program.cache.get_texture("height_texture");
785 if height_texture.is_none() {
786 let height_texture_owned = glium::texture::Texture2d::empty_with_format(
787 display,
788 glium::texture::UncompressedFloatFormat::U8U8U8U8,
789 glium::texture::MipmapsOption::NoMipmap,
790 render_resolution[0],
791 render_resolution[1],
792 )
793 .expect("Failed to create height texture");
794
795 program
796 .cache
797 .insert("height_texture".to_string(), height_texture_owned);
798 }
799
800 let normal_texture = program.cache.get_texture("normal_texture");
801 if normal_texture.is_none() {
802 let normal_texture_owned = glium::texture::Texture2d::empty_with_format(
803 display,
804 glium::texture::UncompressedFloatFormat::U8U8U8U8,
805 glium::texture::MipmapsOption::NoMipmap,
806 render_resolution[0],
807 render_resolution[1],
808 )
809 .expect("Failed to create normal texture");
810
811 program
812 .cache
813 .insert("normal_texture".to_string(), normal_texture_owned);
814 }
815
816 let roughness_texture = program.cache.get_texture("roughness_texture");
817 if roughness_texture.is_none() {
818 let roughness_texture_owned = glium::texture::Texture2d::empty_with_format(
819 display,
820 glium::texture::UncompressedFloatFormat::U8U8U8U8,
821 glium::texture::MipmapsOption::NoMipmap,
822 render_resolution[0],
823 render_resolution[1],
824 )
825 .expect("Failed to create roughness texture");
826
827 program
828 .cache
829 .insert("roughness_texture".to_string(), roughness_texture_owned);
830 }
831
832 let shadow_strength_texture = program.cache.get_texture("shadow_strength_texture");
833 if shadow_strength_texture.is_none() {
834 let shadow_strength_texture_owned = glium::texture::Texture2d::empty_with_format(
835 display,
836 glium::texture::UncompressedFloatFormat::U8U8U8U8,
837 glium::texture::MipmapsOption::NoMipmap,
838 render_resolution[0],
839 render_resolution[1],
840 )
841 .expect("Failed to create shadow strength texture");
842
843 program.cache.insert(
844 "shadow_strength_texture".to_string(),
845 shadow_strength_texture_owned,
846 );
847 }
848
849 let last_drawable_texture = program.cache.get_texture("last_drawable_texture");
850 if last_drawable_texture.is_none() {
851 let last_drawable_texture_owned = glium::texture::Texture2d::empty_with_format(
852 display,
853 glium::texture::UncompressedFloatFormat::U8U8U8U8,
854 glium::texture::MipmapsOption::NoMipmap,
855 render_resolution[0],
856 render_resolution[1],
857 )
858 .expect("Failed to create last drawable texture");
859
860 program.cache.insert(
861 "last_drawable_texture".to_string(),
862 last_drawable_texture_owned,
863 );
864 }
865
866 let reflected_texture = program.get_texture("reflected_texture");
867 if reflected_texture.is_none() {
868 let reflected_texture_owned = glium::texture::Texture2d::empty_with_format(
869 display,
870 glium::texture::UncompressedFloatFormat::U8U8U8U8,
871 glium::texture::MipmapsOption::NoMipmap,
872 render_resolution[0],
873 render_resolution[1],
874 )
875 .expect("Failed to create reflected frame buffer");
876
877 program
878 .cache
879 .insert("reflected_texture".to_string(), reflected_texture_owned);
880 }
881
882 let reflection_texture = program.get_texture("reflection_texture");
883 if reflection_texture.is_none() {
884 let reflection_texture_owned = glium::texture::Texture2d::empty_with_format(
885 display,
886 glium::texture::UncompressedFloatFormat::U8U8U8U8,
887 glium::texture::MipmapsOption::NoMipmap,
888 render_resolution[0],
889 render_resolution[1],
890 )
891 .expect("Failed to create reflection frame buffer");
892
893 program
894 .cache
895 .insert("reflection_texture".to_string(), reflection_texture_owned);
896 }
897
898 let lit_texture = program.get_texture("lit_texture");
899 if lit_texture.is_none() {
900 let lit_texture_owned = glium::texture::Texture2d::empty_with_format(
901 display,
902 glium::texture::UncompressedFloatFormat::U8U8U8U8,
903 glium::texture::MipmapsOption::NoMipmap,
904 render_resolution[0],
905 render_resolution[1],
906 )
907 .expect("Failed to create lit frame buffer");
908
909 program
910 .cache
911 .insert("lit_texture".to_string(), lit_texture_owned);
912 }
913}
914
915fn draw_all_no_post<'a>(
916 drawables: Vec<&dyn Drawable>,
917 program: &'a LumenpyxProgram,
918 camera: &Camera,
919) -> (
920 &'a glium::Texture2d,
921 &'a glium::Texture2d,
922 &'a glium::Texture2d,
923 &'a glium::Texture2d,
924 &'a glium::Texture2d,
925) {
926 let display = &program.display;
927 let render_settings = &program.render_settings;
928 let render_resolution = render_settings
929 .render_resolution
930 .unwrap_or(program.dimensions);
931 if render_resolution < program.dimensions {
932 panic!("Render resolution must be greater than or equal to the window resolution");
933 }
934
935 let albedo_texture = program
936 .get_texture("albedo_texture")
937 .expect("Failed to get albedo texture");
938
939 let height_texture = program
940 .cache
941 .get_texture("height_texture")
942 .expect("Failed to get height texture");
943
944 let normal_texture = program
945 .cache
946 .get_texture("normal_texture")
947 .expect("Failed to get normal texture");
948
949 let roughness_texture = program
950 .cache
951 .get_texture("roughness_texture")
952 .expect("Failed to get roughness texture");
953
954 let shadow_strength_texture = program
955 .cache
956 .get_texture("shadow_strength_texture")
957 .expect("Failed to get shadow strength texture");
958
959 {
960 let last_drawable_texture = program
961 .cache
962 .get_texture("last_drawable_texture")
963 .expect("Failed to get last drawable texture");
964
965 let mut last_drawable_framebuffer =
966 glium::framebuffer::SimpleFrameBuffer::new(display, last_drawable_texture)
967 .expect("Failed to create last drawable framebuffer");
968
969 last_drawable_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
970
971 let last_drawable_sampler =
972 glium::uniforms::Sampler(last_drawable_texture, DEFAULT_BEHAVIOR);
973
974 let this_drawable_sampler = glium::uniforms::Sampler(albedo_texture, DEFAULT_BEHAVIOR);
975
976 let mut albedo_framebuffer =
977 glium::framebuffer::SimpleFrameBuffer::new(display, albedo_texture)
978 .expect("Failed to create albedo framebuffer");
979
980 albedo_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
981
982 let mut height_framebuffer =
983 glium::framebuffer::SimpleFrameBuffer::new(display, height_texture)
984 .expect("Failed to create height framebuffer");
985
986 height_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
987
988 let mut roughness_framebuffer =
989 glium::framebuffer::SimpleFrameBuffer::new(display, roughness_texture)
990 .expect("Failed to create roughness framebuffer");
991
992 roughness_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
993
994 let mut normal_framebuffer =
995 glium::framebuffer::SimpleFrameBuffer::new(display, normal_texture)
996 .expect("Failed to create normal framebuffer");
997
998 normal_framebuffer.clear_color(0.0, 0.0, 1.0, 0.0);
999
1000 let mut shadow_strength_framebuffer =
1001 glium::framebuffer::SimpleFrameBuffer::new(display, shadow_strength_texture)
1002 .expect("Failed to create shadow strength framebuffer");
1003
1004 shadow_strength_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
1005
1006 for drawable in &drawables {
1007 let new_transform =
1008 program.adjust_transform_for_drawable(&drawable.get_transform(), camera);
1009
1010 drawable.draw_albedo(program, &new_transform, &mut albedo_framebuffer);
1011 if render_settings.shadows {
1012 let shadow_strength = drawable.get_recieve_shadows_strength();
1013
1014 shaders::draw_recieve_shadows(
1015 &mut shadow_strength_framebuffer,
1016 &program,
1017 shadow_strength,
1018 last_drawable_sampler,
1019 this_drawable_sampler,
1020 );
1021
1022 albedo_framebuffer.blit_whole_color_to(
1024 &last_drawable_framebuffer,
1025 &glium::BlitTarget {
1026 left: 0,
1027 bottom: 0,
1028 width: render_resolution[0] as i32,
1029 height: render_resolution[1] as i32,
1030 },
1031 glium::uniforms::MagnifySamplerFilter::Nearest,
1032 );
1033 }
1034 }
1035
1036 if render_settings.shadows || render_settings.reflections {
1037 for drawable in &drawables {
1038 let new_transform =
1039 program.adjust_transform_for_drawable(&drawable.get_transform(), camera);
1040
1041 drawable.draw_height(program, &new_transform, &mut height_framebuffer);
1042 }
1043 }
1044
1045 if program.render_settings.reflections {
1046 for drawable in &drawables {
1047 let new_transform =
1048 program.adjust_transform_for_drawable(&drawable.get_transform(), camera);
1049
1050 drawable.draw_roughness(program, &new_transform, &mut roughness_framebuffer);
1051 }
1052 }
1053
1054 if program.render_settings.reflections {
1055 for drawable in &drawables {
1056 let new_transform =
1057 program.adjust_transform_for_drawable(&drawable.get_transform(), camera);
1058
1059 drawable.draw_normal(program, &new_transform, &mut normal_framebuffer);
1060 }
1061 }
1062 }
1063
1064 (
1065 albedo_texture,
1066 normal_texture,
1067 height_texture,
1068 roughness_texture,
1069 shadow_strength_texture,
1070 )
1071}
1072
1073fn draw_lighting<'a>(
1074 lights: Vec<&dyn lights::LightDrawable>,
1075 program: &'a LumenpyxProgram,
1076 camera: &Camera,
1077 albedo_texture: &glium::Texture2d,
1078 height_texture: &glium::Texture2d,
1079 roughness_texture: &glium::Texture2d,
1080 shadow_strength_texture: &glium::Texture2d,
1081) -> &'a glium::Texture2d {
1082 let display = &program.display;
1083 let render_settings = &program.render_settings;
1084 let render_resolution = render_settings
1085 .render_resolution
1086 .unwrap_or(program.dimensions);
1087 if render_resolution < program.dimensions {
1088 panic!("Render resolution must be greater than or equal to the window resolution");
1089 }
1090
1091 let lit_texture = program
1092 .get_texture("lit_texture")
1093 .expect("Failed to get lit texture");
1094
1095 if render_settings.shadows {
1096 let albedo = glium::uniforms::Sampler(albedo_texture, DEFAULT_BEHAVIOR);
1097 let height_sampler = glium::uniforms::Sampler(height_texture, DEFAULT_BEHAVIOR);
1098 let roughness_sampler = glium::uniforms::Sampler(roughness_texture, DEFAULT_BEHAVIOR);
1099 let shadow_strength_sampler =
1100 glium::uniforms::Sampler(shadow_strength_texture, DEFAULT_BEHAVIOR);
1101
1102 let mut lit_framebuffer = glium::framebuffer::SimpleFrameBuffer::new(display, lit_texture)
1103 .expect("Failed to create lit frame buffer");
1104 lit_framebuffer.clear_color(0.0, 0.0, 0.0, 0.0);
1105
1106 for light in lights {
1107 let new_transform =
1108 program.adjust_transform_for_drawable(&light.get_transform(), camera);
1109
1110 light.draw(
1111 program,
1112 new_transform.get_matrix(),
1113 &mut lit_framebuffer,
1114 height_sampler,
1115 albedo,
1116 roughness_sampler,
1117 shadow_strength_sampler,
1118 );
1119 }
1120 }
1121
1122 lit_texture
1123}