From e26c7dbfd3740a8d43c3cb15c94759f5b936e06f Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Wed, 23 Dec 2020 23:47:57 +0100 Subject: [PATCH] Use vertex array and geometry shader to render ships --- glc/src/shader.rs | 2 + glc/src/vertex_array.rs | 44 +++-- .../resources/shader/ship/frag.glsl | 16 +- .../resources/shader/ship/geom.glsl | 46 +++++ .../resources/shader/ship/shared.glsl | 11 ++ .../resources/shader/ship/vert.glsl | 18 +- space-crush-app/src/constants.rs | 1 - space-crush-app/src/misc/text.rs | 2 - space-crush-app/src/render/ships.rs | 165 +++++++++++------- .../src/misc/flagged_storage.rs | 18 +- 10 files changed, 223 insertions(+), 100 deletions(-) create mode 100644 space-crush-app/resources/shader/ship/geom.glsl diff --git a/glc/src/shader.rs b/glc/src/shader.rs index 4486e66..62f8eb0 100644 --- a/glc/src/shader.rs +++ b/glc/src/shader.rs @@ -87,6 +87,7 @@ impl Program { Uniform::Vector2f(v) => gl::uniform_2fv(location, 1, v.as_ptr()), Uniform::Float(v) => gl::uniform_1f(location, v), Uniform::Texture(i) => gl::uniform_1i(location, i), + Uniform::TextureVec(i) => gl::uniform_1iv(location, i.len() as _, i.as_ptr()), } Ok(()) @@ -285,6 +286,7 @@ pub enum Uniform<'a> { Vector2f(&'a Vector2f), Float(f32), Texture(gl::GLint), + TextureVec(&'a [gl::GLint]), } /* IntoUniformLocation */ diff --git a/glc/src/vertex_array.rs b/glc/src/vertex_array.rs index 1f78285..054864c 100644 --- a/glc/src/vertex_array.rs +++ b/glc/src/vertex_array.rs @@ -79,20 +79,36 @@ impl Builder { for pointer in binding.pointers { Error::checked(|| gl::enable_vertex_attrib_array(pointer.index))?; - Error::checked(|| { - gl::vertex_attrib_pointer( - pointer.index, - pointer.size, - pointer.type_.as_enum(), - if pointer.normalize { - gl::TRUE - } else { - gl::FALSE - }, - pointer.stride, - pointer.offset as *const _, - ) - })?; + match pointer.type_ { + DataType::Byte + | DataType::UnsignedByte + | DataType::Short + | DataType::UnsignedShort + | DataType::Int + | DataType::UnsignedInt => Error::checked(|| { + gl::vertex_attrib_i_pointer( + pointer.index, + pointer.size, + pointer.type_.as_enum(), + pointer.stride, + pointer.offset as *const _, + ) + })?, + _ => Error::checked(|| { + gl::vertex_attrib_pointer( + pointer.index, + pointer.size, + pointer.type_.as_enum(), + if pointer.normalize { + gl::TRUE + } else { + gl::FALSE + }, + pointer.stride, + pointer.offset as *const _, + ) + })?, + } if let Some(divisor) = pointer.divisor { Error::checked(|| gl::vertex_attrib_divisor(pointer.index, divisor))?; diff --git a/space-crush-app/resources/shader/ship/frag.glsl b/space-crush-app/resources/shader/ship/frag.glsl index 981fd42..cf6471d 100644 --- a/space-crush-app/resources/shader/ship/frag.glsl +++ b/space-crush-app/resources/shader/ship/frag.glsl @@ -7,23 +7,23 @@ const float GLOW_ALPHA = 0.20; const GlowArgs GLOW_ARGS = { /* step0 */ 0.100, - /* step1 */ 1.900, + /* step1 */ 0.900, /* pulseSize0 */ 0.050, - /* pulseSize1 */ 0.200, + /* pulseSize1 */ 0.100, /* pulseTime */ 2.000, }; -in FragmentData fragmentData; + in FragmentData fragmentData; +flat in int textureId; -uniform vec4 uGlowColor; -uniform sampler2D uTexture; +uniform sampler2D uTexture[3]; out vec4 outColor; void main() { - float alpha = glow(GLOW_ARGS, fragmentData.texCoords - vec2(0.5), uGlobal.time); - vec4 glow = vec4(uGlowColor.rgb, uGlowColor.a * alpha * GLOW_ALPHA); - vec4 tex = texture(uTexture, fragmentData.texCoords); + float alpha = glow(GLOW_ARGS, fragmentData.texCoords, uGlobal.time); + vec4 glow = vec4(fragmentData.color.rgb, fragmentData.color.a * alpha * GLOW_ALPHA); + vec4 tex = texture(uTexture[textureId], fragmentData.texCoords / SHIP_SIZE + vec2(0.5)); outColor = tex * tex.a + glow * (1.0 - tex.a); } diff --git a/space-crush-app/resources/shader/ship/geom.glsl b/space-crush-app/resources/shader/ship/geom.glsl new file mode 100644 index 0000000..b3d9821 --- /dev/null +++ b/space-crush-app/resources/shader/ship/geom.glsl @@ -0,0 +1,46 @@ +#version 450 core + +#pragma include ./shared.glsl +#pragma include ../misc/camera.glsl + +in VertexData vertexData[]; + +layout (points) in; +layout (triangle_strip, max_vertices = 4) out; + + out FragmentData fragmentData; +flat out int textureId; + +void main() { + VertexData d = vertexData[0]; + + vec2 pos = d.pos; + vec2 dir = d.dir * QUAD_SIZE; + + mat4 m = uCamera.projection * uCamera.view * mat4( + vec4(dir.y, -dir.x, 0.0, 0.0), + vec4(dir.x, dir.y, 0.0, 0.0), + vec4( 0.0, 0.0, 0.0, 0.0), + vec4(pos.x, pos.y, 0.0, 1.0)); + + textureId = d.texture; + fragmentData.color = d.color; + + gl_Position = m * vec4(-1.0, -1.0, 0.0, 1.0); + fragmentData.texCoords = vec2(-1.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4(-1.0, 1.0, 0.0, 1.0); + fragmentData.texCoords = vec2(-1.0, -1.0); + EmitVertex(); + + gl_Position = m * vec4( 1.0, -1.0, 0.0, 1.0); + fragmentData.texCoords = vec2( 1.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4( 1.0, 1.0, 0.0, 1.0); + fragmentData.texCoords = vec2( 1.0, -1.0); + EmitVertex(); + + EndPrimitive(); +} diff --git a/space-crush-app/resources/shader/ship/shared.glsl b/space-crush-app/resources/shader/ship/shared.glsl index e72b165..9a3c27b 100644 --- a/space-crush-app/resources/shader/ship/shared.glsl +++ b/space-crush-app/resources/shader/ship/shared.glsl @@ -1,3 +1,14 @@ struct FragmentData { vec2 texCoords; + vec4 color; }; + +struct VertexData { + vec2 pos; + vec2 dir; + vec4 color; + int texture; +}; + +const float QUAD_SIZE = 150.00; // absolute +const float SHIP_SIZE = 0.50; // relative to QUAD_SIZE diff --git a/space-crush-app/resources/shader/ship/vert.glsl b/space-crush-app/resources/shader/ship/vert.glsl index 7baf06f..7781ea3 100644 --- a/space-crush-app/resources/shader/ship/vert.glsl +++ b/space-crush-app/resources/shader/ship/vert.glsl @@ -1,17 +1,17 @@ #version 450 core #pragma include ./shared.glsl -#pragma include ../misc/camera.glsl -const float GLOW_SIZE_FACTOR = 4.00; +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec2 inDirection; +layout (location = 2) in vec4 inColor; +layout (location = 3) in int inTexture; -in vec3 inPosition; - -uniform mat4 uModel; - -out FragmentData fragmentData; +out VertexData vertexData; void main() { - fragmentData.texCoords = inPosition.xy * GLOW_SIZE_FACTOR + vec2(0.5); - gl_Position = uCamera.projection * uCamera.view * uModel * vec4(2.0 * inPosition * GLOW_SIZE_FACTOR, 1.0); + vertexData.pos = inPosition; + vertexData.dir = inDirection; + vertexData.color = inColor; + vertexData.texture = inTexture; } diff --git a/space-crush-app/src/constants.rs b/space-crush-app/src/constants.rs index 5a715ec..6822704 100644 --- a/space-crush-app/src/constants.rs +++ b/space-crush-app/src/constants.rs @@ -5,7 +5,6 @@ use glc::vector::Vector4f; pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0; pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1; -pub const SHIP_SIZE: f32 = 25.0; pub const PLANET_SIZE: f32 = 200.0; pub const ASTEROID_SIZE: f32 = 100.0; diff --git a/space-crush-app/src/misc/text.rs b/space-crush-app/src/misc/text.rs index e239da9..b60d9ca 100644 --- a/space-crush-app/src/misc/text.rs +++ b/space-crush-app/src/misc/text.rs @@ -643,9 +643,7 @@ impl Text { .uniform(2, Uniform::Vector2f(pos)) .warn("Unable to update text offset"); - // gl::draw_arrays(gl::POINTS, 0, inner.vertex_count as _); gl::draw_arrays(gl::POINTS, 0, inner.vertex_count as _); - // gl::draw_arrays_instanced(gl::POINTS, 0, 1, inner.vertex_count as _); } pub fn update(&mut self, mut index: usize, text: S) -> Result<&Self, Error> diff --git a/space-crush-app/src/render/ships.rs b/space-crush-app/src/render/ships.rs index 6a04100..f80e864 100644 --- a/space-crush-app/src/render/ships.rs +++ b/space-crush-app/src/render/ships.rs @@ -1,74 +1,101 @@ +use std::mem::size_of; + use glc::{ - matrix::Matrix4f, + buffer::{Buffer, Usage}, misc::{BindGuard, Bindable}, shader::{Program, Type, Uniform}, texture::Texture, - vector::Vector4f, + vector::{Vector2f, Vector4f}, + vertex_array::{DataType, VertexArray}, }; use space_crush_common::{ components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, - misc::LogResult, + misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, }; -use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; +use specs::{prelude::*, ReadStorage, System, World}; use crate::{ - constants::{ - PLAYER_COLOR_DEFAULT, SHIP_SIZE, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM, - }, + constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, misc::WorldHelper, - resources::Geometry, Error, }; pub struct Ships { program: Program, + vertex_array: VertexArray, texture_bomber: Texture, texture_fighter: Texture, texture_transporter: Texture, - location_model: gl::GLint, - location_glow_color: gl::GLint, + reader_id: ReaderId>, + 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 { 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)?; - let location_model = program.uniform_location("uModel")?; - let location_glow_color = program.uniform_location("uGlowColor")?; - program.bind(); - program.uniform("uTexture", Uniform::Texture(0))?; + program.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; program.unbind(); + const STRIDE: gl::GLsizei = size_of::() as gl::GLsizei; + + const OFFSET_POS: gl::GLsizei = 0; + const OFFSET_DIR: gl::GLsizei = OFFSET_POS + size_of::() as gl::GLsizei; + const OFFSET_CLR: gl::GLsizei = OFFSET_DIR + size_of::() as gl::GLsizei; + const OFFSET_TEX: gl::GLsizei = OFFSET_CLR + size_of::() 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::>() + .register_event_reader(); + let ship_count = 0; + Ok(Self { program, + vertex_array, texture_bomber, texture_fighter, texture_transporter, - location_model, - location_glow_color, + reader_id, + ship_count, }) } } #[derive(SystemData)] pub struct ShipsData<'a> { - geometry: ReadExpect<'a, Geometry>, - position: ReadStorage<'a, Position>, - velocity: ReadStorage<'a, Velocity>, - ship: ReadStorage<'a, Ship>, + positions: ReadStorage<'a, Position>, + velocities: ReadStorage<'a, Velocity>, + players: ReadStorage<'a, Player>, owned: ReadStorage<'a, PlayerOwned>, - player: ReadStorage<'a, Player>, + ships: ReadStorage<'a, Ship>, } impl<'a> System<'a> for Ships { @@ -76,54 +103,72 @@ impl<'a> System<'a> for Ships { fn run(&mut self, data: Self::SystemData) { let ShipsData { - geometry, - position, - velocity, - ship, + positions, + velocities, + players, owned, - player, + ships, } = data; - gl::enable(gl::BLEND); - gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + /* 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!") + } + } + } - let _guard = BindGuard::new(&self.program); + /* 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::()) + .panic("Unable to change buffer size for ship data"); + } - for (p, v, ship, owned) in (&position, &velocity, &ship, owned.maybe()).join() { - let _guard = match ship.type_() { - ShipType::Fighter => BindGuard::new(&self.texture_fighter), - ShipType::Bomber => BindGuard::new(&self.texture_bomber), - ShipType::Transporter => BindGuard::new(&self.texture_transporter), + let data = (&positions, &velocities, &ships, owned.maybe()); + let mut buffer = buffer + .map_mut::(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, }; - - let c = match owned.and_then(|owned| player.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, }; - - let p_x = p.pos().x; - let p_y = p.pos().y; - let d_x = v.dir().x; - let d_y = v.dir().y; - let s = SHIP_SIZE; - - let m = Matrix4f::new( - Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0), - Vector4f::new(-s * d_x, -s * d_y, 0.0, 0.0), - Vector4f::new(0.0, 0.0, s, 0.0), - Vector4f::new(p_x, p_y, 0.0, 1.0), - ); - - self.program - .uniform(self.location_glow_color, Uniform::Vector4f(&c)) - .error("Error while updating glow color"); - self.program - .uniform(self.location_model, Uniform::Matrix4f(&m)) - .error("Error while updating model matrix"); - - geometry.render_quad(); } + 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_MINUS_SRC_ALPHA); + gl::draw_arrays(gl::POINTS, 0, self.ship_count as _); gl::disable(gl::BLEND); } } diff --git a/space-crush-common/src/misc/flagged_storage.rs b/space-crush-common/src/misc/flagged_storage.rs index 4e4d6d5..9009797 100644 --- a/space-crush-common/src/misc/flagged_storage.rs +++ b/space-crush-common/src/misc/flagged_storage.rs @@ -63,8 +63,10 @@ where unsafe fn get_mut(&mut self, id: Index) -> &mut C { let component = self.storage.get_mut(id); - self.channel - .single_write(ComponentEvent::Modified(id, component.clone())); + if self.event_emission { + self.channel + .single_write(ComponentEvent::Modified(id, component.clone())); + } component } @@ -72,15 +74,19 @@ where unsafe fn insert(&mut self, id: Index, component: C) { self.storage.insert(id, component.clone()); - self.channel - .single_write(ComponentEvent::Inserted(id, component)); + if self.event_emission { + self.channel + .single_write(ComponentEvent::Inserted(id, component)); + } } unsafe fn remove(&mut self, id: Index) -> C { let component = self.storage.remove(id); - self.channel - .single_write(ComponentEvent::Removed(id, component.clone())); + if self.event_emission { + self.channel + .single_write(ComponentEvent::Removed(id, component.clone())); + } component }