|
|
|
@@ -1,74 +1,101 @@ |
|
|
|
use std::mem::size_of; |
|
|
|
|
|
|
|
use glc::{ |
|
|
|
matrix::Matrix4f, |
|
|
|
buffer::{Buffer, Usage}, |
|
|
|
misc::{BindGuard, Bindable}, |
|
|
|
shader::{Program, Type, Uniform}, |
|
|
|
texture::Texture, |
|
|
|
vector::Vector4f, |
|
|
|
vector::{Vector2f, Vector4f}, |
|
|
|
vertex_array::{DataType, VertexArray}, |
|
|
|
}; |
|
|
|
use space_crush_common::{ |
|
|
|
components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, |
|
|
|
misc::LogResult, |
|
|
|
misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, |
|
|
|
}; |
|
|
|
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; |
|
|
|
use specs::{prelude::*, ReadStorage, System, World}; |
|
|
|
|
|
|
|
use crate::{ |
|
|
|
constants::{ |
|
|
|
PLAYER_COLOR_DEFAULT, SHIP_SIZE, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM, |
|
|
|
}, |
|
|
|
constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, |
|
|
|
misc::WorldHelper, |
|
|
|
resources::Geometry, |
|
|
|
Error, |
|
|
|
}; |
|
|
|
|
|
|
|
pub struct Ships { |
|
|
|
program: Program, |
|
|
|
vertex_array: VertexArray, |
|
|
|
texture_bomber: Texture, |
|
|
|
texture_fighter: Texture, |
|
|
|
texture_transporter: Texture, |
|
|
|
location_model: gl::GLint, |
|
|
|
location_glow_color: gl::GLint, |
|
|
|
reader_id: ReaderId<ComponentEvent<Ship>>, |
|
|
|
ship_count: usize, |
|
|
|
} |
|
|
|
|
|
|
|
#[repr(C, packed)] |
|
|
|
struct VertexData { |
|
|
|
pos: Vector2f, |
|
|
|
dir: Vector2f, |
|
|
|
color: Vector4f, |
|
|
|
texture: gl::GLint, |
|
|
|
} |
|
|
|
|
|
|
|
impl Ships { |
|
|
|
pub fn new(world: &World) -> Result<Self, Error> { |
|
|
|
let program = world.load_program(vec![ |
|
|
|
(Type::Vertex, "resources/shader/ship/vert.glsl"), |
|
|
|
(Type::Geometry, "resources/shader/ship/geom.glsl"), |
|
|
|
(Type::Fragment, "resources/shader/ship/frag.glsl"), |
|
|
|
])?; |
|
|
|
|
|
|
|
program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; |
|
|
|
program.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?; |
|
|
|
|
|
|
|
let location_model = program.uniform_location("uModel")?; |
|
|
|
let location_glow_color = program.uniform_location("uGlowColor")?; |
|
|
|
|
|
|
|
program.bind(); |
|
|
|
program.uniform("uTexture", Uniform::Texture(0))?; |
|
|
|
program.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; |
|
|
|
program.unbind(); |
|
|
|
|
|
|
|
const STRIDE: gl::GLsizei = size_of::<VertexData>() as gl::GLsizei; |
|
|
|
|
|
|
|
const OFFSET_POS: gl::GLsizei = 0; |
|
|
|
const OFFSET_DIR: gl::GLsizei = OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei; |
|
|
|
const OFFSET_CLR: gl::GLsizei = OFFSET_DIR + size_of::<Vector2f>() as gl::GLsizei; |
|
|
|
const OFFSET_TEX: gl::GLsizei = OFFSET_CLR + size_of::<Vector4f>() as gl::GLsizei; |
|
|
|
|
|
|
|
let vertex_array = VertexArray::builder() |
|
|
|
.bind_buffer(Buffer::new()?) |
|
|
|
.vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE, OFFSET_POS)? |
|
|
|
.vertex_attrib_pointer(1, 2, DataType::Float, false, STRIDE, OFFSET_DIR)? |
|
|
|
.vertex_attrib_pointer(2, 4, DataType::Float, false, STRIDE, OFFSET_CLR)? |
|
|
|
.vertex_attrib_pointer(3, 1, DataType::Int, false, STRIDE, OFFSET_TEX)? |
|
|
|
.build()?; |
|
|
|
|
|
|
|
let texture_bomber = world.load_texture("resources/textures/ship_bomber.png")?; |
|
|
|
let texture_fighter = world.load_texture("resources/textures/ship_fighter.png")?; |
|
|
|
let texture_transporter = world.load_texture("resources/textures/ship_transporter.png")?; |
|
|
|
|
|
|
|
let reader_id = world |
|
|
|
.system_data::<WriteStorage<Ship>>() |
|
|
|
.register_event_reader(); |
|
|
|
let ship_count = 0; |
|
|
|
|
|
|
|
Ok(Self { |
|
|
|
program, |
|
|
|
vertex_array, |
|
|
|
texture_bomber, |
|
|
|
texture_fighter, |
|
|
|
texture_transporter, |
|
|
|
location_model, |
|
|
|
location_glow_color, |
|
|
|
reader_id, |
|
|
|
ship_count, |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#[derive(SystemData)] |
|
|
|
pub struct ShipsData<'a> { |
|
|
|
geometry: ReadExpect<'a, Geometry>, |
|
|
|
position: ReadStorage<'a, Position>, |
|
|
|
velocity: ReadStorage<'a, Velocity>, |
|
|
|
ship: ReadStorage<'a, Ship>, |
|
|
|
positions: ReadStorage<'a, Position>, |
|
|
|
velocities: ReadStorage<'a, Velocity>, |
|
|
|
players: ReadStorage<'a, Player>, |
|
|
|
owned: ReadStorage<'a, PlayerOwned>, |
|
|
|
player: ReadStorage<'a, Player>, |
|
|
|
ships: ReadStorage<'a, Ship>, |
|
|
|
} |
|
|
|
|
|
|
|
impl<'a> System<'a> for Ships { |
|
|
|
@@ -76,54 +103,72 @@ impl<'a> System<'a> for Ships { |
|
|
|
|
|
|
|
fn run(&mut self, data: Self::SystemData) { |
|
|
|
let ShipsData { |
|
|
|
geometry, |
|
|
|
position, |
|
|
|
velocity, |
|
|
|
ship, |
|
|
|
positions, |
|
|
|
velocities, |
|
|
|
players, |
|
|
|
owned, |
|
|
|
player, |
|
|
|
ships, |
|
|
|
} = data; |
|
|
|
|
|
|
|
gl::enable(gl::BLEND); |
|
|
|
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); |
|
|
|
/* handle events */ |
|
|
|
let old_count = self.ship_count; |
|
|
|
let events = ships.channel().read(&mut self.reader_id); |
|
|
|
for event in events { |
|
|
|
match event { |
|
|
|
ComponentEvent::Inserted(_, _) => self.ship_count += 1, |
|
|
|
ComponentEvent::Removed(_, _) => self.ship_count -= 1, |
|
|
|
ComponentEvent::Modified(_, _) => { |
|
|
|
unreachable!("Updates of the ship component should not be tracked!") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
let _guard = BindGuard::new(&self.program); |
|
|
|
/* update vertex array */ |
|
|
|
let buffer = &mut self.vertex_array.buffers_mut()[0]; |
|
|
|
if old_count != self.ship_count { |
|
|
|
buffer |
|
|
|
.buffer_size(Usage::StaticDraw, self.ship_count * size_of::<VertexData>()) |
|
|
|
.panic("Unable to change buffer size for ship data"); |
|
|
|
} |
|
|
|
|
|
|
|
for (p, v, ship, owned) in (&position, &velocity, &ship, owned.maybe()).join() { |
|
|
|
let _guard = match ship.type_() { |
|
|
|
ShipType::Fighter => BindGuard::new(&self.texture_fighter), |
|
|
|
ShipType::Bomber => BindGuard::new(&self.texture_bomber), |
|
|
|
ShipType::Transporter => BindGuard::new(&self.texture_transporter), |
|
|
|
let data = (&positions, &velocities, &ships, owned.maybe()); |
|
|
|
let mut buffer = buffer |
|
|
|
.map_mut::<VertexData>(true) |
|
|
|
.panic("Unable to map buffer for ship data"); |
|
|
|
for (i, (position, velocity, ship, owned)) in data.join().enumerate() { |
|
|
|
let mut d = &mut buffer[i]; |
|
|
|
|
|
|
|
d.pos = *position.pos(); |
|
|
|
d.dir = *velocity.dir(); |
|
|
|
d.color = match owned.and_then(|owned| players.get(owned.owner())) { |
|
|
|
Some(pv) => *pv.color(), |
|
|
|
None => PLAYER_COLOR_DEFAULT, |
|
|
|
}; |
|
|
|
|
|
|
|
let c = match owned.and_then(|owned| player.get(owned.owner())) { |
|
|
|
Some(pv) => pv.color(), |
|
|
|
None => &PLAYER_COLOR_DEFAULT, |
|
|
|
d.texture = match ship.type_() { |
|
|
|
ShipType::Fighter => 0, |
|
|
|
ShipType::Bomber => 1, |
|
|
|
ShipType::Transporter => 2, |
|
|
|
}; |
|
|
|
|
|
|
|
let p_x = p.pos().x; |
|
|
|
let p_y = p.pos().y; |
|
|
|
let d_x = v.dir().x; |
|
|
|
let d_y = v.dir().y; |
|
|
|
let s = SHIP_SIZE; |
|
|
|
|
|
|
|
let m = Matrix4f::new( |
|
|
|
Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0), |
|
|
|
Vector4f::new(-s * d_x, -s * d_y, 0.0, 0.0), |
|
|
|
Vector4f::new(0.0, 0.0, s, 0.0), |
|
|
|
Vector4f::new(p_x, p_y, 0.0, 1.0), |
|
|
|
); |
|
|
|
|
|
|
|
self.program |
|
|
|
.uniform(self.location_glow_color, Uniform::Vector4f(&c)) |
|
|
|
.error("Error while updating glow color"); |
|
|
|
self.program |
|
|
|
.uniform(self.location_model, Uniform::Matrix4f(&m)) |
|
|
|
.error("Error while updating model matrix"); |
|
|
|
|
|
|
|
geometry.render_quad(); |
|
|
|
} |
|
|
|
|
|
|
|
drop(buffer); |
|
|
|
|
|
|
|
/* render ships */ |
|
|
|
let _guard = BindGuard::new(&self.program); |
|
|
|
let _guard = BindGuard::new(&self.vertex_array); |
|
|
|
|
|
|
|
gl::active_texture(gl::TEXTURE2); |
|
|
|
let _guard = BindGuard::new(&self.texture_transporter); |
|
|
|
|
|
|
|
gl::active_texture(gl::TEXTURE1); |
|
|
|
let _guard = BindGuard::new(&self.texture_bomber); |
|
|
|
|
|
|
|
gl::active_texture(gl::TEXTURE0); |
|
|
|
let _guard = BindGuard::new(&self.texture_fighter); |
|
|
|
|
|
|
|
gl::enable(gl::BLEND); |
|
|
|
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); |
|
|
|
gl::draw_arrays(gl::POINTS, 0, self.ship_count as _); |
|
|
|
gl::disable(gl::BLEND); |
|
|
|
} |
|
|
|
} |