From 6e3d980a708edbe88f4393da5112860774f62a9f Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Sat, 26 Dec 2020 02:13:50 +0100 Subject: [PATCH] Implemented ship tail using trandform feedback buffer --- glc/src/buffer.rs | 43 +- glc/src/error.rs | 6 + glc/src/lib.rs | 1 + glc/src/shader.rs | 139 +++-- glc/src/transform_feedback.rs | 117 +++++ glc/src/vertex_array.rs | 115 +++-- .../resources/shader/misc/global.glsl | 1 + .../resources/shader/ship/frag.glsl | 28 - .../resources/shader/ship/glow_frag.glsl | 23 + .../resources/shader/ship/glow_geom.glsl | 44 ++ .../resources/shader/ship/glow_shared.glsl | 12 + .../resources/shader/ship/glow_vert.glsl | 13 + .../resources/shader/ship/shared.glsl | 15 - .../resources/shader/ship/ship_frag.glsl | 14 + .../shader/ship/{geom.glsl => ship_geom.glsl} | 11 +- .../resources/shader/ship/ship_shared.glsl | 11 + .../shader/ship/{vert.glsl => ship_vert.glsl} | 6 +- .../shader/ship/tail_render_frag.glsl | 11 + .../shader/ship/tail_render_geom.glsl | 160 ++++++ .../shader/ship/tail_render_shared.glsl | 11 + .../shader/ship/tail_render_vert.glsl | 23 + .../shader/ship/tail_update_geom.glsl | 40 ++ .../shader/ship/tail_update_shared.glsl | 7 + .../shader/ship/tail_update_vert.glsl | 23 + space-crush-app/src/misc/world.rs | 67 +-- space-crush-app/src/render/asteroids.rs | 4 +- space-crush-app/src/render/planets.rs | 3 +- space-crush-app/src/render/ships.rs | 482 ++++++++++++++---- space-crush-app/src/resources/uniform.rs | 22 +- 29 files changed, 1204 insertions(+), 248 deletions(-) create mode 100644 glc/src/transform_feedback.rs delete mode 100644 space-crush-app/resources/shader/ship/frag.glsl create mode 100644 space-crush-app/resources/shader/ship/glow_frag.glsl create mode 100644 space-crush-app/resources/shader/ship/glow_geom.glsl create mode 100644 space-crush-app/resources/shader/ship/glow_shared.glsl create mode 100644 space-crush-app/resources/shader/ship/glow_vert.glsl delete mode 100644 space-crush-app/resources/shader/ship/shared.glsl create mode 100644 space-crush-app/resources/shader/ship/ship_frag.glsl rename space-crush-app/resources/shader/ship/{geom.glsl => ship_geom.glsl} (77%) create mode 100644 space-crush-app/resources/shader/ship/ship_shared.glsl rename space-crush-app/resources/shader/ship/{vert.glsl => ship_vert.glsl} (64%) create mode 100644 space-crush-app/resources/shader/ship/tail_render_frag.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_render_geom.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_render_shared.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_render_vert.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_update_geom.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_update_shared.glsl create mode 100644 space-crush-app/resources/shader/ship/tail_update_vert.glsl diff --git a/glc/src/buffer.rs b/glc/src/buffer.rs index c248775..59264bc 100644 --- a/glc/src/buffer.rs +++ b/glc/src/buffer.rs @@ -1,6 +1,8 @@ +use std::cell::{Ref, RefCell}; use std::mem::size_of; use std::ops::{Deref, DerefMut}; use std::ptr::null; +use std::rc::Rc; use std::slice::from_raw_parts_mut; use crate::{ @@ -28,6 +30,10 @@ impl Buffer { self.id } + pub fn size(&self) -> usize { + self.size + } + pub fn buffer_data(&mut self, usage: Usage, data: &[T]) -> Result<(), Error> { let size = data.len() * size_of::(); @@ -157,7 +163,10 @@ impl Drop for Buffer { } } -impl<'a> Bindable for (Target, &'a Buffer) { +impl Bindable for (Target, T) +where + T: Deref, +{ fn bind(&self) { gl::bind_buffer(self.0.as_enum(), self.1.id); } @@ -215,6 +224,38 @@ impl DerefMut for MapMut<'_, T> { } } +/* BufferRef */ + +pub trait BufferRef { + type Output: Deref; + + fn as_ref(self) -> Self::Output; +} + +impl<'a> BufferRef for &'a Buffer { + type Output = &'a Buffer; + + fn as_ref(self) -> Self::Output { + self + } +} + +impl<'a> BufferRef for &'a RefCell { + type Output = Ref<'a, Buffer>; + + fn as_ref(self) -> Self::Output { + self.borrow() + } +} + +impl<'a> BufferRef for &'a Rc> { + type Output = Ref<'a, Buffer>; + + fn as_ref(self) -> Self::Output { + self.borrow() + } +} + /* Target */ #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/glc/src/error.rs b/glc/src/error.rs index 775d556..f5fef64 100644 --- a/glc/src/error.rs +++ b/glc/src/error.rs @@ -40,6 +40,12 @@ pub enum Error { #[error("Vertex Array: Expected pointer!")] VertexArrayExpectedPointer, + #[error("Transform Feedback: Index is already in use: {0}!")] + TransformFeedbackIndexAlreadyInUse(gl::GLuint), + + #[error("Transform Feedback: Array Buffer must be for target GL_TRANSFORM_FEEDBACK_BUFFER!")] + TransformFeedbackInvalidBuffer, + #[error("Invalid Parameter!")] InvalidParameter, diff --git a/glc/src/lib.rs b/glc/src/lib.rs index 591ea04..f1dc312 100644 --- a/glc/src/lib.rs +++ b/glc/src/lib.rs @@ -8,5 +8,6 @@ pub mod misc; pub mod numeric; pub mod shader; pub mod texture; +pub mod transform_feedback; pub mod vector; pub mod vertex_array; diff --git a/glc/src/shader.rs b/glc/src/shader.rs index 62f8eb0..1fb158f 100644 --- a/glc/src/shader.rs +++ b/glc/src/shader.rs @@ -20,6 +20,10 @@ pub struct Program { } impl Program { + pub fn builder() -> Builder { + Builder::default() + } + pub fn from_shaders(iter: I) -> Result where I: IntoIterator, @@ -32,43 +36,15 @@ impl Program { I: IntoIterator>, E: From, { - let id = gl::create_program(); - let id = Error::err_if(&0, id)?; + let mut builder = Self::builder(); - let shaders = iter.into_iter().collect::, _>>()?; - for shader in &shaders { - gl::attach_shader(id, shader.id()); + for shader in iter.into_iter() { + builder = builder.add_shader(shader?); } - gl::link_program(id); - - for shader in &shaders { - gl::detach_shader(id, shader.id()); - } - - let mut success = 1; - gl::get_program_iv(id, gl::LINK_STATUS, &mut success); - - if success != 1 { - let mut len = 0; - gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len); - - let mut buffer = Vec::::with_capacity(len as usize + 1); - buffer.resize(len as usize, 0); - - gl::get_program_info_log( - id, - len, - null_mut(), - buffer.as_mut_ptr() as *mut gl::types::GLchar, - ); - - let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?; + let program = builder.build()?; - return Err(Error::ShaderLink(msg.into()).into()); - } - - Ok(Program { id }) + Ok(program) } pub fn id(&self) -> gl::GLuint { @@ -253,6 +229,87 @@ impl Drop for Shader { } } +/* Builder */ + +#[derive(Default)] +pub struct Builder { + shaders: Vec, + transform_feedback_varyings: Option<(TransformFeedbackVaryingsMode, &'static [&'static str])>, +} + +impl Builder { + pub fn add_shader(mut self, shader: Shader) -> Self { + self.shaders.push(shader); + + self + } + + pub fn set_transform_feedback_varyings( + mut self, + mode: TransformFeedbackVaryingsMode, + value: &'static [&'static str], + ) -> Self { + self.transform_feedback_varyings = Some((mode, value)); + + self + } + + pub fn build(self) -> Result { + let id = gl::create_program(); + let id = Error::err_if(&0, id)?; + + for shader in &self.shaders { + gl::attach_shader(id, shader.id()); + } + + if let Some((mode, tfv)) = self.transform_feedback_varyings { + let tfv = tfv + .iter() + .map(|v: &&str| CString::new(*v)) + .collect::, _>>()?; + let tfv = tfv + .iter() + .map(|v: &CString| v.as_ptr() as *const gl::GLchar) + .collect::>(); + let ptr: *const *const gl::GLchar = tfv.as_slice().as_ptr(); + + Error::checked(|| { + gl::transform_feedback_varyings(id, tfv.len() as _, ptr, mode.as_enum()) + })?; + } + + gl::link_program(id); + + for shader in &self.shaders { + gl::detach_shader(id, shader.id()); + } + + let mut success = 1; + gl::get_program_iv(id, gl::LINK_STATUS, &mut success); + + if success != 1 { + let mut len = 0; + gl::get_program_iv(id, gl::INFO_LOG_LENGTH, &mut len); + + let mut buffer = Vec::::with_capacity(len as usize + 1); + buffer.resize(len as usize, 0); + + gl::get_program_info_log( + id, + len, + null_mut(), + buffer.as_mut_ptr() as *mut gl::types::GLchar, + ); + + let msg = from_utf8(&buffer).map_err(Error::Utf8Error)?; + + return Err(Error::ShaderLink(msg.into())); + } + + Ok(Program { id }) + } +} + /* Type */ #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -278,6 +335,22 @@ impl AsEnum for Type { } } +/* TransformFeedbackVaryingsMode */ + +pub enum TransformFeedbackVaryingsMode { + Interleaved, + Separate, +} + +impl AsEnum for TransformFeedbackVaryingsMode { + fn as_enum(&self) -> gl::GLenum { + match self { + Self::Interleaved => gl::INTERLEAVED_ATTRIBS, + Self::Separate => gl::SEPARATE_ATTRIBS, + } + } +} + /* Uniform */ pub enum Uniform<'a> { diff --git a/glc/src/transform_feedback.rs b/glc/src/transform_feedback.rs new file mode 100644 index 0000000..7e53133 --- /dev/null +++ b/glc/src/transform_feedback.rs @@ -0,0 +1,117 @@ +use crate::{ + buffer::{Buffer, BufferRef, Target}, + error::Error, + misc::{BindGuard, Bindable}, +}; + +/* TransformFeedback */ + +pub struct TransformFeedback { + id: gl::GLuint, + buffers: Vec, +} + +impl TransformFeedback { + pub fn builder() -> Builder { + Builder::default() + } + + pub fn id(&self) -> gl::GLuint { + self.id + } + + pub fn buffers(&self) -> &[T] { + &self.buffers + } + + pub fn buffers_mut(&mut self) -> &mut [T] { + &mut self.buffers + } +} + +impl Drop for TransformFeedback { + fn drop(&mut self) { + gl::delete_transform_feedbacks(1, &self.id); + } +} + +impl Bindable for TransformFeedback { + fn bind(&self) { + gl::bind_transform_feedback(gl::TRANSFORM_FEEDBACK, self.id); + } + + fn unbind(&self) { + gl::bind_transform_feedback(gl::TRANSFORM_FEEDBACK, 0); + } +} + +/* Builder */ + +pub struct Builder { + bindings: Vec>, +} + +impl Builder +where + for<'a> &'a T: BufferRef, +{ + pub fn bind_buffer(mut self, index: gl::GLuint, buffer: T) -> Result, Error> { + for binding in &self.bindings { + if binding.index == index { + return Err(Error::TransformFeedbackIndexAlreadyInUse(index)); + } + } + + let binding = Binding { buffer, index }; + + self.bindings.push(binding); + + Ok(self) + } + + pub fn build(self) -> Result, Error> { + let mut id = 0; + + Error::checked(|| gl::create_transform_feedbacks(1, &mut id))?; + + let mut transform_feedback = TransformFeedback { + id, + buffers: Vec::new(), + }; + + let guard = BindGuard::new(&transform_feedback); + + let mut buffers = Vec::new(); + for binding in self.bindings { + Error::checked(|| { + binding + .buffer + .as_ref() + .bind_buffer_base(Target::TransformFeedbackBuffer, binding.index) + })?; + + buffers.push(binding.buffer); + } + + drop(guard); + + transform_feedback.buffers = buffers; + + Ok(transform_feedback) + } +} + +impl Default for Builder { + fn default() -> Self { + Self { + bindings: Vec::new(), + } + } +} + +/* Binding */ + +struct Binding { + buffer: T, + index: gl::GLuint, +} diff --git a/glc/src/vertex_array.rs b/glc/src/vertex_array.rs index 054864c..89ecc1c 100644 --- a/glc/src/vertex_array.rs +++ b/glc/src/vertex_array.rs @@ -1,37 +1,37 @@ use crate::{ - buffer::{Buffer, Target}, + buffer::{Buffer, BufferRef, Target}, error::Error, misc::{AsEnum, BindGuard, Bindable}, }; /* VertexArray */ -pub struct VertexArray { +pub struct VertexArray { id: gl::GLuint, - buffers: Vec, + buffers: Vec, } -impl VertexArray { - pub fn builder() -> Builder { +impl VertexArray { + pub fn builder() -> Builder { Builder::default() } - pub fn buffers(&self) -> &Vec { + pub fn buffers(&self) -> &[T] { &self.buffers } - pub fn buffers_mut(&mut self) -> &mut Vec { + pub fn buffers_mut(&mut self) -> &mut [T] { &mut self.buffers } } -impl Drop for VertexArray { +impl Drop for VertexArray { fn drop(&mut self) { gl::delete_vertex_arrays(1, &self.id); } } -impl Bindable for VertexArray { +impl Bindable for VertexArray { fn bind(&self) { gl::bind_vertex_array(self.id); } @@ -43,13 +43,12 @@ impl Bindable for VertexArray { /* Builder */ -#[derive(Default)] -pub struct Builder { - bindings: Vec, +pub struct Builder { + bindings: Vec>, } -impl Builder { - pub fn bind_buffer(mut self, buffer: Buffer) -> BindingBuilder { +impl Builder { + pub fn bind_buffer(mut self, buffer: T) -> BindingBuilder { let binding = Binding { buffer, pointers: Vec::new(), @@ -59,8 +58,13 @@ impl Builder { BindingBuilder { builder: self } } +} - pub fn build(self) -> Result { +impl Builder +where + for<'a> &'a T: BufferRef, +{ + pub fn build(self) -> Result, Error> { let mut id = 0; Error::checked(|| gl::create_vertex_arrays(1, &mut id))?; @@ -74,7 +78,7 @@ impl Builder { let mut buffers = Vec::new(); for binding in self.bindings { - let guard = BindGuard::new((Target::ArrayBuffer, &binding.buffer)); + let guard = BindGuard::new((Target::ArrayBuffer, binding.buffer.as_ref())); for pointer in binding.pointers { Error::checked(|| gl::enable_vertex_attrib_array(pointer.index))?; @@ -128,14 +132,33 @@ impl Builder { } } +impl Clone for Builder +where + T: Clone, +{ + fn clone(&self) -> Self { + Self { + bindings: self.bindings.clone(), + } + } +} + +impl Default for Builder { + fn default() -> Self { + Self { + bindings: Vec::new(), + } + } +} + /* BindingBuilder */ -pub struct BindingBuilder { - builder: Builder, +pub struct BindingBuilder { + builder: Builder, } -impl BindingBuilder { - pub fn bind_buffer(self, buffer: Buffer) -> Self { +impl BindingBuilder { + pub fn bind_buffer(self, buffer: T) -> Self { self.builder.bind_buffer(buffer) } @@ -187,15 +210,32 @@ impl BindingBuilder { Ok(self) } +} - pub fn build(self) -> Result { +impl BindingBuilder +where + for<'a> &'a T: BufferRef, +{ + pub fn build(self) -> Result, Error> { self.builder.build() } } +impl Clone for BindingBuilder +where + T: Clone, +{ + fn clone(&self) -> Self { + Self { + builder: self.builder.clone(), + } + } +} + /* DataType */ #[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum DataType { Byte, UnsignedByte, @@ -234,19 +274,32 @@ impl AsEnum for DataType { /* Binding */ -struct Binding { - buffer: Buffer, +struct Binding { + buffer: T, pointers: Vec, } +impl Clone for Binding +where + T: Clone, +{ + fn clone(&self) -> Self { + Self { + buffer: self.buffer.clone(), + pointers: self.pointers.clone(), + } + } +} + /* Pointer */ -struct Pointer { - index: gl::GLuint, - size: gl::GLint, - type_: DataType, - normalize: bool, - stride: gl::GLsizei, - offset: gl::GLsizei, - divisor: Option, +#[derive(Clone, Debug)] +pub struct Pointer { + pub index: gl::GLuint, + pub size: gl::GLint, + pub type_: DataType, + pub normalize: bool, + pub stride: gl::GLsizei, + pub offset: gl::GLsizei, + pub divisor: Option, } diff --git a/space-crush-app/resources/shader/misc/global.glsl b/space-crush-app/resources/shader/misc/global.glsl index 0ce8fb9..c5d4abf 100644 --- a/space-crush-app/resources/shader/misc/global.glsl +++ b/space-crush-app/resources/shader/misc/global.glsl @@ -1,3 +1,4 @@ layout (std140) uniform Global { float time; + float delta; } uGlobal; diff --git a/space-crush-app/resources/shader/ship/frag.glsl b/space-crush-app/resources/shader/ship/frag.glsl deleted file mode 100644 index 33f3e51..0000000 --- a/space-crush-app/resources/shader/ship/frag.glsl +++ /dev/null @@ -1,28 +0,0 @@ -#version 450 core - -#pragma include ./shared.glsl -#pragma include ../misc/glow.glsl -#pragma include ../misc/global.glsl - -const GlowArgs GLOW_ARGS = { - /* step0 */ 0.100, - /* step1 */ 0.900, - /* pulseSize0 */ 0.050, - /* pulseSize1 */ 0.100, - /* pulseTime */ 2.000, -}; - - in FragmentData fragmentData; -flat in int textureId; - -uniform sampler2D uTexture[3]; - -out vec4 outColor; - -void main() { - float alpha = glow(GLOW_ARGS, fragmentData.texCoords, uGlobal.time); - vec4 glow = vec4(fragmentData.color, GLOW_ALPHA * alpha); - vec4 tex = texture(uTexture[textureId], 0.5 * GLOW_SIZE * fragmentData.texCoords + vec2(0.5)); - - outColor = tex * tex.a + glow * (1.0 - tex.a); -} diff --git a/space-crush-app/resources/shader/ship/glow_frag.glsl b/space-crush-app/resources/shader/ship/glow_frag.glsl new file mode 100644 index 0000000..4cc4418 --- /dev/null +++ b/space-crush-app/resources/shader/ship/glow_frag.glsl @@ -0,0 +1,23 @@ +#version 450 core + +#pragma include ./glow_shared.glsl +#pragma include ../misc/glow.glsl +#pragma include ../misc/global.glsl + +const GlowArgs GLOW_ARGS = { + /* step0 */ 0.100, + /* step1 */ 0.900, + /* pulseSize0 */ 0.050, + /* pulseSize1 */ 0.100, + /* pulseTime */ 2.000, +}; + +in FragmentData fragmentData; + +out vec4 outColor; + +void main() { + float alpha = glow(GLOW_ARGS, fragmentData.texCoords, uGlobal.time); + + outColor = vec4(fragmentData.color, GLOW_ALPHA * alpha); +} diff --git a/space-crush-app/resources/shader/ship/glow_geom.glsl b/space-crush-app/resources/shader/ship/glow_geom.glsl new file mode 100644 index 0000000..f45f499 --- /dev/null +++ b/space-crush-app/resources/shader/ship/glow_geom.glsl @@ -0,0 +1,44 @@ +#version 450 core + +#pragma include ./glow_shared.glsl +#pragma include ../misc/camera.glsl + +in VertexData vertexData[]; + +layout (points) in; +layout (triangle_strip, max_vertices = 4) out; + +out FragmentData fragmentData; + +void main() { + VertexData d = vertexData[0]; + + vec2 pos = d.pos; + float size = GLOW_SIZE; + + mat4 m = uCamera.projection * uCamera.view * mat4( + vec4( size, 0.0, 0.0, 0.0), + vec4( 0.0, size, 0.0, 0.0), + vec4( 0.0, 0.0, 1.0, 0.0), + vec4(pos.x, pos.y, 0.0, 1.0)); + + 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/glow_shared.glsl b/space-crush-app/resources/shader/ship/glow_shared.glsl new file mode 100644 index 0000000..4db7c06 --- /dev/null +++ b/space-crush-app/resources/shader/ship/glow_shared.glsl @@ -0,0 +1,12 @@ +struct FragmentData { + vec2 texCoords; + vec3 color; +}; + +struct VertexData { + vec2 pos; + vec3 color; +}; + +const float GLOW_SIZE = 100.00; +const float GLOW_ALPHA = 0.05; diff --git a/space-crush-app/resources/shader/ship/glow_vert.glsl b/space-crush-app/resources/shader/ship/glow_vert.glsl new file mode 100644 index 0000000..93ed48c --- /dev/null +++ b/space-crush-app/resources/shader/ship/glow_vert.glsl @@ -0,0 +1,13 @@ +#version 450 core + +#pragma include ./glow_shared.glsl + +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec3 inColor; + +out VertexData vertexData; + +void main() { + vertexData.pos = inPosition; + vertexData.color = inColor; +} diff --git a/space-crush-app/resources/shader/ship/shared.glsl b/space-crush-app/resources/shader/ship/shared.glsl deleted file mode 100644 index 7c6ae68..0000000 --- a/space-crush-app/resources/shader/ship/shared.glsl +++ /dev/null @@ -1,15 +0,0 @@ -struct FragmentData { - vec2 texCoords; - vec3 color; -}; - -struct VertexData { - vec2 pos; - vec2 dir; - vec3 color; - int texture; -}; - -const float SHIP_SIZE = 25.0; // absolute ship size -const float GLOW_SIZE = 4.0; // relative to ship size -const float GLOW_ALPHA = 0.1; diff --git a/space-crush-app/resources/shader/ship/ship_frag.glsl b/space-crush-app/resources/shader/ship/ship_frag.glsl new file mode 100644 index 0000000..6ca366b --- /dev/null +++ b/space-crush-app/resources/shader/ship/ship_frag.glsl @@ -0,0 +1,14 @@ +#version 450 core + +#pragma include ./ship_shared.glsl + + in FragmentData fragmentData; +flat in int textureId; + +uniform sampler2D uTexture[3]; + +out vec4 outColor; + +void main() { + outColor = texture(uTexture[textureId], fragmentData.texCoords); +} diff --git a/space-crush-app/resources/shader/ship/geom.glsl b/space-crush-app/resources/shader/ship/ship_geom.glsl similarity index 77% rename from space-crush-app/resources/shader/ship/geom.glsl rename to space-crush-app/resources/shader/ship/ship_geom.glsl index bb44c39..3c5cf88 100644 --- a/space-crush-app/resources/shader/ship/geom.glsl +++ b/space-crush-app/resources/shader/ship/ship_geom.glsl @@ -1,6 +1,6 @@ #version 450 core -#pragma include ./shared.glsl +#pragma include ./ship_shared.glsl #pragma include ../misc/camera.glsl in VertexData vertexData[]; @@ -15,7 +15,7 @@ void main() { VertexData d = vertexData[0]; vec2 pos = d.pos; - vec2 dir = d.dir * SHIP_SIZE * GLOW_SIZE; + vec2 dir = d.dir * SHIP_SIZE; mat4 m = uCamera.projection * uCamera.view * mat4( vec4(dir.y, -dir.x, 0.0, 0.0), @@ -24,14 +24,13 @@ void main() { 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); + fragmentData.texCoords = vec2( 0.0, 1.0); EmitVertex(); gl_Position = m * vec4(-1.0, 1.0, 0.0, 1.0); - fragmentData.texCoords = vec2(-1.0, -1.0); + fragmentData.texCoords = vec2( 0.0, 0.0); EmitVertex(); gl_Position = m * vec4( 1.0, -1.0, 0.0, 1.0); @@ -39,7 +38,7 @@ void main() { EmitVertex(); gl_Position = m * vec4( 1.0, 1.0, 0.0, 1.0); - fragmentData.texCoords = vec2( 1.0, -1.0); + fragmentData.texCoords = vec2( 1.0, 0.0); EmitVertex(); EndPrimitive(); diff --git a/space-crush-app/resources/shader/ship/ship_shared.glsl b/space-crush-app/resources/shader/ship/ship_shared.glsl new file mode 100644 index 0000000..1867768 --- /dev/null +++ b/space-crush-app/resources/shader/ship/ship_shared.glsl @@ -0,0 +1,11 @@ +struct FragmentData { + vec2 texCoords; +}; + +struct VertexData { + vec2 pos; + vec2 dir; + int texture; +}; + +const float SHIP_SIZE = 25.00; diff --git a/space-crush-app/resources/shader/ship/vert.glsl b/space-crush-app/resources/shader/ship/ship_vert.glsl similarity index 64% rename from space-crush-app/resources/shader/ship/vert.glsl rename to space-crush-app/resources/shader/ship/ship_vert.glsl index 2228081..514ea4a 100644 --- a/space-crush-app/resources/shader/ship/vert.glsl +++ b/space-crush-app/resources/shader/ship/ship_vert.glsl @@ -1,17 +1,15 @@ #version 450 core -#pragma include ./shared.glsl +#pragma include ./ship_shared.glsl layout (location = 0) in vec2 inPosition; layout (location = 1) in vec2 inDirection; -layout (location = 2) in vec3 inColor; -layout (location = 3) in int inTexture; +layout (location = 2) in int inTexture; out VertexData vertexData; void main() { vertexData.pos = inPosition; vertexData.dir = inDirection; - vertexData.color = inColor; vertexData.texture = inTexture; } diff --git a/space-crush-app/resources/shader/ship/tail_render_frag.glsl b/space-crush-app/resources/shader/ship/tail_render_frag.glsl new file mode 100644 index 0000000..5fdd6b6 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_frag.glsl @@ -0,0 +1,11 @@ +#version 450 core + +#pragma include ./tail_render_shared.glsl + +in FragmentData fragmentData; + +out vec4 outColor; + +void main() { + outColor = fragmentData.color; +} diff --git a/space-crush-app/resources/shader/ship/tail_render_geom.glsl b/space-crush-app/resources/shader/ship/tail_render_geom.glsl new file mode 100644 index 0000000..53e36f6 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_geom.glsl @@ -0,0 +1,160 @@ +#version 450 core + +#pragma include ./tail_render_shared.glsl +#pragma include ../misc/camera.glsl + +/* +Vertex Layout: + .-- 0 --- 3 --- 6 --- 9 --- 12 --- 15 + / | \ | \ | \ | \ | \ | + SHIP ----- 1 --- 4 --- 7 --- 10 --- 13 --- 16 + \ | / | / | / | / | / | + `-- 2 --- 5 --- 8 --- 11 --- 14 --- 17 +*/ + +in VertexData vertexData[]; + +layout (points) in; +layout (triangle_strip, max_vertices = 24) out; + +out FragmentData fragmentData; + +struct Vertex { + vec2 pos; + vec4 clr; +}; + +vec2 ortho2(vec2 pos, vec2 ref) { + vec2 dir = normalize(pos - ref); + + return vec2(dir.y, -dir.x); +} + +vec2 ortho3(vec2 ref0, vec2 pos, vec2 ref1) { + vec2 dir0 = ortho2(ref0, pos); + vec2 dir1 = ortho2(pos, ref1); + + return normalize(dir0 + dir1); +} + +void emit(Vertex v) { + gl_Position = uCamera.projection * uCamera.view * vec4(v.pos, 0.0, 1.0); + fragmentData.color = v.clr; + + EmitVertex(); +} + +void main() { + VertexData d = vertexData[0]; + + vec2 ortho; + Vertex vertices[18]; + + /* tail[0] */ + + ortho = ortho2(d.tail[0], d.tail[1]); + + vertices[0].pos = d.tail[0] + TAIL_WIDTH * ortho; + vertices[0].clr = vec4(d.color, 0.8 * TAIL_ALPHA); + + vertices[1].pos = d.tail[0]; + vertices[1].clr = vec4(d.color, 1.0 * TAIL_ALPHA); + + vertices[2].pos = d.tail[0] - TAIL_WIDTH * ortho; + vertices[2].clr = vec4(d.color, 0.8 * TAIL_ALPHA); + + /* tail[1] */ + + ortho = ortho3(d.tail[0], d.tail[1], d.tail[2]); + + vertices[3].pos = d.tail[1] + TAIL_WIDTH * ortho; + vertices[3].clr = vec4(d.color, 0.6 * TAIL_ALPHA); + + vertices[4].pos = d.tail[1]; + vertices[4].clr = vec4(d.color, 0.8 * TAIL_ALPHA); + + vertices[5].pos = d.tail[1] - TAIL_WIDTH * ortho; + vertices[5].clr = vec4(d.color, 0.6 * TAIL_ALPHA); + + /* tail[2] */ + + ortho = ortho3(d.tail[1], d.tail[2], d.tail[3]); + + vertices[6].pos = d.tail[2] + TAIL_WIDTH * ortho; + vertices[6].clr = vec4(d.color, 0.4 * TAIL_ALPHA); + + vertices[7].pos = d.tail[2]; + vertices[7].clr = vec4(d.color, 0.6 * TAIL_ALPHA); + + vertices[8].pos = d.tail[2] - TAIL_WIDTH * ortho; + vertices[8].clr = vec4(d.color, 0.4 * TAIL_ALPHA); + + /* tail[3] */ + + ortho = ortho3(d.tail[2], d.tail[3], d.tail[4]); + + vertices[9].pos = d.tail[3] + TAIL_WIDTH * ortho; + vertices[9].clr = vec4(d.color, 0.2 * TAIL_ALPHA); + + vertices[10].pos = d.tail[3]; + vertices[10].clr = vec4(d.color, 0.4 * TAIL_ALPHA); + + vertices[11].pos = d.tail[3] - TAIL_WIDTH * ortho; + vertices[11].clr = vec4(d.color, 0.2 * TAIL_ALPHA); + + /* tail[4] */ + + ortho = ortho3(d.tail[3], d.tail[4], d.tail[5]); + + vertices[12].pos = d.tail[4] + TAIL_WIDTH * ortho; + vertices[12].clr = vec4(d.color, 0.0 * TAIL_ALPHA); + + vertices[13].pos = d.tail[4]; + vertices[13].clr = vec4(d.color, 0.2 * TAIL_ALPHA); + + vertices[14].pos = d.tail[4] - TAIL_WIDTH * ortho; + vertices[14].clr = vec4(d.color, 0.0 * TAIL_ALPHA); + + /* tail[5] */ + + ortho = ortho2(d.tail[4], d.tail[5]); + + vertices[15].pos = d.tail[5] + TAIL_WIDTH * ortho; + vertices[15].clr = vec4(d.color, 0.0 * TAIL_ALPHA); + + vertices[16].pos = d.tail[5]; + vertices[16].clr = vec4(d.color, 0.0 * TAIL_ALPHA); + + vertices[17].pos = d.tail[5] - TAIL_WIDTH * ortho; + vertices[17].clr = vec4(d.color, 0.0 * TAIL_ALPHA); + + /* emit */ + + emit(vertices[1]); + emit(vertices[0]); + emit(vertices[4]); + emit(vertices[3]); + emit(vertices[7]); + emit(vertices[6]); + emit(vertices[10]); + emit(vertices[9]); + emit(vertices[13]); + emit(vertices[12]); + emit(vertices[16]); + emit(vertices[15]); + EndPrimitive(); + + emit(vertices[1]); + emit(vertices[2]); + emit(vertices[4]); + emit(vertices[5]); + emit(vertices[7]); + emit(vertices[8]); + emit(vertices[10]); + emit(vertices[11]); + emit(vertices[13]); + emit(vertices[14]); + emit(vertices[16]); + emit(vertices[17]); + EndPrimitive(); +} diff --git a/space-crush-app/resources/shader/ship/tail_render_shared.glsl b/space-crush-app/resources/shader/ship/tail_render_shared.glsl new file mode 100644 index 0000000..5942335 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_shared.glsl @@ -0,0 +1,11 @@ +struct FragmentData { + vec4 color; +}; + +struct VertexData { + vec3 color; + vec2 tail[6]; +}; + +const float TAIL_WIDTH = 3.0; +const float TAIL_ALPHA = 0.2; diff --git a/space-crush-app/resources/shader/ship/tail_render_vert.glsl b/space-crush-app/resources/shader/ship/tail_render_vert.glsl new file mode 100644 index 0000000..f68bca5 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_vert.glsl @@ -0,0 +1,23 @@ +#version 450 core + +#pragma include ./tail_render_shared.glsl + +layout (location = 0) in vec3 inColor; +layout (location = 1) in vec2 inTail0; +layout (location = 2) in vec2 inTail1; +layout (location = 3) in vec2 inTail2; +layout (location = 4) in vec2 inTail3; +layout (location = 5) in vec2 inTail4; +layout (location = 6) in vec2 inTail5; + +out VertexData vertexData; + +void main() { + vertexData.color = inColor; + vertexData.tail[0] = inTail0; + vertexData.tail[1] = inTail1; + vertexData.tail[2] = inTail2; + vertexData.tail[3] = inTail3; + vertexData.tail[4] = inTail4; + vertexData.tail[5] = inTail5; +} diff --git a/space-crush-app/resources/shader/ship/tail_update_geom.glsl b/space-crush-app/resources/shader/ship/tail_update_geom.glsl new file mode 100644 index 0000000..928bee5 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_geom.glsl @@ -0,0 +1,40 @@ +#version 450 core + +#pragma include ./tail_update_shared.glsl +#pragma include ../misc/global.glsl + +in VertexData vertexData[]; + +layout (points) in; +layout (points, max_vertices = 1) out; + +out vec2 outTail0; +out vec2 outTail1; +out vec2 outTail2; +out vec2 outTail3; +out vec2 outTail4; +out vec2 outTail5; + +vec2 move(vec2 ref, vec2 point) { + vec2 dir = ref - point; + float len = length(dir); + float diff = max(len - TAIL_LEN, 0.0); + float force = min(TAIL_FORCE * diff * uGlobal.delta, diff); + + return point + normalize(dir) * force; +} + +void main() { + VertexData d = vertexData[0]; + + outTail0 = d.pos; + + outTail1 = move(d.pos, d.tail[1]); + outTail2 = move(d.tail[1], d.tail[2]); + outTail3 = move(d.tail[2], d.tail[3]); + outTail4 = move(d.tail[3], d.tail[4]); + outTail5 = move(d.tail[4], d.tail[5]); + + EmitVertex(); + EndPrimitive(); +} diff --git a/space-crush-app/resources/shader/ship/tail_update_shared.glsl b/space-crush-app/resources/shader/ship/tail_update_shared.glsl new file mode 100644 index 0000000..e52edc3 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_shared.glsl @@ -0,0 +1,7 @@ +struct VertexData { + vec2 pos; + vec2 tail[6]; +}; + +const float TAIL_LEN = 10.0; +const float TAIL_FORCE = 10.0; diff --git a/space-crush-app/resources/shader/ship/tail_update_vert.glsl b/space-crush-app/resources/shader/ship/tail_update_vert.glsl new file mode 100644 index 0000000..de664e1 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_vert.glsl @@ -0,0 +1,23 @@ +#version 450 core + +#pragma include ./tail_update_shared.glsl + +layout (location = 0) in vec2 inPosition; +layout (location = 1) in vec2 inTail0; +layout (location = 2) in vec2 inTail1; +layout (location = 3) in vec2 inTail2; +layout (location = 4) in vec2 inTail3; +layout (location = 5) in vec2 inTail4; +layout (location = 6) in vec2 inTail5; + +out VertexData vertexData; + +void main() { + vertexData.pos = inPosition; + vertexData.tail[0] = inTail0; + vertexData.tail[1] = inTail1; + vertexData.tail[2] = inTail2; + vertexData.tail[3] = inTail3; + vertexData.tail[4] = inTail4; + vertexData.tail[5] = inTail5; +} diff --git a/space-crush-app/src/misc/world.rs b/space-crush-app/src/misc/world.rs index 7348253..d5226f9 100644 --- a/space-crush-app/src/misc/world.rs +++ b/space-crush-app/src/misc/world.rs @@ -17,6 +17,8 @@ pub trait WorldHelper { where I: IntoIterator; + fn load_shader(&self, type_: Type, file: &str) -> Result; + fn load_texture(&self, path: &str) -> Result; } @@ -25,39 +27,40 @@ impl WorldHelper for World { where I: IntoIterator, { - let vfs = self.fetch::(); + Program::from_shaders_result(iter.into_iter().map(|(t, p)| self.load_shader(t, p))) + } - Program::from_shaders_result(iter.into_iter().map(|(t, p)| { - let path = vfs.join(p)?; - let mut file = path.open_file()?; - let path = path.parent().unwrap(); - let shader = Shader::from_reader(t, &mut file, |include| { - let p = if include.starts_with('.') { - path.join(include) - } else { - vfs.join(include) - }; - - let p = match p { - Ok(p) => p, - Err(err) => return Err(GlcError::ShaderInclude(format!("{}", err))), - }; - - let mut r = match p.open_file() { - Ok(r) => r, - Err(err) => return Err(GlcError::ShaderInclude(format!("{}", err))), - }; - - let mut code = String::default(); - if let Err(err) = r.read_to_string(&mut code) { - return Err(GlcError::ShaderInclude(format!("{}", err))); - } - - Ok(code) - })?; - - Ok(shader) - })) + fn load_shader(&self, type_: Type, path: &str) -> Result { + let vfs = self.fetch::(); + let path = vfs.join(path)?; + let mut file = path.open_file()?; + let path = path.parent().unwrap(); + let shader = Shader::from_reader(type_, &mut file, |include| { + let p = if include.starts_with('.') { + path.join(include) + } else { + vfs.join(include) + }; + + let p = match p { + Ok(p) => p, + Err(err) => return Err(GlcError::ShaderInclude(format!("{}", err))), + }; + + let mut r = match p.open_file() { + Ok(r) => r, + Err(err) => return Err(GlcError::ShaderInclude(format!("{}", err))), + }; + + let mut code = String::default(); + if let Err(err) = r.read_to_string(&mut code) { + return Err(GlcError::ShaderInclude(format!("{}", err))); + } + + Ok(code) + })?; + + Ok(shader) } fn load_texture(&self, path: &str) -> Result { diff --git a/space-crush-app/src/render/asteroids.rs b/space-crush-app/src/render/asteroids.rs index c30a95d..82bdcf1 100644 --- a/space-crush-app/src/render/asteroids.rs +++ b/space-crush-app/src/render/asteroids.rs @@ -167,10 +167,10 @@ impl<'a> System<'a> for Asteroids { let _guard = BindGuard::new(&self.vertex_array); gl::active_texture(gl::TEXTURE1); - let _guard = BindGuard::new(&self.texture_crystal); + self.texture_crystal.bind(); gl::active_texture(gl::TEXTURE0); - let _guard = BindGuard::new(&self.texture_metal); + self.texture_metal.bind(); gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); diff --git a/space-crush-app/src/render/planets.rs b/space-crush-app/src/render/planets.rs index 31b0cc8..eee1454 100644 --- a/space-crush-app/src/render/planets.rs +++ b/space-crush-app/src/render/planets.rs @@ -155,7 +155,8 @@ impl<'a> System<'a> for Planets { /* render planets */ let _guard = BindGuard::new(&self.program); let _guard = BindGuard::new(&self.vertex_array); - let _guard = BindGuard::new(&self.texture); + + self.texture.bind(); gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); diff --git a/space-crush-app/src/render/ships.rs b/space-crush-app/src/render/ships.rs index 9466013..2d4e69c 100644 --- a/space-crush-app/src/render/ships.rs +++ b/space-crush-app/src/render/ships.rs @@ -1,10 +1,13 @@ -use std::mem::size_of; +use std::cell::RefCell; +use std::mem::{size_of, swap}; +use std::rc::Rc; use glc::{ buffer::{Buffer, Usage}, misc::{BindGuard, Bindable}, - shader::{Program, Type, Uniform}, + shader::{Program, TransformFeedbackVaryingsMode, Type, Uniform}, texture::Texture, + transform_feedback::TransformFeedback, vector::{Vector2f, Vector3f}, vertex_array::{DataType, VertexArray}, }; @@ -12,7 +15,12 @@ use space_crush_common::{ components::{Player, PlayerOwned, Position, Ship, ShipType, Velocity}, misc::{ComponentEvent, LogResult, StorageHelper, StorageHelperMut}, }; -use specs::{prelude::*, ReadStorage, System, World}; +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}, @@ -21,154 +29,444 @@ use crate::{ }; pub struct Ships { - program: Program, - vertex_array: VertexArray, + program_ship: Program, + program_glow: Program, + program_tail_update: Program, + program_tail_render: Program, + + ship_data: BufferRef, + ship_glow: VertexArray, + ship_render: VertexArray, + + input: TailObjects, + output: TailObjects, + texture_bomber: Texture, texture_fighter: Texture, texture_transporter: Texture, + reader_id: ReaderId>, - ship_count: usize, + + need_init: BitSet, + id_to_index: Vec, + index_to_id: Vec, +} + +#[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, + tail_render: VertexArray, + transform_feedback: TransformFeedback, } +type BufferRef = Rc>; + #[repr(C, packed)] -struct VertexData { +#[derive(Debug, Clone, Copy)] +struct ShipData { pos: Vector2f, dir: Vector2f, color: Vector3f, texture: gl::GLint, } +#[repr(C, packed)] +#[derive(Debug, Clone, Copy)] +struct TailData { + pos: [Vector2f; 6], +} + 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 ship */ + let program_ship = world.load_program(vec![ + (Type::Vertex, SHDR_SHIP_VERT), + (Type::Geometry, SHDR_SHIP_GEOM), + (Type::Fragment, SHDR_SHIP_FRAG), ])?; - program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; - program.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?; + program_ship.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; - program.bind(); - program.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; - program.unbind(); + program_ship.bind(); + program_ship.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; + program_ship.unbind(); - const STRIDE: gl::GLsizei = size_of::() as gl::GLsizei; + /* program glow */ + let program_glow = world.load_program(vec![ + (Type::Vertex, SHDR_GLOW_VERT), + (Type::Geometry, SHDR_GLOW_GEOM), + (Type::Fragment, SHDR_GLOW_FRAG), + ])?; - 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; + program_glow.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; + program_glow.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?; - 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, 3, DataType::Float, false, STRIDE, OFFSET_CLR)? - .vertex_attrib_pointer(3, 1, DataType::Int, false, STRIDE, OFFSET_TEX)? + /* 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 glow */ + let ship_glow = VertexArray::builder() + .bind_buffer(ship_data.clone()) + .vertex_attrib_pointer(0, 2, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_POS)? + .vertex_attrib_pointer(1, 3, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_CLR)? + .build()?; + + /* 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, 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::>() .register_event_reader(); - let ship_count = 0; + + /* rest */ + let need_init = BitSet::new(); + let id_to_index = Vec::new(); + let index_to_id = Vec::new(); Ok(Self { - program, - vertex_array, + program_ship, + program_glow, + program_tail_update, + program_tail_render, + + ship_data, + ship_glow, + ship_render, + + input, + output, + texture_bomber, texture_fighter, texture_transporter, + reader_id, - ship_count, + + need_init, + id_to_index, + index_to_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>, -} - -impl<'a> System<'a> for Ships { - type SystemData = ShipsData<'a>; - fn run(&mut self, data: Self::SystemData) { - let ShipsData { - positions, - velocities, - players, - owned, - ships, - } = data; - - /* handle events */ - let old_count = self.ship_count; - let events = ships.channel().read(&mut self.reader_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(_, _) => self.ship_count += 1, - ComponentEvent::Removed(_, _) => self.ship_count -= 1, + 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!") } } } + } - /* 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"); - } - - let data = (&positions, &velocities, &ships, owned.maybe()); - let mut buffer = buffer - .map_mut::(true) + fn update_vertices(&self, d: &ShipsData<'_>) { + let mut borrow_ship = self.ship_data.borrow_mut(); + let mut buf_ship = borrow_ship + .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, - }; - d.texture = match ship.type_() { - ShipType::Fighter => 0, - ShipType::Bomber => 1, - ShipType::Transporter => 2, - }; + + 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::(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_tails(&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(buffer); + drop(guard_trfm); + drop(guard_data); + drop(guard_prog); - /* render ships */ - let _guard = BindGuard::new(&self.program); - let _guard = BindGuard::new(&self.vertex_array); + swap(&mut self.input, &mut self.output); + } + + fn render_glow(&self) { + let _guard = BindGuard::new(&self.program_glow); + let _guard = BindGuard::new(&self.ship_glow); + + 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_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); + self.texture_transporter.bind(); gl::active_texture(gl::TEXTURE1); - let _guard = BindGuard::new(&self.texture_bomber); + self.texture_bomber.bind(); gl::active_texture(gl::TEXTURE0); - let _guard = BindGuard::new(&self.texture_fighter); + self.texture_fighter.bind(); gl::enable(gl::BLEND); - gl::blend_func(gl::SRC_ALPHA, gl::ONE); - gl::draw_arrays(gl::POINTS, 0, self.ship_count as _); + gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); + 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::() * 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::() * 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 index = self.id_to_index[id as usize]; + + /* update id/index maps */ + let last_index = self.index_to_id.len() - 1; + let last_id = self.index_to_id.pop().unwrap(); + + self.id_to_index[last_id as usize] = index; + + /* move ship data */ + let mut buffer = self.ship_data.borrow_mut(); + let mut buffer = buffer + .map_mut::(false) + .panic("Unable to map buffer for ship data"); + buffer[index as usize] = buffer[last_index as usize]; + + /* move tail data */ + let mut buffer = self.input.tail_data.borrow_mut(); + let mut buffer = buffer + .map_mut::(false) + .panic("Unable to map buffer for tail data"); + buffer[index as usize] = buffer[last_index as usize]; + } } + +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_tails(); + self.render_glow(); + self.render_tails(); + self.render_ships(); + } +} + +impl TailObjects { + fn new(ship_data: &BufferRef) -> Result { + /* tail data */ + let tail_data = Rc::new(RefCell::new(Buffer::new()?)); + tail_data + .borrow_mut() + .buffer_size(Usage::DynamicDraw, size_of::() 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)? + .vertex_attrib_pointer(6, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL5)? + .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)? + .vertex_attrib_pointer(6, 2, DataType::Float, false, TAIL_STRIDE, TAIL_OFFSET_TL5)? + .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_GLOW_VERT: &str = "resources/shader/ship/glow_vert.glsl"; +const SHDR_GLOW_GEOM: &str = "resources/shader/ship/glow_geom.glsl"; +const SHDR_GLOW_FRAG: &str = "resources/shader/ship/glow_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", "outTail5", +]; + +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::() as gl::GLsizei; +const SHIP_OFFSET_POS: gl::GLsizei = 0; +const SHIP_OFFSET_DIR: gl::GLsizei = SHIP_OFFSET_POS + size_of::() as gl::GLsizei; +const SHIP_OFFSET_CLR: gl::GLsizei = SHIP_OFFSET_DIR + size_of::() as gl::GLsizei; +const SHIP_OFFSET_TEX: gl::GLsizei = SHIP_OFFSET_CLR + size_of::() as gl::GLsizei; + +const TAIL_STRIDE: gl::GLsizei = size_of::() as gl::GLsizei; +const TAIL_OFFSET_TL0: gl::GLsizei = 0; +const TAIL_OFFSET_TL1: gl::GLsizei = TAIL_OFFSET_TL0 + size_of::() as gl::GLsizei; +const TAIL_OFFSET_TL2: gl::GLsizei = TAIL_OFFSET_TL1 + size_of::() as gl::GLsizei; +const TAIL_OFFSET_TL3: gl::GLsizei = TAIL_OFFSET_TL2 + size_of::() as gl::GLsizei; +const TAIL_OFFSET_TL4: gl::GLsizei = TAIL_OFFSET_TL3 + size_of::() as gl::GLsizei; +const TAIL_OFFSET_TL5: gl::GLsizei = TAIL_OFFSET_TL4 + size_of::() as gl::GLsizei; diff --git a/space-crush-app/src/resources/uniform.rs b/space-crush-app/src/resources/uniform.rs index c65a7cf..2af9132 100644 --- a/space-crush-app/src/resources/uniform.rs +++ b/space-crush-app/src/resources/uniform.rs @@ -8,30 +8,46 @@ use glc::{ pub struct Uniform { buffer: Buffer, start_time: Instant, + last_time: Instant, } #[repr(C, packed)] struct Data { time: f32, + delta: f32, } impl Uniform { pub fn new() -> Result { let mut buffer = Buffer::new()?; - buffer.buffer_data(Usage::StaticDraw, &[Data { time: 0.0 }])?; + buffer.buffer_data( + Usage::StaticDraw, + &[Data { + time: 0.0, + delta: 0.0, + }], + )?; + + let now = Instant::now(); Ok(Self { buffer, - start_time: Instant::now(), + start_time: now, + last_time: now, }) } pub fn update(&mut self) -> Result<(), Error> { - let time = Instant::now().duration_since(self.start_time).as_secs_f32(); + let now = Instant::now(); + let time = now.duration_since(self.start_time).as_secs_f32(); + let delta = now.duration_since(self.last_time).as_secs_f32(); + + self.last_time = now; let mut data = self.buffer.map_mut::(true)?; data[0].time = time; + data[0].delta = delta; Ok(()) }