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