254 lines
7.5 KiB
Rust

//! 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<f32>, Point2<f32>),
) {
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<f32> =
(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<f32>, Point2<f32>),
) {
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<f32>, Point2<f32>),
) {
let system = state.ct.get_system(state.current_system);
for o in &system.objects {
// Position adjusted for parallax
let pos: Point2<f32> = (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,
});
}
}
}