use egui::{Color32, Stroke, Ui, epaint::CircleShape}; use fastnoise_lite::NoiseType; use glam::IVec2; use rapier2d::pipeline::QueryFilter; use crate::{ Camera, Config, Diagnostics, content::materials::MaterialId, input::InputManager, proc_gen::WorldGenerator, sim::{rb_manager::DebugRenderMode, sim_manager::SimManager}, }; const DEBUG_RENDER_MODES: [(&str, DebugRenderMode); 6] = [ ("Collider shapes", DebugRenderMode::COLLIDER_SHAPES), ("Collider AABBs", DebugRenderMode::COLLIDER_AABBS), ("Rigid body axes", DebugRenderMode::RIGID_BODY_AXES), ("Joints", DebugRenderMode::JOINTS), ("Contacts", DebugRenderMode::CONTACTS), ("Solver contacts", DebugRenderMode::SOLVER_CONTACTS), ]; pub fn draw_egui<'a>( ui: &mut Ui, config: &mut Config, camera: &mut Camera, diagnostics: &Diagnostics, input_manager: &InputManager, sim: &mut SimManager, world_generator: &mut WorldGenerator, ) { puffin::profile_function!(); ui.heading("Config"); ui.add(egui::Slider::new(&mut config.brush_radius, 1.0..=100.0).text("Brush radius")); let material = config.brush_material.def(); // material combobox egui::ComboBox::from_label("Select a brush material") .selected_text(format!( "Material: [{}, d={}]", material.name, material.density, )) .icon(move |ui, rect, _, _| { ui.painter().add(egui::Shape::Circle(CircleShape { center: rect.center(), radius: rect.height() / 2.0, stroke: Stroke::NONE, fill: Color32::from_rgb(material.color.0, material.color.1, material.color.2), })); }) .show_ui(ui, |ui| { for material_id in MaterialId::ALL { ui.selectable_value( &mut config.brush_material, material_id, material_id.def().name, ); } }); let dropper_material = config.dropper_material.def(); egui::ComboBox::from_label("Select a dropper material") .selected_text(format!( "Material: [{}, d={}]", dropper_material.name, dropper_material.density, )) .icon(move |ui, rect, _, _| { ui.painter().add(egui::Shape::Circle(CircleShape { center: rect.center(), radius: rect.height() / 2.0, stroke: Stroke::NONE, fill: Color32::from_rgb( dropper_material.color.0, dropper_material.color.1, dropper_material.color.2, ), })); }) .show_ui(ui, |ui| { for material_id in MaterialId::ALL { ui.selectable_value( &mut config.dropper_material, material_id, material_id.def().name, ); } }); ui.checkbox(&mut config.use_threading, "Use multithreading"); ui.checkbox(&mut config.cells_render, "Draw cells"); ui.checkbox(&mut config.debug_render, "Draw physics debug lines"); if config.debug_render { ui.indent("debug_render_modes", |ui| { for (name, flag) in DEBUG_RENDER_MODES { let mut enabled = config.debug_render_mode.contains(flag); if ui.checkbox(&mut enabled, name).changed() { config.debug_render_mode.set(flag, enabled); } } }); } ui.label(format!("Real FPS: {}", diagnostics.fps)); ui.heading("Camera"); ui.add(egui::Slider::new(&mut camera.zoom, 0.0..=10.0).text("Zoom")); ui.heading("Proc gen"); if ui.button("Generate").clicked() { world_generator.update_params(config.world_generator_config); for cx in -5..5 { for cy in -5..5 { let chunk = world_generator.generate_chunk(IVec2::new(cx, cy)); sim.cell_manager.upsert(cx, cy, chunk); } } } ui.add( egui::Slider::new( &mut config.world_generator_config.proc_gen_seed, -100000..=100000, ) .text("Seed"), ); ui.add( egui::Slider::new( &mut config.world_generator_config.occupied_threshold, 0.0..=1.0, ) .text("Occupation threshold"), ); ui.add( egui::Slider::new( &mut config.world_generator_config.stone_threshold, 0.0..=1.0, ) .text("Stone threshold"), ); ui.label("Cave noise"); ui.add( egui::Slider::new( &mut config.world_generator_config.cave_noise_freq, 0.0..=1.0, ) .text("Frequency"), ); ui.label("Hardness noise"); ui.add( egui::Slider::new( &mut config.world_generator_config.hardness_noise_freq, 0.0..=1.0, ) .text("Frequency"), ); ui.label(format!( "Mouse (world): x,y=({x}, {y})", x = input_manager.world_mouse_pos.x, y = input_manager.world_mouse_pos.y )); ui.label(format!( "Mouse (chunk): x,y=({x}, {y})", x = input_manager.chunk_mouse_pos.x, y = input_manager.chunk_mouse_pos.y )); if let Some(&idx) = sim.cell_manager.chunk_position_to_chunk_idx.get(&( input_manager.chunk_mouse_pos.x, input_manager.chunk_mouse_pos.y, )) { ui.label(format!( "Sleeping={}", sim.cell_manager.chunks[idx].sleeping )); } ui.label(format!( "Mouse (local): x,y=({x}, {y})", x = input_manager.local_mouse_pos.x, y = input_manager.local_mouse_pos.y )); ui.heading("Entity"); if let Some(cell) = sim.cell_manager.get_cell_from_game_position( input_manager.world_mouse_pos.x.round() as i32, input_manager.world_mouse_pos.y.round() as i32, ) { let material = cell.material.def(); let cell_label = ui.label( egui::RichText::new(format!("Cell: {}", material.name)).color(Color32::LIGHT_BLUE), ); ui.painter().add(egui::Shape::Circle(CircleShape { center: cell_label.rect.right_center() + egui::vec2(10.0, 0.0), radius: cell_label.rect.height() / 2.5, stroke: Stroke::NONE, fill: Color32::from_rgb(material.color.0, material.color.1, material.color.2), })); ui.label(egui::RichText::new(format!("Flags: {:b}", cell.flags)).color(Color32::YELLOW)); ui.label(egui::RichText::new(format!("Data: {:b}", cell.data)).color(Color32::RED)); } let mut colliders_at_mouse = sim .rb_manager .physics_manager .world .intersect_point(input_manager.world_mouse_pos, QueryFilter::only_dynamic()); if let Some((_, collider)) = colliders_at_mouse.next() && let Some(rb_h) = collider.parent() && let Some(rb) = sim.rb_manager.physics_manager.world.bodies.get(rb_h) { ui.label("RB"); ui.label(format!( "Position: {:.02}, rotation: {:.02}", rb.translation(), rb.rotation().angle() )); ui.label(format!( "Velocity: {:.02}, sleeping: {}", rb.linvel(), rb.is_sleeping() )); } }