|
- use std::mem::size_of;
-
- use glc::{
- buffer::{Buffer, Usage},
- misc::{BindGuard, Bindable},
- shader::{Program, Type, Uniform},
- texture::Texture,
- vector::{Vector2f, Vector4f},
- vertex_array::{DataType, VertexArray},
- };
- use space_crush_common::{
- components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity},
- misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut},
- };
- use specs::{prelude::*, ReadStorage, System, World};
-
- use crate::{
- constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM},
- misc::WorldHelper,
- Error,
- };
-
- pub struct Ships {
- program: Program,
- vertex_array: VertexArray,
- texture_bomber: Texture,
- texture_fighter: Texture,
- texture_transporter: Texture,
- 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)?;
-
- program.bind();
- 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,
- reader_id,
- ship_count,
- })
- }
- }
-
- #[derive(SystemData)]
- pub struct ShipsData<'a> {
- positions: ReadStorage<'a, Position>,
- velocities: ReadStorage<'a, Velocity>,
- players: ReadStorage<'a, Player>,
- owned: ReadStorage<'a, PlayerOwned>,
- ships: ReadStorage<'a, Ship>,
- }
-
- impl<'a> System<'a> for Ships {
- type SystemData = ShipsData<'a>;
-
- fn run(&mut self, data: Self::SystemData) {
- let ShipsData {
- positions,
- velocities,
- players,
- owned,
- ships,
- } = data;
-
- /* 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!")
- }
- }
- }
-
- /* 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");
- }
-
- 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,
- };
- d.texture = match ship.type_() {
- ShipType::Fighter => 0,
- ShipType::Bomber => 1,
- ShipType::Transporter => 2,
- };
- }
-
- 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);
- gl::draw_arrays(gl::POINTS, 0, self.ship_count as _);
- gl::disable(gl::BLEND);
- }
- }
|