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() + ¤t_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}