//! GPUState routines for drawing items in a systemsim use bytemuck; use galactica_system::data::ShipState; use galactica_util::to_radians; use nalgebra::{Point2, Point3}; use crate::{ globaluniform::ObjectData, vertexbuffer::types::ObjectInstance, GPUState, RenderInput, }; impl GPUState { pub(super) fn phys_push_ship( &mut self, state: &RenderInput, // NE and SW corners of screen screen_clip: (Point2, Point2), ) { for ship in state.systemsim.iter_ships() { // TODO: move collapse sequence here? let ship_pos; let ship_ang; let ship_cnt; match ship.get_data().get_state() { ShipState::Dead | ShipState::Landed { .. } => continue, ShipState::Collapsing { .. } | ShipState::Flying { .. } => { let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); let pos = *r.translation(); ship_pos = Point3::new(pos.x, pos.y, 1.0); let ship_rot = r.rotation(); ship_ang = ship_rot.angle(); ship_cnt = state.ct.get_ship(ship.get_data().get_content()); } ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => { let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); let pos = *r.translation(); ship_pos = Point3::new(pos.x, pos.y, *current_z); let ship_rot = r.rotation(); ship_ang = ship_rot.angle(); ship_cnt = state.ct.get_ship(ship.get_data().get_content()); } } // Position adjusted for parallax // TODO: adjust parallax for zoom? // 1.0 is z-coordinate, which is constant for ships let pos: Point2 = (Point2::new(ship_pos.x, ship_pos.y) - state.camera_pos) / ship_pos.z; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. // This is in game units. // // We take the maximum to account for rotated sprites. let m = (ship_cnt.size / ship_pos.z) * ship_cnt.sprite.aspect.max(1.0); // Don't draw sprites that are off the screen if pos.x < screen_clip.0.x - m || pos.y > screen_clip.0.y + m || pos.x > screen_clip.1.x + m || pos.y < screen_clip.1.y - m { continue; } let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, ObjectData::SIZE * idx as u64, bytemuck::cast_slice(&[ObjectData { xpos: ship_pos.x, ypos: ship_pos.y, zpos: ship_pos.z, angle: ship_ang, size: ship_cnt.size, parent: 0, is_child: 0, _padding: Default::default(), }]), ); // Push this object's instance let anim_state = ship.get_anim_state(); self.state.push_object_buffer(ObjectInstance { texture_index: anim_state.texture_index(), texture_fade: anim_state.fade, object_index: idx as u32, }); if { let is_flying = match ship.get_data().get_state() { ShipState::Flying { .. } | ShipState::UnLanding { .. } | ShipState::Landing { .. } => true, _ => false, }; ship.get_controls().thrust && is_flying } { for (engine_point, anim) in ship.iter_engine_anim() { self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, ObjectData::SIZE * self.state.get_object_counter() as u64, bytemuck::cast_slice(&[ObjectData { // Note that we adjust the y-coordinate for half-height, // not the x-coordinate, even though our ships point east // at 0 degrees. This is because this is placed pre-rotation, // and the parent rotation adjustment in our object shader // automatically accounts for this. xpos: engine_point.pos.x, ypos: engine_point.pos.y - engine_point.size / 2.0, zpos: 1.0, // We still need an adjustment here, though, // since engine sprites point north (with exhaust towards the south) angle: to_radians(90.0), size: engine_point.size, parent: idx as u32, is_child: 1, _padding: Default::default(), }]), ); let anim_state = anim.get_texture_idx(); self.state.push_object_buffer(ObjectInstance { texture_index: anim_state.texture_index(), texture_fade: anim_state.fade, object_index: self.state.get_object_counter() as u32, }); } } } } pub(super) fn phys_push_projectile( &mut self, state: &RenderInput, // NE and SW corners of screen screen_clip: (Point2, Point2), ) { for p in state.systemsim.iter_projectiles() { let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap(); let proj_pos = *r.translation(); let proj_rot = r.rotation(); let proj_ang = proj_rot.angle(); let proj_cnt = &p.content; // TODO: don't clone this? // Position adjusted for parallax // TODO: adjust parallax for zoom? // 1.0 is z-coordinate, which is constant for projectiles let pos = (proj_pos - state.camera_pos) / 1.0; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. // This is in game units. // // We take the maximum to account for rotated sprites. let m = (proj_cnt.size / 1.0) * proj_cnt.sprite.aspect.max(1.0); // Don't draw sprites that are off the screen if pos.x < screen_clip.0.x - m || pos.y > screen_clip.0.y + m || pos.x > screen_clip.1.x + m || pos.y < screen_clip.1.y - m { continue; } let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, ObjectData::SIZE * idx as u64, bytemuck::cast_slice(&[ObjectData { xpos: proj_pos.x, ypos: proj_pos.y, zpos: 1.0, angle: proj_ang, size: 0f32.max(proj_cnt.size + p.size_rng), parent: 0, is_child: 0, _padding: Default::default(), }]), ); let anim_state = p.get_anim_state(); self.state.push_object_buffer(ObjectInstance { texture_index: anim_state.texture_index(), texture_fade: anim_state.fade, object_index: idx as u32, }); } } pub(super) fn phys_push_system( &mut self, state: &RenderInput, // NE and SW corners of screen screen_clip: (Point2, Point2), ) { let system = state.ct.get_system(state.current_system); for o in &system.objects { // Position adjusted for parallax let pos: Point2 = (Point2::new(o.pos.x, o.pos.y) - state.camera_pos) / o.pos.z; // Game dimensions of this sprite post-scale. // Post-scale width or height, whichever is larger. // This is in game units. // // We take the maximum to account for rotated sprites. let m = (o.size / o.pos.z) * o.sprite.aspect.max(1.0); // Don't draw sprites that are off the screen if pos.x < screen_clip.0.x - m || pos.y > screen_clip.0.y + m || pos.x > screen_clip.1.x + m || pos.y < screen_clip.1.y - m { continue; } let idx = self.state.get_object_counter(); // Write this object's location data self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, ObjectData::SIZE * idx as u64, bytemuck::cast_slice(&[ObjectData { xpos: o.pos.x, ypos: o.pos.y, zpos: o.pos.z, angle: o.angle, size: o.size, parent: 0, is_child: 0, _padding: Default::default(), }]), ); let sprite = state.ct.get_sprite(o.sprite); let texture_a = sprite.get_section(sprite.default_section).frames[0]; // ANIMATE // Push this object's instance self.state.push_object_buffer(ObjectInstance { texture_index: [texture_a, texture_a], texture_fade: 1.0, object_index: idx as u32, }); } } }