|
- use std::cell::RefCell;
- use std::mem::{size_of, swap};
- use std::rc::Rc;
-
- use glc::{
- buffer::{Buffer, Usage},
- misc::{BindGuard, Bindable},
- shader::{Program, TransformFeedbackVaryingsMode, Type, Uniform},
- texture::Texture,
- transform_feedback::TransformFeedback,
- vector::{Vector2f, Vector3f},
- vertex_array::{DataType, VertexArray},
- };
- use space_crush_common::{
- components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity},
- misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut},
- };
- use specs::{
- hibitset::{BitSet, BitSetAll, BitSetLike},
- prelude::*,
- world::Index as Id,
- ReadStorage, System, World,
- };
-
- use crate::{
- constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM},
- misc::WorldHelper,
- Error,
- };
-
- pub struct Ships {
- program_ship: Program,
- program_tail_update: Program,
- program_tail_render: Program,
-
- ship_data: BufferRef,
- ship_render: VertexArray<BufferRef>,
-
- input: TailObjects,
- output: TailObjects,
-
- texture_bomber: Texture,
- texture_fighter: Texture,
- texture_transporter: Texture,
-
- reader_id: ReaderId<ComponentEvent<Ship>>,
-
- need_init: BitSet,
- id_to_index: Vec<u32>,
- index_to_id: Vec<Id>,
- }
-
- #[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>,
- }
-
- struct TailObjects {
- tail_data: BufferRef,
- tail_update: VertexArray<BufferRef>,
- tail_render: VertexArray<BufferRef>,
- transform_feedback: TransformFeedback<BufferRef>,
- }
-
- type BufferRef = Rc<RefCell<Buffer>>;
-
- #[repr(C, packed)]
- struct ShipData {
- pos: Vector2f,
- dir: Vector2f,
- color: Vector3f,
- texture: gl::GLint,
- }
-
- #[repr(C, packed)]
- #[derive(Debug)]
- struct TailData {
- pos: [Vector2f; 5],
- }
-
- impl Ships {
- pub fn new(world: &World) -> Result<Self, Error> {
- /* program ship */
- let program_ship = world.load_program(vec![
- (Type::Vertex, SHDR_SHIP_VERT),
- (Type::Geometry, SHDR_SHIP_GEOM),
- (Type::Fragment, SHDR_SHIP_FRAG),
- ])?;
-
- program_ship.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
- program_ship.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?;
-
- program_ship.bind();
- program_ship.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?;
- program_ship.unbind();
-
- /* program tail update */
- let program_tail_update = Program::builder()
- .add_shader(world.load_shader(Type::Vertex, SHDR_TAIL_UPDATE_VERT)?)
- .add_shader(world.load_shader(Type::Geometry, SHDR_TAIL_UPDATE_GEOM)?)
- .set_transform_feedback_varyings(
- TransformFeedbackVaryingsMode::Interleaved,
- SHDR_TAIL_VARYINGS,
- )
- .build()?;
-
- program_tail_update.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?;
-
- /* program tail render */
- let program_tail_render = world.load_program(vec![
- (Type::Vertex, SHDR_TAIL_RENDER_VERT),
- (Type::Geometry, SHDR_TAIL_RENDER_GEOM),
- (Type::Fragment, SHDR_TAIL_RENDER_FRAG),
- ])?;
-
- program_tail_render.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
-
- /* ship data */
- let ship_data = Rc::new(RefCell::new(Buffer::new()?));
-
- /* vertex array ship */
- let ship_render = VertexArray::builder()
- .bind_buffer(ship_data.clone())
- .vertex_attrib_pointer(0, 2, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_POS)?
- .vertex_attrib_pointer(1, 2, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_DIR)?
- .vertex_attrib_pointer(2, 3, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_CLR)?
- .vertex_attrib_pointer(3, 1, DataType::Int, false, SHIP_STRIDE, SHIP_OFFSET_TEX)?
- .build()?;
-
- /* tail objects */
- let input = TailObjects::new(&ship_data)?;
- let output = TailObjects::new(&ship_data)?;
-
- /* textures */
- 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")?;
-
- /* event readers */
- let reader_id = world
- .system_data::<WriteStorage<Ship>>()
- .register_event_reader();
-
- /* rest */
- let need_init = BitSet::new();
- let id_to_index = Vec::new();
- let index_to_id = Vec::new();
-
- Ok(Self {
- program_ship,
- program_tail_update,
- program_tail_render,
-
- ship_data,
- ship_render,
-
- input,
- output,
-
- texture_bomber,
- texture_fighter,
- texture_transporter,
-
- reader_id,
-
- need_init,
- id_to_index,
- index_to_id,
- })
- }
-
- fn handle_events(&mut self, d: &ShipsData<'_>) {
- self.need_init.clear();
- let events = d.ships.channel().read(&mut self.reader_id);
- for event in events {
- match event {
- ComponentEvent::Inserted(id, _) => self.add_ship(*id),
- ComponentEvent::Removed(id, _) => self.remove_ship(*id),
- ComponentEvent::Modified(_, _) => {
- unreachable!("Updates of the ship component should not be tracked!")
- }
- }
- }
- }
-
- fn update_vertices(&self, d: &ShipsData<'_>) {
- let mut borrow_ship = self.ship_data.borrow_mut();
- let mut buf_ship = borrow_ship
- .map_mut::<ShipData>(true)
- .panic("Unable to map buffer for ship data");
-
- let ids = BitSetAll;
- let data = (
- &ids as &dyn BitSetLike,
- &d.positions,
- &d.velocities,
- &d.ships,
- d.owned.maybe(),
- );
- for (id, position, velocity, ship, owned) in data.join() {
- let index = self.id_to_index[id as usize];
-
- let mut s = &mut buf_ship[index as usize];
- s.pos = *position.pos();
- s.dir = *velocity.dir();
-
- if self.need_init.contains(id) {
- s.color = match owned.and_then(|owned| d.players.get(owned.owner())) {
- Some(pv) => *pv.color(),
- None => PLAYER_COLOR_DEFAULT,
- };
- s.texture = match ship.type_() {
- ShipType::Fighter => 0,
- ShipType::Bomber => 1,
- ShipType::Transporter => 2,
- };
-
- let mut pos = *position.pos();
- let mut borrow_tail = self.input.tail_data.borrow_mut();
- let mut buf_tail = borrow_tail
- .map_mut::<TailData>(true)
- .panic("Unable to map buffer for tail data");
- for p in &mut buf_tail[index as usize].pos {
- pos -= Vector2f::new(10.0, 0.0);
- *p = pos;
- }
- }
- }
- }
-
- fn update_tail(&mut self) {
- let guard_prog = BindGuard::new(&self.program_tail_update);
- let guard_trfm = BindGuard::new(&self.output.transform_feedback);
- let guard_data = BindGuard::new(&self.input.tail_update);
-
- gl::enable(gl::RASTERIZER_DISCARD);
- gl::begin_transform_feedback(gl::POINTS);
- gl::draw_arrays(gl::POINTS, 0, self.index_to_id.len() as _);
- gl::end_transform_feedback();
- gl::disable(gl::RASTERIZER_DISCARD);
-
- drop(guard_trfm);
- drop(guard_data);
- drop(guard_prog);
-
- swap(&mut self.input, &mut self.output);
- }
-
- fn render_tails(&self) {
- let _guard = BindGuard::new(&self.program_tail_render);
- let _guard = BindGuard::new(&self.input.tail_render);
-
- gl::enable(gl::BLEND);
- gl::blend_func(gl::SRC_ALPHA, gl::ONE);
- gl::draw_arrays(gl::POINTS, 0, self.index_to_id.len() as _);
- gl::disable(gl::BLEND);
- }
-
- fn render_ships(&self) {
- let _guard = BindGuard::new(&self.program_ship);
- let _guard = BindGuard::new(&self.ship_render);
-
- 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.index_to_id.len() as _);
- gl::disable(gl::BLEND);
- }
-
- fn add_ship(&mut self, id: Id) {
- /* store id */
- self.need_init.add(id);
-
- let id = id as usize;
- if self.id_to_index.len() <= id {
- self.id_to_index.resize_with(id + 1, Default::default);
- }
-
- let index = self.index_to_id.len();
- if self.index_to_id.len() <= index {
- self.index_to_id.resize_with(index + 1, Default::default);
- }
-
- self.id_to_index[id] = index as u32;
- self.index_to_id[index] = id as u32;
-
- /* update ship buffer */
- let size = size_of::<TailData>() * self.index_to_id.len();
- let mut buffer = self.ship_data.borrow_mut();
- if size > buffer.size() {
- buffer
- .buffer_size(Usage::DynamicDraw, size)
- .panic("Unable to change buffer size for ship data");
- }
-
- /* update tail buffer */
- let size = size_of::<TailData>() * self.index_to_id.len();
-
- let mut buffer = self.input.tail_data.borrow_mut();
- if size > buffer.size() {
- buffer
- .buffer_size(Usage::DynamicDraw, size)
- .panic("Unable to change buffer size for input tail data");
- }
-
- let mut buffer = self.output.tail_data.borrow_mut();
- if size > buffer.size() {
- buffer
- .buffer_size(Usage::DynamicDraw, size)
- .panic("Unable to change buffer size for output tail data");
- }
- }
-
- fn remove_ship(&mut self, id: Id) {
- let _id = id;
- // TODO
- }
- }
-
- impl<'a> System<'a> for Ships {
- type SystemData = ShipsData<'a>;
-
- fn run(&mut self, data: Self::SystemData) {
- self.handle_events(&data);
- self.update_vertices(&data);
- self.update_tail();
- self.render_tails();
- self.render_ships();
- }
- }
-
- impl TailObjects {
- fn new(ship_data: &BufferRef) -> Result<Self, Error> {
- /* tail data */
- let tail_data = Rc::new(RefCell::new(Buffer::new()?));
- tail_data
- .borrow_mut()
- .buffer_size(Usage::DynamicDraw, size_of::<TailData>() as _)?;
-
- /* vertex array tail update */
- let tail_update = VertexArray::builder()
- .bind_buffer(ship_data.clone())
- .vertex_attrib_pointer(0, 2, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_POS)?
- .bind_buffer(tail_data.clone())
- .vertex_attrib_pointer(1, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL0)?
- .vertex_attrib_pointer(2, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL1)?
- .vertex_attrib_pointer(3, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL2)?
- .vertex_attrib_pointer(4, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL3)?
- .vertex_attrib_pointer(5, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL4)?
- .build()?;
-
- /* vertex array tail render */
- let tail_render = VertexArray::builder()
- .bind_buffer(ship_data.clone())
- .vertex_attrib_pointer(0, 3, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_CLR)?
- .bind_buffer(tail_data.clone())
- .vertex_attrib_pointer(1, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL0)?
- .vertex_attrib_pointer(2, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL1)?
- .vertex_attrib_pointer(3, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL2)?
- .vertex_attrib_pointer(4, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL3)?
- .vertex_attrib_pointer(5, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL4)?
- .build()?;
-
- /* transform feedback buffer */
- let transform_feedback = TransformFeedback::builder()
- .bind_buffer(0, tail_data.clone())?
- .build()?;
-
- Ok(Self {
- tail_data,
- tail_update,
- tail_render,
- transform_feedback,
- })
- }
- }
-
- const SHDR_SHIP_VERT: &str = "resources/shader/ship/ship_vert.glsl";
- const SHDR_SHIP_GEOM: &str = "resources/shader/ship/ship_geom.glsl";
- const SHDR_SHIP_FRAG: &str = "resources/shader/ship/ship_frag.glsl";
-
- const SHDR_TAIL_UPDATE_VERT: &str = "resources/shader/ship/tail_update_vert.glsl";
- const SHDR_TAIL_UPDATE_GEOM: &str = "resources/shader/ship/tail_update_geom.glsl";
- const SHDR_TAIL_VARYINGS: &[&str] = &["outTail0", "outTail1", "outTail2", "outTail3", "outTail4"];
-
- const SHDR_TAIL_RENDER_VERT: &str = "resources/shader/ship/tail_render_vert.glsl";
- const SHDR_TAIL_RENDER_GEOM: &str = "resources/shader/ship/tail_render_geom.glsl";
- const SHDR_TAIL_RENDER_FRAG: &str = "resources/shader/ship/tail_render_frag.glsl";
-
- const SHIP_STRIDE: gl::GLsizei = size_of::<ShipData>() as gl::GLsizei;
- const SHIP_OFFSET_POS: gl::GLsizei = 0;
- const SHIP_OFFSET_DIR: gl::GLsizei = SHIP_OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei;
- const SHIP_OFFSET_CLR: gl::GLsizei = SHIP_OFFSET_DIR + size_of::<Vector2f>() as gl::GLsizei;
- const SHIP_OFFSET_TEX: gl::GLsizei = SHIP_OFFSET_CLR + size_of::<Vector3f>() as gl::GLsizei;
-
- const TAIL_STRIDE: gl::GLsizei = size_of::<TailData>() as gl::GLsizei;
- const TAIL_OFFSET_TL0: gl::GLsizei = 0;
- const TAIL_OFFSET_TL1: gl::GLsizei = TAIL_OFFSET_TL0 + size_of::<Vector2f>() as gl::GLsizei;
- const TAIL_OFFSET_TL2: gl::GLsizei = TAIL_OFFSET_TL1 + size_of::<Vector2f>() as gl::GLsizei;
- const TAIL_OFFSET_TL3: gl::GLsizei = TAIL_OFFSET_TL2 + size_of::<Vector2f>() as gl::GLsizei;
- const TAIL_OFFSET_TL4: gl::GLsizei = TAIL_OFFSET_TL3 + size_of::<Vector2f>() as gl::GLsizei;
|