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