gloss_renderer/
gui.rs

1use crate::{
2    components::{
3        Colors, DiffuseImg, DiffuseTex, Faces, ImgConfig, LightEmit, MeshColorType, ModelMatrix, Name, NormalImg, NormalTex, Normals, PointColorType,
4        PosLookat, Projection, Renderable, RoughnessImg, RoughnessTex, ShadowCaster, ShadowMapDirty, UVs, Verts, VisLines, VisMesh, VisNormals,
5        VisOutline, VisPoints, VisWireframe,
6    },
7    config::Config,
8    selector::Selector,
9    viewer::Runner,
10};
11
12use crate::plugin_manager::gui::window::{GuiWindowType, WindowPivot, WindowPositionType};
13
14use crate::{
15    builders,
16    forward_renderer::Renderer,
17    plugin_manager::plugins::Plugins,
18    scene::{Scene, GLOSS_FLOOR_NAME},
19};
20
21use egui::{style::TextCursorStyle, CornerRadius};
22use gloss_geometry::geom;
23use gloss_utils::string::float2string;
24use log::debug;
25
26use easy_wgpu::gpu::Gpu;
27use egui_wgpu::ScreenDescriptor;
28
29use egui::style::{HandleShape, NumericColorSpace, ScrollStyle};
30
31use gloss_utils::{
32    abi_stable_aliases::std_types::{ROption::RSome, RString, RVec},
33    memory::get_last_relevant_func_name,
34    tensor::DynamicMatrixOps,
35};
36
37use re_memory::{accounting_allocator, CallstackStatistics, MemoryUse};
38
39use log::error;
40use winit::window::Window;
41
42use crate::plugin_manager::gui::widgets::Widgets as WidgetsFFI;
43use egui::{
44    epaint,
45    epaint::AlphaFromCoverage,
46    epaint::Shadow,
47    scroll_area,
48    style::{Interaction, Selection, Spacing, WidgetVisuals, Widgets},
49    Align, Align2, Color32, FontId, Layout, RichText, ScrollArea, Slider, Stroke, Style, Ui, Vec2, Visuals,
50};
51use egui_winit::{self, EventResponse};
52use epaint::Margin;
53
54use gloss_hecs::{CommandBuffer, Entity};
55
56use std::{collections::HashMap, path::Path};
57
58use nalgebra as na;
59
60// check the integration example here: https://docs.rs/egui/latest/egui/
61// info of other people trying to do custom stuff: https://users.rust-lang.org/t/egui-is-it-possible-to-avoid-using-eframe/70470/22
62// some other example of a large codebase using egui: https://github.com/parasyte/cartunes
63// example of egui-winit and egui-wgpu: https://github.com/hasenbanck/egui_example/blob/master/src/main.rs
64// official example of egui-wgpu: https://github.com/emilk/egui/blob/master/crates/egui_demo_app/src/apps/custom3d_wgpu.rs
65
66// integration
67const SIDE_PANEL_WIDTH: f32 = 250.0;
68const SPACING_1: f32 = 10.0;
69
70/// Separate the egui ctx from the rest of the gui because borrow checker
71/// complains when we modify state mutably of the gui and also have immutable
72/// reference to `egui_ctx`. having a mutable widget the deal only with state
73/// solved this
74pub struct GuiMainWidget {
75    //contains the gui state
76    pub selected_mesh_name: String,
77    pub selected_entity: Option<Entity>,
78    pub selected_light_name: String,
79    pub selected_light_entity: Option<Entity>,
80    pub wgputex_2_eguitex: HashMap<wgpu::Texture, epaint::TextureId>,
81    pub hovered_diffuse_tex: bool,
82    pub hovered_normal_tex: bool,
83    pub hovered_roughness_tex: bool,
84    default_texture: Option<easy_wgpu::texture::Texture>,
85    //gizmo stuff
86    // gizmo_mode: GizmoMode,
87    // gizmo_orientation: GizmoOrientation,
88}
89impl Default for GuiMainWidget {
90    #[allow(clippy::derivable_impls)]
91    fn default() -> GuiMainWidget {
92        GuiMainWidget {
93            selected_mesh_name: String::new(),
94            selected_light_name: String::new(),
95            selected_entity: None,
96            selected_light_entity: None,
97            wgputex_2_eguitex: HashMap::new(),
98            hovered_diffuse_tex: false,
99            hovered_normal_tex: false,
100            hovered_roughness_tex: false,
101            default_texture: None,
102            // gizmo_mode: GizmoMode::Translate,
103            // gizmo_orientation: GizmoOrientation::Local,
104        }
105    }
106}
107impl GuiMainWidget {
108    pub fn new(gpu: &Gpu) -> Self {
109        let path_tex = concat!(env!("CARGO_MANIFEST_DIR"), "/../../data/uv_checker.png");
110        debug!("path_tex {path_tex}");
111        let default_texture = easy_wgpu::texture::Texture::create_default_texture(gpu.device(), gpu.queue());
112
113        Self {
114            default_texture: Some(default_texture),
115            ..Default::default()
116        }
117    }
118}
119
120type CbFnType = fn(&mut GuiMainWidget, ctx: &egui::Context, ui: &mut Ui, renderer: &Renderer, scene: &mut Scene);
121pub struct Gui {
122    egui_ctx: egui::Context,           //we do all the gui rendering inside this context
123    pub egui_state: egui_winit::State, //integrator with winit https://github.com/emilk/egui/blob/master/crates/egui-winit/src/lib.rs#L55
124    //similar to voiding on github
125    egui_renderer: egui_wgpu::Renderer,
126    width: u32,
127    height: u32,
128    pub hidden: bool,
129    gui_main_widget: GuiMainWidget,
130    command_buffer: CommandBuffer, //defer insertions and deletion of scene entities for whenever we apply this command buffer
131    //callbacks
132    //https://users.rust-lang.org/t/callback-with-generic/52426/5
133    //https://stackoverflow.com/questions/66832392/sending-method-as-callback-function-to-field-object-in-rust
134    //https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust
135    //https://www.reddit.com/r/rust/comments/gi2pld/callback_functions_the_right_way/
136    //https://github.com/rhaiscript/rhai/issues/178
137    //https://www.reddit.com/r/rust/comments/ymingb/what_is_the_idiomatic_approach_to_eventscallbacks/iv5pgz9/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button
138
139    //callback function that can add gui elements either inside the sidebar or outside the sidebar
140    callbacks: Vec<CbFnType>,
141    callbacks_for_selected_mesh: Vec<CbFnType>,
142}
143
144impl Gui {
145    pub fn new(window: &winit::window::Window, gpu: &Gpu, surface_format: wgpu::TextureFormat) -> Self {
146        #[allow(clippy::cast_possible_truncation)] //it's ok, we don't have very big numbers
147        let native_pixels_per_point = window.scale_factor() as f32;
148
149        let egui_renderer = egui_wgpu::Renderer::new(gpu.device(), surface_format, None, 1, false);
150
151        let egui_ctx = egui::Context::default();
152        let egui_state = egui_winit::State::new(
153            egui_ctx.clone(), //just a shallow clone since it's behind an Arc
154            egui::ViewportId::default(),
155            window,
156            Some(native_pixels_per_point),
157            None,
158            Some(2048), //TODO maybe find the concrete value, for now we leave 2048 because wasm
159        ); //state that gets all the events from the window and gather them
160           //https://github.com/emilk/egui/blob/bdc8795b0476c25faab927fc3c731f2d79f2098f/crates/eframe/src/native/epi_integration.rs#L361
161
162        //size of the gui window. Will get resized automatically
163        let width = 100;
164        let height = 100;
165
166        //Gui state based on what the user does
167        let gui_main_widget = GuiMainWidget::new(gpu);
168
169        // Mutate global style with above changes
170        #[allow(unused_mut)]
171        let mut style = style();
172
173        //on web the view only renders when there is a mouse being dragged or when
174        // there is an input so animations are choppy if you don't move the mouse.
175        // Therefore we disable them
176        cfg_if::cfg_if! {
177            if #[cfg(target_arch = "wasm32")] {
178                style.animation_time = 0.0;
179            }
180        }
181        egui_ctx.set_style(style);
182
183        let command_buffer = CommandBuffer::new();
184
185        Self {
186            egui_ctx,
187            egui_state,
188            egui_renderer,
189            width,
190            height,
191            hidden: false,
192            gui_main_widget,
193            command_buffer,
194            callbacks: Vec::new(),
195            callbacks_for_selected_mesh: Vec::new(),
196        }
197    }
198
199    pub fn resize(&mut self, width: u32, height: u32) {
200        self.width = width;
201        self.height = height;
202    }
203
204    //TODO rename to "process gui event" for coherency with the other event
205    // processing things
206    pub fn on_event(&mut self, window: &Window, event: &winit::event::WindowEvent) -> EventResponse {
207        self.egui_state.on_window_event(window, event)
208    }
209
210    /// # Panics
211    /// Will panic is the path is not valid unicode
212    pub fn on_drop(&mut self, path_buf: &Path, scene: &mut Scene) {
213        self.gui_main_widget.set_default_selected_entity(scene);
214
215        let path = path_buf.to_str().unwrap();
216        let entity = self.gui_main_widget.selected_entity.unwrap();
217        if self.gui_main_widget.hovered_diffuse_tex {
218            scene
219                .world
220                .insert_one(entity, DiffuseImg::new_from_path(path, &ImgConfig::default()))
221                .ok();
222        }
223        if self.gui_main_widget.hovered_normal_tex {
224            scene.world.insert_one(entity, NormalImg::new_from_path(path, &ImgConfig::default())).ok();
225        }
226        if self.gui_main_widget.hovered_roughness_tex {
227            scene
228                .world
229                .insert_one(entity, RoughnessImg::new_from_path(path, &ImgConfig::default()))
230                .ok();
231        }
232    }
233
234    pub fn wants_pointer_input(&self) -> bool {
235        //tryng to solve https://github.com/urholaukkarinen/egui-gizmo/issues/19
236        self.egui_ctx.wants_pointer_input()
237    }
238
239    pub fn is_hovering(&self) -> bool {
240        self.egui_ctx.is_pointer_over_area()
241    }
242
243    //inspiration from voidin renderer on github
244    //https://github.com/pudnax/voidin/blob/91e6b564008879388f3777bcb6154c656bfc533c/crates/app/src/app.rs#L643
245    #[allow(clippy::too_many_arguments)]
246    pub fn render(
247        &mut self,
248        window: &winit::window::Window,
249        gpu: &Gpu,
250        renderer: &Renderer,
251        runner: &Runner,
252        scene: &mut Scene,
253        plugins: &Plugins,
254        config: &mut Config,
255        out_view: &wgpu::TextureView,
256    ) {
257        if self.hidden {
258            return;
259        }
260        self.begin_frame();
261
262        let screen_descriptor = ScreenDescriptor {
263            size_in_pixels: [self.width, self.height],
264            pixels_per_point: self.egui_ctx.pixels_per_point(),
265        };
266
267        let full_output = self.egui_ctx.run(self.egui_state.take_egui_input(window), |ctx: &egui::Context| {
268            // ui_builder(ctx) // THIS ACTUALLY RENDERS GUI
269            self.gui_main_widget.build_gui(
270                self.width,
271                self.height,
272                ctx,
273                renderer,
274                &mut self.egui_renderer,
275                gpu,
276                scene,
277                config,
278                runner,
279                &mut self.command_buffer,
280                &mut self.callbacks,
281                &mut self.callbacks_for_selected_mesh,
282                plugins,
283            );
284        });
285        let paint_jobs = self.egui_ctx.tessellate(full_output.shapes, self.egui_ctx.pixels_per_point());
286        let textures_delta = full_output.textures_delta;
287
288        let mut encoder = gpu
289            .device()
290            .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("Gui") });
291        {
292            for (texture_id, image_delta) in &textures_delta.set {
293                self.egui_renderer.update_texture(gpu.device(), gpu.queue(), *texture_id, image_delta);
294            }
295            for texture_id in &textures_delta.free {
296                self.egui_renderer.free_texture(texture_id);
297            }
298            self.egui_renderer
299                .update_buffers(gpu.device(), gpu.queue(), &mut encoder, &paint_jobs, &screen_descriptor);
300
301            let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
302                label: Some("UI Pass"),
303                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
304                    view: out_view,
305                    resolve_target: None,
306                    ops: wgpu::Operations {
307                        load: wgpu::LoadOp::Load,
308                        store: wgpu::StoreOp::Store,
309                    },
310                })],
311                depth_stencil_attachment: None,
312                timestamp_writes: None,
313                occlusion_query_set: None,
314            });
315            self.egui_renderer
316                .render(&mut render_pass.forget_lifetime(), paint_jobs.as_slice(), &screen_descriptor);
317        }
318        gpu.queue().submit(Some(encoder.finish()));
319        self.end_frame(scene);
320    }
321
322    fn begin_frame(&self) {}
323
324    fn end_frame(&mut self, scene: &mut Scene) {
325        self.command_buffer.run_on(&mut scene.world);
326    }
327
328    pub fn add_callback(
329        &mut self,
330        f: fn(&mut GuiMainWidget, ctx: &egui::Context, ui: &mut Ui, renderer: &Renderer, scene: &mut Scene),
331        draw_in_global_panel: bool,
332    ) {
333        if draw_in_global_panel {
334            self.callbacks.push(f);
335        } else {
336            self.callbacks_for_selected_mesh.push(f);
337        }
338    }
339}
340
341impl GuiMainWidget {
342    #[allow(clippy::too_many_arguments)]
343    #[allow(clippy::too_many_lines)]
344    fn build_gui(
345        &mut self,
346        screen_width: u32,
347        screen_height: u32,
348        ctx: &egui::Context,
349        renderer: &Renderer,
350        egui_renderer: &mut egui_wgpu::Renderer,
351        gpu: &Gpu,
352        scene: &mut Scene,
353        config: &mut Config,
354        runner: &Runner,
355        command_buffer: &mut CommandBuffer,
356        callbacks: &mut [CbFnType],
357        callbacks_for_selected_mesh: &mut [CbFnType],
358        plugins: &Plugins,
359    ) {
360        self.set_default_selected_entity(scene);
361
362        //draw point indices for the selected entity
363        if let Some(ent) = self.selected_entity {
364            if let Ok(mut c) = scene.get_comp::<&mut VisPoints>(&ent) {
365                self.draw_verts_indices(ctx, scene, screen_width, screen_height, &mut c);
366            }
367        }
368
369        egui::SidePanel::left("my_left_panel").default_width(SIDE_PANEL_WIDTH).show(ctx, |ui| {
370            egui::ScrollArea::vertical().show(ui, |ui| {
371                // Scene
372                // We use this most if not all of the time, might as well leave it open by default
373                egui::CollapsingHeader::new("Scene").default_open(true).show(ui, |ui| {
374                    ui.group(|ui| {
375                        ScrollArea::vertical()
376                            .max_height(200.0)
377                            .scroll_bar_visibility(scroll_area::ScrollBarVisibility::AlwaysVisible)
378                            .auto_shrink([false, false])
379                            .show(ui, |ui| {
380                                ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {
381                                    ui.spacing_mut().item_spacing.y = 0.0;
382                                    ui.spacing_mut().button_padding.y = 4.0;
383
384                                    //get all entities that are renderable and sort by name
385                                    let entities = scene.get_renderables(true);
386                                    /* ---------------------------------- NOTE ---------------------------------- */
387                                    // We have 2 ways to select an entity - click2select and through this GUI
388                                    // Both of them should work together and using one should update the state of the other.
389                                    // Both selection methods are managed by the Selector resource.
390                                    // Since the click2select supports the idea of deselection, so should the GUI.
391                                    /* -------------------------------------------------------------------------- */
392
393                                    // The selector may have been set by click2select, respect that selection instead of starting afresh
394                                    let mut nothing_selected = true;
395                                    if let Ok(selector) = scene.get_resource::<&mut Selector>() {
396                                        if let Some(current_selected) = &selector.current_selected {
397                                            nothing_selected = false;
398                                            let name = current_selected.clone();
399                                            if self.selected_mesh_name != name.clone() {
400                                                let entity = scene.get_entity_with_name(&name);
401                                                self.selected_entity = entity;
402                                            }
403                                            self.selected_mesh_name = name;
404                                        }
405                                    }
406                                    if nothing_selected {
407                                        // If we dont have a selector resource OR if selection is None, leave the selection GUI in a deselected state
408                                        self.selected_mesh_name = String::new();
409                                        self.selected_entity = None;
410                                    }
411
412                                    // Manage selection via the GUI
413                                    // Go through all visible meshes and show their names as selectable options
414                                    for entity in entities {
415                                        let e_ref = scene.world.entity(entity).unwrap();
416                                        // get the name of the mesh which acts like a unique id
417                                        let name = e_ref.get::<&Name>().expect("The entity has no name").0.clone();
418                                        // GUI for this concrete mesh
419                                        // if we click we can see options for vis
420                                        let _res = ui.selectable_value(&mut self.selected_mesh_name, name.clone(), &name);
421
422                                        if name == self.selected_mesh_name {
423                                            // First, turn off outline for previous selection
424                                            if let Some(prev_entity) = self.selected_entity {
425                                                if let Ok(mut vis_outline) = scene.world.get::<&mut VisOutline>(prev_entity) {
426                                                    vis_outline.show_outline = false;
427                                                }
428                                            }
429
430                                            // Set new selection
431                                            self.selected_entity = Some(entity);
432
433                                            // Set selector and update outline for new selection
434                                            let selector = Selector {
435                                                current_selected: Some(name.clone()),
436                                            };
437                                            scene.add_resource(selector);
438                                            if let Ok(mut vis_outline) = scene.world.get::<&mut VisOutline>(entity) {
439                                                vis_outline.show_outline = true;
440                                            }
441                                            //make a side window
442                                            self.draw_vis(ctx, renderer, scene, entity, command_buffer, callbacks_for_selected_mesh);
443                                        }
444                                    }
445                                });
446                            });
447                    });
448                });
449
450                // Resources in the scene (global components)
451                egui::CollapsingHeader::new("Resources").show(ui, |ui| {
452                    self.draw_comps(ui, scene, scene.get_entity_resource(), command_buffer);
453                });
454
455                // Params
456                egui::CollapsingHeader::new("Textures").show(ui, |ui| {
457                    self.draw_textures(ui, scene, egui_renderer, gpu, command_buffer);
458                });
459
460                //Move
461                // egui::CollapsingHeader::new("Move")
462                // .show(ui, |ui| self.draw_move(ui, scene, command_buffer));
463
464                // Params
465                egui::CollapsingHeader::new("Params").show(ui, |ui| {
466                    self.draw_params(ui, scene, config, command_buffer);
467                });
468
469                // Lights
470                egui::CollapsingHeader::new("Lights").show(ui, |ui| self.draw_lights(ui, scene, command_buffer));
471
472                // Cam
473                egui::CollapsingHeader::new("Camera").show(ui, |ui| self.draw_cam(ui, scene, command_buffer));
474
475                // Io
476                egui::CollapsingHeader::new("Io").show(ui, |ui| {
477                    self.draw_io(ui, scene, command_buffer, self.selected_entity);
478                });
479
480                // profiling
481                egui::CollapsingHeader::new("Profiling").show(ui, |ui| self.draw_profiling(ui, scene, command_buffer));
482
483                // Plugins
484                egui::CollapsingHeader::new("Plugins").show(ui, |ui| {
485                    self.draw_plugins(ui, scene, plugins, command_buffer);
486                });
487
488                //fps
489                ui.separator();
490                let dt = runner.dt();
491                let fps = 1.0 / dt.as_secs_f32();
492                let ms = dt.as_millis();
493                let fps_string = format!("{fps:.0}");
494                let ms_string = format!("{ms:.2}");
495                ui.label(egui::RichText::new("FPS: ".to_owned() + &fps_string));
496                ui.label(egui::RichText::new("dt(ms): ".to_owned() + &ms_string));
497
498                // A `scope` creates a temporary [`Ui`] in which you can change settings:
499                // TODO: Change wrap mode?
500                ui.scope(|ui| {
501                    ui.visuals_mut().override_text_color = Some(egui::Color32::RED);
502                    ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
503                    ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
504                });
505
506                ui.add(egui::Separator::default());
507                for f in callbacks.iter() {
508                    f(self, ctx, ui, renderer, scene);
509                }
510
511                for system_and_metadata in plugins.gui_systems.iter() {
512                    let sys = &system_and_metadata.0;
513                    let func = sys.f;
514                    let gui_window = func(&self.selected_entity.into(), scene);
515                    let window_name = gui_window.window_name;
516                    let widgets = gui_window.widgets;
517                    let window_type = gui_window.window_type;
518                    if widgets.is_empty() {
519                        continue; //there's no widgets so there's nothing to
520                                  // draw
521                    }
522
523                    //recursivelly draw all widgets
524                    // https://stackoverflow.com/a/72862424
525                    let mut draw_widgets = |ui: &mut Ui| {
526                        //we make a helper so that we can call recursivelly
527                        fn helper(ui: &mut Ui, widgets: &RVec<WidgetsFFI>, selected_entity: Entity, scene: &mut Scene) {
528                            for widget in widgets.iter() {
529                                match widget {
530                                    WidgetsFFI::Slider(slider) => {
531                                        let mut val = slider.init_val;
532                                        if let RSome(slider_width) = slider.width {
533                                            ui.spacing_mut().slider_width = slider_width;
534                                            //changes size od slider2
535                                        }
536                                        let res = ui.add(
537                                            Slider::new(&mut val, slider.min..=slider.max)
538                                                .fixed_decimals(3)
539                                                .text(slider.name.as_str()),
540                                        );
541                                        if res.dragged() {
542                                            (slider.f_change)(val, &slider.name, &selected_entity, scene);
543                                        }
544                                        // } else {
545                                        if res.drag_stopped() {
546                                            // Updated method here
547                                            if let RSome(func) = slider.f_no_change {
548                                                func(&slider.name.clone(), &selected_entity, scene);
549                                            }
550                                        }
551                                        // if res.drag_released() {
552                                        //     if let RSome(func) =
553                                        // slider.f_no_change {
554                                        //         func(slider.name.clone(),
555                                        // selected_entity, scene);
556                                        //     }
557                                        // }
558                                    }
559                                    WidgetsFFI::Checkbox(checkbox) => {
560                                        let mut val = checkbox.init_val;
561                                        let res = ui.add(egui::Checkbox::new(&mut val, checkbox.name.as_str()));
562                                        if res.clicked() {
563                                            (checkbox.f_clicked)(val, &checkbox.name, &selected_entity, scene);
564                                        }
565                                    }
566                                    WidgetsFFI::Button(button) => {
567                                        if ui.add(egui::Button::new(button.name.as_str())).clicked() {
568                                            (button.f_clicked)(&button.name, &selected_entity, scene);
569                                        }
570                                    }
571                                    WidgetsFFI::SelectableList(selectable_list) => {
572                                        let mut draw_selectables = |ui: &mut Ui| {
573                                            for item in selectable_list.items.iter() {
574                                                if ui.add(egui::Button::selectable(item.is_selected, item.name.to_string())).clicked() {
575                                                    (item.f_clicked)(&item.name, &selected_entity, scene);
576                                                }
577                                            }
578                                        };
579
580                                        if selectable_list.is_horizontal {
581                                            ui.horizontal(draw_selectables);
582                                        } else {
583                                            draw_selectables(ui);
584                                        }
585                                    }
586                                    WidgetsFFI::Horizontal(widgets) => {
587                                        ui.horizontal(|ui| {
588                                            helper(ui, widgets, selected_entity, scene);
589                                        });
590                                    }
591                                }
592                            }
593                        }
594                        if let Some(selected_entity) = self.selected_entity {
595                            //finally call the helper function so that we start the recursion
596                            helper(ui, &widgets, selected_entity, scene);
597                        } else {
598                            // If we don't have a selected entity, we create a dummy entity and pass it to the helper function
599                            // This is to make sure that the gui is still drawn even for a GuiSystem that doesn't need an entity (SceneAnimation in smpl-rs)
600                            // GuiSystems that do need an entity will not run since the dummy entity has no components
601                            let dummy_entity = scene.get_or_create_hidden_entity("DummyEntity").entity();
602                            helper(ui, &widgets, dummy_entity, scene);
603                        }
604                    };
605
606                    match window_type {
607                        #[allow(clippy::cast_precision_loss)]
608                        GuiWindowType::FloatWindow(pivot, position, position_type) => {
609                            // egui::Window::new(window_name.to_string()).show(ctx, &mut draw_widgets);
610                            let pos_x = (screen_width as f32 - SIDE_PANEL_WIDTH) * position.0[0];
611                            let pos_y = (screen_height as f32) * position.0[1];
612                            let pivot = match pivot {
613                                WindowPivot::LeftBottom => Align2::LEFT_BOTTOM,
614                                WindowPivot::LeftCenter => Align2::LEFT_CENTER,
615                                WindowPivot::LeftTop => Align2::LEFT_TOP,
616                                WindowPivot::CenterBottom => Align2::CENTER_BOTTOM,
617                                WindowPivot::CenterCenter => Align2::CENTER_CENTER,
618                                WindowPivot::CenterTop => Align2::CENTER_TOP,
619                                WindowPivot::RightBottom => Align2::RIGHT_BOTTOM,
620                                WindowPivot::RightCenter => Align2::RIGHT_CENTER,
621                                WindowPivot::RightTop => Align2::RIGHT_TOP,
622                            };
623
624                            let mut win = egui::Window::new(window_name.to_string()).pivot(pivot);
625                            match position_type {
626                                WindowPositionType::Fixed => {
627                                    win = win.fixed_pos([pos_x, pos_y]);
628                                }
629                                WindowPositionType::Initial => {
630                                    win = win.default_pos([pos_x, pos_y]);
631                                }
632                            }
633                            // win.show(ctx, &mut draw_widgets);
634                            win.show(ctx, &mut draw_widgets);
635                        }
636                        GuiWindowType::Sidebar => {
637                            egui::CollapsingHeader::new(window_name.to_string()).show(ui, &mut draw_widgets);
638                        }
639                    };
640                }
641            });
642        });
643    }
644
645    // if no selected mesh is set yet, any query with the name will fail.
646    // we can use this function to set it to some default value(usually the first
647    // mesh in my list)
648    fn set_default_selected_entity(&mut self, scene: &Scene) {
649        for e_ref in scene.world.iter() {
650            // let entity = e_ref.entity();
651            let is_renderable = e_ref.has::<Renderable>();
652            if is_renderable {
653                //get the name of the mesh which acts like a unique id
654                let name = e_ref.get::<&Name>().expect("The entity has no name").0.clone();
655
656                //if it's the first time we encounter a renderable mesh,  we set the selected
657                // name to this one
658                if self.selected_mesh_name.is_empty() && name != GLOSS_FLOOR_NAME {
659                    self.selected_mesh_name.clone_from(&name);
660                    self.selected_entity = Some(e_ref.entity());
661                }
662            }
663        }
664    }
665
666    #[allow(clippy::too_many_arguments)]
667    fn draw_vis(
668        &mut self,
669        ctx: &egui::Context,
670        renderer: &Renderer,
671        scene: &mut Scene,
672        entity: Entity,
673        command_buffer: &mut CommandBuffer,
674        callbacks_for_selected_mesh: &mut [CbFnType],
675    ) {
676        let e_ref = scene.world.entity(entity).unwrap();
677        let has_vis_points = e_ref.has::<VisPoints>();
678        let has_vis_lines = e_ref.has::<VisLines>();
679        let has_vis_outline = e_ref.has::<VisOutline>();
680        let _has_vis_wireframe = e_ref.has::<VisWireframe>();
681        let has_vis_mesh = e_ref.has::<VisMesh>();
682        let _has_vis_normals = e_ref.has::<VisNormals>();
683        let mut _window = egui::Window::new("vis_points")
684            // .auto_sized()
685            .default_width(100.0)
686            // .min_height(600.0)
687            .resizable(false)
688            // .collapsible(true)
689            .title_bar(false)
690            .scroll([false, false])
691            .anchor(Align2::LEFT_TOP, Vec2::new(SIDE_PANEL_WIDTH + 12.0, 0.0))
692            .show(ctx, |ui| {
693                //dummy vis options that we use to draw invisible widgets
694                //we need this because when we don't have a component we still want to draw an
695                // empty space for it and we use this dummy widget to figure out how much space
696                // we need points
697                if has_vis_points {
698                    ui.add_space(SPACING_1);
699                    let mut c = scene.get_comp::<&mut VisPoints>(&entity).unwrap();
700                    self.draw_vis_points(ui, scene, entity, command_buffer, has_vis_points, &mut c);
701                }
702                //mesh
703                if has_vis_mesh {
704                    ui.add_space(SPACING_1);
705                    // let mut c = scene.get_comp::<&mut VisMesh>(&entity);
706                    self.draw_vis_mesh(ui, scene, entity, command_buffer, has_vis_mesh);
707                }
708                //lines
709                if has_vis_lines {
710                    ui.add_space(SPACING_1);
711                    let mut c = scene.get_comp::<&mut VisLines>(&entity).unwrap();
712                    self.draw_vis_lines(ui, scene, entity, command_buffer, has_vis_lines, &mut c);
713                }
714                //outline
715                if has_vis_outline {
716                    ui.add_space(SPACING_1);
717                    let mut c = scene.get_comp::<&mut VisOutline>(&entity).unwrap();
718                    self.draw_vis_outline(ui, scene, entity, command_buffer, has_vis_outline, &mut c);
719                }
720                // TODO: Keep this?
721                //wireframe
722                // if has_vis_wireframe {
723                //     ui.add_space(SPACING_1);
724                //     let mut c = scene.get_comp::<&mut VisWireframe>(&entity);
725                //     self.draw_vis_wireframe(
726                //         // ctx,
727                //         ui,
728                //         // renderer,
729                //         scene,
730                //         entity,
731                //         command_buffer,
732                //         has_vis_wireframe,
733                //         &mut c,
734                //     );
735                // }
736                //normals
737                // if has_vis_normals {
738                //     ui.add_space(SPACING_1);
739                //     let mut c = scene.get_comp::<&mut VisNormals>(&entity);
740                //     self.draw_vis_normals(
741                //         // ctx,
742                //         ui,
743                //         // renderer,
744                //         scene,
745                //         entity,
746                //         command_buffer,
747                //         has_vis_wireframe,
748                //         &mut c,
749                //     );
750                // }
751
752                ui.label("Components");
753                ui.separator();
754                self.draw_comps(ui, scene, entity, command_buffer);
755
756                for f in callbacks_for_selected_mesh.iter() {
757                    f(self, ctx, ui, renderer, scene);
758                }
759            });
760    }
761
762    #[allow(clippy::cast_precision_loss)]
763    fn draw_verts_indices(&self, ctx: &egui::Context, scene: &Scene, screen_width: u32, screen_height: u32, c: &mut VisPoints) {
764        if !c.show_points_indices {
765            return;
766        }
767        //TODO remove all these unwraps
768        egui::Area::new(egui::Id::new("verts_indices"))
769            .interactable(false)
770            .anchor(Align2::LEFT_TOP, egui::vec2(0.0, 0.0))
771            .show(ctx, |ui| {
772                if let Some(ent) = self.selected_entity {
773                    let verts = scene.get_comp::<&Verts>(&ent).unwrap();
774                    let model_matrix = scene.get_comp::<&ModelMatrix>(&ent).unwrap();
775                    let cam = scene.get_current_cam().unwrap();
776                    let view = cam.view_matrix(scene);
777                    let proj = cam.proj_matrix(scene);
778                    for (idx, vert) in verts.0.to_dmatrix().row_iter().enumerate() {
779                        let point_world = model_matrix.0 * na::Point3::from(vert.fixed_columns::<3>(0).transpose());
780                        let point_screen = cam.project(
781                            point_world,
782                            view,
783                            proj,
784                            na::Vector2::<f32>::new(screen_width as f32, screen_height as f32),
785                        );
786
787                        let widget_max_size = egui::vec2(35.0, 35.0);
788                        let widget_rect = egui::Rect::from_min_size(
789                            egui::pos2(
790                                point_screen.x / ctx.pixels_per_point() - widget_max_size.x / 2.0,
791                                (screen_height as f32 - point_screen.y) / ctx.pixels_per_point(),
792                            ),
793                            widget_max_size,
794                        );
795                        ui.put(widget_rect, egui::Label::new(idx.to_string()));
796                    }
797                };
798            });
799    }
800
801    fn draw_vis_points(&self, ui: &mut Ui, _scene: &Scene, entity: Entity, command_buffer: &mut CommandBuffer, is_visible: bool, c: &mut VisPoints) {
802        // VIS POINTS
803        ui.label("Points");
804        ui.separator();
805        ui.add_enabled_ui(is_visible, |ui| {
806            let res = ui.checkbox(&mut c.show_points, "Show points");
807            ui.checkbox(&mut c.show_points_indices, "Show point indices");
808            if res.clicked() {
809                command_buffer.insert_one(entity, ShadowMapDirty);
810            }
811            //all the other guis are disabled if we don't show points
812            if c.show_points {
813                //point_color
814                ui.horizontal(|ui| {
815                    ui.color_edit_button_rgba_premultiplied(&mut c.point_color.data.0.as_mut_slice()[0]);
816                    ui.label("Point color");
817                });
818                //point_size
819                ui.horizontal(|ui| {
820                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
821                    ui.add(Slider::new(&mut c.point_size, 0.0..=30.0).text("Size"))
822                });
823                ui.checkbox(&mut c.is_point_size_in_world_space, "isSizeInWorld");
824                // zbuffer
825                ui.checkbox(&mut c.zbuffer, "Use Z-Buffer");
826                //colortype
827                egui::ComboBox::new(0, "Color") //the id has to be unique to other comboboxes
828                    .selected_text(format!("{:?}", c.color_type))
829                    .show_ui(ui, |ui| {
830                        // ui.style_mut().wrap = Some(false);
831                        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
832                        ui.set_min_width(60.0);
833                        ui.selectable_value(&mut c.color_type, PointColorType::Solid, "Solid");
834                        ui.selectable_value(&mut c.color_type, PointColorType::PerVert, "PerVert");
835                    });
836            }
837        });
838    }
839
840    fn draw_vis_mesh(&mut self, ui: &mut Ui, scene: &mut Scene, entity: Entity, command_buffer: &mut CommandBuffer, is_visible: bool) {
841        let mut c = scene.get_comp::<&mut VisMesh>(&entity).unwrap();
842
843        // VIS MESH
844        ui.label("Mesh");
845        ui.separator();
846
847        // Use `add_enabled_ui` to conditionally enable UI elements
848        ui.add_enabled_ui(is_visible, |ui| {
849            let res = ui.checkbox(&mut c.show_mesh, "Show mesh");
850            if res.clicked() {
851                command_buffer.insert_one(entity, ShadowMapDirty);
852            }
853
854            let _name = scene.get_comp::<&Name>(&entity).unwrap().0.clone();
855
856            // The following settings are only enabled if `show_mesh` is true
857            if c.show_mesh {
858                // Solid color editor
859                ui.horizontal(|ui| {
860                    ui.color_edit_button_rgba_unmultiplied(&mut c.solid_color.data.0.as_mut_slice()[0]);
861                    ui.label("Solid color");
862                });
863
864                // Metalness slider
865                ui.horizontal(|ui| {
866                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
867                    ui.add(Slider::new(&mut c.metalness, 0.0..=1.0).text("Metal"));
868                });
869
870                // Roughness slider
871                ui.horizontal(|ui| {
872                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
873                    ui.add(Slider::new(&mut c.perceptual_roughness, 0.0..=1.0).text("Rough"));
874                });
875
876                // Roughness black level slider
877                ui.horizontal(|ui| {
878                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
879                    ui.add(Slider::new(&mut c.roughness_black_lvl, 0.0..=1.0).text("RoughBlackLvl"));
880                });
881
882                // Opacity slider
883                ui.horizontal(|ui| {
884                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
885                    ui.add(Slider::new(&mut c.opacity, 0.0..=1.0).text("Opacity"));
886                });
887
888                // SSS checkbox
889                ui.checkbox(&mut c.needs_sss, "Needs SSS");
890
891                // Color type selection combo box
892                egui::ComboBox::new(1, "Color")
893                    .selected_text(format!("{:?}", c.color_type))
894                    .show_ui(ui, |ui| {
895                        ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Wrap);
896                        ui.set_min_width(60.0);
897                        ui.selectable_value(&mut c.color_type, MeshColorType::Solid, "Solid");
898                        ui.selectable_value(&mut c.color_type, MeshColorType::PerVert, "PerVert");
899                        ui.selectable_value(&mut c.color_type, MeshColorType::Texture, "Texture");
900                        ui.selectable_value(&mut c.color_type, MeshColorType::UV, "UV");
901                        ui.selectable_value(&mut c.color_type, MeshColorType::Normal, "Normal");
902                        ui.selectable_value(&mut c.color_type, MeshColorType::NormalViewCoords, "NormalViewCoords");
903                    });
904
905                // UV scale slider
906                ui.horizontal(|ui| {
907                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
908                    ui.add(Slider::new(&mut c.uv_scale, 0.0..=3000.0).text("UVScale"));
909                });
910            }
911        });
912    }
913    fn draw_vis_outline(
914        &mut self,
915        ui: &mut Ui,
916        _scene: &Scene,
917        _entity: Entity,
918        _command_buffer: &mut CommandBuffer,
919        is_visible: bool,
920        c: &mut VisOutline,
921    ) {
922        ui.label("Outline");
923        ui.separator();
924
925        // Use `add_enabled_ui` to conditionally enable UI elements
926        ui.add_enabled_ui(is_visible, |ui| {
927            // Do not expose this to the user, we will use this internally to toggle the outline
928            // let res = ui.checkbox(&mut c.show_outline, "Show outline");
929            // if res.clicked() {
930            //     command_buffer.insert_one(entity, ShadowMapDirty);
931            // }
932
933            if c.show_outline {
934                // Outline color editor
935                ui.horizontal(|ui| {
936                    ui.color_edit_button_rgba_unmultiplied(&mut c.outline_color.data.0.as_mut_slice()[0]);
937                    ui.label("Outline color");
938                });
939
940                // Outline width slider
941                ui.horizontal(|ui| {
942                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; // Adjust slider width
943                    ui.add(Slider::new(&mut c.outline_width, 0.0..=25.0).text("Width"));
944                });
945            }
946        });
947    }
948    fn draw_vis_lines(
949        &mut self,
950        ui: &mut Ui,
951        _scene: &Scene,
952        entity: Entity,
953        command_buffer: &mut CommandBuffer,
954        is_visible: bool,
955        c: &mut VisLines,
956    ) {
957        ui.label("Lines");
958        ui.separator();
959
960        // Use `add_enabled_ui` to conditionally enable UI elements
961        ui.add_enabled_ui(is_visible, |ui| {
962            let res = ui.checkbox(&mut c.show_lines, "Show lines");
963            if res.clicked() {
964                command_buffer.insert_one(entity, ShadowMapDirty);
965            }
966
967            if c.show_lines {
968                // Solid color editor
969                ui.horizontal(|ui| {
970                    ui.color_edit_button_rgba_unmultiplied(&mut c.line_color.data.0.as_mut_slice()[0]);
971                    ui.label("Solid color");
972                });
973
974                // Line width slider
975                ui.horizontal(|ui| {
976                    ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; // Adjust slider width
977                    ui.add(Slider::new(&mut c.line_width, 0.0..=30.0).text("Width"));
978                });
979
980                // Z-buffer and antialiasing options
981                ui.checkbox(&mut c.zbuffer, "Use Z-Buffer");
982                ui.checkbox(&mut c.antialias_edges, "Antialias");
983            }
984        });
985    }
986
987    // TODO: Probably remove this, can always add back later if needed
988    // fn draw_vis_wireframe(
989    //     &mut self,
990    //     // ctx: &egui::Context,
991    //     ui: &mut Ui,
992    //     // renderer: &Renderer,
993    //     _scene: &Scene,
994    //     entity: Entity,
995    //     command_buffer: &mut CommandBuffer,
996    //     is_visible: bool,
997    //     c: &mut VisWireframe,
998    // ) {
999    //     //VIS Wireframe
1000    //     ui.label("Wireframe");
1001    //     ui.separator();
1002    //     ui.add_visible_ui(is_visible, |ui| {
1003    //         let res = ui.checkbox(&mut c.show_wireframe, "Show wireframe");
1004    //         if res.clicked() {
1005    //             command_buffer.insert_one(entity, ShadowMapDirty);
1006    //         }
1007    //         if c.show_wireframe {
1008    //             // ui.add_enabled_ui(c.show_wireframe, |ui| {
1009    //             //solid_color
1010    //             ui.horizontal(|ui| {
1011    //                 ui.color_edit_button_rgba_unmultiplied(
1012    //                     &mut c.wire_color.data.0.as_mut_slice()[0],
1013    //                 );
1014    //                 ui.label("Wire color");
1015    //             });
1016    //             //width
1017    //             ui.horizontal(|ui| {
1018    //                 ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
1019    // //changes size od slider                 ui.add(Slider::new(&mut
1020    // c.wire_width, 0.0..=8.0).text("Width"))             });
1021    //             // });
1022    //         }
1023    //     });
1024    // }
1025
1026    // fn draw_vis_normals(
1027    //     &mut self,
1028    //     // ctx: &egui::Context,
1029    //     ui: &mut Ui,
1030    //     // renderer: &Renderer,
1031    //     _scene: &Scene,
1032    //     entity: Entity,
1033    //     command_buffer: &mut CommandBuffer,
1034    //     is_visible: bool,
1035    //     c: &mut VisNormals,
1036    // ) {
1037    //     //VIS Normals
1038    //     ui.label("Normals");
1039    //     ui.separator();
1040    //     ui.add_visible_ui(is_visible, |ui| {
1041    //         let res = ui.checkbox(&mut c.show_normals, "Show Normals");
1042    //         if res.clicked() {
1043    //             command_buffer.insert_one(entity, ShadowMapDirty);
1044    //         }
1045    //         if c.show_normals {
1046    //             // ui.add_enabled_ui(c.show_wireframe, |ui| {
1047    //             //solid_color
1048    //             ui.horizontal(|ui| {
1049    //                 ui.color_edit_button_rgba_unmultiplied(
1050    //                     &mut c.normals_color.data.0.as_mut_slice()[0],
1051    //                 );
1052    //                 ui.label("Normal color");
1053    //             });
1054    //             //width
1055    //             ui.horizontal(|ui| {
1056    //                 ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
1057    // //changes size od slider                 ui.add(Slider::new(&mut
1058    // c.normals_width, 0.0..=8.0).text("Width"))             });
1059    //             //scale
1060    //             ui.horizontal(|ui| {
1061    //                 ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0;
1062    // //changes size od slider                 ui.add(Slider::new(&mut
1063    // c.normals_scale, 0.0..=1.0).text("Scale"))             });
1064    //         }
1065    //     });
1066    // }
1067
1068    fn draw_comps(&mut self, ui: &mut Ui, scene: &Scene, entity: Entity, _command_buffer: &mut CommandBuffer) {
1069        let e_ref = scene.world.entity(entity).unwrap();
1070        //print component names
1071        let comp_infos: Vec<gloss_hecs::TypeInfo> = e_ref.component_infos().collect();
1072        let comp_full_names: Vec<String> = comp_infos.iter().map(gloss_hecs::TypeInfo::name).collect();
1073        // let mut comp_names: Vec<String> = comp_full_names
1074        //     .iter()
1075        //     .map(|n| n.split("::").last().unwrap().to_string())
1076        //     .collect();
1077        // Some of our comps are now generic so they need to be handled
1078        let mut comp_names: Vec<String> = comp_full_names
1079            .iter()
1080            .map(|n| {
1081                // Split at the '<' character if it exists, otherwise split at "::" and take the
1082                // last part
1083                if let Some(pos) = n.find('<') {
1084                    n[..pos].split("::").last().unwrap().to_string()
1085                } else {
1086                    n.split("::").last().unwrap().to_string()
1087                }
1088            })
1089            .collect();
1090
1091        //concat also the type id
1092        let mut comp_names_and_type = Vec::new();
1093        for (comp_name, comp_info) in comp_names.iter().zip(comp_infos.iter()) {
1094            let comp_info_str = format!("{:?}", comp_info.id());
1095            comp_names_and_type.push(comp_name.to_owned() + &comp_info_str);
1096        }
1097        comp_names.sort();
1098        comp_names_and_type.sort();
1099
1100        for name in comp_names {
1101            ui.label(RichText::new(name).font(FontId::proportional(10.0)));
1102        }
1103    }
1104
1105    fn draw_textures(
1106        &mut self,
1107        ui: &mut Ui,
1108        scene: &mut Scene,
1109        egui_renderer: &mut egui_wgpu::Renderer,
1110        gpu: &Gpu,
1111        _command_buffer: &mut CommandBuffer,
1112    ) {
1113        if scene.nr_renderables() == 0 {
1114            return;
1115        }
1116        let Some(entity) = scene.get_entity_with_name(&self.selected_mesh_name) else {
1117            error!("Selected mesh does not exist with name {}", self.selected_mesh_name);
1118            return;
1119        };
1120
1121        //get diffuse tex
1122        ui.label("Diffuse");
1123        ui.separator();
1124        let diffuse_tex = scene.get_comp::<&DiffuseTex>(&entity).unwrap();
1125        //if we have a CPU texture, we get the corresponding GPU texture, if not, we get a default GPU tex
1126        let tex: &easy_wgpu::texture::Texture = scene
1127            .world
1128            .get::<&DiffuseImg>(entity)
1129            .map_or_else(|_| self.default_texture.as_ref().unwrap(), |_| &diffuse_tex.0);
1130        //get egui textureid
1131        let diffuse_egui_tex_id = self
1132            .wgputex_2_eguitex
1133            .entry(tex.texture.clone())
1134            .or_insert_with(|| egui_renderer.register_native_texture(gpu.device(), &tex.view, wgpu::FilterMode::Linear));
1135        //show img
1136        let res = ui.add(egui::Image::from_texture((*diffuse_egui_tex_id, Vec2::new(120.0, 120.0))));
1137        self.hovered_diffuse_tex = res.hovered();
1138
1139        //get normal tex
1140        ui.label("Normal");
1141        ui.separator();
1142        let normal_tex = scene.get_comp::<&NormalTex>(&entity).unwrap();
1143        //if we have a CPU texture, we get the corresponding GPU texture, if not, we get a default GPU tex
1144        let tex: &easy_wgpu::texture::Texture = scene
1145            .world
1146            .get::<&NormalImg>(entity)
1147            .map_or_else(|_| self.default_texture.as_ref().unwrap(), |_| &normal_tex.0);
1148        //get egui textureid
1149        let normal_egui_tex_id = self
1150            .wgputex_2_eguitex
1151            .entry(tex.texture.clone())
1152            .or_insert_with(|| egui_renderer.register_native_texture(gpu.device(), &tex.view, wgpu::FilterMode::Linear));
1153        //show img
1154        let res = ui.add(egui::Image::from_texture((*normal_egui_tex_id, Vec2::new(120.0, 120.0))));
1155        self.hovered_normal_tex = res.hovered();
1156
1157        //get roughness tex
1158        ui.label("Roughness");
1159        ui.separator();
1160        let roughness_tex = scene.get_comp::<&RoughnessTex>(&entity).unwrap();
1161        //if we have a CPU texture, we get the corresponding GPU texture, if not, we get a default GPU tex
1162        let tex: &easy_wgpu::texture::Texture = scene
1163            .world
1164            .get::<&RoughnessImg>(entity)
1165            .map_or_else(|_| self.default_texture.as_ref().unwrap(), |_| &roughness_tex.0);
1166        //get egui textureid
1167        let roughness_egui_tex_id = self
1168            .wgputex_2_eguitex
1169            .entry(tex.texture.clone())
1170            .or_insert_with(|| egui_renderer.register_native_texture(gpu.device(), &tex.view, wgpu::FilterMode::Linear));
1171        //show img
1172        let res = ui.add(egui::Image::from_texture((*roughness_egui_tex_id, Vec2::new(120.0, 120.0))));
1173        self.hovered_roughness_tex = res.hovered();
1174    }
1175
1176    // TODO: Keep or remove?
1177    // #[allow(clippy::similar_names)]
1178    // fn draw_move(&mut self, ui: &mut Ui, scene: &Scene, command_buffer: &mut
1179    // CommandBuffer) {     egui::ComboBox::from_label("Mode")
1180    //         .selected_text(format!("{:?}", self.gizmo_mode))
1181    //         .show_ui(ui, |ui| {
1182    //             ui.selectable_value(&mut self.gizmo_mode, GizmoMode::Rotate,
1183    // "Rotate");             ui.selectable_value(&mut self.gizmo_mode,
1184    // GizmoMode::Translate, "Translate");             ui.selectable_value(&mut
1185    // self.gizmo_mode, GizmoMode::Scale, "Scale");         });
1186
1187    //     egui::ComboBox::from_label("Orientation")
1188    //         .selected_text(format!("{:?}", self.gizmo_orientation))
1189    //         .show_ui(ui, |ui| {
1190    //             ui.selectable_value(
1191    //                 &mut self.gizmo_orientation,
1192    //                 GizmoOrientation::Global,
1193    //                 "Global",
1194    //             );
1195    //             ui.selectable_value(
1196    //                 &mut self.gizmo_orientation,
1197    //                 GizmoOrientation::Local,
1198    //                 "Local",
1199    //             );
1200    //         });
1201
1202    //     //get camera
1203    //     let cam = scene.get_current_cam().unwrap();
1204    //     if !cam.is_initialized(scene) {
1205    //         return;
1206    //     }
1207    //     let view = cam.view_matrix(scene);
1208    //     let proj = cam.proj_matrix(scene);
1209    //     let v: [[f32; 4]; 4] = view.into();
1210    //     let p: [[f32; 4]; 4] = proj.into();
1211
1212    //     //model matrix
1213    //     // let entity = scene.get_entity_with_name(&self.selected_mesh_name);
1214    //     if let Some(entity) = self.selected_entity {
1215    //         let model_matrix = scene.get_comp::<&ModelMatrix>(&entity).unwrap();
1216    //         let mm: [[f32; 4]; 4] = model_matrix.0.to_homogeneous().into();
1217
1218    //         let gizmo = Gizmo::new("My gizmo")
1219    //             .view_matrix(v.into())
1220    //             .projection_matrix(p.into())
1221    //             .model_matrix(mm.into())
1222    //             .mode(self.gizmo_mode)
1223    //             .orientation(self.gizmo_orientation);
1224
1225    //         let possible_gizmo_response = gizmo.interact(ui);
1226
1227    //         if let Some(gizmo_response) = possible_gizmo_response {
1228    //             let _new_model_matrix = gizmo_response.transform();
1229
1230    //             //TODO you can actually do gizmo_response.translation and
1231    // gizmo_reponse.quat directly...
1232
1233    //             //get T
1234    //             let new_t_mint = gizmo_response.translation;
1235    //             let mut new_t = na::Translation3::<f32>::identity();
1236    //             new_t.x = new_t_mint.x;
1237    //             new_t.y = new_t_mint.y;
1238    //             new_t.z = new_t_mint.z;
1239    //             //get R
1240    //             let new_q_mint = gizmo_response.rotation;
1241    //             let mut new_quat = na::Quaternion::<f32>::identity();
1242    //             new_quat.i = new_q_mint.v.x;
1243    //             new_quat.j = new_q_mint.v.y;
1244    //             new_quat.k = new_q_mint.v.z;
1245    //             new_quat.w = new_q_mint.s;
1246    //             let new_quat_unit =
1247    // na::UnitQuaternion::from_quaternion(new_quat);             let new_rot =
1248    // new_quat_unit.into();             //get scale
1249    //             let new_scale_mint = gizmo_response.scale;
1250    //             let new_scale =
1251    // new_scale_mint.x.max(new_scale_mint.y).max(new_scale_mint.z); //TODO For now
1252    // we only get one scale value
1253
1254    //             // let new_scale = gizmo_response.scale.max_element(); //TODO For
1255    // now we only get one scale value             //
1256    // // println!("gizmo scale is {:?}", gizmo_response.scale);             //
1257    // // println!("new_scale {:?}", new_scale);             //combine
1258    //             let mut new_model_mat = na::SimilarityMatrix3::<f32>::identity();
1259    //             new_model_mat.append_rotation_mut(&new_rot);
1260    //             new_model_mat.append_translation_mut(&new_t);
1261    //             new_model_mat.set_scaling(new_scale);
1262
1263    //             //set
1264    //             // mesh.set_model_matrix(scene, new_isometry);
1265    //             let new_model_matrix = ModelMatrix(new_model_mat);
1266    //             command_buffer.insert_one(entity, new_model_matrix);
1267
1268    //             //if it has pos lookat we also modify that
1269    //             if scene.world.has::<PosLookat>(entity).unwrap() {
1270    //                 let pos_lookat =
1271    // scene.get_comp::<&PosLookat>(&entity).unwrap();                 // TODO
1272    // make poslookat clone                 let pos_lookat =
1273    //                     PosLookat::new_from_model_matrix(new_model_mat,
1274    // pos_lookat.dist_lookat());
1275    // command_buffer.insert_one(entity, pos_lookat);             }
1276    //         }
1277    //     }
1278    // }
1279
1280    fn draw_params(&mut self, ui: &mut Ui, _scene: &mut Scene, config: &mut Config, _command_buffer: &mut CommandBuffer) {
1281        //TODO get all things from config
1282
1283        //ambient factor
1284        ui.horizontal(|ui| {
1285            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1286            ui.add(Slider::new(&mut config.render.ambient_factor, 0.0..=1.0).text("AmbientFactor"))
1287        });
1288        //environment_factor
1289        ui.horizontal(|ui| {
1290            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1291            ui.add(Slider::new(&mut config.render.environment_factor, 0.0..=5.0).text("EnvFactor"))
1292        });
1293        //bg_color
1294        ui.horizontal(|ui| {
1295            ui.color_edit_button_rgba_premultiplied(&mut config.render.bg_color.data.0.as_mut_slice()[0]);
1296            ui.label("bg_color");
1297        });
1298        //distance fade
1299        ui.checkbox(config.render.enable_distance_fade.as_mut().unwrap_or(&mut false), "DistanceFade");
1300        //distance fade start
1301        ui.horizontal(|ui| {
1302            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1303            ui.add(Slider::new(config.render.distance_fade_start.as_mut().unwrap_or(&mut 0.0), 1.0..=100.0).text("FadeStart"))
1304        });
1305        //distance fade end
1306        ui.horizontal(|ui| {
1307            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1308            ui.add(Slider::new(config.render.distance_fade_end.as_mut().unwrap_or(&mut 0.0), 1.0..=100.0).text("FadeEnd"))
1309        });
1310        //saturation
1311        ui.horizontal(|ui| {
1312            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1313            ui.add(Slider::new(&mut config.render.saturation, 0.0..=2.0).text("Saturation"))
1314        });
1315        //gamma
1316        ui.horizontal(|ui| {
1317            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1318            ui.add(Slider::new(&mut config.render.gamma, 0.5..=1.5).text("Gamma"))
1319        });
1320        //exposure
1321        ui.horizontal(|ui| {
1322            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1323            ui.add(Slider::new(&mut config.render.exposure, -5.0..=5.0).text("Exposure"))
1324        });
1325    }
1326
1327    #[allow(clippy::too_many_lines)]
1328    fn draw_lights(&mut self, ui: &mut Ui, scene: &mut Scene, command_buffer: &mut CommandBuffer) {
1329        // //get all entities that are renderable and sort by name
1330        let entities = scene.get_lights(true);
1331
1332        //go through all the lights and show their name
1333        ui.group(|ui| {
1334            ScrollArea::vertical()
1335                .max_height(200.0)
1336                .scroll_bar_visibility(scroll_area::ScrollBarVisibility::AlwaysVisible)
1337                .auto_shrink([false, false])
1338                .show(ui, |ui| {
1339                    ui.with_layout(Layout::top_down_justified(Align::LEFT), |ui| {
1340                        ui.spacing_mut().item_spacing.y = 0.0;
1341                        ui.spacing_mut().button_padding.y = 4.0;
1342                        for entity in entities {
1343                            let e_ref = scene.world.entity(entity).unwrap();
1344
1345                            //get the name of the mesh which acts like a unique id
1346                            let name = e_ref.get::<&Name>().expect("The entity has no name").0.clone();
1347
1348                            //if it's the first time we encounter a renderable mesh,  we set the selected
1349                            // name to this one
1350                            if self.selected_light_name.is_empty() {
1351                                self.selected_light_name.clone_from(&name);
1352                            }
1353
1354                            //GUI for this concrete mesh
1355                            //if we click we can see options for vis
1356                            let _res = ui.selectable_value(&mut self.selected_light_name, name.clone(), &name);
1357
1358                            if name == self.selected_light_name {
1359                                self.selected_light_entity = Some(entity);
1360                            }
1361                        }
1362                    });
1363                });
1364        });
1365
1366        if let Some(entity) = self.selected_light_entity {
1367            ui.label("LightEmit");
1368            ui.separator();
1369            //color
1370            ui.horizontal(|ui| {
1371                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1372                ui.color_edit_button_rgb(&mut comp_light_emit.color.data.0.as_mut_slice()[0]);
1373                ui.label("color");
1374            });
1375            //intensity
1376            ui.horizontal(|ui| {
1377                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1378                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1379                ui.add(Slider::new(&mut comp_light_emit.intensity, 0.0..=1_000_000.0).text("intensity"))
1380            });
1381            //cast shadows
1382            let mut is_shadow_casting: bool = scene.world.has::<ShadowCaster>(entity).unwrap();
1383            let res = ui.checkbox(&mut is_shadow_casting, "CastShadows");
1384            if res.changed() {
1385                if is_shadow_casting {
1386                    //TODO adding this parameter just adds it with these daault. Maybe we need
1387                    // another way to temporarily disable shadows
1388                    command_buffer.insert_one(
1389                        entity,
1390                        ShadowCaster {
1391                            shadow_res: 2048,
1392                            shadow_bias_fixed: 2e-5,
1393                            shadow_bias: 0.15,
1394                            shadow_bias_normal: 1.5,
1395                        },
1396                    );
1397                } else {
1398                    command_buffer.remove_one::<ShadowCaster>(entity);
1399                }
1400            }
1401            // shadow_res
1402            if scene.world.has::<ShadowCaster>(entity).unwrap() {
1403                //we only mutate the component if we modify the slider because we want the
1404                // change detection of hecs to only pick-up and detect when we actually change
1405                // the value
1406                let shadow_caster_read = scene.get_comp::<&ShadowCaster>(&entity).unwrap();
1407                let mut shadow_res = shadow_caster_read.shadow_res;
1408                let mut shadow_bias_fixed = shadow_caster_read.shadow_bias_fixed;
1409                let mut shadow_bias = shadow_caster_read.shadow_bias;
1410                let mut shadow_bias_normal = shadow_caster_read.shadow_bias_normal;
1411                drop(shadow_caster_read);
1412                // ui.horizontal(|ui| {
1413                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1414                let res = ui.add(Slider::new(&mut shadow_res, 128..=4096).text("shadow_res"));
1415                if res.changed() {
1416                    //now we actually mutate the component
1417                    let mut shadow_caster = scene.get_comp::<&mut ShadowCaster>(&entity).unwrap();
1418                    shadow_caster.shadow_res = shadow_res;
1419                }
1420                //shadow bias fixed
1421                let res = ui.add(Slider::new(&mut shadow_bias_fixed, 0.0..=0.5).text("shadow_bias_fixed"));
1422                if res.changed() {
1423                    //now we actually mutate the component
1424                    let mut shadow_caster = scene.get_comp::<&mut ShadowCaster>(&entity).unwrap();
1425                    shadow_caster.shadow_bias_fixed = shadow_bias_fixed;
1426                }
1427
1428                //shadow bias light
1429                let res = ui.add(Slider::new(&mut shadow_bias, 0.0..=0.4).text("shadow_bias"));
1430                if res.changed() {
1431                    //now we actually mutate the component
1432                    let mut shadow_caster = scene.get_comp::<&mut ShadowCaster>(&entity).unwrap();
1433                    shadow_caster.shadow_bias = shadow_bias;
1434                }
1435                //shadow bias normal
1436                let res = ui.add(Slider::new(&mut shadow_bias_normal, 0.0..=50.0).text("shadow_bias_normal"));
1437                if res.changed() {
1438                    //now we actually mutate the component
1439                    let mut shadow_caster = scene.get_comp::<&mut ShadowCaster>(&entity).unwrap();
1440                    shadow_caster.shadow_bias_normal = shadow_bias_normal;
1441                }
1442                // });
1443            }
1444            //range
1445            ui.horizontal(|ui| {
1446                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1447                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1448                ui.add(Slider::new(&mut comp_light_emit.range, 0.0..=10000.0).text("range"))
1449            });
1450            //radius
1451            ui.horizontal(|ui| {
1452                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1453                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1454                ui.add(Slider::new(&mut comp_light_emit.radius, 0.0..=10.0).text("radius"))
1455            });
1456            //inner_angle
1457            ui.horizontal(|ui| {
1458                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1459                let outer_angle = comp_light_emit.outer_angle;
1460                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1461                ui.add(Slider::new(&mut comp_light_emit.inner_angle, 0.0..=outer_angle).text("inner_angle"))
1462            });
1463            //outer_angle
1464            ui.horizontal(|ui| {
1465                let mut comp_light_emit = scene.get_comp::<&mut LightEmit>(&entity).unwrap();
1466                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1467                ui.add(Slider::new(&mut comp_light_emit.outer_angle, 0.0..=std::f32::consts::PI / 2.0).text("outer_angle"))
1468            });
1469            ui.horizontal(|ui| {
1470                let mut comp_proj = scene.get_comp::<&mut Projection>(&entity).unwrap();
1471                let (mut near, _) = comp_proj.near_far();
1472                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1473                let res = ui.add(Slider::new(&mut near, 0.0..=20.0).text("near"));
1474                if res.changed() {
1475                    comp_proj.set_near(near);
1476                }
1477            });
1478            //far
1479            ui.horizontal(|ui| {
1480                let mut comp_proj = scene.get_comp::<&mut Projection>(&entity).unwrap();
1481                let (_, mut far) = comp_proj.near_far();
1482                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1483                let res = ui.add(Slider::new(&mut far, 0.0..=2000.0).text("far"));
1484                if res.changed() {
1485                    comp_proj.set_far(far);
1486                }
1487            });
1488            {
1489                let mut poslookat = scene.get_comp::<&mut PosLookat>(&entity).unwrap();
1490                ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1491                ui.add(Slider::new(&mut poslookat.position.x, -40.0..=40.0).text("x"));
1492                ui.add(Slider::new(&mut poslookat.position.y, -40.0..=40.0).text("y"));
1493                ui.add(Slider::new(&mut poslookat.position.z, -40.0..=40.0).text("z"));
1494            }
1495            //view plane that cna be used to manipulate the light
1496            let mut has_mesh = scene.world.has::<Renderable>(entity).unwrap();
1497            let res = ui.checkbox(&mut has_mesh, "ShowLight");
1498            if res.changed() {
1499                if has_mesh {
1500                    //add plane
1501                    let center = scene.get_comp::<&PosLookat>(&entity).unwrap().position;
1502                    let normal = scene.get_comp::<&PosLookat>(&entity).unwrap().direction();
1503                    //move the plane a bit behind the light so it doesn't cast a shadow
1504                    let center = center - normal * 0.03;
1505                    let mut builder = builders::build_plane(center, normal, 0.3, 0.3, false);
1506                    let _ = scene.world.insert(entity, builder.build());
1507                    let _ = scene.world.insert_one(
1508                        entity,
1509                        VisMesh {
1510                            solid_color: na::Vector4::<f32>::new(1.0, 1.0, 1.0, 1.0),
1511                            ..Default::default()
1512                        },
1513                    );
1514                    let _ = scene.world.insert_one(entity, Renderable);
1515                } else {
1516                    command_buffer.remove_one::<Renderable>(entity);
1517                }
1518            }
1519        }
1520    }
1521
1522    #[allow(clippy::too_many_lines)]
1523    fn draw_cam(&mut self, ui: &mut Ui, scene: &mut Scene, _command_buffer: &mut CommandBuffer) {
1524        // get all entities that are renderable and sort by name
1525        let _entities = scene.get_lights(true);
1526        let cam = scene.get_current_cam().unwrap();
1527
1528        if let Ok(mut projection) = scene.world.get::<&mut Projection>(cam.entity) {
1529            let (mut near, mut far) = projection.near_far();
1530            ui.label("Projection");
1531            ui.separator();
1532            ui.spacing_mut().slider_width = SIDE_PANEL_WIDTH / 3.0; //changes size od slider
1533            let res = ui.add(Slider::new(&mut near, 1e-5..=1.0).text("near"));
1534            if res.changed() {
1535                projection.set_near(near);
1536            }
1537            let res = ui.add(Slider::new(&mut far, near..=10000.0).text("far"));
1538            if res.changed() {
1539                projection.set_far(far);
1540            }
1541        }
1542    }
1543
1544    #[allow(clippy::too_many_lines)]
1545    #[allow(clippy::cast_precision_loss)]
1546    fn draw_plugins(&mut self, ui: &mut Ui, _scene: &mut Scene, plugins: &Plugins, _command_buffer: &mut CommandBuffer) {
1547        // //get all entities that are renderable and sort by name
1548        // let entities = scene.get_lights(true);
1549        egui::Grid::new("grid_plugins").show(ui, |ui| {
1550            for system_and_metadata in plugins.logic_systems.iter() {
1551                let metadata = &system_and_metadata.1;
1552                let sys = &system_and_metadata.0;
1553                let name = sys.name.clone().unwrap_or(RString::from("unknown_name"));
1554                let ms = metadata.execution_time.as_nanos() as f32 / 1_000_000.0;
1555                let ms_string = format!("{ms:.2}");
1556                //draw in green if the system took more than 0.01ms (todo we need a better way
1557                // to check if the system was ran)
1558                let was_ran = ms > 0.07;
1559                // ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
1560                if was_ran {
1561                    ui.label(egui::RichText::new(name).strong());
1562                    ui.label(egui::RichText::new(ms_string).strong());
1563                } else {
1564                    ui.label(egui::RichText::new(name).weak());
1565                    ui.label(egui::RichText::new(ms_string).weak());
1566                }
1567                ui.end_row();
1568            }
1569        });
1570
1571        // //go through all the lights and show their name
1572        // TODO: Keep or remove?
1573        // ui.group(|ui| {
1574        //     ScrollArea::vertical()
1575        //         .max_height(200.0)
1576        //         .scroll_bar_visibility(scroll_area::ScrollBarVisibility::AlwaysVisible)
1577        //         .auto_shrink([false, false])
1578        //         .show(ui, |ui| {
1579        //             ui.with_layout(Layout::top_down_justified(Align::LEFT),
1580        // |ui| {                 ui.spacing_mut().item_spacing.y = 0.0;
1581        //                 ui.spacing_mut().button_padding.y = 4.0;
1582        //                 for entity in entities {
1583        //                     let e_ref = scene.world.entity(entity).unwrap();
1584
1585        //                     //get the name of the mesh which acts like a
1586        // unique id                     let name = e_ref
1587        //                         .get::<&Name>()
1588        //                         .expect("The entity has no name")
1589        //                         .0
1590        //                         .clone();
1591
1592        //                     //if it's the first time we encounter a
1593        // renderable mesh,  we set the selected name to this one
1594        //                     if self.selected_light_name.is_empty() {
1595        //                         self.selected_light_name = name.clone();
1596        //                     }
1597
1598        //                     //GUI for this concrete mesh
1599        //                     //if we click we can see options for vis
1600        //                     let _res = ui.selectable_value(
1601        //                         &mut self.selected_light_name,
1602        //                         name.clone(),
1603        //                         &name,
1604        //                     );
1605
1606        //                     if name == self.selected_light_name {
1607        //                         self.selected_light_entity = Some(entity);
1608        //                     }
1609        //                 }
1610        //             });
1611        //         });
1612        // });
1613    }
1614
1615    #[allow(clippy::too_many_lines)]
1616    fn draw_io(&mut self, ui: &mut Ui, scene: &mut Scene, _command_buffer: &mut CommandBuffer, selected_entity: Option<Entity>) {
1617        //save obj
1618        if let Some(selected_entity) = selected_entity {
1619            if ui.add(egui::Button::new("Save Obj")).clicked() {
1620                //get v, f and possibly uv from the entity
1621                if scene.world.has::<Verts>(selected_entity).unwrap() && scene.world.has::<Faces>(selected_entity).unwrap() {
1622                    let v = scene.get_comp::<&Verts>(&selected_entity).unwrap();
1623                    // let uv = scene.get_comp::<&UVs>(&selected_entity);
1624
1625                    //transform vertices
1626                    let mm = scene.get_comp::<&ModelMatrix>(&selected_entity).unwrap();
1627                    let v = geom::transform_verts(&v.0.to_dmatrix(), &mm.0);
1628
1629                    let f = scene.get_comp::<&Faces>(&selected_entity).ok().map(|f| f.0.clone());
1630
1631                    let uv = scene.get_comp::<&UVs>(&selected_entity).ok().map(|uv| uv.0.clone());
1632
1633                    let normals = scene
1634                        .get_comp::<&Normals>(&selected_entity)
1635                        .ok()
1636                        .map(|normals| geom::transform_vectors(&normals.0.to_dmatrix(), &mm.0));
1637
1638                    //TODO make the path parametrizable
1639                    geom::save_obj(
1640                        &v,
1641                        f.map(|faces| faces.to_dmatrix()).as_ref(),
1642                        // None,
1643                        uv.map(|faces| faces.to_dmatrix()).as_ref(),
1644                        normals.as_ref(),
1645                        "./saved_obj.obj",
1646                    );
1647                }
1648            }
1649        }
1650
1651        //save ply
1652        if let Some(selected_entity) = selected_entity {
1653            if ui.add(egui::Button::new("Save Ply")).clicked() {
1654                //get v, f and possibly uv from the entity
1655                if scene.world.has::<Verts>(selected_entity).unwrap() && scene.world.has::<Faces>(selected_entity).unwrap() {
1656                    let v = scene.get_comp::<&Verts>(&selected_entity).unwrap();
1657
1658                    //transform vertices
1659                    let mm = scene.get_comp::<&ModelMatrix>(&selected_entity).unwrap();
1660                    let v = geom::transform_verts(&v.0.to_dmatrix(), &mm.0);
1661
1662                    let f = scene.get_comp::<&Faces>(&selected_entity).ok().map(|f| f.0.clone());
1663
1664                    let uv = scene.get_comp::<&UVs>(&selected_entity).ok().map(|uv| uv.0.clone());
1665
1666                    let normals = scene
1667                        .get_comp::<&Normals>(&selected_entity)
1668                        .ok()
1669                        .map(|normals| geom::transform_vectors(&normals.0.to_dmatrix(), &mm.0));
1670
1671                    let colors = scene.get_comp::<&Colors>(&selected_entity).ok().map(|colors| colors.0.clone());
1672
1673                    //TODO make the path parametrizable
1674                    geom::save_ply(
1675                        &v,
1676                        f.map(|faces| faces.to_dmatrix()).as_ref(),
1677                        // None,
1678                        uv.map(|uvs| uvs.to_dmatrix()).as_ref(),
1679                        normals.as_ref(),
1680                        colors.map(|colors| colors.to_dmatrix()).as_ref(),
1681                        "./saved_ply.ply",
1682                    );
1683                }
1684            }
1685        }
1686    }
1687
1688    #[allow(clippy::too_many_lines)]
1689    #[allow(unused_variables)]
1690    fn draw_profiling(&mut self, ui: &mut Ui, _scene: &mut Scene, _command_buffer: &mut CommandBuffer) {
1691        // TODO: Keep or remove?
1692        // cfg_if::cfg_if! {
1693        //     if #[cfg(feature = "peak-alloc")] {
1694        //         let current_mem = PEAK_ALLOC.current_usage_as_mb() as u32;
1695        //         let peak_mem = PEAK_ALLOC.peak_usage_as_mb() as u32;
1696        //         ui.label("current_mem (MB): ".to_owned() + &current_mem.to_string());
1697        //         ui.label("peak_mem(MB): ".to_owned() + &peak_mem.to_string());
1698        //     }
1699        // }
1700
1701        // cfg_if::cfg_if! {
1702        //     if #[cfg(feature = "talc")] {
1703
1704        //         cfg_if::cfg_if! {
1705        //         if #[cfg(target_arch = "wasm32")] {
1706        //             use crate::ALLOCATOR;
1707        //             let talc = ALLOCATOR.lock();
1708        //             let counters = talc.get_counters();
1709        //             ui.label("available to claim MB: ".to_owned() +
1710        // &(counters.available_bytes/(1024*1024)).to_string());
1711        // ui.label("nr gaps: ".to_owned() + &counters.fragment_count.to_string());
1712        //             ui.label("claimed MB: ".to_owned() +
1713        // &(counters.claimed_bytes/(1024*1024)).to_string());
1714        // ui.label("unavailable MB: ".to_owned() +
1715        // &(counters.overhead_bytes()/(1024*1024)).to_string());
1716        // ui.label("total_freed MB: ".to_owned() +
1717        // &(counters.total_freed_bytes()/(1024*1024)).to_string());         }}
1718        //     }
1719        // }
1720
1721        // cfg_if::cfg_if! {
1722        //     if #[cfg(feature = "jemallocator")] {
1723
1724        //         ui.label("current_mem (MB): ".to_owned());
1725        //     }
1726        // }
1727
1728        // cfg_if::cfg_if! {
1729        //     if #[cfg(feature = "mimalloc")] {
1730        //         ui.label("mimalloc");
1731
1732        //         ui.label("current_mem (MB): ".to_owned());
1733        //     }
1734        // }
1735
1736        //for accounting allocator
1737        let memory_usage = MemoryUse::capture();
1738        if let Some(mem_resident) = memory_usage.resident {
1739            ui.label(egui::RichText::new(
1740                "MB resident: ".to_owned() + &(mem_resident / (1024 * 1024)).to_string(),
1741            ));
1742        }
1743        if let Some(mem_counted) = memory_usage.counted {
1744            ui.label(egui::RichText::new(
1745                "MB counted: ".to_owned() + &(mem_counted / (1024 * 1024)).to_string(),
1746            ));
1747        }
1748
1749        //are we tracking callstacks
1750        let mut is_tracking = re_memory::accounting_allocator::is_tracking_callstacks();
1751        let res = ui.checkbox(&mut is_tracking, "Track memory callstacks");
1752        if res.clicked() {
1753            re_memory::accounting_allocator::set_tracking_callstacks(is_tracking);
1754        }
1755
1756        // //make a memory bar where we show all the memory and the parts that are free in
1757        // // red, parts that are allocated in red only works in wasm because it
1758        // // uses a linear memory model
1759        // let size_per_mb = 1.0;
1760        // // #[cfg(target_arch = "wasm32")]
1761        // {
1762        //     use egui::{epaint::RectShape, Sense, Shape};
1763        //     let unknown_mem = Color32::from_rgb(150, 150, 150);
1764        //     let allocated_mem = Color32::from_rgb(255, 10, 10);
1765
1766        //     //make per mb colors
1767        //     let mut mb2color: Vec<egui::Color32> = Vec::new();
1768        //     if let Some(mem_resident) = memory_usage.resident {
1769        //         let mb_resident = mem_resident / (1024 * 1024);
1770        //         mb2color.resize(mb_resident.try_into().unwrap(), unknown_mem);
1771
1772        //         // Older usage was
1773        //         // let live_allocs = accounting_allocator::live_allocs_list();
1774        //         // re_memory does not seem to expose this the same way as before. We can't get the memory addresses.
1775        //         // Using backtrace string as a unique identifier since re_memory doesn't expose memory addresses anymore.
1776        //         // This maintains similar functionality while working with the available API.
1777        //         // let tracking_stats = accounting_allocator::tracking_stats().unwrap();
1778        //         // let top_callstacks = tracking_stats.top_callstacks;
1779        //         // let live_allocs = top_callstacks.;
1780        //         // let live_allocs = GLOBAL.tracking_stats().unwrap().top_callstacks;
1781
1782        //         // use re_memory::allocation
1783        //         // tracking_stats.
1784        //         // let live_allocs_big = accounting_allocator::BIG_ALL &BIG_ALLOCATION_TRACKER.lock();
1785        //         // use re_memory::accounting_allocator::;
1786        //         let live_allocs: Vec<(usize, usize)> = accounting_allocator::tracking_stats()
1787        //             .map(|stats| {
1788        //                 stats
1789        //                     .top_callstacks
1790        //                     .iter()
1791        //                     .map(|cs| (cs.readable_backtrace.to_string().len(), cs.extant.size))
1792        //                     .collect()
1793        //             })
1794        //             .unwrap_or_default();
1795
1796        //         // Older usage was
1797        //         // let min_ptr = accounting_allocator::min_ptr_alloc_memory();
1798        //         // We dont have this fn anymore, so we do something similar to above
1799        //         // Using first tracked allocation as minimum pointer since re_memory doesn't expose global min ptr.
1800        //         #[allow(clippy::get_first)] // getting borrow issues when using .first()
1801        //         let min_ptr = accounting_allocator::tracking_stats().map_or(0, |stats| {
1802        //             stats.top_callstacks.get(0).map_or(0, |cs| cs.readable_backtrace.to_string().len())
1803        //         });
1804
1805        //         //for each live allow, paint the value with Red( allocated )
1806        //         for (ptr, size) in live_allocs {
1807        //             let ptr_mb = (ptr - min_ptr) / (1024 * 1024);
1808        //             let size_mb = size / (1024 * 1024);
1809        //             //for each mb paint it allocated
1810        //             for local_mb_idx in 0..size_mb {
1811        //                 let idx = ptr_mb + local_mb_idx;
1812        //                 if idx < mb2color.len() {
1813        //                     mb2color[idx] = allocated_mem;
1814        //                 }
1815        //             }
1816        //         }
1817        //     }
1818
1819        //     //draw bar
1820        //     if let Some(mem_resident) = memory_usage.resident {
1821        //         let mb_resident = mem_resident / (1024 * 1024);
1822        //         ui.horizontal(|ui| {
1823        //             ui.spacing_mut().item_spacing = egui::vec2(0.5, 0.0);
1824        //             // for i in 0..mb_resident {
1825        //             #[allow(clippy::cast_sign_loss)]
1826        //             #[allow(clippy::cast_possible_truncation)]
1827        //             for i in 0..(mb_resident as usize) {
1828        //                 // let cursor = Ui::cursor(ui);
1829        //                 let rect_size = egui::Vec2::new(size_per_mb, 10.0);
1830        //                 let rect = ui.allocate_exact_size(rect_size, Sense::click()).0;
1831        //                 let rounding = Rounding::default();
1832        //                 #[allow(arithmetic_overflow)]
1833        //                 // let fill_color = egui::Color32::from_rgb(50 * 3, 0, 0);
1834        //                 // let fill_color =
1835        //                 let fill_color = if i < mb2color.len() {
1836        //                     mb2color[i]
1837        //                 } else {
1838        //                     egui::Color32::from_rgb(50, 0, 0)
1839        //                 };
1840        //                 let stroke = Stroke::default();
1841        //                 let rect_shape = RectShape::new(rect, rounding, fill_color, stroke);
1842        //                 ui.painter().add(Shape::Rect(rect_shape));
1843        //             }
1844        //         });
1845        //     }
1846        // }
1847
1848        // TODO: Find where and how in the re_memory API to get the memory addresses
1849        // TODO: Should we factor in sampling rate here? should be a reasonable estimate regardless?
1850
1851        //make a memory bar showing proportion of allocated vs free memory
1852        let size_per_mb = 1.0;
1853        #[cfg(target_arch = "wasm32")]
1854        {
1855            use egui::{epaint::RectShape, Sense, Shape, StrokeKind};
1856            let unknown_mem = Color32::from_rgb(150, 150, 150);
1857            let allocated_mem = Color32::from_rgb(255, 10, 10);
1858
1859            //make per mb colors
1860            let mut mb2color: Vec<egui::Color32> = Vec::new();
1861            if let Some(mem_resident) = memory_usage.resident {
1862                let mb_resident = mem_resident / (1024 * 1024);
1863                mb2color.resize(mb_resident.try_into().unwrap(), unknown_mem);
1864
1865                // Calculate total allocated memory from tracking stats
1866                let total_allocated: usize =
1867                    accounting_allocator::tracking_stats().map_or(0, |stats| stats.top_callstacks.iter().map(|cs| cs.extant.size).sum());
1868
1869                // Convert to MB and fill that proportion of the bar with red
1870                let allocated_mb = total_allocated / (1024 * 1024);
1871                for i in 0..allocated_mb {
1872                    if i < mb2color.len() {
1873                        mb2color[i] = allocated_mem;
1874                    }
1875                }
1876            }
1877
1878            //draw bar
1879            if let Some(mem_resident) = memory_usage.resident {
1880                let mb_resident = mem_resident / (1024 * 1024);
1881                ui.horizontal(|ui| {
1882                    ui.spacing_mut().item_spacing = egui::vec2(0.5, 0.0);
1883                    #[allow(clippy::cast_sign_loss)]
1884                    #[allow(clippy::cast_possible_truncation)]
1885                    for i in 0..(mb_resident as usize) {
1886                        let rect_size = egui::Vec2::new(size_per_mb, 10.0);
1887                        let rect = ui.allocate_exact_size(rect_size, Sense::click()).0;
1888                        let rounding = CornerRadius::default();
1889                        let fill_color = if i < mb2color.len() {
1890                            mb2color[i]
1891                        } else {
1892                            egui::Color32::from_rgb(50, 0, 0)
1893                        };
1894                        let stroke = Stroke::default();
1895                        let stroke_kind = StrokeKind::Inside;
1896                        let rect_shape = RectShape::new(rect, rounding, fill_color, stroke, stroke_kind);
1897                        ui.painter().add(Shape::Rect(rect_shape));
1898                    }
1899                });
1900            }
1901        }
1902
1903        ui.add_space(15.0);
1904        if let Some(tracks) = accounting_allocator::tracking_stats() {
1905            #[allow(clippy::cast_precision_loss)]
1906            for (i, cb) in tracks.top_callstacks.iter().enumerate() {
1907                //callstack name will be the nr mb
1908                let mb_total = cb.extant.size as f32 / (1024.0 * 1024.0);
1909
1910                let cb_stack = cb.readable_backtrace.to_string();
1911                let last_relevant_func_name = get_last_relevant_func_name(&cb_stack);
1912
1913                let text_header = egui::RichText::new(float2string(mb_total, 1) + " MB: " + &last_relevant_func_name).size(12.0);
1914
1915                ui.push_id(i, |ui| {
1916                    egui::CollapsingHeader::new(text_header).show(ui, |ui| self.draw_callstack_profiling(ui, cb));
1917                });
1918            }
1919        }
1920    }
1921
1922    #[allow(clippy::too_many_lines)]
1923    #[allow(unused_variables)]
1924    fn draw_callstack_profiling(&mut self, ui: &mut Ui, cb: &CallstackStatistics) {
1925        let text = "cb: ".to_owned() + &cb.readable_backtrace.to_string();
1926        ui.label(egui::RichText::new(text).size(11.0));
1927        ui.label("count".to_owned() + &(cb.extant.count).to_string());
1928        ui.label("size".to_owned() + &(cb.extant.size / (1024 * 1024)).to_string());
1929        ui.label("stochastic_rate".to_owned() + &(cb.stochastic_rate).to_string());
1930    }
1931}
1932
1933// Generated by egui-themer (https://github.com/grantshandy/egui-themer).
1934#[allow(clippy::too_many_lines)]
1935pub fn style() -> Style {
1936    Style {
1937        spacing: Spacing {
1938            item_spacing: Vec2 { x: 8.0, y: 3.0 },
1939            window_margin: Margin {
1940                left: 6,
1941                right: 6,
1942                top: 6,
1943                bottom: 6,
1944            },
1945            button_padding: Vec2 { x: 4.0, y: 1.0 },
1946            menu_margin: Margin {
1947                left: 6,
1948                right: 6,
1949                top: 6,
1950                bottom: 6,
1951            },
1952            indent: 18.0,
1953            interact_size: Vec2 { x: 40.0, y: 18.0 },
1954            slider_width: 100.0,
1955            combo_width: 100.0,
1956            text_edit_width: 280.0,
1957            icon_width: 14.0,
1958            icon_width_inner: 8.0,
1959            icon_spacing: 4.0,
1960            tooltip_width: 600.0,
1961            indent_ends_with_horizontal_line: false,
1962            combo_height: 200.0,
1963            scroll: ScrollStyle {
1964                bar_width: 6.0,
1965                handle_min_length: 12.0,
1966                bar_inner_margin: 4.0,
1967                bar_outer_margin: 0.0,
1968                ..Default::default()
1969            },
1970            ..Default::default()
1971        },
1972        interaction: Interaction {
1973            resize_grab_radius_side: 5.0,
1974            resize_grab_radius_corner: 10.0,
1975            show_tooltips_only_when_still: true,
1976            tooltip_delay: 0.5,
1977            ..Default::default()
1978        },
1979        visuals: Visuals {
1980            dark_mode: true,
1981            override_text_color: None,
1982            widgets: Widgets {
1983                noninteractive: WidgetVisuals {
1984                    bg_fill: Color32::from_rgba_premultiplied(27, 27, 27, 255),
1985                    weak_bg_fill: Color32::from_rgba_premultiplied(27, 27, 27, 255),
1986                    bg_stroke: Stroke {
1987                        width: 1.0,
1988                        color: Color32::from_rgba_premultiplied(60, 60, 60, 255),
1989                    },
1990                    corner_radius: CornerRadius::ZERO,
1991                    fg_stroke: Stroke {
1992                        width: 1.0,
1993                        color: Color32::from_rgba_premultiplied(140, 140, 140, 255),
1994                    },
1995                    expansion: 0.0,
1996                },
1997                inactive: WidgetVisuals {
1998                    bg_fill: Color32::from_rgba_premultiplied(60, 60, 60, 255),
1999                    weak_bg_fill: Color32::from_rgba_premultiplied(60, 60, 60, 255),
2000                    bg_stroke: Stroke {
2001                        width: 0.0,
2002                        color: Color32::from_rgba_premultiplied(0, 0, 0, 0),
2003                    },
2004                    corner_radius: CornerRadius::ZERO,
2005                    fg_stroke: Stroke {
2006                        width: 1.0,
2007                        color: Color32::from_rgba_premultiplied(180, 180, 180, 255),
2008                    },
2009                    expansion: 0.0,
2010                },
2011                hovered: WidgetVisuals {
2012                    bg_fill: Color32::from_rgba_premultiplied(70, 70, 70, 255),
2013                    weak_bg_fill: Color32::from_rgba_premultiplied(70, 70, 70, 255),
2014                    bg_stroke: Stroke {
2015                        width: 1.0,
2016                        color: Color32::from_rgba_premultiplied(150, 150, 150, 255),
2017                    },
2018                    corner_radius: CornerRadius::ZERO,
2019                    fg_stroke: Stroke {
2020                        width: 1.5,
2021                        color: Color32::from_rgba_premultiplied(240, 240, 240, 255),
2022                    },
2023                    expansion: 1.0,
2024                },
2025                active: WidgetVisuals {
2026                    bg_fill: Color32::from_rgba_premultiplied(55, 55, 55, 255),
2027                    weak_bg_fill: Color32::from_rgba_premultiplied(55, 55, 55, 255),
2028                    bg_stroke: Stroke {
2029                        width: 1.0,
2030                        color: Color32::from_rgba_premultiplied(255, 255, 255, 255),
2031                    },
2032                    corner_radius: CornerRadius::ZERO,
2033                    fg_stroke: Stroke {
2034                        width: 2.0,
2035                        color: Color32::from_rgba_premultiplied(255, 255, 255, 255),
2036                    },
2037                    expansion: 1.0,
2038                },
2039                open: WidgetVisuals {
2040                    bg_fill: Color32::from_rgba_premultiplied(27, 27, 27, 255),
2041                    weak_bg_fill: Color32::from_rgba_premultiplied(27, 27, 27, 255),
2042                    bg_stroke: Stroke {
2043                        width: 1.0,
2044                        color: Color32::from_rgba_premultiplied(60, 60, 60, 255),
2045                    },
2046                    corner_radius: CornerRadius::ZERO,
2047                    fg_stroke: Stroke {
2048                        width: 1.0,
2049                        color: Color32::from_rgba_premultiplied(210, 210, 210, 255),
2050                    },
2051                    expansion: 0.0,
2052                },
2053            },
2054            selection: Selection {
2055                bg_fill: Color32::from_rgba_premultiplied(0, 92, 128, 255),
2056                stroke: Stroke {
2057                    width: 1.0,
2058                    color: Color32::from_rgba_premultiplied(192, 222, 255, 255),
2059                },
2060            },
2061            hyperlink_color: Color32::from_rgba_premultiplied(90, 170, 255, 255),
2062            faint_bg_color: Color32::from_rgba_premultiplied(0, 0, 0, 0),
2063            extreme_bg_color: Color32::from_rgba_premultiplied(17, 17, 17, 255),
2064            code_bg_color: Color32::from_rgba_premultiplied(64, 64, 64, 255),
2065            warn_fg_color: Color32::from_rgba_premultiplied(255, 143, 0, 255),
2066            error_fg_color: Color32::from_rgba_premultiplied(255, 0, 0, 255),
2067            window_corner_radius: CornerRadius::ZERO,
2068            window_shadow: Shadow {
2069                // extrusion: 5.0,
2070                color: Color32::from_rgba_premultiplied(0, 0, 0, 96),
2071                ..Default::default()
2072            },
2073            window_fill: Color32::from_rgba_premultiplied(27, 27, 27, 255),
2074            window_stroke: Stroke {
2075                width: 0.0,
2076                color: Color32::from_rgba_premultiplied(71, 71, 71, 255),
2077            },
2078            menu_corner_radius: CornerRadius::ZERO,
2079            panel_fill: Color32::from_rgba_premultiplied(20, 20, 20, 255),
2080            popup_shadow: Shadow {
2081                // extrusion: 16.0,
2082                color: Color32::from_rgba_premultiplied(0, 0, 0, 96),
2083                ..Default::default()
2084            },
2085            resize_corner_size: 12.0,
2086            // text_cursor_width: 2.0,
2087            text_cursor: TextCursorStyle::default(),
2088            // text_cursor_preview: false,
2089            clip_rect_margin: 3.0,
2090            button_frame: true,
2091            collapsing_header_frame: false,
2092            indent_has_left_vline: true,
2093            striped: false,
2094            slider_trailing_fill: true,
2095            window_highlight_topmost: true,
2096            handle_shape: HandleShape::Circle,
2097            interact_cursor: None,
2098            image_loading_spinners: true,
2099            numeric_color_space: NumericColorSpace::GammaByte,
2100            text_alpha_from_coverage: AlphaFromCoverage::DARK_MODE_DEFAULT,
2101            weak_text_alpha: 0.6,
2102            weak_text_color: None,
2103            text_edit_bg_color: None, // use `extreme_bg_color` by default
2104            disabled_alpha: 0.5,
2105        },
2106        animation_time: 0.08333,
2107        explanation_tooltips: false,
2108        ..Default::default()
2109    }
2110}