Fixed system rendering

master
Mark 2024-01-09 21:14:57 -08:00
parent 64885a8b6d
commit 03b6ea5ed7
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
8 changed files with 268 additions and 225 deletions

View File

@ -104,7 +104,7 @@ pub struct Object {
/// This object's position, in game coordinates, /// This object's position, in game coordinates,
/// relative to the system's center (0, 0). /// relative to the system's center (0, 0).
pub position: Point3<f32>, pub pos: Point3<f32>,
/// This object's sprite's angle. /// This object's sprite's angle.
pub angle: Rad<f32>, pub angle: Rad<f32>,
@ -200,13 +200,14 @@ impl crate::Build for System {
objects.push(Object { objects.push(Object {
sprite: handle, sprite: handle,
position: resolve_position(&system.object, &obj, cycle_detector) pos: resolve_position(&system.object, &obj, cycle_detector)
.with_context(|| format!("In object {:#?}", label))?, .with_context(|| format!("In object {:#?}", label))?,
size: obj.size, size: obj.size,
angle: Deg(obj.angle.unwrap_or(0.0)).into(), 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 { content.systems.push(Self {
name: system_name, name: system_name,
objects, objects,

View File

@ -157,10 +157,11 @@ impl Game {
camera_zoom: self.camera.zoom, camera_zoom: self.camera.zoom,
current_time: self.start_instant.elapsed().as_secs_f32(), current_time: self.start_instant.elapsed().as_secs_f32(),
content: &self.content, content: &self.content,
world: &self.world, world: &self.world, // TODO: maybe system should be stored here?
particles: &mut self.new_particles, particles: &mut self.new_particles,
player_data: self.player, player_data: self.player,
data: &self.gamedata, data: &self.gamedata,
current_system: content::SystemHandle { index: 0 },
} }
} }
} }

View File

@ -7,8 +7,6 @@
mod gamedata; mod gamedata;
mod handles; mod handles;
pub mod ship; pub mod ship;
mod system;
pub use gamedata::*; pub use gamedata::*;
pub use handles::*; pub use handles::*;
pub use system::{System, SystemObject};

View File

@ -1,37 +0,0 @@
use cgmath::{Point3, Rad};
use galactica_content as content;
// TODO: rework
pub struct System {
pub name: String,
pub bodies: Vec<SystemObject>,
}
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<f32>,
pub size: f32,
pub angle: Rad<f32>,
}

View File

@ -19,7 +19,7 @@ impl GPUState {
let radar_size = 300.0; let radar_size = 300.0;
let hide_range = 0.85; let hide_range = 0.85;
let shrink_distance = 20.0; 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 ship_scale = 1.0 / 10.0;
let player_world_object = state.world.get_ship(state.player_data).unwrap(); let player_world_object = state.world.get_ship(state.player_data).unwrap();
@ -29,7 +29,7 @@ impl GPUState {
.unwrap(); .unwrap();
let player_position = util::rigidbody_position(player_body); 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 ship_sprite = state.content.get_sprite_handle("ui::shipblip");
let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow"); let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow");
@ -54,9 +54,9 @@ impl GPUState {
); );
self.vertex_buffers.ui_counter += 1; self.vertex_buffers.ui_counter += 1;
/*
// Draw system objects // 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 size = (o.size / o.pos.z) / (radar_range * system_object_scale);
let p = Point2 { let p = Point2 {
x: o.pos.x, x: o.pos.x,
@ -72,24 +72,35 @@ impl GPUState {
// Don't draw super tiny sprites, they flicker // Don't draw super tiny sprites, they flicker
continue; continue;
} }
out.push(UiSprite {
sprite: planet_sprite, // Enforce buffer limit
pos: AnchoredUiPosition::NwC( if self.vertex_buffers.ui_counter as u64
Point2 { > 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, x: radar_size / 2.0 + 10.0,
y: radar_size / -2.0 - 10.0, y: radar_size / -2.0 - 10.0,
} + (d * (radar_size / 2.0)), } + (d * (radar_size / 2.0)))
), .into(),
dimensions: Point2 { angle: o.angle.0,
x: planet_sprite.aspect, size,
y: 1.0, color: [0.5, 0.5, 0.5, 1.0],
} * size, sprite_index: planet_sprite.get_index(),
angle: o.angle, }]),
color: Some([0.5, 0.5, 0.5, 1.0]), );
}); self.vertex_buffers.ui_counter += 1;
} }
} }
*/
// Draw ships // Draw ships
for (s, r) in state.world.iter_ship_body() { for (s, r) in state.world.iter_ship_body() {
@ -123,6 +134,7 @@ impl GPUState {
} + (d * (radar_size / 2.0)); } + (d * (radar_size / 2.0));
// Enforce buffer limit // Enforce buffer limit
// TODO: cleaner solution. don't do this everywhere.
if self.vertex_buffers.ui_counter as u64 if self.vertex_buffers.ui_counter as u64
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT > galactica_constants::UI_SPRITE_INSTANCE_LIMIT
{ {

View File

@ -364,13 +364,9 @@ impl GPUState {
// Order matters, it determines what is drawn on top. // Order matters, it determines what is drawn on top.
// The order inside ships and projectiles doesn't matter, // The order inside ships and projectiles doesn't matter,
// but ships should always be under projectiles. // but ships should always be under projectiles.
for s in state.world.iter_ships() { self.world_push_system(state, (clip_ne, clip_sw));
self.world_push_ship(state, (clip_ne, clip_sw), &s); self.world_push_ship(state, (clip_ne, clip_sw));
} self.world_push_projectile(state, (clip_ne, clip_sw));
for p in state.world.iter_projectiles() {
self.world_push_projectile(state, (clip_ne, clip_sw), &p);
}
self.hud_add_radar(state); self.hud_add_radar(state);
self.hud_add_status(state); self.hud_add_status(state);

View File

@ -2,10 +2,7 @@
use bytemuck; use bytemuck;
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2}; use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
use galactica_world::{ use galactica_world::util;
objects::{ProjectileWorldObject, ShipWorldObject},
util,
};
use crate::{ use crate::{
globaluniform::ObjectData, globaluniform::ObjectData,
@ -19,114 +16,115 @@ impl GPUState {
state: &RenderState, state: &RenderState,
// NE and SW corners of screen // NE and SW corners of screen
screen_clip: (Point2<f32>, Point2<f32>), screen_clip: (Point2<f32>, Point2<f32>),
s: &ShipWorldObject,
) { ) {
let r = state.world.get_rigid_body(s.rigid_body).unwrap(); for s in state.world.iter_ships() {
let ship_pos = util::rigidbody_position(&r); let r = state.world.get_rigid_body(s.rigid_body).unwrap();
let ship_rot = util::rigidbody_rotation(r); let ship_pos = util::rigidbody_position(&r);
let ship_ang = -ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }); // TODO: inconsistent angles. Fix! let ship_rot = util::rigidbody_rotation(r);
let ship_cnt = state.content.get_ship(s.data_handle.content_handle()); 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 // Position adjusted for parallax
// TODO: adjust parallax for zoom? // TODO: adjust parallax for zoom?
// 1.0 is z-coordinate, which is constant for ships // 1.0 is z-coordinate, which is constant for ships
let pos: Point2<f32> = (ship_pos - state.camera_pos.to_vec()) / 1.0; let pos: Point2<f32> = (ship_pos - state.camera_pos.to_vec()) / 1.0;
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Post-scale width or height, whichever is larger. // Post-scale width or height, whichever is larger.
// This is in game units. // This is in game units.
// //
// We take the maximum to account for rotated sprites. // We take the maximum to account for rotated sprites.
let m = (ship_cnt.size / 1.0) * ship_cnt.sprite.aspect.max(1.0); let m = (ship_cnt.size / 1.0) * ship_cnt.sprite.aspect.max(1.0);
// Don't draw sprites that are off the screen // Don't draw sprites that are off the screen
if pos.x < screen_clip.0.x - m if pos.x < screen_clip.0.x - m
|| pos.y > screen_clip.0.y + m || pos.y > screen_clip.0.y + m
|| pos.x > screen_clip.1.x + m || pos.x > screen_clip.1.x + m
|| pos.y < screen_clip.1.y - m || pos.y < screen_clip.1.y - m
{ {
return; continue;
} }
let idx = self.vertex_buffers.object_counter; let idx = self.vertex_buffers.object_counter;
// Write this object's location data // Write this object's location data
self.queue.write_buffer( self.queue.write_buffer(
&self.global_uniform.object_buffer, &self.global_uniform.object_buffer,
ObjectData::SIZE * idx as u64, ObjectData::SIZE * idx as u64,
bytemuck::cast_slice(&[ObjectData { bytemuck::cast_slice(&[ObjectData {
xpos: ship_pos.x, xpos: ship_pos.x,
ypos: ship_pos.y, ypos: ship_pos.y,
zpos: 1.0, zpos: 1.0,
angle: ship_ang.0, angle: ship_ang.0,
size: ship_cnt.size, size: ship_cnt.size,
parent: 0, parent: 0,
is_child: 0, is_child: 0,
_padding: Default::default(), _padding: Default::default(),
}]), }]),
); );
// Enforce buffer limit // Enforce buffer limit
if self.vertex_buffers.object_counter as u64 if self.vertex_buffers.object_counter as u64
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
{ {
// TODO: no panic, handle this better. // TODO: no panic, handle this better.
panic!("Sprite limit exceeded!") panic!("Sprite limit exceeded!")
} }
// Push this object's instance // Push this object's instance
self.queue.write_buffer( self.queue.write_buffer(
&self.vertex_buffers.object.instances, &self.vertex_buffers.object.instances,
ObjectInstance::SIZE * self.vertex_buffers.object_counter, ObjectInstance::SIZE * self.vertex_buffers.object_counter,
bytemuck::cast_slice(&[ObjectInstance { bytemuck::cast_slice(&[ObjectInstance {
sprite_index: ship_cnt.sprite.get_index(), sprite_index: ship_cnt.sprite.get_index(),
object_index: idx as u32, object_index: idx as u32,
}]), }]),
); );
self.vertex_buffers.object_counter += 1; self.vertex_buffers.object_counter += 1;
// This will be None if this ship is dead. // This will be None if this ship is dead.
// (physics object stays around to complete the death animation) // (physics object stays around to complete the death animation)
// If that is the case, we're done, no flares to draw anyway! // If that is the case, we're done, no flares to draw anyway!
let ship = match state.data.get_ship(s.data_handle) { let ship = match state.data.get_ship(s.data_handle) {
None => return, None => continue,
Some(s) => s, Some(s) => s,
}; };
let flare = ship.get_outfits().get_flare_sprite(state.content); let flare = ship.get_outfits().get_flare_sprite(state.content);
if s.get_controls().thrust && flare.is_some() { if s.get_controls().thrust && flare.is_some() {
for engine_point in &ship_cnt.engines { for engine_point in &ship_cnt.engines {
self.queue.write_buffer( self.queue.write_buffer(
&self.global_uniform.object_buffer, &self.global_uniform.object_buffer,
ObjectData::SIZE * self.vertex_buffers.object_counter as u64, ObjectData::SIZE * self.vertex_buffers.object_counter as u64,
bytemuck::cast_slice(&[ObjectData { bytemuck::cast_slice(&[ObjectData {
xpos: engine_point.pos.x, xpos: engine_point.pos.x,
ypos: engine_point.pos.y - engine_point.size / 2.0, ypos: engine_point.pos.y - engine_point.size / 2.0,
zpos: 1.0, zpos: 1.0,
angle: 0.0, angle: 0.0,
size: engine_point.size, size: engine_point.size,
parent: idx as u32, parent: idx as u32,
is_child: 1, is_child: 1,
_padding: Default::default(), _padding: Default::default(),
}]), }]),
); );
// Enforce buffer limit // Enforce buffer limit
if self.vertex_buffers.object_counter as u64 if self.vertex_buffers.object_counter as u64
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT > galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
{ {
// TODO: no panic, handle this better. // TODO: no panic, handle this better.
panic!("Sprite limit exceeded!") 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, state: &RenderState,
// NE and SW corners of screen // NE and SW corners of screen
screen_clip: (Point2<f32>, Point2<f32>), screen_clip: (Point2<f32>, Point2<f32>),
p: &ProjectileWorldObject,
) { ) {
let r = state.world.get_rigid_body(p.rigid_body).unwrap(); for p in state.world.iter_projectiles() {
let proj_pos = util::rigidbody_position(&r); let r = state.world.get_rigid_body(p.rigid_body).unwrap();
let proj_rot = util::rigidbody_rotation(r); let proj_pos = util::rigidbody_position(&r);
let proj_ang = -proj_rot.angle(Vector2 { x: 1.0, y: 0.0 }); let proj_rot = util::rigidbody_rotation(r);
let proj_cnt = &p.content; // TODO: don't clone this? 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 // Position adjusted for parallax
// TODO: adjust parallax for zoom? // TODO: adjust parallax for zoom?
// 1.0 is z-coordinate, which is constant for ships // 1.0 is z-coordinate, which is constant for ships
let pos: Point2<f32> = (proj_pos - state.camera_pos.to_vec()) / 1.0; let pos: Point2<f32> = (proj_pos - state.camera_pos.to_vec()) / 1.0;
// Game dimensions of this sprite post-scale. // Game dimensions of this sprite post-scale.
// Post-scale width or height, whichever is larger. // Post-scale width or height, whichever is larger.
// This is in game units. // This is in game units.
// //
// We take the maximum to account for rotated sprites. // We take the maximum to account for rotated sprites.
let m = (proj_cnt.size / 1.0) * proj_cnt.sprite.aspect.max(1.0); let m = (proj_cnt.size / 1.0) * proj_cnt.sprite.aspect.max(1.0);
// Don't draw sprites that are off the screen // Don't draw sprites that are off the screen
if pos.x < screen_clip.0.x - m if pos.x < screen_clip.0.x - m
|| pos.y > screen_clip.0.y + m || pos.y > screen_clip.0.y + m
|| pos.x > screen_clip.1.x + m || pos.x > screen_clip.1.x + m
|| pos.y < screen_clip.1.y - m || pos.y < screen_clip.1.y - m
{ {
return; 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; pub(super) fn world_push_system(
// Write this object's location data &mut self,
self.queue.write_buffer( state: &RenderState,
&self.global_uniform.object_buffer, // NE and SW corners of screen
ObjectData::SIZE * idx as u64, screen_clip: (Point2<f32>, Point2<f32>),
bytemuck::cast_slice(&[ObjectData { ) {
xpos: proj_pos.x, let system = state.content.get_system(state.current_system);
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 for o in &system.objects {
if self.vertex_buffers.object_counter as u64 // Position adjusted for parallax
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT let pos: Point2<f32> = (Point2 {
{ x: o.pos.x,
// TODO: no panic, handle this better. y: o.pos.y,
panic!("Sprite limit exceeded!") } - 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;
} }
} }

View File

@ -1,5 +1,5 @@
use cgmath::Point2; use cgmath::Point2;
use galactica_content::Content; use galactica_content::{Content, SystemHandle};
use galactica_gameobject::{GameData, GameShipHandle}; use galactica_gameobject::{GameData, GameShipHandle};
use galactica_world::{ParticleBuilder, World}; use galactica_world::{ParticleBuilder, World};
@ -11,6 +11,9 @@ pub struct RenderState<'a> {
/// Player ship data /// Player ship data
pub player_data: GameShipHandle, pub player_data: GameShipHandle,
/// The system we're currently in
pub current_system: SystemHandle,
/// Height of screen, in world units /// Height of screen, in world units
pub camera_zoom: f32, pub camera_zoom: f32,