You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.7 KiB

  1. use std::mem::size_of;
  2. use glc::{
  3. buffer::{Buffer, Usage},
  4. misc::{BindGuard, Bindable},
  5. shader::{Program, Type, Uniform},
  6. texture::Texture,
  7. vector::{Vector2f, Vector4f},
  8. vertex_array::{DataType, VertexArray},
  9. };
  10. use space_crush_common::{
  11. components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity},
  12. misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut},
  13. };
  14. use specs::{prelude::*, ReadStorage, System, World};
  15. use crate::{
  16. constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM},
  17. misc::WorldHelper,
  18. Error,
  19. };
  20. pub struct Ships {
  21. program: Program,
  22. vertex_array: VertexArray,
  23. texture_bomber: Texture,
  24. texture_fighter: Texture,
  25. texture_transporter: Texture,
  26. reader_id: ReaderId<ComponentEvent<Ship>>,
  27. ship_count: usize,
  28. }
  29. #[repr(C, packed)]
  30. struct VertexData {
  31. pos: Vector2f,
  32. dir: Vector2f,
  33. color: Vector4f,
  34. texture: gl::GLint,
  35. }
  36. impl Ships {
  37. pub fn new(world: &World) -> Result<Self, Error> {
  38. let program = world.load_program(vec![
  39. (Type::Vertex, "resources/shader/ship/vert.glsl"),
  40. (Type::Geometry, "resources/shader/ship/geom.glsl"),
  41. (Type::Fragment, "resources/shader/ship/frag.glsl"),
  42. ])?;
  43. program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;
  44. program.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?;
  45. program.bind();
  46. program.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?;
  47. program.unbind();
  48. const STRIDE: gl::GLsizei = size_of::<VertexData>() as gl::GLsizei;
  49. const OFFSET_POS: gl::GLsizei = 0;
  50. const OFFSET_DIR: gl::GLsizei = OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei;
  51. const OFFSET_CLR: gl::GLsizei = OFFSET_DIR + size_of::<Vector2f>() as gl::GLsizei;
  52. const OFFSET_TEX: gl::GLsizei = OFFSET_CLR + size_of::<Vector4f>() as gl::GLsizei;
  53. let vertex_array = VertexArray::builder()
  54. .bind_buffer(Buffer::new()?)
  55. .vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE, OFFSET_POS)?
  56. .vertex_attrib_pointer(1, 2, DataType::Float, false, STRIDE, OFFSET_DIR)?
  57. .vertex_attrib_pointer(2, 4, DataType::Float, false, STRIDE, OFFSET_CLR)?
  58. .vertex_attrib_pointer(3, 1, DataType::Int, false, STRIDE, OFFSET_TEX)?
  59. .build()?;
  60. let texture_bomber = world.load_texture("resources/textures/ship_bomber.png")?;
  61. let texture_fighter = world.load_texture("resources/textures/ship_fighter.png")?;
  62. let texture_transporter = world.load_texture("resources/textures/ship_transporter.png")?;
  63. let reader_id = world
  64. .system_data::<WriteStorage<Ship>>()
  65. .register_event_reader();
  66. let ship_count = 0;
  67. Ok(Self {
  68. program,
  69. vertex_array,
  70. texture_bomber,
  71. texture_fighter,
  72. texture_transporter,
  73. reader_id,
  74. ship_count,
  75. })
  76. }
  77. }
  78. #[derive(SystemData)]
  79. pub struct ShipsData<'a> {
  80. positions: ReadStorage<'a, Position>,
  81. velocities: ReadStorage<'a, Velocity>,
  82. players: ReadStorage<'a, Player>,
  83. owned: ReadStorage<'a, PlayerOwned>,
  84. ships: ReadStorage<'a, Ship>,
  85. }
  86. impl<'a> System<'a> for Ships {
  87. type SystemData = ShipsData<'a>;
  88. fn run(&mut self, data: Self::SystemData) {
  89. let ShipsData {
  90. positions,
  91. velocities,
  92. players,
  93. owned,
  94. ships,
  95. } = data;
  96. /* handle events */
  97. let old_count = self.ship_count;
  98. let events = ships.channel().read(&mut self.reader_id);
  99. for event in events {
  100. match event {
  101. ComponentEvent::Inserted(_, _) => self.ship_count += 1,
  102. ComponentEvent::Removed(_, _) => self.ship_count -= 1,
  103. ComponentEvent::Modified(_, _) => {
  104. unreachable!("Updates of the ship component should not be tracked!")
  105. }
  106. }
  107. }
  108. /* update vertex array */
  109. let buffer = &mut self.vertex_array.buffers_mut()[0];
  110. if old_count != self.ship_count {
  111. buffer
  112. .buffer_size(Usage::StaticDraw, self.ship_count * size_of::<VertexData>())
  113. .panic("Unable to change buffer size for ship data");
  114. }
  115. let data = (&positions, &velocities, &ships, owned.maybe());
  116. let mut buffer = buffer
  117. .map_mut::<VertexData>(true)
  118. .panic("Unable to map buffer for ship data");
  119. for (i, (position, velocity, ship, owned)) in data.join().enumerate() {
  120. let mut d = &mut buffer[i];
  121. d.pos = *position.pos();
  122. d.dir = *velocity.dir();
  123. d.color = match owned.and_then(|owned| players.get(owned.owner())) {
  124. Some(pv) => *pv.color(),
  125. None => PLAYER_COLOR_DEFAULT,
  126. };
  127. d.texture = match ship.type_() {
  128. ShipType::Fighter => 0,
  129. ShipType::Bomber => 1,
  130. ShipType::Transporter => 2,
  131. };
  132. }
  133. drop(buffer);
  134. /* render ships */
  135. let _guard = BindGuard::new(&self.program);
  136. let _guard = BindGuard::new(&self.vertex_array);
  137. gl::active_texture(gl::TEXTURE2);
  138. let _guard = BindGuard::new(&self.texture_transporter);
  139. gl::active_texture(gl::TEXTURE1);
  140. let _guard = BindGuard::new(&self.texture_bomber);
  141. gl::active_texture(gl::TEXTURE0);
  142. let _guard = BindGuard::new(&self.texture_fighter);
  143. gl::enable(gl::BLEND);
  144. gl::blend_func(gl::SRC_ALPHA, gl::ONE);
  145. gl::draw_arrays(gl::POINTS, 0, self.ship_count as _);
  146. gl::disable(gl::BLEND);
  147. }
  148. }