@@ -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<T>(&mut self, usage: Usage, data: &[T]) -> Result<(), Error> { | |||
let size = data.len() * size_of::<T>(); | |||
@@ -157,7 +163,10 @@ impl Drop for Buffer { | |||
} | |||
} | |||
impl<'a> Bindable for (Target, &'a Buffer) { | |||
impl<T> Bindable for (Target, T) | |||
where | |||
T: Deref<Target = Buffer>, | |||
{ | |||
fn bind(&self) { | |||
gl::bind_buffer(self.0.as_enum(), self.1.id); | |||
} | |||
@@ -215,6 +224,38 @@ impl<T> DerefMut for MapMut<'_, T> { | |||
} | |||
} | |||
/* BufferRef */ | |||
pub trait BufferRef { | |||
type Output: Deref<Target = Buffer>; | |||
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<Buffer> { | |||
type Output = Ref<'a, Buffer>; | |||
fn as_ref(self) -> Self::Output { | |||
self.borrow() | |||
} | |||
} | |||
impl<'a> BufferRef for &'a Rc<RefCell<Buffer>> { | |||
type Output = Ref<'a, Buffer>; | |||
fn as_ref(self) -> Self::Output { | |||
self.borrow() | |||
} | |||
} | |||
/* Target */ | |||
#[derive(Debug, Copy, Clone, Eq, PartialEq)] | |||
@@ -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, | |||
@@ -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; |
@@ -20,6 +20,10 @@ pub struct Program { | |||
} | |||
impl Program { | |||
pub fn builder() -> Builder { | |||
Builder::default() | |||
} | |||
pub fn from_shaders<I>(iter: I) -> Result<Self, Error> | |||
where | |||
I: IntoIterator<Item = Shader>, | |||
@@ -32,43 +36,15 @@ impl Program { | |||
I: IntoIterator<Item = Result<Shader, E>>, | |||
E: From<Error>, | |||
{ | |||
let id = gl::create_program(); | |||
let id = Error::err_if(&0, id)?; | |||
let mut builder = Self::builder(); | |||
let shaders = iter.into_iter().collect::<Result<Vec<_>, _>>()?; | |||
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::<u8>::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<Shader>, | |||
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<Program, Error> { | |||
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::<Result<Vec<_>, _>>()?; | |||
let tfv = tfv | |||
.iter() | |||
.map(|v: &CString| v.as_ptr() as *const gl::GLchar) | |||
.collect::<Vec<_>>(); | |||
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::<u8>::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> { | |||
@@ -0,0 +1,117 @@ | |||
use crate::{ | |||
buffer::{Buffer, BufferRef, Target}, | |||
error::Error, | |||
misc::{BindGuard, Bindable}, | |||
}; | |||
/* TransformFeedback */ | |||
pub struct TransformFeedback<T = Buffer> { | |||
id: gl::GLuint, | |||
buffers: Vec<T>, | |||
} | |||
impl<T> TransformFeedback<T> { | |||
pub fn builder() -> Builder<T> { | |||
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<T> Drop for TransformFeedback<T> { | |||
fn drop(&mut self) { | |||
gl::delete_transform_feedbacks(1, &self.id); | |||
} | |||
} | |||
impl<T> Bindable for TransformFeedback<T> { | |||
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<T> { | |||
bindings: Vec<Binding<T>>, | |||
} | |||
impl<T> Builder<T> | |||
where | |||
for<'a> &'a T: BufferRef, | |||
{ | |||
pub fn bind_buffer(mut self, index: gl::GLuint, buffer: T) -> Result<Builder<T>, 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<TransformFeedback<T>, 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<T> Default for Builder<T> { | |||
fn default() -> Self { | |||
Self { | |||
bindings: Vec::new(), | |||
} | |||
} | |||
} | |||
/* Binding */ | |||
struct Binding<T> { | |||
buffer: T, | |||
index: gl::GLuint, | |||
} |
@@ -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<T = Buffer> { | |||
id: gl::GLuint, | |||
buffers: Vec<Buffer>, | |||
buffers: Vec<T>, | |||
} | |||
impl VertexArray { | |||
pub fn builder() -> Builder { | |||
impl<T> VertexArray<T> { | |||
pub fn builder() -> Builder<T> { | |||
Builder::default() | |||
} | |||
pub fn buffers(&self) -> &Vec<Buffer> { | |||
pub fn buffers(&self) -> &[T] { | |||
&self.buffers | |||
} | |||
pub fn buffers_mut(&mut self) -> &mut Vec<Buffer> { | |||
pub fn buffers_mut(&mut self) -> &mut [T] { | |||
&mut self.buffers | |||
} | |||
} | |||
impl Drop for VertexArray { | |||
impl<T> Drop for VertexArray<T> { | |||
fn drop(&mut self) { | |||
gl::delete_vertex_arrays(1, &self.id); | |||
} | |||
} | |||
impl Bindable for VertexArray { | |||
impl<T> Bindable for VertexArray<T> { | |||
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<Binding>, | |||
pub struct Builder<T> { | |||
bindings: Vec<Binding<T>>, | |||
} | |||
impl Builder { | |||
pub fn bind_buffer(mut self, buffer: Buffer) -> BindingBuilder { | |||
impl<T> Builder<T> { | |||
pub fn bind_buffer(mut self, buffer: T) -> BindingBuilder<T> { | |||
let binding = Binding { | |||
buffer, | |||
pointers: Vec::new(), | |||
@@ -59,8 +58,13 @@ impl Builder { | |||
BindingBuilder { builder: self } | |||
} | |||
} | |||
pub fn build(self) -> Result<VertexArray, Error> { | |||
impl<T> Builder<T> | |||
where | |||
for<'a> &'a T: BufferRef, | |||
{ | |||
pub fn build(self) -> Result<VertexArray<T>, 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<T> Clone for Builder<T> | |||
where | |||
T: Clone, | |||
{ | |||
fn clone(&self) -> Self { | |||
Self { | |||
bindings: self.bindings.clone(), | |||
} | |||
} | |||
} | |||
impl<T> Default for Builder<T> { | |||
fn default() -> Self { | |||
Self { | |||
bindings: Vec::new(), | |||
} | |||
} | |||
} | |||
/* BindingBuilder */ | |||
pub struct BindingBuilder { | |||
builder: Builder, | |||
pub struct BindingBuilder<T> { | |||
builder: Builder<T>, | |||
} | |||
impl BindingBuilder { | |||
pub fn bind_buffer(self, buffer: Buffer) -> Self { | |||
impl<T> BindingBuilder<T> { | |||
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<VertexArray, Error> { | |||
impl<T> BindingBuilder<T> | |||
where | |||
for<'a> &'a T: BufferRef, | |||
{ | |||
pub fn build(self) -> Result<VertexArray<T>, Error> { | |||
self.builder.build() | |||
} | |||
} | |||
impl<T> Clone for BindingBuilder<T> | |||
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<T> { | |||
buffer: T, | |||
pointers: Vec<Pointer>, | |||
} | |||
impl<T> Clone for Binding<T> | |||
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<gl::GLuint>, | |||
#[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<gl::GLuint>, | |||
} |
@@ -1,3 +1,4 @@ | |||
layout (std140) uniform Global { | |||
float time; | |||
float delta; | |||
} uGlobal; |
@@ -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 | |||
@@ -1,6 +1,6 @@ | |||
#version 450 core | |||
#pragma include ./shared.glsl | |||
#pragma include ./ship_shared.glsl | |||
#pragma include ../misc/camera.glsl | |||
in VertexData vertexData[]; |
@@ -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; |
@@ -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; |
@@ -0,0 +1,11 @@ | |||
#version 450 core | |||
#pragma include ./tail_render_shared.glsl | |||
in FragmentData fragmentData; | |||
out vec4 outColor; | |||
void main() { | |||
outColor = fragmentData.color; | |||
} |
@@ -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(); | |||
} |
@@ -0,0 +1,8 @@ | |||
struct FragmentData { | |||
vec4 color; | |||
}; | |||
struct VertexData { | |||
vec3 color; | |||
vec2 tail[5]; | |||
}; |
@@ -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; | |||
} |
@@ -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(); | |||
} |
@@ -0,0 +1,7 @@ | |||
struct VertexData { | |||
vec2 pos; | |||
vec2 tail[5]; | |||
}; | |||
const float TAIL_LEN = 10.0; | |||
const float TAIL_FORCE = 1.0; |
@@ -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; | |||
} |
@@ -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; |
@@ -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<Rc<Buffer>>; | |||
/* 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<Rc<Buffer>>, | |||
update_array: Option<VertexArray<Rc<Buffer>>>, | |||
render_array: Option<VertexArray<Rc<Buffer>>>, | |||
} | |||
impl Buffers { | |||
fn from_builder(builder: &Builder) -> Result<Self, Error> { | |||
let mut transform_feedback = TransformFeedback::builder(); | |||
let mut update_array: Option<VertexArrayBuilder> = None; | |||
let mut render_array: Option<VertexArrayBuilder> = 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<VertexArrayBuilder>, | |||
buffer: &Rc<Buffer>, | |||
pointers: &[Pointer], | |||
) -> Result<Option<VertexArrayBuilder>, 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<BufferData>, | |||
} | |||
impl Builder { | |||
pub fn add_buffer(mut self) -> BufferBuilder { | |||
self.buffers.push(BufferData::default()); | |||
BufferBuilder { builder: self } | |||
} | |||
fn build(self) -> Result<Particles, Error> { | |||
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, Error> { | |||
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, Error> { | |||
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<Particles, Error> { | |||
self.builder.build() | |||
} | |||
} | |||
/* BufferData */ | |||
#[derive(Default)] | |||
struct BufferData { | |||
update_pointers: Vec<Pointer>, | |||
render_pointers: Vec<Pointer>, | |||
} |
@@ -17,6 +17,8 @@ pub trait WorldHelper { | |||
where | |||
I: IntoIterator<Item = (Type, &'static str)>; | |||
fn load_shader(&self, type_: Type, file: &str) -> Result<Shader, Error>; | |||
fn load_texture(&self, path: &str) -> Result<Texture, Error>; | |||
} | |||
@@ -25,39 +27,40 @@ impl WorldHelper for World { | |||
where | |||
I: IntoIterator<Item = (Type, &'static str)>, | |||
{ | |||
let vfs = self.fetch::<Vfs>(); | |||
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<Shader, Error> { | |||
let vfs = self.fetch::<Vfs>(); | |||
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<Texture, Error> { | |||
@@ -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<BufferRef>, | |||
input: TailObjects, | |||
output: TailObjects, | |||
texture_bomber: Texture, | |||
texture_fighter: Texture, | |||
texture_transporter: Texture, | |||
reader_id: ReaderId<ComponentEvent<Ship>>, | |||
ship_count: usize, | |||
need_init: BitSet, | |||
id_to_index: Vec<u32>, | |||
index_to_id: Vec<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>, | |||
} | |||
struct TailObjects { | |||
tail_data: BufferRef, | |||
tail_update: VertexArray<BufferRef>, | |||
tail_render: VertexArray<BufferRef>, | |||
transform_feedback: TransformFeedback<BufferRef>, | |||
} | |||
type BufferRef = Rc<RefCell<Buffer>>; | |||
#[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<Self, Error> { | |||
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::<VertexData>() 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::<Vector2f>() as gl::GLsizei; | |||
const OFFSET_CLR: gl::GLsizei = OFFSET_DIR + size_of::<Vector2f>() as gl::GLsizei; | |||
const OFFSET_TEX: gl::GLsizei = OFFSET_CLR + size_of::<Vector3f>() 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::<WriteStorage<Ship>>() | |||
.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::<VertexData>()) | |||
.panic("Unable to change buffer size for ship data"); | |||
} | |||
let data = (&positions, &velocities, &ships, owned.maybe()); | |||
let mut buffer = buffer | |||
.map_mut::<VertexData>(true) | |||
fn update_vertices(&self, d: &ShipsData<'_>) { | |||
let mut borrow_ship = self.ship_data.borrow_mut(); | |||
let mut buf_ship = borrow_ship | |||
.map_mut::<ShipData>(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::<TailData>(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::<TailData>() * 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::<TailData>() * 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<Self, Error> { | |||
/* tail data */ | |||
let tail_data = Rc::new(RefCell::new(Buffer::new()?)); | |||
tail_data | |||
.borrow_mut() | |||
.buffer_size(Usage::DynamicDraw, size_of::<TailData>() 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::<ShipData>() as gl::GLsizei; | |||
const SHIP_OFFSET_POS: gl::GLsizei = 0; | |||
const SHIP_OFFSET_DIR: gl::GLsizei = SHIP_OFFSET_POS + size_of::<Vector2f>() as gl::GLsizei; | |||
const SHIP_OFFSET_CLR: gl::GLsizei = SHIP_OFFSET_DIR + size_of::<Vector2f>() as gl::GLsizei; | |||
const SHIP_OFFSET_TEX: gl::GLsizei = SHIP_OFFSET_CLR + size_of::<Vector3f>() as gl::GLsizei; | |||
const TAIL_STRIDE: gl::GLsizei = size_of::<TailData>() as gl::GLsizei; | |||
const TAIL_OFFSET_TL0: gl::GLsizei = 0; | |||
const TAIL_OFFSET_TL1: gl::GLsizei = TAIL_OFFSET_TL0 + size_of::<Vector2f>() as gl::GLsizei; | |||
const TAIL_OFFSET_TL2: gl::GLsizei = TAIL_OFFSET_TL1 + size_of::<Vector2f>() as gl::GLsizei; | |||
const TAIL_OFFSET_TL3: gl::GLsizei = TAIL_OFFSET_TL2 + size_of::<Vector2f>() as gl::GLsizei; | |||
const TAIL_OFFSET_TL4: gl::GLsizei = TAIL_OFFSET_TL3 + size_of::<Vector2f>() as gl::GLsizei; |
@@ -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<Self, Error> { | |||
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::<Data>(true)?; | |||
data[0].time = time; | |||
data[0].delta = delta; | |||
Ok(()) | |||
} | |||