pub mod camera; pub mod render; mod body_render; mod orbit_render; use std::time::Duration; use cgmath::InnerSpace; use winit::event::ElementState; use winit::keyboard::KeyCode; use crate::solar_system::SolarSystem; use crate::solar_system::SystemId; use crate::timeman::TimeMan; use crate::ui; use crate::wgpuctx::SceneCtx; use crate::wgpuctx::WgpuCtx; use render::*; use body_render::*; use orbit_render::*; use camera::*; pub struct TacticalMap { camera: Camera, pmatrix: Projection, camera_controller: CameraController, system_for_render: Option, body_renderer: BodyRenderer, grid_renderer: GridRenderer, orbit_renderer: OrbitRenderer } impl TacticalMap { pub fn new(wgpuctx: &WgpuCtx) -> Self { let surface_size = winit::dpi::PhysicalSize::new( wgpuctx.surface_config().width, wgpuctx.surface_config().height ); let camera = Camera::new( wgpuctx, cgmath::Vector3::new(0.0, 0.0, 1.0), cgmath::Deg(0.0), cgmath::Rad(0.0)); let projection = Projection::new( surface_size.width, surface_size.height, cgmath::Deg(60.0), (0.01, 1000.0)); Self { camera: camera, pmatrix: projection, camera_controller: CameraController::new(), system_for_render: None, body_renderer: BodyRenderer::new(wgpuctx), grid_renderer: GridRenderer::new(wgpuctx), orbit_renderer: OrbitRenderer::new(wgpuctx), } } pub fn resize( &mut self, width: u32, height: u32) { self.pmatrix.resize(width, height); } pub fn update( &mut self, solar_system: &SolarSystem, ui_state: &mut ui::State, dt: Duration) { ui_state.camera_info.camera_scale = self.camera.get_scale(); ui_state.camera_info.camera_pos = Some(self.camera.get_abs_position()); ui_state.camera_info.camera_rot = Some(self.camera.get_rotation()); self.camera.set_target(ui_state.camera_info.target); self.camera_controller.update(&mut self.camera, solar_system, ui_state, dt); } pub fn keyboard_input( &mut self, key_code: KeyCode, key_state: ElementState) { self.camera_controller.keyboard_input(key_code, key_state); } pub fn draw( &mut self, wgpuctx: &WgpuCtx, solar_system: &SolarSystem, timeman: &TimeMan, ) -> Result<(), wgpu::SurfaceError> { self.camera.update_buffer(wgpuctx, &self.pmatrix); if self.system_for_render.is_none() || self.system_for_render.unwrap() != solar_system.id() { self.body_renderer.mark_to_rebuild(); self.orbit_renderer.mark_to_rebuild(); } self.system_for_render = Some(solar_system.id()); //Update buffers for the current system and time. self.body_renderer.rebuild(wgpuctx, solar_system); self.orbit_renderer.rebuild(wgpuctx, solar_system); match self.orbit_renderer.update(wgpuctx, solar_system, timeman.seconds()) { Ok(()) => {}, Err(e) => println!("Tactical map orbit render update error: {}", e) } match self.body_renderer.update( wgpuctx, solar_system, timeman.seconds()) { Ok(()) => {}, Err(e) => println!("Tactical map body render update error: {}", e) } Ok(()) } pub fn prepare( &self, scene: &mut SceneCtx) { self.camera.stage_changes(scene.encoder_mut()); } pub fn paint( &self, pass: &mut wgpu::RenderPass<'_>) { self.grid_renderer.render( pass, &self.camera); self.orbit_renderer.render(pass, &self.camera); self.body_renderer.render( pass, &self.camera); } pub fn paint_labels( &self, solar_system: &SolarSystem, screen_size: egui::Vec2, ui: &mut egui::Ui) { let view_matrix = self.camera.view_matrix(); let proj_matrix = self.pmatrix.projection_matrix(); let pv_matrix = proj_matrix * view_matrix; //Paint body labels let bodies = solar_system.bodies(); bodies.iter().for_each(|body| { let scaled_radius = (screen_size.y * body.radius() * self.camera.get_scale()).max(16.0); let world_pos = solar_system.body_position(body); let local_pos = world_pos - self.camera.get_abs_position(); let origin_pos = match body.get_orbit() { Some(orbit) => { solar_system.body_position(&bodies[orbit.parent()]) }, None => cgmath::vec3(0.0, 0.0, 0.0) }; let origin_local_pos = origin_pos - self.camera.get_abs_position(); let scaled_pos = local_pos * self.camera.get_scale() as f64; let scaled_origin_pos = origin_local_pos * self.camera.get_scale() as f64; let clip_pos = pv_matrix * scaled_pos.map(|v| { v as f32 }).extend(1.0); let origin_clip_pos = pv_matrix * scaled_origin_pos.map(|v| { v as f32 }).extend(1.0); let ndc_pos = clip_pos.truncate() / clip_pos.w; let origin_ndc_pos = origin_clip_pos.truncate() / origin_clip_pos.w; if (ndc_pos - origin_ndc_pos).magnitude() < 0.05 { if ndc_pos != origin_ndc_pos { return; } } let screen_pos = egui::pos2( ((ndc_pos.x + 1.0) * 0.5) * screen_size.x, (((-ndc_pos.y + 1.0) * 0.5) * screen_size.y) + (scaled_radius / clip_pos.w).max(16.0)); if clip_pos.z < 0.0 { return; } ui.put( egui::Rect::from_pos(screen_pos).expand(64.0), egui::Label::new( egui::RichText::new(body.name()) .color(egui::Color32::WHITE) ).selectable(false).sense(egui::Sense::empty())); }); } } // impl SystemViewport