Skip to main content

enigma_3d/
lib.rs

1use std::any::Any;
2use std::collections::HashMap;
3use std::sync::{Arc, Mutex};
4use std::time::{Duration, Instant};
5use egui_glium::EguiGlium;
6use winit::window::Window;
7use glium::glutin::surface::WindowSurface;
8use glium::{Display, Surface, Texture2d, uniform};
9use glium::uniforms::UniformBuffer;
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12use winit::event::{Event, WindowEvent};
13use winit::event_loop::{ControlFlow};
14use crate::audio::{AudioClip, AudioEngine};
15use crate::shadow::ShadowMaps;
16use crate::shadow::{directional_light_space_matrix, view_matrix, perspective_90_matrix, mat4_mul, CUBE_FACE_DIRS, face_viewport};
17use crate::camera::{Camera, CameraSerializer};
18use crate::collision_world::MouseState;
19use crate::data::AppStateData;
20use crate::event::EventModifiers;
21use crate::geometry::BoneTransforms;
22use crate::light::{Light, LightEmissionType};
23use crate::logging::{EnigmaError, EnigmaMessage, EnigmaWarning};
24use crate::material::Material;
25use crate::object::{Object, ObjectInstance};
26use crate::postprocessing::PostProcessingEffect;
27use crate::texture::Texture;
28
29pub mod shader;
30pub mod geometry;
31pub mod debug_geo;
32pub mod texture;
33pub mod material;
34pub mod object;
35pub mod light;
36pub mod camera;
37pub mod event;
38pub mod collision_world;
39pub mod default_events;
40pub mod postprocessing;
41pub mod ui;
42pub mod resources;
43pub mod data;
44pub mod example_resources;
45pub mod animation;
46pub mod logging;
47pub mod audio;
48pub mod shadow;
49
50pub fn init_default(app_state: &mut AppState) {
51    app_state.set_renderscale(1);
52    app_state.set_fps(60);
53    app_state.set_max_buffers(3);
54
55    if app_state.get_camera().is_none(){
56        app_state.set_camera(Camera::default());
57    }
58
59    app_state.inject_event(
60        event::EventCharacteristic::MousePress(event::MouseButton::Left),
61        Arc::new(default_events::select_object),
62        None,
63    );
64    app_state.inject_event(
65        event::EventCharacteristic::MousePress(event::MouseButton::Right),
66        Arc::new(default_events::select_object_add),
67        None,
68    );
69
70    //event functions for moving the camera
71    // adding the camera move and rotation speed as a state data entry. this allows us to retrieve it in all camera related functions while having
72    // a unique place to control it. See, that we need to pass the value in with explicit type declaration, this is so enigma can properly use it
73    app_state.add_state_data("camera_move_speed", Box::new(10.0f32));
74    app_state.add_state_data("camera_rotate_speed", Box::new(2.0f32));
75
76    app_state.inject_event(
77        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::W),
78        Arc::new(default_events::camera_fly_forward),
79        Some(EventModifiers::new(false, false, false)),
80    );
81    app_state.inject_event(
82        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::A),
83        Arc::new(default_events::camera_fly_left),
84        Some(EventModifiers::new(false, false, false)),
85    );
86    app_state.inject_event(
87        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::S),
88        Arc::new(default_events::camera_fly_backward),
89        Some(EventModifiers::new(false, false, false)),
90    );
91    app_state.inject_event(
92        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::D),
93        Arc::new(default_events::camera_fly_right),
94        Some(EventModifiers::new(false, false, false)),
95    );
96    app_state.inject_event(
97        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::Space),
98        Arc::new(default_events::camera_up),
99        Some(EventModifiers::new(false, false, false)),
100    );
101    app_state.inject_event(
102        event::EventCharacteristic::KeyPress(event::VirtualKeyCode::Space),
103        Arc::new(default_events::camera_down),
104        Some(EventModifiers::new(true, false, false)),
105    );
106    app_state.inject_event(
107        event::EventCharacteristic::MouseDown(event::MouseButton::Right),
108        Arc::new(default_events::camera_rotate),
109        Some(EventModifiers::new(true, false, false)),
110    );
111}
112
113#[derive(Serialize, Deserialize)]
114pub struct AppStateSerializer {
115    pub camera: Option<CameraSerializer>,
116    pub light: Vec<light::LightSerializer>,
117    pub ambient_light: Option<light::LightSerializer>,
118    pub skybox: Option<object::ObjectSerializer>,
119    pub materials: Vec<material::MaterialSerializer>,
120    pub skybox_texture: Option<texture::TextureSerializer>,
121    pub objects: Vec<object::ObjectSerializer>,
122    pub object_selection: Vec<String>,
123}
124
125pub struct AppState {
126    pub fps: u64,
127    pub camera: Option<camera::Camera>,
128    pub light: Vec<light::Light>,
129    pub ambient_light: Option<light::Light>,
130    pub skybox: Option<object::Object>,
131    pub skybox_texture: Option<texture::Texture>,
132    pub objects: Vec<object::Object>,
133    pub materials: Vec<material::Material>,
134    pub object_selection: Vec<Uuid>,
135    pub event_injections: Vec<(event::EventCharacteristic, event::EventFunction, event::EventModifiers)>,
136    pub update_injections: Vec<event::EventFunction>,
137    pub gui_injections: Vec<ui::GUIDrawFunction>,
138    pub post_processes: Vec<Box<dyn PostProcessingEffect>>,
139    pub display: Option<glium::Display<WindowSurface>>,
140    pub time: f32,
141    pub delta_time: f32,
142    pub render_scale: u32,
143    pub max_buffers: usize,
144    mouse_state: MouseState,
145    last_event_time: Instant,
146    last_frame_time: Instant,
147    is_mouse_down: bool,
148    pub state_data: Vec<AppStateData>,
149    audio_engine: AudioEngine,
150    audio_clips:  HashMap<String, AudioClip>,
151    pub shadow_resolution: u32,
152    pub shadow_distance: f32,
153}
154
155pub struct EventLoop {
156    pub event_loop: winit::event_loop::EventLoop<()>,
157    pub window: Window,
158    pub display: Display<WindowSurface>,
159    pub modifiers: EventModifiers,
160    gui_renderer: Option<EguiGlium>,
161}
162
163impl AppState {
164    pub fn new() -> Self {
165        AppState {
166            fps: 60,
167            camera: None,
168            skybox: None,
169            skybox_texture: None,
170            objects: Vec::new(),
171            materials: Vec::new(),
172            object_selection: Vec::new(),
173            light: Vec::new(),
174            ambient_light: None,
175            event_injections: Vec::new(),
176            update_injections: Vec::new(),
177            post_processes: Vec::new(),
178            display: None,
179            time: 0.0,
180            delta_time: 0.0,
181            render_scale: 1,
182            max_buffers: 3,
183            mouse_state: MouseState::new(),
184            gui_injections: Vec::new(),
185            state_data: Vec::new(),
186            last_event_time: Instant::now(),
187            last_frame_time: Instant::now(),
188            is_mouse_down: false,
189            audio_engine: AudioEngine::new(),
190            audio_clips: HashMap::new(),
191            shadow_resolution: 1024,
192            shadow_distance: 50.0,
193        }
194    }
195
196    pub fn add_audio(&mut self, clip: AudioClip){
197        if self.audio_clips.contains_key(&clip.name){
198            EnigmaError::new(Some("Cannot add audio clip, since it is already added"), true).log();
199            return;
200        }
201        self.audio_clips.insert(clip.name.to_string(), clip);
202    }
203
204    pub fn play_audio_once(&mut self, name: &str){
205        self.audio_engine.play_clip_once(name, &self.audio_clips);
206    }
207
208    pub fn play_audio_loop(&mut self, name: &str) {
209        self.audio_engine.play_clip_loop(name, &self.audio_clips);
210    }
211
212    pub fn stop_audio(&mut self, name: &str) {
213        self.audio_engine.stop_clip(name);
214    }
215
216    pub fn toggle_pause_audio(&mut self, name: &str) {
217        self.audio_engine.toggle_pause_clip(name);
218    }
219
220    pub fn set_audio_volume(&mut self, name: &str, volume: f32){
221        self.audio_engine.set_clip_volume(name, volume);
222    }
223
224    fn setup_skybox_instance(&self, display: &Display<WindowSurface>, sky_box_matrix: &Option<[[f32; 4]; 4]>) -> Option<(Uuid, object::ObjectInstance)> {
225        match &self.skybox {
226            Some(skybox) => {
227                let mut instance = ObjectInstance::new(display);
228                let model_matrix = sky_box_matrix.unwrap_or_else(|| {
229                    [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
230                });
231                instance.set_vertex_buffers(skybox.get_vertex_buffers(display));
232                instance.set_index_buffers(skybox.get_index_buffers(display));
233                instance.instance_matrices.push(model_matrix);
234                let data = instance.instance_matrices
235                    .iter()
236                    .map(|i| geometry::InstanceAttribute {
237                        model_matrix: *i,
238                    })
239                    .collect::<Vec<_>>();
240                instance.instance_attributes = glium::vertex::VertexBuffer::dynamic(display, &data).unwrap();
241                Some((skybox.get_unique_id(), instance))
242            }
243            None => None
244        }
245    }
246
247    fn setup_instances(&mut self, display: &Display<WindowSurface>, model_matrices: &HashMap<Uuid, [[f32; 4]; 4]>) -> HashMap<Uuid, object::ObjectInstance> {
248        let mut instances = HashMap::new();
249        // sort objects for transparent rendering
250        self.objects.sort_by(|a, b| {
251            let distance_a = (self.camera.expect("failed to retrieve camera").transform.get_position() - a.transform.get_position()).len();
252            let distance_b = (self.camera.expect("failed to retrieve camera").transform.get_position() - b.transform.get_position()).len();
253            distance_b.partial_cmp(&distance_a).unwrap()
254        });
255
256        // iterating over the objects, making instances
257        for object in self.objects.iter() {
258            let instance_id = object.get_instance_id();
259            let model_matrix = model_matrices.get(&object.get_unique_id()).unwrap_or_else(|| {
260                &[[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
261            });
262            if !instances.contains_key(&instance_id) {
263                let mut object_instance = ObjectInstance::new(display);
264                object_instance.set_vertex_buffers(object.get_vertex_buffers(display));
265                object_instance.set_index_buffers(object.get_index_buffers(display));
266                instances.insert(instance_id, object_instance);
267            }
268            instances.get_mut(&instance_id).expect("No instance of this uuid found. which is weird, because we just added it above").add_instance(*model_matrix);
269
270
271            //updating instance attributes
272            match instances.get_mut(&instance_id) {
273                Some(instance) => {
274                    let data = instance.instance_matrices
275                        .iter()
276                        .map(|i| geometry::InstanceAttribute {
277                            model_matrix: *i,
278                        })
279                        .collect::<Vec<_>>();
280                    instance.instance_attributes = glium::vertex::VertexBuffer::dynamic(display, &data).unwrap();
281                }
282                None => panic!("Something went wrong, when adding the instance")
283            }
284        }
285        instances
286    }
287
288    pub fn to_serializer(&self) -> AppStateSerializer {
289        EnigmaMessage::new(Some("An AppState Serializer does not completely serialize the AppState but only scene objects like Objects, Camera, Lights. It does NOT serialize any injections like code in form of functions or GUI!"), true).log();
290        let camera = match self.camera {
291            Some(camera) => Some(camera.to_serializer()),
292            None => None,
293        };
294        let light = self.light.iter().map(|l| l.to_serializer()).collect();
295        let ambient_light = match &self.ambient_light {
296            Some(light) => Some(light.to_serializer()),
297            None => None,
298        };
299        let skybox = match &self.skybox {
300            Some(skybox) => Some(skybox.to_serializer()),
301            None => None,
302        };
303        let skybox_texture = match &self.skybox_texture {
304            Some(texture) => Some(texture.to_serializer()),
305            None => None,
306        };
307        let objects = self.objects.iter().map(|o| o.to_serializer()).collect();
308        let materials = self.materials.iter().map(|o| o.to_serializer()).collect();
309        let object_selection = self.object_selection.iter().map(|o| o.to_string()).collect();
310        AppStateSerializer {
311            camera,
312            light,
313            ambient_light,
314            skybox,
315            skybox_texture,
316            objects,
317            materials,
318            object_selection,
319        }
320    }
321
322    pub fn inject_serializer(&mut self, serializer: AppStateSerializer, display: Display<WindowSurface>, additive: bool) {
323        self.camera = match serializer.camera {
324            Some(camera) => Some(Camera::from_serializer(camera)),
325            None => None,
326        };
327        match serializer.ambient_light {
328            Some(light) => {
329                self.add_light(Light::from_serializer(light), LightEmissionType::Ambient);
330            }
331            None => {}
332        };
333        self.skybox = match serializer.skybox {
334            Some(skybox) => Some(Object::from_serializer(skybox)),
335            None => None,
336        };
337        self.skybox_texture = match serializer.skybox_texture {
338            Some(texture) => Some(Texture::from_serializer(texture, &display)),
339            None => None,
340        };
341
342        if !additive {
343            self.light.clear();
344            self.objects.clear();
345            self.object_selection.clear();
346        }
347        for l in serializer.light {
348            self.add_light(Light::from_serializer(l), LightEmissionType::Source);
349        }
350        for o in serializer.objects {
351            self.add_object(Object::from_serializer(o));
352        }
353        for m in serializer.materials {
354            self.add_material(Material::from_serializer(m, &display));
355        }
356        for o in serializer.object_selection {
357            self.object_selection.push(Uuid::parse_str(&o).unwrap());
358        }
359    }
360
361    pub fn add_state_data(&mut self, name: &str, data: Box<dyn Any>) {
362        self.state_data.push(AppStateData::new(name, data));
363    }
364
365    pub fn add_material(&mut self, material: Material) {
366        self.materials.push(material);
367    }
368
369    pub fn get_material(&self, uuid: &Uuid) -> Option<&Material> {
370        for material in &self.materials {
371            if &material.uuid == uuid {
372                return Some(&material);
373            }
374        }
375        None
376    }
377
378    pub fn get_material_by_name(&self, name: &str) -> Option<&Material> {
379        for material in &self.materials {
380            if &material.name == name {
381                return Some(&material);
382            }
383        }
384        None
385    }
386
387    pub fn get_state_data_value<T: 'static>(&self, name: &str) -> Option<&T> {
388        for data in self.state_data.iter() {
389            if data.get_name() == name {
390                // Attempt to downcast to the requested type T
391                if let Some(value) = data.get_value().downcast_ref::<T>() {
392                    return Some(value);
393                }
394            }
395        }
396        None
397    }
398
399    pub fn get_state_data_value_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
400        for data in self.state_data.iter_mut() {
401            if data.get_name() == name {
402                // Attempt to downcast to the requested type T
403                if let Some(value) = data.get_value_mut().downcast_mut::<T>() {
404                    return Some(value);
405                }
406            }
407        }
408        None
409    }
410
411    pub fn set_state_data_value(&mut self, name: &str, value: Box<dyn Any>) {
412        for data in &mut self.state_data {
413            if data.get_name() == name {
414                data.set_value(value);
415                return;
416            }
417        }
418        // If no existing data is found with the name, add as new state data
419        self.add_state_data(name, value);
420    }
421
422    pub fn inject_gui(&mut self, function: ui::GUIDrawFunction) {
423        self.gui_injections.push(function);
424    }
425
426    pub fn add_post_process(&mut self, post_process: Box<dyn PostProcessingEffect>) {
427        self.post_processes.push(post_process);
428    }
429
430    pub fn get_post_processes(&self) -> &Vec<Box<dyn PostProcessingEffect>> {
431        &self.post_processes
432    }
433
434    pub fn get_post_processes_mut(&mut self) -> &mut Vec<Box<dyn PostProcessingEffect>> {
435        &mut self.post_processes
436    }
437
438    pub fn get_mouse_state(&self) -> &MouseState {
439        &self.mouse_state
440    }
441
442    pub fn get_mouse_state_mut(&mut self) -> &mut MouseState {
443        &mut self.mouse_state
444    }
445
446    pub fn convert_to_arc_mutex(self) -> Arc<Mutex<Self>> {
447        Arc::new(Mutex::new(self))
448    }
449
450    pub fn add_object(&mut self, object: object::Object) {
451        self.objects.push(object);
452    }
453
454    pub fn get_objects(&self) -> &Vec<object::Object> {
455        &self.objects
456    }
457
458    pub fn get_object(&self, name: &str) -> Option<&object::Object> {
459        for object in self.objects.iter() {
460            if object.name == name {
461                return Some(object);
462            }
463        }
464        None
465    }
466
467    pub fn get_object_mut(&mut self, name: &str) -> Option<&mut object::Object> {
468        for object in self.objects.iter_mut() {
469            if object.name == name {
470                return Some(object);
471            }
472        }
473        None
474    }
475
476    pub fn get_object_by_uuid(&self, uuid: &Uuid) -> Option<&object::Object> {
477        for object in self.objects.iter() {
478            if &object.get_unique_id() == uuid {
479                return Some(object);
480            }
481        }
482        None
483    }
484
485    pub fn get_object_by_uuid_mut(&mut self, uuid: Uuid) -> Option<&mut object::Object> {
486        for object in self.objects.iter_mut() {
487            if object.get_unique_id() == uuid {
488                return Some(object);
489            }
490        }
491        None
492    }
493
494    pub fn get_selected_objects_mut(&mut self) -> Vec<&mut object::Object> {
495        let mut selected = Vec::new();
496        for object in self.objects.iter_mut() {
497            if self.object_selection.contains(&object.get_unique_id()) {
498                selected.push(object);
499            }
500        }
501        selected
502    }
503
504    pub fn add_light(&mut self, light: light::Light, light_type: LightEmissionType) {
505        match light_type {
506            LightEmissionType::Source => self.light.push(light),
507            LightEmissionType::Ambient => self.ambient_light = Some(light),
508        }
509    }
510
511    pub fn remove_light(&mut self, index: usize, light_type: LightEmissionType) {
512        match light_type {
513            LightEmissionType::Source => {
514                if index >= self.light.len() {
515                    panic!("Index out of bounds");
516                }
517                self.light.remove(index);
518            }
519            LightEmissionType::Ambient => {
520                self.ambient_light = None;
521            }
522        };
523    }
524
525    pub fn get_lights(&self) -> &Vec<light::Light> {
526        &self.light
527    }
528
529    pub fn set_fps(&mut self, fps: u64) {
530        self.fps = fps;
531    }
532
533    pub fn get_fps(&self) -> u64 {
534        self.fps
535    }
536
537    pub fn get_objects_mut(&mut self) -> &mut Vec<object::Object> {
538        &mut self.objects
539    }
540
541    pub fn set_camera(&mut self, camera: camera::Camera) {
542        self.camera = Some(camera);
543    }
544
545    pub fn get_camera(&self) -> &Option<camera::Camera> {
546        &self.camera
547    }
548
549    pub fn get_camera_mut(&mut self) -> &mut Option<camera::Camera> {
550        &mut self.camera
551    }
552
553    pub fn set_renderscale(&mut self, scale: u32) {
554        self.render_scale = scale;
555    }
556
557    pub fn get_renderscale(&self) -> u32 {
558        self.render_scale
559    }
560
561    pub fn set_max_buffers(&mut self, max_buffers: usize) {
562        self.max_buffers = max_buffers;
563    }
564
565    pub fn get_max_buffers(&self) -> usize {
566        self.max_buffers
567    }
568
569    pub fn inject_event(&mut self, characteristic: event::EventCharacteristic, function: event::EventFunction, modifiers: Option<event::EventModifiers>) {
570        match modifiers {
571            Some(modifiers) => self.event_injections.push((characteristic, function, modifiers)),
572            None => self.event_injections.push((characteristic, function, event::EventModifiers::default())),
573        }
574    }
575    pub fn inject_update_function(&mut self, function: event::EventFunction) {
576        self.update_injections.push(function);
577    }
578
579    pub fn set_skybox(&mut self, skybox: object::Object) {
580        self.skybox = Some(skybox);
581    }
582
583    pub fn set_skybox_from_texture(&mut self, texture: Texture, event_loop: &EventLoop){
584        let mut material = crate::material::Material::unlit(event_loop.get_display_clone(), false);
585        material.set_name("INTERNAL::SkyBox");
586
587        material.set_texture(texture, crate::material::TextureType::Albedo);
588        // create a default object
589        let mut object = Object::load_from_gltf_resource(resources::skybox(), None);
590        // set the material
591        object.add_material(material.uuid);
592        object.get_shapes_mut()[0].set_material_from_object_list(0);
593        object.name = "Skybox".to_string();
594        object.transform.set_scale([1.0, 1.0, 1.0]);
595        self.add_material(material);
596        self.set_skybox(object);
597    }
598
599    pub fn get_skybox(&self) -> &Option<object::Object> {
600        &self.skybox
601    }
602
603    pub fn get_skybox_mut(&mut self) -> &mut Option<object::Object> {
604        &mut self.skybox
605    }
606
607    pub fn set_shadow_resolution(&mut self, resolution: crate::light::ShadowResolution) {
608        self.shadow_resolution = resolution.value();
609    }
610
611    pub fn get_shadow_resolution(&self) -> u32 {
612        self.shadow_resolution
613    }
614
615    pub fn set_shadow_distance(&mut self, distance: f32) {
616        self.shadow_distance = distance;
617    }
618
619    pub fn get_shadow_distance(&self) -> f32 {
620        self.shadow_distance
621    }
622}
623
624impl EventLoop {
625    pub fn new(title: &str, width: u32, height: u32) -> Self {
626        let event_loop = winit::event_loop::EventLoopBuilder::new().build();
627        let (window, display) = glium::backend::glutin::SimpleWindowBuilder::new()
628            .with_title(title)
629            .with_inner_size(width, height)
630            .build(&event_loop);
631        EventLoop {
632            event_loop,
633            window,
634            display,
635            modifiers: EventModifiers::default(),
636            gui_renderer: None,
637        }
638    }
639    pub fn get_display_clone(&self) -> Display<WindowSurface> {
640        self.display.clone()
641    }
642
643    pub fn get_display_reference(&self) -> &Display<WindowSurface> {
644        &self.display
645    }
646
647    pub fn spawn_skybox(&mut self, app_state: &mut AppState) -> (crate::object::Object, texture::Texture) {
648        if let Some(current_skybox_object) = app_state.get_skybox().clone() {
649            // If we have an existing skybox, try to get its texture
650            if let Some(texture_uuid) = current_skybox_object.get_materials().first() {
651                if let Some(material) = app_state.get_material(texture_uuid) {
652                    if let Some(texture) = &material.albedo {
653                        // Successfully found texture, clone it and return with the existing object
654                        return (
655                            current_skybox_object,
656                            texture.get_texture_clone(self.get_display_reference())
657                        );
658                    }
659                }
660            }
661
662            // If we reached here, we couldn't get the texture from the existing skybox
663            let mut logger = EnigmaWarning::new(None, true);
664            logger.extent("Failed to get texture from existing skybox. Creating default skybox...");
665            logger.log();
666        }
667
668        let mut material = crate::material::Material::unlit(self.display.clone(), false);
669        material.set_name("INTERNAL::SkyBox");
670
671        material.set_texture_from_resource(resources::skybox_texture(), crate::material::TextureType::Albedo);
672
673        // create a default object
674        let mut object = Object::load_from_gltf_resource(resources::skybox(), None);
675
676        // set the material
677        object.add_material(material.uuid);
678        object.get_shapes_mut()[0].set_material_from_object_list(0);
679
680        object.name = "Skybox".to_string();
681
682        object.transform.set_scale([1.0, 1.0, 1.0]);
683
684        app_state.add_material(material);
685        // skybox texture
686        let skybox_texture = texture::Texture::from_resource(&self.display, resources::skybox_texture());
687        (object, skybox_texture)
688    }
689
690    pub fn set_icon_from_path(&self, path: &str) {
691        let image = image::open(path).expect("failed to load icon").to_rgba8();
692        let image_dimensions = image.dimensions();
693        let data = image.into_raw();
694        let icon = winit::window::Icon::from_rgba(data, image_dimensions.0, image_dimensions.1).expect("failed to load icon");
695        self.window.set_window_icon(Some(icon));
696    }
697
698    pub fn set_icon_from_resource(&self, data: &[u8]) {
699        let image = image::load_from_memory(data).expect("failed to load icon").to_rgba8();
700        let image_dimensions = image.dimensions();
701        let data = image.into_raw();
702        let icon = winit::window::Icon::from_rgba(data, image_dimensions.0, image_dimensions.1).expect("failed to load icon");
703        self.window.set_window_icon(Some(icon));
704    }
705
706    // This is just the render loop . an actual event loop still needs to be set up
707    pub fn run(mut self, app_state: Arc<Mutex<AppState>>) {
708        let mut temp_app_state = app_state.lock().unwrap();
709        temp_app_state.display = Some(self.display.clone());
710
711        //spawning skybox
712        let (skybox, skybox_texture) = self.spawn_skybox(&mut temp_app_state);
713        temp_app_state.set_skybox(skybox);
714
715
716        // managing fps
717        let mut next_frame_time = Instant::now();
718        let nanos = 1_000_000_000 / temp_app_state.fps;
719        let frame_duration = Duration::from_nanos(nanos); // 60 FPS (1,000,000,000 ns / 60)
720
721        let mut texture = Texture2d::empty(&self.display, self.window.inner_size().width * temp_app_state.render_scale, self.window.inner_size().height * temp_app_state.render_scale).expect("Failed to create texture");
722        let mut depth_texture = glium::texture::DepthTexture2d::empty(&self.display, self.window.inner_size().width * temp_app_state.render_scale, self.window.inner_size().height * temp_app_state.render_scale).expect("Failed to create depth texture");
723
724        let mut buffer_textures: Vec<Texture2d> = Vec::new();
725        for _ in 0..temp_app_state.max_buffers {
726            buffer_textures.push(Texture2d::empty(&self.display, self.window.inner_size().width * temp_app_state.render_scale, self.window.inner_size().height * temp_app_state.render_scale).expect("Failed to create texture"));
727        }
728
729        let mut shadow_maps = ShadowMaps::new(&self.display, temp_app_state.shadow_resolution);
730
731        let shadow_dir_program = glium::Program::from_source(
732            &self.display,
733            resources::shadow_depth_vert_shader(),
734            resources::shadow_depth_dir_frag_shader(),
735            None,
736        ).expect("Failed to compile directional shadow shader");
737
738        let shadow_point_program = glium::Program::from_source(
739            &self.display,
740            resources::shadow_depth_vert_shader(),
741            resources::shadow_depth_point_frag_shader(),
742            None,
743        ).expect("Failed to compile point shadow shader");
744
745        //dropping modified appstate
746        drop(temp_app_state);
747
748        // prepare post processing
749        let screen_vert_rect = postprocessing::get_screen_vert_rect(&self.display);
750        let screen_indices_rect = postprocessing::get_screen_indices_rect(&self.display);
751        let screen_program = postprocessing::get_screen_program(&self.display);
752
753        //initializing GUI
754        match self.gui_renderer {
755            Some(_) => {}
756            None => {
757                let egui_glium = EguiGlium::new(&self.display, &self.window, &self.event_loop);
758                self.gui_renderer = Some(egui_glium);
759            }
760        }
761        // run loop
762        self.event_loop.run(move |event, _window_target, control_flow| {
763            // unpacking appstate
764            let mut app_state = app_state.lock().unwrap();
765            let light = app_state.light.clone();
766            let ambient_light = app_state.ambient_light.clone();
767            let camera = app_state.camera.clone();
768            let event_injections = app_state.event_injections.clone();
769            let update_injections = app_state.update_injections.clone();
770            let gui_injections = app_state.gui_injections.clone();
771
772            *control_flow = ControlFlow::WaitUntil(next_frame_time);
773            next_frame_time = Instant::now() + frame_duration;
774
775            // passing framebuffer
776            let texture = &mut texture;
777            let depth_texture = &mut depth_texture;
778            let buffer_textures = &mut buffer_textures;
779            let mut framebuffer = glium::framebuffer::SimpleFrameBuffer::with_depth_buffer(&self.display, &*texture, &*depth_texture).expect("Failed to create framebuffer");
780
781            // passing skybox
782            let skybox_texture = &skybox_texture;
783
784            match event {
785                Event::WindowEvent { event, .. } => match event {
786                    WindowEvent::CloseRequested => { *control_flow = ControlFlow::Exit; }
787                    WindowEvent::Resized(new_size) => {
788                        let response = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer").on_event(&event);
789                        if !response.consumed {
790                            app_state.camera.as_mut().expect("failed to retrieve camera").set_aspect(new_size.width as f32, new_size.height as f32);
791                            self.display.resize(new_size.into());
792                            if let Some(app_state_display) = app_state.display.as_mut() {
793                                app_state_display.resize(new_size.into());
794                            }
795                        }
796                    }
797                    WindowEvent::ModifiersChanged(modifiers) => {
798                        self.modifiers.ctrl = modifiers.ctrl();
799                        self.modifiers.shift = modifiers.shift();
800                        self.modifiers.alt = modifiers.alt();
801                    }
802                    WindowEvent::CursorMoved { position, .. } => {
803                        let response = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer").on_event(&event);
804                        if !response.consumed {
805                            app_state.get_mouse_state_mut().update_position((position.x, position.y));
806                        }
807                    }
808                    WindowEvent::MouseInput { state, button, .. } => {
809                        let mut response = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer").on_event(&event);
810                        if !response.consumed {
811                            for (characteristic, function, modifiers) in &event_injections {
812                                if let event::EventCharacteristic::MouseDown(mouse_button) = characteristic {
813                                    if button == *mouse_button && modifiers == &self.modifiers {
814                                        if state == winit::event::ElementState::Pressed {
815                                            app_state.is_mouse_down = true;
816                                            app_state.last_event_time = Instant::now();
817                                            function(&mut app_state);
818                                        } else {
819                                            app_state.is_mouse_down = false;
820                                        }
821                                    }
822                                } else if let event::EventCharacteristic::MousePress(_) = characteristic {
823                                    if modifiers == &self.modifiers {
824                                        function(&mut app_state);
825                                        response.consumed = true;
826                                    }
827                                }
828                            }
829                        }
830                    }
831                    WindowEvent::KeyboardInput { input, .. } => {
832                        let response = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer").on_event(&event);
833                        if !response.consumed {
834                            for (characteristic, function, modifiers) in event_injections {
835                                if let event::EventCharacteristic::KeyPress(key_code) = characteristic {
836                                    if input.state == winit::event::ElementState::Pressed && input.virtual_keycode == Some(key_code) && modifiers == self.modifiers {
837                                        function(&mut app_state);
838                                    }
839                                }
840                            };
841                        }
842                    }
843                    _ => {
844                        _ = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer").on_event(&event);
845                    }
846                }
847                Event::RedrawRequested(_) => {
848                    let current_time = Instant::now();
849                    app_state.delta_time = (current_time - app_state.last_frame_time).as_secs_f32();
850                    app_state.last_frame_time = current_time;
851                    app_state.time += app_state.delta_time;
852                    // updating materials
853                    for material in app_state.materials.iter_mut() {
854                        material.update();
855                    }
856                    // updating objects
857                    let deltatime = app_state.delta_time;
858                    for object in app_state.objects.iter_mut() {
859                        object.update(deltatime);
860                    }
861
862                    let render_target = &mut framebuffer;
863                    render_target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
864                    let model_matrices: std::collections::HashMap<Uuid, [[f32; 4]; 4]> = app_state.objects.iter_mut().map(|x| (x.get_unique_id(), x.transform.get_matrix())).collect();
865                    let bone_uniform_buffers: std::collections::HashMap<Uuid, UniformBuffer<BoneTransforms>> = app_state.objects.iter_mut().map(|x| (x.get_unique_id(), x.get_bone_transform_buffer(&self.display))).collect();
866                    let object_instances = app_state.setup_instances(&self.display, &model_matrices);
867
868                    // --- Shadow pass ---
869                    if shadow_maps.resolution != app_state.shadow_resolution {
870                        shadow_maps = ShadowMaps::new(&self.display, app_state.shadow_resolution);
871                    }
872                    shadow_maps.clear();
873
874                    let shadow_draw_params = glium::DrawParameters {
875                        depth: glium::Depth {
876                            test: glium::draw_parameters::DepthTest::IfLess,
877                            write: true,
878                            ..Default::default()
879                        },
880                        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
881                        ..Default::default()
882                    };
883
884                    let cam_pos: [f32; 3] = match camera {
885                        Some(ref c) => { let p = c.transform.get_position(); [p.x, p.y, p.z] }
886                        None => [0.0, 0.0, 0.0],
887                    };
888
889                    for (light_index, light_item) in light.iter().enumerate().take(4) {
890                        if !light_item.cast_shadow { continue; }
891
892                        if light_item.is_directional() {
893                            // --- Directional shadow map ---
894                            let half = app_state.shadow_distance;
895                            let lsm = directional_light_space_matrix(light_item.direction, cam_pos, half);
896                            shadow_maps.light_space_matrices[light_index] = lsm;
897
898                            let shadow_tex = glium::texture::Texture2d::empty_with_format(
899                                &self.display,
900                                glium::texture::UncompressedFloatFormat::F32,
901                                glium::texture::MipmapsOption::NoMipmap,
902                                shadow_maps.resolution,
903                                shadow_maps.resolution,
904                            ).expect("Failed to create directional shadow texture");
905
906                            {
907                                let mut fb = glium::framebuffer::SimpleFrameBuffer::with_depth_buffer(
908                                    &self.display,
909                                    &shadow_tex,
910                                    &shadow_maps.dir_depth_rb,
911                                ).expect("Failed to create directional shadow framebuffer");
912                                fb.clear_color_and_depth((1.0, 0.0, 0.0, 1.0), 1.0);
913
914                                for (instance_id, object_instance) in object_instances.iter() {
915                                    if let Some(object) = app_state.get_object_by_uuid(instance_id) {
916                                        if object.get_materials().is_empty() { continue; }
917                                        let has_skeleton = object.get_skeleton().is_some();
918                                        let bone_transform = bone_uniform_buffers.get(&object.get_unique_id())
919                                            .expect("Missing bone transforms in shadow pass");
920                                        for ((buffer, _mat_index), indices) in object_instance.vertex_buffers.iter()
921                                            .zip(object_instance.index_buffers.iter())
922                                        {
923                                            let uniforms = glium::uniform! {
924                                                light_space_matrix: lsm,
925                                                has_skeleton: has_skeleton,
926                                                BoneTransforms: bone_transform,
927                                            };
928                                            fb.draw(
929                                                (buffer, object_instance.instance_attributes.per_instance().unwrap()),
930                                                indices,
931                                                &shadow_dir_program,
932                                                &uniforms,
933                                                &shadow_draw_params,
934                                            ).expect("Failed to draw shadow pass");
935                                        }
936                                    }
937                                }
938                            }
939                            shadow_maps.directional_maps[light_index] = Some(shadow_tex);
940
941                        } else {
942                            // --- Point light shadow map (atlas) ---
943                            let far_plane = 100.0f32;
944                            shadow_maps.point_far_planes[light_index] = far_plane;
945                            let res = shadow_maps.resolution;
946
947                            let atlas_tex = glium::texture::Texture2d::empty_with_format(
948                                &self.display,
949                                glium::texture::UncompressedFloatFormat::F32,
950                                glium::texture::MipmapsOption::NoMipmap,
951                                res * 2,
952                                res * 3,
953                            ).expect("Failed to create point shadow atlas texture");
954
955                            // Clear entire atlas to 1.0 (no shadow) before rendering faces
956                            {
957                                let mut clear_fb = glium::framebuffer::SimpleFrameBuffer::with_depth_buffer(
958                                    &self.display,
959                                    &atlas_tex,
960                                    &shadow_maps.point_depth_rb,
961                                ).expect("Failed to create atlas clear framebuffer");
962                                clear_fb.clear_color_and_depth((1.0, 0.0, 0.0, 1.0), 1.0);
963                            }
964
965                            let near = 0.1f32;
966                            let proj = perspective_90_matrix(near, far_plane);
967                            let lp = light_item.position;
968
969                            for face in 0..6usize {
970                                let (dir, up) = CUBE_FACE_DIRS[face];
971                                let view = view_matrix(&lp, &dir, &up);
972                                let lsm = mat4_mul(proj, view);
973                                let viewport = face_viewport(face, res);
974
975                                let face_draw_params = glium::DrawParameters {
976                                    depth: glium::Depth {
977                                        test: glium::draw_parameters::DepthTest::IfLess,
978                                        write: true,
979                                        ..Default::default()
980                                    },
981                                    backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
982                                    viewport: Some(viewport),
983                                    ..Default::default()
984                                };
985
986                                let mut fb = glium::framebuffer::SimpleFrameBuffer::with_depth_buffer(
987                                    &self.display,
988                                    &atlas_tex,
989                                    &shadow_maps.point_depth_rb,
990                                ).expect("Failed to create point shadow framebuffer");
991
992                                for (instance_id, object_instance) in object_instances.iter() {
993                                    if let Some(object) = app_state.get_object_by_uuid(instance_id) {
994                                        if object.get_materials().is_empty() { continue; }
995                                        let has_skeleton = object.get_skeleton().is_some();
996                                        let bone_transform = bone_uniform_buffers.get(&object.get_unique_id())
997                                            .expect("Missing bone transforms in shadow pass");
998                                        for ((buffer, _mat_index), indices) in object_instance.vertex_buffers.iter()
999                                            .zip(object_instance.index_buffers.iter())
1000                                        {
1001                                            let uniforms = glium::uniform! {
1002                                                light_space_matrix: lsm,
1003                                                has_skeleton: has_skeleton,
1004                                                BoneTransforms: bone_transform,
1005                                                light_pos: lp,
1006                                                far_plane: far_plane,
1007                                            };
1008                                            fb.draw(
1009                                                (buffer, object_instance.instance_attributes.per_instance().unwrap()),
1010                                                indices,
1011                                                &shadow_point_program,
1012                                                &uniforms,
1013                                                &face_draw_params,
1014                                            ).expect("Failed to draw shadow pass");
1015                                        }
1016                                    }
1017                                }
1018                            }
1019                            shadow_maps.point_maps[light_index] = Some(atlas_tex);
1020                        }
1021                    }
1022                    // --- End shadow pass ---
1023
1024                    // render objects opaque
1025                    let opaque_rendering_parameter = glium::DrawParameters {
1026                        depth: glium::Depth {
1027                            test: glium::draw_parameters::DepthTest::IfLess,
1028                            write: true,
1029                            ..Default::default()
1030                        },
1031                        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
1032                        ..Default::default()
1033                    };
1034
1035                    for (instance_id, object_instance) in object_instances.iter() {
1036                        let object_option = app_state.get_object_by_uuid(&instance_id);
1037                        match object_option {
1038                            Some(object) => {
1039                                let closest_lights = object.get_closest_lights(&light);
1040                                let has_skeleton = object.get_skeleton().is_some();
1041                                let bone_transform = bone_uniform_buffers.get(&object.get_unique_id()).expect("Missing Bone Transform Uniforms for Object");
1042                                for ((buffer, mat_index), indices) in object_instance.vertex_buffers.iter().zip(object_instance.index_buffers.iter()) {
1043                                    let mat_uuid: &Uuid = &object.get_materials()[*mat_index];
1044                                    match app_state.get_material(mat_uuid) {
1045                                        Some(material) => {
1046                                            if material.render_transparent {
1047                                                continue;
1048                                            }
1049                                            let uniforms = &material.get_uniforms(&closest_lights, ambient_light, camera, &bone_transform, has_skeleton, skybox_texture, &shadow_maps);
1050                                            render_target.draw((buffer, object_instance.instance_attributes.per_instance().expect("Error, unwrapping per instance in opaque draw")), indices, &material.program, uniforms, &opaque_rendering_parameter).expect("Failed to draw object");
1051                                        }
1052                                        None => ()
1053                                    }
1054                                }
1055                            }
1056                            None => EnigmaError::new(Some(smart_format!("Error, instancing the Object Instance with the instance id {}, because no Object with that Id could be found", instance_id).as_str()), true).log()
1057                        }
1058                    }
1059
1060                    // render skybox
1061                    let skybox_rendering_parameter = glium::DrawParameters {
1062                        depth: glium::Depth {
1063                            test: glium::draw_parameters::DepthTest::IfLess,
1064                            write: false,
1065                            ..Default::default()
1066                        },
1067                        backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockwise,
1068                        ..Default::default()
1069                    };
1070
1071                    //First get the matrix outside of the closure
1072                    let skybox_model_matrix = match app_state.get_skybox_mut() {
1073                        Some(obj) => Some(obj.transform.get_matrix().clone()),
1074                        None => None
1075                    };
1076                    let skybox_instance = app_state.setup_skybox_instance(&self.display, &skybox_model_matrix);
1077
1078                    match skybox_instance {
1079                        Some((skybox_id, instance)) => {
1080                            let object_option = app_state.get_skybox();
1081                            match object_option {
1082                                Some(skybox) => {
1083                                    let closest_lights = skybox.get_closest_lights(&light);
1084                                    let skybox_bone_buffer = skybox.get_bone_transform_buffer(&self.display);
1085                                    for ((buffer, mat_index), indices) in instance.vertex_buffers.iter().zip(instance.index_buffers.iter()) {
1086                                        let mat_uuid: &Uuid = &skybox.get_materials()[*mat_index];
1087                                        match app_state.get_material(mat_uuid) {
1088                                            Some(material) => {
1089                                                let uniforms = &material.get_uniforms(&closest_lights, ambient_light, camera, &skybox_bone_buffer, false, skybox_texture, &shadow_maps);
1090                                                render_target.draw((buffer, instance.instance_attributes.per_instance().expect("Error, unwrapping per instance in skybox draw")), indices, &material.program, uniforms, &skybox_rendering_parameter).expect("Failed to draw object");
1091                                            }
1092                                            None => ()
1093                                        }
1094                                    }
1095                                }
1096                                None => EnigmaError::new(Some(smart_format!("Error, instancing the Skybox Instance with the instance id {}, because no Object with that Id could be found", skybox_id).as_str()), true).log()
1097                            }
1098                        }
1099                        None => {}
1100                    }
1101
1102                    // render objects transparent
1103                    let transparent_rendering_parameter = glium::DrawParameters {
1104                        blend: glium::Blend::alpha_blending(),
1105                        ..opaque_rendering_parameter
1106                    };
1107                    for (instance_id, object_instance) in object_instances.iter() {
1108                        let object_option = app_state.get_object_by_uuid(&instance_id);
1109                        match object_option {
1110                            Some(object) => {
1111                                let closest_lights = object.get_closest_lights(&light);
1112                                let has_skeleton = object.get_skeleton().is_some();
1113                                let bone_transform = bone_uniform_buffers.get(&object.get_unique_id()).expect("Missing Bone Transform Uniforms for Object");
1114                                for ((buffer, mat_index), indices) in object_instance.vertex_buffers.iter().zip(object_instance.index_buffers.iter()) {
1115                                    let mat_uuid: &Uuid = &object.get_materials()[*mat_index];
1116                                    match app_state.get_material(mat_uuid) {
1117                                        Some(material) => {
1118                                            if !material.render_transparent {
1119                                                continue;
1120                                            }
1121                                            let uniforms = &material.get_uniforms(&closest_lights, ambient_light, camera, &bone_transform, has_skeleton, skybox_texture, &shadow_maps);
1122                                            render_target.draw((buffer, object_instance.instance_attributes.per_instance().expect("Error, unwrapping per instance in transparent draw")), indices, &material.program, uniforms, &transparent_rendering_parameter).expect("Failed to draw object");
1123                                        }
1124                                        None => ()
1125                                    }
1126                                }
1127                            }
1128                            None => EnigmaError::new(Some(smart_format!("Error, instancing the Transparent Object Instance with the instance id {}, because no Object with that Id could be found", instance_id).as_str()), true).log()
1129                        }
1130                    }
1131
1132                    // execute post processing#
1133                    for process in app_state.get_post_processes() {
1134                        process.render(&app_state, &screen_vert_rect, &screen_indices_rect, &mut framebuffer, &texture, &depth_texture, &buffer_textures);
1135                    }
1136
1137                    // drawing to screen
1138                    let mut screen_target = self.display.draw();
1139                    let screen_uniforms = uniform! {
1140                        scene: &*texture,
1141                    };
1142                    screen_target.draw(
1143                        &screen_vert_rect,
1144                        &screen_indices_rect,
1145                        &screen_program,
1146                        &screen_uniforms,
1147                        &Default::default(),
1148                    ).expect("Failed to draw screen");
1149
1150                    // drawing GUI
1151                    let gui_renderer = self.gui_renderer.as_mut().expect("Failed to retrieve gui renderer");
1152                    gui_renderer.run(&self.window, |egui_context| {
1153                        for function in gui_injections.iter() {
1154                            function(egui_context, &mut app_state);
1155                        }
1156                    });
1157                    gui_renderer.paint(&self.display, &mut screen_target);
1158                    screen_target.finish().expect("Failed to swap buffers");
1159                }
1160                Event::MainEventsCleared => {
1161
1162                    // executing mouse down events
1163                    if app_state.is_mouse_down && app_state.last_event_time.elapsed() >= Duration::from_millis(100) {
1164                        for (characteristic, function, modifiers) in &event_injections {
1165                            if let event::EventCharacteristic::MouseDown(_) = characteristic {
1166                                if modifiers == &self.modifiers {
1167                                    function(&mut app_state);
1168                                    app_state.last_event_time = Instant::now();
1169                                }
1170                            }
1171                        }
1172                    }
1173
1174                    // executing update functions
1175                    for function in update_injections {
1176                        function(&mut app_state);
1177                    }
1178                    self.window.request_redraw();
1179                }
1180                _ => (),
1181            }
1182        });
1183    }
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188    use super::*;
1189    use crate::light::ShadowResolution;
1190
1191    #[test]
1192    fn appstate_shadow_defaults() {
1193        let s = AppState::new();
1194        assert_eq!(s.get_shadow_resolution(), 1024);
1195        assert_eq!(s.get_shadow_distance(), 50.0);
1196    }
1197
1198    #[test]
1199    fn appstate_shadow_setters() {
1200        let mut s = AppState::new();
1201        s.set_shadow_resolution(ShadowResolution::High);
1202        assert_eq!(s.get_shadow_resolution(), 2048);
1203        s.set_shadow_distance(75.0);
1204        assert_eq!(s.get_shadow_distance(), 75.0);
1205    }
1206}