| @@ -8,6 +8,7 @@ use crate::{ | |||||
| error::Error, | error::Error, | ||||
| matrix::Matrix4f, | matrix::Matrix4f, | ||||
| misc::{AsEnum, Bindable}, | misc::{AsEnum, Bindable}, | ||||
| vector::Vector4f, | |||||
| }; | }; | ||||
| /* Programm */ | /* Programm */ | ||||
| @@ -76,6 +77,7 @@ impl Program { | |||||
| pub fn uniform(&self, location: gl::GLint, uniform: Uniform<'_>) { | pub fn uniform(&self, location: gl::GLint, uniform: Uniform<'_>) { | ||||
| match uniform { | match uniform { | ||||
| Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()), | Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()), | ||||
| Uniform::Vector4f(v) => gl::uniform_4fv(location, 1, v.as_ptr()), | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -208,4 +210,5 @@ impl AsEnum for Type { | |||||
| pub enum Uniform<'a> { | pub enum Uniform<'a> { | ||||
| Matrix4f(&'a Matrix4f), | Matrix4f(&'a Matrix4f), | ||||
| Vector4f(&'a Vector4f), | |||||
| } | } | ||||
| @@ -0,0 +1,35 @@ | |||||
| #version 450 core | |||||
| const float GLOW_STEP_0 = 0.480; // inner (min = 0.0) | |||||
| const float GLOW_STEP_1 = 0.975; // outer (max = 0.5) | |||||
| const float GLOW_PULSE_SIZE_0 = 0.010; // inner (+/-) | |||||
| const float GLOW_PULSE_SIZE_1 = 0.025; // outer (+/-) | |||||
| const float GLOW_PULSE_TIME = 2.000; | |||||
| in FragmentData { | |||||
| vec2 texCoords; | |||||
| } data; | |||||
| layout (std140, binding = 2) uniform Global { | |||||
| float time; | |||||
| } global; | |||||
| layout(location = 3) uniform vec4 glowColor; | |||||
| uniform sampler2D tex; | |||||
| out vec4 color; | |||||
| void main() { | |||||
| vec2 texCoords = data.texCoords - vec2(0.5); | |||||
| float radius = length(texCoords); | |||||
| float bgPulse = sin(GLOW_PULSE_TIME * global.time); | |||||
| float alpha = 1.0 - smoothstep( | |||||
| GLOW_STEP_0 + GLOW_PULSE_SIZE_0 * bgPulse, | |||||
| GLOW_STEP_1 + GLOW_PULSE_SIZE_1 * bgPulse, | |||||
| radius); | |||||
| vec4 tex = texture(tex, data.texCoords); | |||||
| vec4 glow = vec4(glowColor.rgb, glowColor.a * alpha); | |||||
| color = tex * tex.a + glow * (1.0 - tex.a); | |||||
| } | |||||
| @@ -0,0 +1,22 @@ | |||||
| #version 450 core | |||||
| const float GLOW_SIZE_FACTOR = 2.00; | |||||
| layout (location = 0) in vec3 position; | |||||
| layout (std140, binding = 0) uniform Camera { | |||||
| mat4 projection; | |||||
| mat4 view; | |||||
| vec2 size; | |||||
| } camera; | |||||
| layout (location = 1) uniform mat4 model; | |||||
| out FragmentData { | |||||
| vec2 texCoords; | |||||
| } data; | |||||
| void main() { | |||||
| data.texCoords = position.xy * GLOW_SIZE_FACTOR + vec2(0.5); | |||||
| gl_Position = camera.projection * camera.view * model * vec4(position * GLOW_SIZE_FACTOR, 1.0); | |||||
| } | |||||
| @@ -1,7 +1,7 @@ | |||||
| #version 450 core | #version 450 core | ||||
| in FragmentData { | in FragmentData { | ||||
| vec2 tex_coords; | |||||
| vec2 texCoords; | |||||
| } data; | } data; | ||||
| uniform sampler2D tex; | uniform sampler2D tex; | ||||
| @@ -9,5 +9,5 @@ uniform sampler2D tex; | |||||
| out vec4 color; | out vec4 color; | ||||
| void main() { | void main() { | ||||
| color = texture(tex, data.tex_coords); | |||||
| color = texture(tex, data.texCoords); | |||||
| } | } | ||||
| @@ -11,10 +11,10 @@ layout (std140, binding = 0) uniform Camera { | |||||
| layout (location = 1) uniform mat4 model; | layout (location = 1) uniform mat4 model; | ||||
| out FragmentData { | out FragmentData { | ||||
| vec2 tex_coords; | |||||
| vec2 texCoords; | |||||
| } data; | } data; | ||||
| void main() { | void main() { | ||||
| data.tex_coords = position.xy + vec2(0.5); | |||||
| data.texCoords = position.xy + vec2(0.5); | |||||
| gl_Position = camera.projection * camera.view * model * vec4(position, 1.0); | gl_Position = camera.projection * camera.view * model * vec4(position, 1.0); | ||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| #version 450 core | #version 450 core | ||||
| in FragmentData { | in FragmentData { | ||||
| vec2 tex_coord; | |||||
| vec2 texCoords; | |||||
| vec4 color; | vec4 color; | ||||
| } data; | } data; | ||||
| @@ -10,7 +10,7 @@ uniform sampler2D tex; | |||||
| out vec4 color; | out vec4 color; | ||||
| void main() { | void main() { | ||||
| float alpha = texture(tex, data.tex_coord).r; | |||||
| float alpha = texture(tex, data.texCoords).r; | |||||
| if (alpha <= 0.0) { | if (alpha <= 0.0) { | ||||
| discard; | discard; | ||||
| @@ -15,33 +15,33 @@ layout (std140, binding = 0) uniform Camera { | |||||
| layout (location = 1) uniform mat4 model; | layout (location = 1) uniform mat4 model; | ||||
| out FragmentData { | out FragmentData { | ||||
| vec2 tex_coord; | |||||
| vec2 texCoords; | |||||
| vec4 color; | vec4 color; | ||||
| } data; | } data; | ||||
| void main() { | void main() { | ||||
| vec2 position = vec2(0.0); | vec2 position = vec2(0.0); | ||||
| vec2 tex_coord = vec2(0.0); | |||||
| vec2 texCoords = vec2(0.0); | |||||
| switch (gl_VertexID) { | switch (gl_VertexID) { | ||||
| case 0: | case 0: | ||||
| position = vec2(pos_min.x, pos_max.y); | position = vec2(pos_min.x, pos_max.y); | ||||
| tex_coord = vec2(tex_min.x, tex_max.y); | |||||
| texCoords = vec2(tex_min.x, tex_max.y); | |||||
| break; | break; | ||||
| case 1: | case 1: | ||||
| position = vec2(pos_min.x, pos_min.y); | position = vec2(pos_min.x, pos_min.y); | ||||
| tex_coord = vec2(tex_min.x, tex_min.y); | |||||
| texCoords = vec2(tex_min.x, tex_min.y); | |||||
| break; | break; | ||||
| case 2: | case 2: | ||||
| position = vec2(pos_max.x, pos_max.y); | position = vec2(pos_max.x, pos_max.y); | ||||
| tex_coord = vec2(tex_max.x, tex_max.y); | |||||
| texCoords = vec2(tex_max.x, tex_max.y); | |||||
| break; | break; | ||||
| case 3: | case 3: | ||||
| position = vec2(pos_max.x, pos_min.y); | position = vec2(pos_max.x, pos_min.y); | ||||
| tex_coord = vec2(tex_max.x, tex_min.y); | |||||
| texCoords = vec2(tex_max.x, tex_min.y); | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -54,6 +54,6 @@ void main() { | |||||
| gl_Position = ortho * vec4(position, 0.0, 1.0); | gl_Position = ortho * vec4(position, 0.0, 1.0); | ||||
| data.tex_coord = tex_coord; | |||||
| data.texCoords = texCoords; | |||||
| data.color = color; | data.color = color; | ||||
| } | } | ||||
| @@ -9,8 +9,8 @@ use specs::{Dispatcher, DispatcherBuilder, World}; | |||||
| pub use error::Error; | pub use error::Error; | ||||
| use misc::{Events, TextManager, Window}; | use misc::{Events, TextManager, Window}; | ||||
| use render::{Debug, Init, Test}; | |||||
| use resources::{Camera, Config, Geometry, State}; | |||||
| use render::{Debug, Init, Planets}; | |||||
| use resources::{Camera, Config, Geometry, State, Uniform}; | |||||
| use systems::StateUpdate; | use systems::StateUpdate; | ||||
| pub struct App<'a, 'b> { | pub struct App<'a, 'b> { | ||||
| @@ -28,6 +28,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| world.insert(config); | world.insert(config); | ||||
| world.insert(Camera::new()?); | world.insert(Camera::new()?); | ||||
| world.insert(Uniform::new()?); | |||||
| world.insert(Geometry::new()?); | world.insert(Geometry::new()?); | ||||
| world.insert(State::default()); | world.insert(State::default()); | ||||
| @@ -36,7 +37,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(StateUpdate::new(world)?, "state_update", &[]) | .with(StateUpdate::new(world)?, "state_update", &[]) | ||||
| .with_thread_local(Init::new(world)?) | .with_thread_local(Init::new(world)?) | ||||
| .with_thread_local(Test::new(world)?) | |||||
| .with_thread_local(Planets::new(world)?) | |||||
| .with_thread_local(Debug::new(&text_manager)?) | .with_thread_local(Debug::new(&text_manager)?) | ||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -33,6 +33,8 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
| let mut common = Dispatcher::new(&mut world); | let mut common = Dispatcher::new(&mut world); | ||||
| let mut app = App::new(&mut world)?; | let mut app = App::new(&mut world)?; | ||||
| create_test_world(&mut world); | |||||
| while app.is_running() { | while app.is_running() { | ||||
| common.process(&world); | common.process(&world); | ||||
| app.process(&world)?; | app.process(&world)?; | ||||
| @@ -40,3 +42,18 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| fn create_test_world(world: &mut World) { | |||||
| use glc::vector::Vector2f; | |||||
| use space_crush_common::components::{Planet, Position}; | |||||
| use specs::Builder; | |||||
| world | |||||
| .create_entity() | |||||
| .with(Position { | |||||
| pos: Vector2f::default(), | |||||
| size: 500.0, | |||||
| }) | |||||
| .with(Planet) | |||||
| .build(); | |||||
| } | |||||
| @@ -42,7 +42,11 @@ impl WorldHelper for World { | |||||
| let mut texture = Texture::new(Target::Texture2D)?; | let mut texture = Texture::new(Target::Texture2D)?; | ||||
| texture.upload(&data, true)?; | texture.upload(&data, true)?; | ||||
| texture.set_filter(FilterMin::LinearMipmapLinear, FilterMag::Linear)?; | texture.set_filter(FilterMin::LinearMipmapLinear, FilterMag::Linear)?; | ||||
| texture.set_wrap(Wrap::ClampToEdge, Wrap::ClampToEdge, Wrap::ClampToEdge)?; | |||||
| texture.set_wrap( | |||||
| Wrap::ClampToBorder, | |||||
| Wrap::ClampToBorder, | |||||
| Wrap::ClampToBorder, | |||||
| )?; | |||||
| Ok(texture) | Ok(texture) | ||||
| } | } | ||||
| @@ -11,7 +11,7 @@ use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; | |||||
| use crate::{ | use crate::{ | ||||
| misc::{MouseEvent, WorldHelper}, | misc::{MouseEvent, WorldHelper}, | ||||
| resources::{Camera, Config, Geometry, State}, | |||||
| resources::{Camera, Config, Geometry, State, Uniform}, | |||||
| Error, | Error, | ||||
| }; | }; | ||||
| @@ -41,6 +41,7 @@ impl Init { | |||||
| #[derive(SystemData)] | #[derive(SystemData)] | ||||
| pub struct InitData<'a> { | pub struct InitData<'a> { | ||||
| camera: WriteExpect<'a, Camera>, | camera: WriteExpect<'a, Camera>, | ||||
| uniform: WriteExpect<'a, Uniform>, | |||||
| state: ReadExpect<'a, State>, | state: ReadExpect<'a, State>, | ||||
| config: ReadExpect<'a, Config>, | config: ReadExpect<'a, Config>, | ||||
| geometry: ReadExpect<'a, Geometry>, | geometry: ReadExpect<'a, Geometry>, | ||||
| @@ -53,6 +54,7 @@ impl<'a> System<'a> for Init { | |||||
| fn run(&mut self, data: Self::SystemData) { | fn run(&mut self, data: Self::SystemData) { | ||||
| let InitData { | let InitData { | ||||
| mut camera, | mut camera, | ||||
| mut uniform, | |||||
| state, | state, | ||||
| config, | config, | ||||
| geometry, | geometry, | ||||
| @@ -106,6 +108,10 @@ impl<'a> System<'a> for Init { | |||||
| } | } | ||||
| } | } | ||||
| if let Err(err) = uniform.update() { | |||||
| error!("Error while updating global uniform data: {}", err); | |||||
| } | |||||
| /* render background */ | /* render background */ | ||||
| self.program.bind(); | self.program.bind(); | ||||
| geometry.render_quad(); | geometry.render_quad(); | ||||
| @@ -1,7 +1,7 @@ | |||||
| mod debug; | mod debug; | ||||
| mod init; | mod init; | ||||
| mod test; | |||||
| mod planets; | |||||
| pub use debug::Debug; | pub use debug::Debug; | ||||
| pub use init::Init; | pub use init::Init; | ||||
| pub use test::Test; | |||||
| pub use planets::Planets; | |||||
| @@ -0,0 +1,88 @@ | |||||
| use glc::{ | |||||
| matrix::Matrix4f, | |||||
| misc::Bindable, | |||||
| shader::{Program, Type, Uniform}, | |||||
| texture::Texture, | |||||
| vector::Vector4f, | |||||
| }; | |||||
| use space_crush_common::{ | |||||
| components::{Planet, Position}, | |||||
| misc::WorldHelper as _, | |||||
| }; | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||||
| use crate::{ | |||||
| misc::WorldHelper, | |||||
| resources::{Camera, Geometry, Uniform as GlobalUniform}, | |||||
| Error, | |||||
| }; | |||||
| pub struct Planets { | |||||
| program: Program, | |||||
| texture: Texture, | |||||
| } | |||||
| impl Planets { | |||||
| pub fn new(world: &World) -> Result<Self, Error> { | |||||
| let program = world.load_program(vec![ | |||||
| (Type::Vertex, "resources/shader/planet.vert"), | |||||
| (Type::Fragment, "resources/shader/planet.frag"), | |||||
| ])?; | |||||
| program.bind(); | |||||
| world.resource::<Camera>()?.bind(0)?; | |||||
| world.resource::<GlobalUniform>()?.bind(2)?; | |||||
| program.uniform(3, Uniform::Vector4f(&Vector4f::new(1.0, 1.0, 1.0, 0.1))); | |||||
| program.unbind(); | |||||
| let texture = world.load_texture("resources/textures/planet01.png")?; | |||||
| Ok(Self { program, texture }) | |||||
| } | |||||
| } | |||||
| #[derive(SystemData)] | |||||
| pub struct PlanetsData<'a> { | |||||
| geometry: ReadExpect<'a, Geometry>, | |||||
| position: ReadStorage<'a, Position>, | |||||
| planet: ReadStorage<'a, Planet>, | |||||
| } | |||||
| impl<'a> System<'a> for Planets { | |||||
| type SystemData = PlanetsData<'a>; | |||||
| fn run(&mut self, data: Self::SystemData) { | |||||
| let PlanetsData { | |||||
| geometry, | |||||
| position, | |||||
| planet, | |||||
| } = data; | |||||
| gl::enable(gl::BLEND); | |||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | |||||
| self.texture.bind(); | |||||
| self.program.bind(); | |||||
| for (p, _) in (&position, &planet).join() { | |||||
| let x = p.pos.x; | |||||
| let y = p.pos.y; | |||||
| let s = p.size; | |||||
| let m = Matrix4f::new( | |||||
| Vector4f::new(s, 0.0, 0.0, 0.0), | |||||
| Vector4f::new(0.0, s, 0.0, 0.0), | |||||
| Vector4f::new(0.0, 0.0, s, 0.0), | |||||
| Vector4f::new(x, y, 0.0, 1.0), | |||||
| ); | |||||
| self.program.uniform(1, Uniform::Matrix4f(&m)); | |||||
| geometry.render_quad(); | |||||
| } | |||||
| self.program.unbind(); | |||||
| self.texture.unbind(); | |||||
| gl::disable(gl::BLEND); | |||||
| } | |||||
| } | |||||
| @@ -1,69 +0,0 @@ | |||||
| use glc::{ | |||||
| matrix::{Angle, Matrix4f}, | |||||
| misc::Bindable, | |||||
| shader::{Program, Type, Uniform}, | |||||
| texture::Texture, | |||||
| vector::Vector3f, | |||||
| }; | |||||
| use space_crush_common::{misc::WorldHelper as _, resources::Global}; | |||||
| use specs::{ReadExpect, System, World}; | |||||
| use crate::{ | |||||
| misc::WorldHelper, | |||||
| resources::{Camera, Geometry}, | |||||
| Error, | |||||
| }; | |||||
| pub struct Test { | |||||
| model: Matrix4f, | |||||
| program: Program, | |||||
| texture: Texture, | |||||
| } | |||||
| impl Test { | |||||
| pub fn new(world: &World) -> Result<Self, Error> { | |||||
| let model = Matrix4f::scale(Vector3f::new(500.0, 500.0, 500.0)); | |||||
| let program = world.load_program(vec![ | |||||
| (Type::Vertex, "resources/shader/quad.vert"), | |||||
| (Type::Fragment, "resources/shader/quad.frag"), | |||||
| ])?; | |||||
| program.bind(); | |||||
| world.resource::<Camera>()?.bind(0)?; | |||||
| program.unbind(); | |||||
| let texture = world.load_texture("resources/textures/planet01.png")?; | |||||
| Ok(Self { | |||||
| model, | |||||
| program, | |||||
| texture, | |||||
| }) | |||||
| } | |||||
| } | |||||
| impl<'a> System<'a> for Test { | |||||
| type SystemData = (ReadExpect<'a, Global>, ReadExpect<'a, Geometry>); | |||||
| fn run(&mut self, (global, geometry): Self::SystemData) { | |||||
| self.model = self.model | |||||
| * Matrix4f::rotate( | |||||
| Vector3f::new(0.0, 0.0, 1.0), | |||||
| Angle::Deg(10.0 * global.delta), | |||||
| ); | |||||
| gl::enable(gl::BLEND); | |||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | |||||
| self.texture.bind(); | |||||
| self.program.bind(); | |||||
| self.program.uniform(1, Uniform::Matrix4f(&self.model)); | |||||
| geometry.render_quad(); | |||||
| self.program.unbind(); | |||||
| self.texture.unbind(); | |||||
| gl::disable(gl::BLEND); | |||||
| } | |||||
| } | |||||
| @@ -2,8 +2,10 @@ mod camera; | |||||
| mod config; | mod config; | ||||
| mod geometry; | mod geometry; | ||||
| mod state; | mod state; | ||||
| mod uniform; | |||||
| pub use camera::Camera; | pub use camera::Camera; | ||||
| pub use config::Config; | pub use config::Config; | ||||
| pub use geometry::Geometry; | pub use geometry::Geometry; | ||||
| pub use state::State; | pub use state::State; | ||||
| pub use uniform::Uniform; | |||||
| @@ -0,0 +1,42 @@ | |||||
| use std::time::Instant; | |||||
| use glc::{ | |||||
| array_buffer::{ArrayBuffer, Target, Usage}, | |||||
| error::Error, | |||||
| }; | |||||
| pub struct Uniform { | |||||
| buffer: ArrayBuffer, | |||||
| start_time: Instant, | |||||
| } | |||||
| #[repr(C, packed)] | |||||
| struct Data { | |||||
| time: f32, | |||||
| } | |||||
| impl Uniform { | |||||
| pub fn new() -> Result<Self, Error> { | |||||
| let mut buffer = ArrayBuffer::new(Target::UniformBuffer)?; | |||||
| buffer.buffer_data(Usage::StaticDraw, &[Data { time: 0.0 }])?; | |||||
| Ok(Self { | |||||
| buffer, | |||||
| start_time: Instant::now(), | |||||
| }) | |||||
| } | |||||
| pub fn update(&mut self) -> Result<(), Error> { | |||||
| let time = Instant::now().duration_since(self.start_time).as_secs_f32(); | |||||
| let mut data = self.buffer.map_mut::<Data>(true)?; | |||||
| data[0].time = time; | |||||
| Ok(()) | |||||
| } | |||||
| pub fn bind(&self, index: gl::GLuint) -> Result<(), Error> { | |||||
| Error::checked(|| self.buffer.bind_buffer_base(index)) | |||||
| } | |||||
| } | |||||
| @@ -1,3 +1,7 @@ | |||||
| mod planet; | |||||
| mod position; | mod position; | ||||
| mod velocity; | |||||
| pub use planet::Planet; | |||||
| pub use position::Position; | pub use position::Position; | ||||
| pub use velocity::Velocity; | |||||
| @@ -0,0 +1,8 @@ | |||||
| use specs::{Component, NullStorage}; | |||||
| #[derive(Default)] | |||||
| pub struct Planet; | |||||
| impl Component for Planet { | |||||
| type Storage = NullStorage<Self>; | |||||
| } | |||||
| @@ -1,24 +1,11 @@ | |||||
| use std::ops::{Deref, DerefMut}; | |||||
| use glc::vector::Vector2f; | use glc::vector::Vector2f; | ||||
| use specs::{Component, VecStorage}; | use specs::{Component, VecStorage}; | ||||
| pub struct Position(pub Vector2f); | |||||
| pub struct Position { | |||||
| pub pos: Vector2f, | |||||
| pub size: f32, | |||||
| } | |||||
| impl Component for Position { | impl Component for Position { | ||||
| type Storage = VecStorage<Self>; | type Storage = VecStorage<Self>; | ||||
| } | } | ||||
| impl Deref for Position { | |||||
| type Target = Vector2f; | |||||
| fn deref(&self) -> &Self::Target { | |||||
| &self.0 | |||||
| } | |||||
| } | |||||
| impl DerefMut for Position { | |||||
| fn deref_mut(&mut self) -> &mut Self::Target { | |||||
| &mut self.0 | |||||
| } | |||||
| } | |||||