|
- 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::{Asteroid, AsteroidType, Player, PlayerOwned, Position},
- misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut},
- };
- use specs::{prelude::*, ReadStorage, System, World};
-
- use crate::{
- constants::{
- ASTEROID_SIZE, PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA,
- UNIFORM_BUFFER_INDEX_UNIFORM,
- },
- misc::WorldHelper,
- Error,
- };
-
- pub struct Asteroids {
- program: Program,
- texture_metal: Texture,
- texture_crystal: Texture,
- vertex_array: VertexArray,
- reader_id: ReaderId<ComponentEvent<Asteroid>>,
- asteroid_count: usize,
- }
-
- #[repr(C, packed)]
- struct VertexData {
- pos: Vector2f,
- size: gl::GLfloat,
- color: Vector4f,
- texture: gl::GLint,
- }
-
- impl Asteroids {
- pub fn new(world: &mut World) -> Result<Self, Error> {
- WriteStorage::<Asteroid>::setup(world);
-
- let program = world.load_program(vec![
- (Type::Vertex, "resources/shader/asteroid/vert.glsl"),
- (Type::Geometry, "resources/shader/asteroid/geom.glsl"),
- (Type::Fragment, "resources/shader/asteroid/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]))?;
- program.unbind();
-
- let texture_metal = world.load_texture("resources/textures/asteroid_metal.png")?;
- let texture_crystal = world.load_texture("resources/textures/asteroid_crystal.png")?;
-
- const STRIDE: gl::GLsizei = size_of::<VertexData>() as gl::GLsizei;
-
- const OFFSET_POS: gl::GLsizei = 0;
- const OFFSET_SIZ: gl::GLsizei = OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei;
- const OFFSET_CLR: gl::GLsizei = OFFSET_SIZ + size_of::<gl::GLfloat>() 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, 1, DataType::Float, false, STRIDE, OFFSET_SIZ)?
- .vertex_attrib_pointer(2, 4, DataType::Float, false, STRIDE, OFFSET_CLR)?
- .vertex_attrib_pointer(3, 1, DataType::Int, false, STRIDE, OFFSET_TEX)?
- .build()?;
-
- let reader_id = world
- .system_data::<WriteStorage<Asteroid>>()
- .register_event_reader();
- let asteroid_count = 0;
-
- Ok(Self {
- program,
- texture_metal,
- texture_crystal,
- vertex_array,
- reader_id,
- asteroid_count,
- })
- }
- }
-
- #[derive(SystemData)]
- pub struct AsteroidsData<'a> {
- positions: ReadStorage<'a, Position>,
- asteroids: ReadStorage<'a, Asteroid>,
- players: ReadStorage<'a, Player>,
- owned: ReadStorage<'a, PlayerOwned>,
- }
-
- impl<'a> System<'a> for Asteroids {
- type SystemData = AsteroidsData<'a>;
-
- fn run(&mut self, data: Self::SystemData) {
- let AsteroidsData {
- positions,
- asteroids,
- players,
- owned,
- } = data;
-
- /* handle events */
- let mut need_update = false;
- let events = asteroids.channel().read(&mut self.reader_id);
- for event in events {
- match event {
- ComponentEvent::Inserted(_, _) => {
- need_update = true;
-
- self.asteroid_count += 1;
- }
- ComponentEvent::Removed(_, _) => {
- need_update = true;
-
- self.asteroid_count -= 1;
- }
- ComponentEvent::Modified(_, _) => {
- unreachable!("Updates of the asteroid component should not be tracked!")
- }
- }
- }
-
- /* update vertex array */
- let buffer = &mut self.vertex_array.buffers_mut()[0];
- if need_update {
- buffer
- .buffer_size(
- Usage::StaticDraw,
- self.asteroid_count * size_of::<VertexData>(),
- )
- .panic("Unable to change buffer size for asteroid data");
-
- let data = (&positions, &asteroids, owned.maybe());
- let mut buffer = buffer
- .map_mut::<VertexData>(true)
- .panic("Unable to map buffer for asteroid data");
-
- for (i, (position, asteroid, owned)) in data.join().enumerate() {
- let mut d = &mut buffer[i];
-
- d.pos = *position.pos();
- d.size = position.shape().circle().unwrap_or(ASTEROID_SIZE);
- d.color = match owned.and_then(|owned| players.get(owned.owner())) {
- Some(pv) => *pv.color(),
- None => PLAYER_COLOR_DEFAULT,
- };
- d.texture = match asteroid.type_() {
- AsteroidType::Metal => 0,
- AsteroidType::Crystal => 1,
- };
- }
- }
-
- /* render asteroids */
- let _guard = BindGuard::new(&self.program);
- let _guard = BindGuard::new(&self.vertex_array);
-
- gl::active_texture(gl::TEXTURE1);
- let _guard = BindGuard::new(&self.texture_crystal);
-
- gl::active_texture(gl::TEXTURE0);
- let _guard = BindGuard::new(&self.texture_metal);
-
- gl::enable(gl::BLEND);
- gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
- gl::draw_arrays(gl::POINTS, 0, self.asteroid_count as _);
- gl::disable(gl::BLEND);
- }
- }
|