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.

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