From 8c9022c740b7d505a889efbe4d2d35b398c30118 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Fri, 25 Dec 2020 04:57:50 +0100 Subject: [PATCH] WIP --- 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 + .../shader/ship/{frag.glsl => ship_frag.glsl} | 2 +- .../shader/ship/{geom.glsl => ship_geom.glsl} | 2 +- .../ship/{shared.glsl => ship_shared.glsl} | 6 +- .../shader/ship/{vert.glsl => ship_vert.glsl} | 2 +- .../shader/ship/tail_render_frag.glsl | 11 + .../shader/ship/tail_render_geom.glsl | 40 ++ .../shader/ship/tail_render_shared.glsl | 8 + .../shader/ship/tail_render_vert.glsl | 21 + .../shader/ship/tail_update_geom.glsl | 38 ++ .../shader/ship/tail_update_shared.glsl | 7 + .../shader/ship/tail_update_vert.glsl | 21 + space-crush-app/src/misc/mod.rs | 2 + space-crush-app/src/misc/particles.rs | 222 ++++++++++ space-crush-app/src/misc/world.rs | 67 +-- space-crush-app/src/render/ships.rs | 415 ++++++++++++++---- space-crush-app/src/resources/uniform.rs | 22 +- 23 files changed, 1114 insertions(+), 194 deletions(-) create mode 100644 glc/src/transform_feedback.rs rename space-crush-app/resources/shader/ship/{frag.glsl => ship_frag.glsl} (95%) rename space-crush-app/resources/shader/ship/{geom.glsl => ship_geom.glsl} (97%) rename space-crush-app/resources/shader/ship/{shared.glsl => ship_shared.glsl} (50%) rename space-crush-app/resources/shader/ship/{vert.glsl => ship_vert.glsl} (91%) 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 create mode 100644 space-crush-app/src/misc/particles.rs 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/ship_frag.glsl similarity index 95% rename from space-crush-app/resources/shader/ship/frag.glsl rename to space-crush-app/resources/shader/ship/ship_frag.glsl index 33f3e51..f514736 100644 --- a/space-crush-app/resources/shader/ship/frag.glsl +++ b/space-crush-app/resources/shader/ship/ship_frag.glsl @@ -1,6 +1,6 @@ #version 450 core -#pragma include ./shared.glsl +#pragma include ./ship_shared.glsl #pragma include ../misc/glow.glsl #pragma include ../misc/global.glsl diff --git a/space-crush-app/resources/shader/ship/geom.glsl b/space-crush-app/resources/shader/ship/ship_geom.glsl similarity index 97% rename from space-crush-app/resources/shader/ship/geom.glsl rename to space-crush-app/resources/shader/ship/ship_geom.glsl index bb44c39..1fdb497 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[]; diff --git a/space-crush-app/resources/shader/ship/shared.glsl b/space-crush-app/resources/shader/ship/ship_shared.glsl similarity index 50% rename from space-crush-app/resources/shader/ship/shared.glsl rename to space-crush-app/resources/shader/ship/ship_shared.glsl index 7c6ae68..cf74b7d 100644 --- a/space-crush-app/resources/shader/ship/shared.glsl +++ b/space-crush-app/resources/shader/ship/ship_shared.glsl @@ -10,6 +10,6 @@ struct VertexData { 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; +const float SHIP_SIZE = 25.00; // absolute ship size +const float GLOW_SIZE = 4.00; // relative to ship size +const float GLOW_ALPHA = 0.05; diff --git a/space-crush-app/resources/shader/ship/vert.glsl b/space-crush-app/resources/shader/ship/ship_vert.glsl similarity index 91% rename from space-crush-app/resources/shader/ship/vert.glsl rename to space-crush-app/resources/shader/ship/ship_vert.glsl index 2228081..0547ca1 100644 --- a/space-crush-app/resources/shader/ship/vert.glsl +++ b/space-crush-app/resources/shader/ship/ship_vert.glsl @@ -1,6 +1,6 @@ #version 450 core -#pragma include ./shared.glsl +#pragma include ./ship_shared.glsl layout (location = 0) in vec2 inPosition; layout (location = 1) in vec2 inDirection; 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..5f95ac9 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_geom.glsl @@ -0,0 +1,40 @@ +#version 450 core + +#pragma include ./tail_render_shared.glsl +#pragma include ../misc/camera.glsl + +in VertexData vertexData[]; + +layout (points) in; +layout (line_strip, max_vertices = 6) out; + +out FragmentData fragmentData; + +void main() { + mat4 m = uCamera.projection * uCamera.view; + + VertexData d = vertexData[0]; + fragmentData.color = vec4(d.color, 1.0); + + gl_Position = m * vec4(d.tail[0], 0.0, 1.0); + fragmentData.color = vec4(1.0, 0.0, 0.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4(d.tail[1], 0.0, 1.0); + fragmentData.color = vec4(1.0, 1.0, 0.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4(d.tail[2], 0.0, 1.0); + fragmentData.color = vec4(0.0, 1.0, 0.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4(d.tail[3], 0.0, 1.0); + fragmentData.color = vec4(0.0, 1.0, 1.0, 1.0); + EmitVertex(); + + gl_Position = m * vec4(d.tail[4], 0.0, 1.0); + fragmentData.color = vec4(0.0, 0.0, 1.0, 1.0); + EmitVertex(); + + 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..7f96be8 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_shared.glsl @@ -0,0 +1,8 @@ +struct FragmentData { + vec4 color; +}; + +struct VertexData { + vec3 color; + vec2 tail[5]; +}; 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..3d75e7a --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_render_vert.glsl @@ -0,0 +1,21 @@ +#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; + +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; +} 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..d9ab355 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_geom.glsl @@ -0,0 +1,38 @@ +#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; + +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]); + + 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..96c75ac --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_shared.glsl @@ -0,0 +1,7 @@ +struct VertexData { + vec2 pos; + vec2 tail[5]; +}; + +const float TAIL_LEN = 10.0; +const float TAIL_FORCE = 1.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..3dc4b87 --- /dev/null +++ b/space-crush-app/resources/shader/ship/tail_update_vert.glsl @@ -0,0 +1,21 @@ +#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; + +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; +} diff --git a/space-crush-app/src/misc/mod.rs b/space-crush-app/src/misc/mod.rs index 02a959c..5829f36 100644 --- a/space-crush-app/src/misc/mod.rs +++ b/space-crush-app/src/misc/mod.rs @@ -1,4 +1,5 @@ mod events; +// mod particles; mod text; mod window; mod world; @@ -6,6 +7,7 @@ mod world; pub use events::{ ControlEvent, Events, KeyboardEvent, MouseButton, MouseEvent, VirtualKeyCode, WindowEvent, }; +// pub use particles::Particles; pub use text::{HorizontalAlign, Text, TextCache, TextManager, VerticalAlign}; pub use window::Window; pub use world::WorldHelper; diff --git a/space-crush-app/src/misc/particles.rs b/space-crush-app/src/misc/particles.rs new file mode 100644 index 0000000..cc1c7c6 --- /dev/null +++ b/space-crush-app/src/misc/particles.rs @@ -0,0 +1,222 @@ +use std::mem::swap; +use std::rc::Rc; + +use glc::{ + buffer::Buffer, + misc::BindGuard, + transform_feedback::TransformFeedback, + vertex_array::{BindingBuilder, DataType, Pointer, VertexArray}, +}; +use space_crush_common::return_if_none; + +use crate::Error; + +type VertexArrayBuilder = BindingBuilder>; + +/* Particles */ + +pub struct Particles { + input: Buffers, + output: Buffers, +} + +impl Particles { + pub fn builder() -> Builder { + Builder::default() + } + + pub fn update(&mut self) { + let update_array = return_if_none!(&self.input.update_array); + + gl::enable(gl::RASTERIZER_DISCARD); + + let guard_buffer = BindGuard::new(update_array); + let guard_transform_feedback = BindGuard::new(&self.output.transform_feedback); + + gl::begin_transform_feedback(gl::POINTS); + + // TODO gl::draw_arrays(mode, 0, count as _); + + gl::end_transform_feedback(); + + gl::disable(gl::RASTERIZER_DISCARD); + + drop(guard_buffer); + drop(guard_transform_feedback); + + swap(&mut self.input, &mut self.output); + } +} + +/* Buffer */ + +struct Buffers { + transform_feedback: TransformFeedback>, + update_array: Option>>, + render_array: Option>>, +} + +impl Buffers { + fn from_builder(builder: &Builder) -> Result { + let mut transform_feedback = TransformFeedback::builder(); + let mut update_array: Option = None; + let mut render_array: Option = None; + + let mut index = 0; + for b in &builder.buffers { + if b.render_pointers.is_empty() && b.update_pointers.is_empty() { + continue; + } + + let buffer = Rc::new(Buffer::new()?); + transform_feedback = transform_feedback.bind_buffer(index, buffer.clone())?; + update_array = update_vertex_array(update_array, &buffer, &b.update_pointers)?; + render_array = update_vertex_array(render_array, &buffer, &b.render_pointers)?; + + index += 1; + } + + let transform_feedback = transform_feedback.build()?; + let update_array = match update_array { + Some(update_array) => Some(update_array.build()?), + None => None, + }; + let render_array = match render_array { + Some(render_array) => Some(render_array.build()?), + None => None, + }; + + Ok(Self { + transform_feedback, + update_array, + render_array, + }) + } +} + +fn update_vertex_array( + mut builder: Option, + buffer: &Rc, + pointers: &[Pointer], +) -> Result, Error> { + if !pointers.is_empty() { + builder = match builder { + Some(builder) => Some(builder.bind_buffer(buffer.clone())), + None => Some(VertexArray::builder().bind_buffer(buffer.clone())), + }; + + for p in pointers { + builder = Some(builder.unwrap().vertex_attrib_pointer( + p.index, + p.size, + p.type_, + p.normalize, + p.stride, + p.offset, + )?); + } + } + + Ok(builder) +} + +/* Builder */ + +#[derive(Default)] +pub struct Builder { + buffers: Vec, +} + +impl Builder { + pub fn add_buffer(mut self) -> BufferBuilder { + self.buffers.push(BufferData::default()); + + BufferBuilder { builder: self } + } + + fn build(self) -> Result { + let input = Buffers::from_builder(&self)?; + let output = Buffers::from_builder(&self)?; + + Ok(Particles { input, output }) + } +} + +/* BufferBuilder */ + +#[derive(Default)] +pub struct BufferBuilder { + builder: Builder, +} + +impl BufferBuilder { + pub fn add_buffer(self) -> BufferBuilder { + self.builder.add_buffer() + } + + pub fn update_attrib_pointer( + mut self, + index: gl::GLuint, + size: gl::GLint, + type_: DataType, + normalize: bool, + stride: gl::GLsizei, + offset: gl::GLsizei, + ) -> Result { + self.builder + .buffers + .last_mut() + .unwrap() + .update_pointers + .push(Pointer { + index, + size, + type_, + normalize, + stride, + offset, + divisor: None, + }); + + Ok(self) + } + + pub fn render_attrib_pointer( + mut self, + index: gl::GLuint, + size: gl::GLint, + type_: DataType, + normalize: bool, + stride: gl::GLsizei, + offset: gl::GLsizei, + ) -> Result { + self.builder + .buffers + .last_mut() + .unwrap() + .render_pointers + .push(Pointer { + index, + size, + type_, + normalize, + stride, + offset, + divisor: None, + }); + + Ok(self) + } + + pub fn build(self) -> Result { + self.builder.build() + } +} + +/* BufferData */ + +#[derive(Default)] +struct BufferData { + update_pointers: Vec, + render_pointers: Vec, +} 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/ships.rs b/space-crush-app/src/render/ships.rs index 9466013..c97e6b2 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,141 +29,240 @@ use crate::{ }; pub struct Ships { - program: Program, - vertex_array: VertexArray, + program_ship: Program, + program_tail_update: Program, + program_tail_render: Program, + + ship_data: BufferRef, + 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 { +struct ShipData { pos: Vector2f, dir: Vector2f, color: Vector3f, texture: gl::GLint, } +#[repr(C, packed)] +#[derive(Debug)] +struct TailData { + pos: [Vector2f; 5], +} + 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_ship.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?; + + program_ship.bind(); + program_ship.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; + program_ship.unbind(); - program.bind(); - program.uniform("uTexture", Uniform::TextureVec(&[0, 1, 2]))?; - program.unbind(); + /* 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), + ])?; - const STRIDE: gl::GLsizei = size_of::() as gl::GLsizei; + program_tail_render.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; - 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; + /* ship data */ + let ship_data = Rc::new(RefCell::new(Buffer::new()?)); - 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)? + /* 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, 3, DataType::Float, false, SHIP_STRIDE, SHIP_OFFSET_CLR)? + .vertex_attrib_pointer(3, 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_tail_update, + program_tail_render, + + ship_data, + 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_tail(&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(guard_trfm); + drop(guard_data); + drop(guard_prog); + + swap(&mut self.input, &mut self.output); + } - drop(buffer); + fn render_tails(&self) { + let _guard = BindGuard::new(&self.program_tail_render); + let _guard = BindGuard::new(&self.input.tail_render); - /* render ships */ - let _guard = BindGuard::new(&self.program); - let _guard = BindGuard::new(&self.vertex_array); + 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); @@ -168,7 +275,139 @@ impl<'a> System<'a> for Ships { gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE); - gl::draw_arrays(gl::POINTS, 0, self.ship_count as _); + 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 _id = id; + // TODO + } +} + +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_tail(); + 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)? + .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)? + .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_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"]; + +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; 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(()) }