//! GPUState routines for drawing items in a systemsim use bytemuck; use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2}; use galactica_systemsim::util; use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT; use crate::{ globaluniform::ObjectData, vertexbuffer::{types::ObjectInstance, BufferObject}, GPUState, RenderInput, }; impl GPUState { pub(super) fn sysim_push_ship( &mut self, state: &RenderInput, // NE and SW corners of screen screen_clip: (Point2, Point2), ) { for s in state.systemsim.iter_ships() { let r = state.systemsim.get_rigid_body(s.rigid_body).unwrap(); let ship_pos = util::rigidbody_position(&r); let ship_rot = util::rigidbody_rotation(r); let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix! let ship_cnt = state.content.get_ship(s.data_handle.content_handle()); // Position adjusted for parallax // TODO: adjust parallax for zoom? // 1.0 is z-coordinate, which is constant for ships let pos: Point2 = (ship_pos - state.camera_pos.to_vec()) / 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 = (ship_cnt.size / 1.0) * 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.vertex_buffers.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: 1.0, angle: ship_ang.0, size: ship_cnt.size, parent: 0, is_child: 0, _padding: Default::default(), }]), ); // Enforce buffer limit if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. panic!("Sprite limit exceeded!") } // Push this object's instance self.state.queue.write_buffer( &self.state.vertex_buffers.object.instances, ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, bytemuck::cast_slice(&[ObjectInstance { sprite_index: ship_cnt.sprite.get_index(), object_index: idx as u32, }]), ); self.state.vertex_buffers.object_counter += 1; // This will be None if this ship is dead. // (physics object stays around to complete the death animation) // If that is the case, we're done, no flares to draw anyway! let ship = match state.data.get_ship(s.data_handle) { None => continue, Some(s) => s, }; let flare = ship.get_outfits().get_flare_sprite(state.content); if s.get_controls().thrust && flare.is_some() { for engine_point in &ship_cnt.engines { self.state.queue.write_buffer( &self.state.global_uniform.object_buffer, ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64, bytemuck::cast_slice(&[ObjectData { xpos: engine_point.pos.x, ypos: engine_point.pos.y - engine_point.size / 2.0, zpos: 1.0, angle: 0.0, size: engine_point.size, parent: idx as u32, is_child: 1, _padding: Default::default(), }]), ); // Enforce buffer limit if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. panic!("Sprite limit exceeded!") } self.state.queue.write_buffer( &self.state.vertex_buffers.object.instances, ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, bytemuck::cast_slice(&[ObjectInstance { sprite_index: flare.unwrap().get_index(), object_index: self.state.vertex_buffers.object_counter as u32, }]), ); self.state.vertex_buffers.object_counter += 1; } } } } pub(super) fn sysim_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 = util::rigidbody_position(&r); let proj_rot = util::rigidbody_rotation(r); let proj_ang = -proj_rot.angle(Vector2 { x: 1.0, y: 0.0 }); 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 ships let pos: Point2 = (proj_pos - state.camera_pos.to_vec()) / 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.vertex_buffers.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.0, size: 0f32.max(proj_cnt.size + p.size_rng), parent: 0, is_child: 0, _padding: Default::default(), }]), ); // Enforce buffer limit if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. panic!("Sprite limit exceeded!") } // Push this object's instance self.state.queue.write_buffer( &self.state.vertex_buffers.object.instances, ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, bytemuck::cast_slice(&[ObjectInstance { sprite_index: proj_cnt.sprite.get_index(), object_index: idx as u32, }]), ); self.state.vertex_buffers.object_counter += 1; } } pub(super) fn sysim_push_system( &mut self, state: &RenderInput, // NE and SW corners of screen screen_clip: (Point2, Point2), ) { let system = state.content.get_system(state.current_system); for o in &system.objects { // Position adjusted for parallax let pos: Point2 = (Point2 { x: o.pos.x, y: o.pos.y, } - state.camera_pos.to_vec()) / 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.vertex_buffers.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.0, size: o.size, parent: 0, is_child: 0, _padding: Default::default(), }]), ); // Enforce buffer limit if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT { // TODO: no panic, handle this better. panic!("Sprite limit exceeded!") } // Push this object's instance self.state.queue.write_buffer( &self.state.vertex_buffers.object.instances, ObjectInstance::SIZE * self.state.vertex_buffers.object_counter, bytemuck::cast_slice(&[ObjectInstance { sprite_index: o.sprite.get_index(), object_index: idx as u32, }]), ); self.state.vertex_buffers.object_counter += 1; } } }