From 03b6ea5ed749f928a4968e6145eb9d05e26d2131 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 9 Jan 2024 21:14:57 -0800 Subject: [PATCH] Fixed system rendering --- crates/content/src/part/system.rs | 5 +- crates/galactica/src/game.rs | 3 +- crates/gameobject/src/lib.rs | 2 - crates/gameobject/src/system.rs | 37 --- crates/render/src/gpustate/hud.rs | 48 ++-- crates/render/src/gpustate/mod.rs | 10 +- crates/render/src/gpustate/world.rs | 383 ++++++++++++++++------------ crates/render/src/renderstate.rs | 5 +- 8 files changed, 268 insertions(+), 225 deletions(-) delete mode 100644 crates/gameobject/src/system.rs diff --git a/crates/content/src/part/system.rs b/crates/content/src/part/system.rs index b24fe81..7cf3c86 100644 --- a/crates/content/src/part/system.rs +++ b/crates/content/src/part/system.rs @@ -104,7 +104,7 @@ pub struct Object { /// This object's position, in game coordinates, /// relative to the system's center (0, 0). - pub position: Point3, + pub pos: Point3, /// This object's sprite's angle. pub angle: Rad, @@ -200,13 +200,14 @@ impl crate::Build for System { objects.push(Object { sprite: handle, - position: resolve_position(&system.object, &obj, cycle_detector) + pos: resolve_position(&system.object, &obj, cycle_detector) .with_context(|| format!("In object {:#?}", label))?, size: obj.size, angle: Deg(obj.angle.unwrap_or(0.0)).into(), }); } + objects.sort_by(|a, b| a.pos.z.total_cmp(&b.pos.z)); content.systems.push(Self { name: system_name, objects, diff --git a/crates/galactica/src/game.rs b/crates/galactica/src/game.rs index 426a8e0..9a5504c 100644 --- a/crates/galactica/src/game.rs +++ b/crates/galactica/src/game.rs @@ -157,10 +157,11 @@ impl Game { camera_zoom: self.camera.zoom, current_time: self.start_instant.elapsed().as_secs_f32(), content: &self.content, - world: &self.world, + world: &self.world, // TODO: maybe system should be stored here? particles: &mut self.new_particles, player_data: self.player, data: &self.gamedata, + current_system: content::SystemHandle { index: 0 }, } } } diff --git a/crates/gameobject/src/lib.rs b/crates/gameobject/src/lib.rs index 71ba7dd..adfd51b 100644 --- a/crates/gameobject/src/lib.rs +++ b/crates/gameobject/src/lib.rs @@ -7,8 +7,6 @@ mod gamedata; mod handles; pub mod ship; -mod system; pub use gamedata::*; pub use handles::*; -pub use system::{System, SystemObject}; diff --git a/crates/gameobject/src/system.rs b/crates/gameobject/src/system.rs deleted file mode 100644 index 08348b2..0000000 --- a/crates/gameobject/src/system.rs +++ /dev/null @@ -1,37 +0,0 @@ -use cgmath::{Point3, Rad}; -use galactica_content as content; - -// TODO: rework - -pub struct System { - pub name: String, - pub bodies: Vec, -} - -impl System { - pub fn new(ct: &content::Content, handle: content::SystemHandle) -> Self { - let sys = ct.get_system(handle); - let mut s = System { - name: sys.name.clone(), - bodies: Vec::new(), - }; - - for o in &sys.objects { - s.bodies.push(SystemObject { - pos: o.position, - sprite: o.sprite, - size: o.size, - angle: o.angle, - }); - } - - return s; - } -} - -pub struct SystemObject { - pub sprite: content::SpriteHandle, - pub pos: Point3, - pub size: f32, - pub angle: Rad, -} diff --git a/crates/render/src/gpustate/hud.rs b/crates/render/src/gpustate/hud.rs index 69c9077..c71a783 100644 --- a/crates/render/src/gpustate/hud.rs +++ b/crates/render/src/gpustate/hud.rs @@ -19,7 +19,7 @@ impl GPUState { let radar_size = 300.0; let hide_range = 0.85; let shrink_distance = 20.0; - //let system_object_scale = 1.0 / 600.0; + let system_object_scale = 1.0 / 600.0; let ship_scale = 1.0 / 10.0; let player_world_object = state.world.get_ship(state.player_data).unwrap(); @@ -29,7 +29,7 @@ impl GPUState { .unwrap(); let player_position = util::rigidbody_position(player_body); - //let planet_sprite = state.content.get_sprite_handle("ui::planetblip"); + let planet_sprite = state.content.get_sprite_handle("ui::planetblip"); let ship_sprite = state.content.get_sprite_handle("ui::shipblip"); let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow"); @@ -54,9 +54,9 @@ impl GPUState { ); self.vertex_buffers.ui_counter += 1; - /* // Draw system objects - for o in &system.bodies { + let system = state.content.get_system(state.current_system); + for o in &system.objects { let size = (o.size / o.pos.z) / (radar_range * system_object_scale); let p = Point2 { x: o.pos.x, @@ -72,24 +72,35 @@ impl GPUState { // Don't draw super tiny sprites, they flicker continue; } - out.push(UiSprite { - sprite: planet_sprite, - pos: AnchoredUiPosition::NwC( - Point2 { + + // Enforce buffer limit + if self.vertex_buffers.ui_counter as u64 + > galactica_constants::UI_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("UI limit exceeded!") + } + + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.ui.instances, + UiInstance::SIZE * self.vertex_buffers.ui_counter, + bytemuck::cast_slice(&[UiInstance { + anchor: PositionAnchor::NwC.to_int(), + position: (Point2 { x: radar_size / 2.0 + 10.0, y: radar_size / -2.0 - 10.0, - } + (d * (radar_size / 2.0)), - ), - dimensions: Point2 { - x: planet_sprite.aspect, - y: 1.0, - } * size, - angle: o.angle, - color: Some([0.5, 0.5, 0.5, 1.0]), - }); + } + (d * (radar_size / 2.0))) + .into(), + angle: o.angle.0, + size, + color: [0.5, 0.5, 0.5, 1.0], + sprite_index: planet_sprite.get_index(), + }]), + ); + self.vertex_buffers.ui_counter += 1; } } - */ // Draw ships for (s, r) in state.world.iter_ship_body() { @@ -123,6 +134,7 @@ impl GPUState { } + (d * (radar_size / 2.0)); // Enforce buffer limit + // TODO: cleaner solution. don't do this everywhere. if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT { diff --git a/crates/render/src/gpustate/mod.rs b/crates/render/src/gpustate/mod.rs index e86feb4..65fc0cb 100644 --- a/crates/render/src/gpustate/mod.rs +++ b/crates/render/src/gpustate/mod.rs @@ -364,13 +364,9 @@ impl GPUState { // Order matters, it determines what is drawn on top. // The order inside ships and projectiles doesn't matter, // but ships should always be under projectiles. - for s in state.world.iter_ships() { - self.world_push_ship(state, (clip_ne, clip_sw), &s); - } - - for p in state.world.iter_projectiles() { - self.world_push_projectile(state, (clip_ne, clip_sw), &p); - } + self.world_push_system(state, (clip_ne, clip_sw)); + self.world_push_ship(state, (clip_ne, clip_sw)); + self.world_push_projectile(state, (clip_ne, clip_sw)); self.hud_add_radar(state); self.hud_add_status(state); diff --git a/crates/render/src/gpustate/world.rs b/crates/render/src/gpustate/world.rs index de99ea2..41d98bb 100644 --- a/crates/render/src/gpustate/world.rs +++ b/crates/render/src/gpustate/world.rs @@ -2,10 +2,7 @@ use bytemuck; use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2}; -use galactica_world::{ - objects::{ProjectileWorldObject, ShipWorldObject}, - util, -}; +use galactica_world::util; use crate::{ globaluniform::ObjectData, @@ -19,114 +16,115 @@ impl GPUState { state: &RenderState, // NE and SW corners of screen screen_clip: (Point2, Point2), - s: &ShipWorldObject, ) { - let r = state.world.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()); + for s in state.world.iter_ships() { + let r = state.world.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; + // 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); + // 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 - { - return; - } + // 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.vertex_buffers.object_counter; - // Write this object's location data - self.queue.write_buffer( - &self.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(), - }]), - ); + let idx = self.vertex_buffers.object_counter; + // Write this object's location data + self.queue.write_buffer( + &self.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.vertex_buffers.object_counter as u64 - > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT - { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") - } + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } - // Push this object's instance - self.queue.write_buffer( - &self.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: ship_cnt.sprite.get_index(), - object_index: idx as u32, - }]), - ); - self.vertex_buffers.object_counter += 1; + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: ship_cnt.sprite.get_index(), + object_index: idx as u32, + }]), + ); + self.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 => return, - Some(s) => s, - }; + // 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.queue.write_buffer( - &self.global_uniform.object_buffer, - ObjectData::SIZE * self.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(), - }]), - ); + 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.queue.write_buffer( + &self.global_uniform.object_buffer, + ObjectData::SIZE * self.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.vertex_buffers.object_counter as u64 - > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT - { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") + // Enforce buffer limit + if self.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: flare.unwrap().get_index(), + object_index: self.vertex_buffers.object_counter as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; } - - self.queue.write_buffer( - &self.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: flare.unwrap().get_index(), - object_index: self.vertex_buffers.object_counter as u32, - }]), - ); - self.vertex_buffers.object_counter += 1; } } } @@ -136,69 +134,140 @@ impl GPUState { state: &RenderState, // NE and SW corners of screen screen_clip: (Point2, Point2), - p: &ProjectileWorldObject, ) { - let r = state.world.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? + for p in state.world.iter_projectiles() { + let r = state.world.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; + // 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); + // 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 - { - return; + // 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.vertex_buffers.object_counter; + // Write this object's location data + self.queue.write_buffer( + &self.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.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: proj_cnt.sprite.get_index(), + object_index: idx as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; } + } - let idx = self.vertex_buffers.object_counter; - // Write this object's location data - self.queue.write_buffer( - &self.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(), - }]), - ); + pub(super) fn world_push_system( + &mut self, + state: &RenderState, + // NE and SW corners of screen + screen_clip: (Point2, Point2), + ) { + let system = state.content.get_system(state.current_system); - // Enforce buffer limit - if self.vertex_buffers.object_counter as u64 - > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT - { - // TODO: no panic, handle this better. - panic!("Sprite limit exceeded!") + 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.vertex_buffers.object_counter; + // Write this object's location data + self.queue.write_buffer( + &self.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.vertex_buffers.object_counter as u64 + > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT + { + // TODO: no panic, handle this better. + panic!("Sprite limit exceeded!") + } + + // Push this object's instance + self.queue.write_buffer( + &self.vertex_buffers.object.instances, + ObjectInstance::SIZE * self.vertex_buffers.object_counter, + bytemuck::cast_slice(&[ObjectInstance { + sprite_index: o.sprite.get_index(), + object_index: idx as u32, + }]), + ); + self.vertex_buffers.object_counter += 1; } - - // Push this object's instance - self.queue.write_buffer( - &self.vertex_buffers.object.instances, - ObjectInstance::SIZE * self.vertex_buffers.object_counter, - bytemuck::cast_slice(&[ObjectInstance { - sprite_index: proj_cnt.sprite.get_index(), - object_index: idx as u32, - }]), - ); - self.vertex_buffers.object_counter += 1; } } diff --git a/crates/render/src/renderstate.rs b/crates/render/src/renderstate.rs index 09de124..811d4f6 100644 --- a/crates/render/src/renderstate.rs +++ b/crates/render/src/renderstate.rs @@ -1,5 +1,5 @@ use cgmath::Point2; -use galactica_content::Content; +use galactica_content::{Content, SystemHandle}; use galactica_gameobject::{GameData, GameShipHandle}; use galactica_world::{ParticleBuilder, World}; @@ -11,6 +11,9 @@ pub struct RenderState<'a> { /// Player ship data pub player_data: GameShipHandle, + /// The system we're currently in + pub current_system: SystemHandle, + /// Height of screen, in world units pub camera_zoom: f32,