diff --git a/glc/src/shader.rs b/glc/src/shader.rs index f532cff..71cdea6 100644 --- a/glc/src/shader.rs +++ b/glc/src/shader.rs @@ -8,6 +8,7 @@ use crate::{ error::Error, matrix::Matrix4f, misc::{AsEnum, Bindable}, + vector::Vector4f, }; /* Programm */ @@ -76,6 +77,7 @@ impl Program { pub fn uniform(&self, location: gl::GLint, uniform: Uniform<'_>) { match uniform { 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> { Matrix4f(&'a Matrix4f), + Vector4f(&'a Vector4f), } diff --git a/space-crush-app/resources/shader/planet.frag b/space-crush-app/resources/shader/planet.frag new file mode 100644 index 0000000..5cd25b0 --- /dev/null +++ b/space-crush-app/resources/shader/planet.frag @@ -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); +} diff --git a/space-crush-app/resources/shader/planet.vert b/space-crush-app/resources/shader/planet.vert new file mode 100644 index 0000000..7e755a6 --- /dev/null +++ b/space-crush-app/resources/shader/planet.vert @@ -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); +} diff --git a/space-crush-app/resources/shader/quad.frag b/space-crush-app/resources/shader/quad.frag index 927be7b..8b23549 100644 --- a/space-crush-app/resources/shader/quad.frag +++ b/space-crush-app/resources/shader/quad.frag @@ -1,7 +1,7 @@ #version 450 core in FragmentData { - vec2 tex_coords; + vec2 texCoords; } data; uniform sampler2D tex; @@ -9,5 +9,5 @@ uniform sampler2D tex; out vec4 color; void main() { - color = texture(tex, data.tex_coords); + color = texture(tex, data.texCoords); } diff --git a/space-crush-app/resources/shader/quad.vert b/space-crush-app/resources/shader/quad.vert index bb58d6f..8d00085 100644 --- a/space-crush-app/resources/shader/quad.vert +++ b/space-crush-app/resources/shader/quad.vert @@ -11,10 +11,10 @@ layout (std140, binding = 0) uniform Camera { layout (location = 1) uniform mat4 model; out FragmentData { - vec2 tex_coords; + vec2 texCoords; } data; 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); } diff --git a/space-crush-app/resources/shader/text.frag b/space-crush-app/resources/shader/text.frag index b8a952d..3550c3e 100644 --- a/space-crush-app/resources/shader/text.frag +++ b/space-crush-app/resources/shader/text.frag @@ -1,7 +1,7 @@ #version 450 core in FragmentData { - vec2 tex_coord; + vec2 texCoords; vec4 color; } data; @@ -10,7 +10,7 @@ uniform sampler2D tex; out vec4 color; void main() { - float alpha = texture(tex, data.tex_coord).r; + float alpha = texture(tex, data.texCoords).r; if (alpha <= 0.0) { discard; diff --git a/space-crush-app/resources/shader/text.vert b/space-crush-app/resources/shader/text.vert index 41fce6d..28cf3e1 100644 --- a/space-crush-app/resources/shader/text.vert +++ b/space-crush-app/resources/shader/text.vert @@ -15,33 +15,33 @@ layout (std140, binding = 0) uniform Camera { layout (location = 1) uniform mat4 model; out FragmentData { - vec2 tex_coord; + vec2 texCoords; vec4 color; } data; void main() { vec2 position = vec2(0.0); - vec2 tex_coord = vec2(0.0); + vec2 texCoords = vec2(0.0); switch (gl_VertexID) { case 0: 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; case 1: 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; case 2: 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; case 3: 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; } @@ -54,6 +54,6 @@ void main() { gl_Position = ortho * vec4(position, 0.0, 1.0); - data.tex_coord = tex_coord; + data.texCoords = texCoords; data.color = color; } diff --git a/space-crush-app/src/lib.rs b/space-crush-app/src/lib.rs index 945fef3..e238c92 100644 --- a/space-crush-app/src/lib.rs +++ b/space-crush-app/src/lib.rs @@ -9,8 +9,8 @@ use specs::{Dispatcher, DispatcherBuilder, World}; pub use error::Error; 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; pub struct App<'a, 'b> { @@ -28,6 +28,7 @@ impl<'a, 'b> App<'a, 'b> { world.insert(config); world.insert(Camera::new()?); + world.insert(Uniform::new()?); world.insert(Geometry::new()?); world.insert(State::default()); @@ -36,7 +37,7 @@ impl<'a, 'b> App<'a, 'b> { let mut dispatcher = DispatcherBuilder::new() .with(StateUpdate::new(world)?, "state_update", &[]) .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)?) .build(); dispatcher.setup(world); diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index e456b0c..efa92dd 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -33,6 +33,8 @@ fn run(vfs: Vfs) -> Result<(), Error> { let mut common = Dispatcher::new(&mut world); let mut app = App::new(&mut world)?; + create_test_world(&mut world); + while app.is_running() { common.process(&world); app.process(&world)?; @@ -40,3 +42,18 @@ fn run(vfs: Vfs) -> Result<(), Error> { 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(); +} diff --git a/space-crush-app/src/misc/world.rs b/space-crush-app/src/misc/world.rs index bd17582..178253f 100644 --- a/space-crush-app/src/misc/world.rs +++ b/space-crush-app/src/misc/world.rs @@ -42,7 +42,11 @@ impl WorldHelper for World { let mut texture = Texture::new(Target::Texture2D)?; texture.upload(&data, true)?; 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) } diff --git a/space-crush-app/src/render/init.rs b/space-crush-app/src/render/init.rs index 7e666dc..180ab1e 100644 --- a/space-crush-app/src/render/init.rs +++ b/space-crush-app/src/render/init.rs @@ -11,7 +11,7 @@ use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; use crate::{ misc::{MouseEvent, WorldHelper}, - resources::{Camera, Config, Geometry, State}, + resources::{Camera, Config, Geometry, State, Uniform}, Error, }; @@ -41,6 +41,7 @@ impl Init { #[derive(SystemData)] pub struct InitData<'a> { camera: WriteExpect<'a, Camera>, + uniform: WriteExpect<'a, Uniform>, state: ReadExpect<'a, State>, config: ReadExpect<'a, Config>, geometry: ReadExpect<'a, Geometry>, @@ -53,6 +54,7 @@ impl<'a> System<'a> for Init { fn run(&mut self, data: Self::SystemData) { let InitData { mut camera, + mut uniform, state, config, 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 */ self.program.bind(); geometry.render_quad(); diff --git a/space-crush-app/src/render/mod.rs b/space-crush-app/src/render/mod.rs index 6ceaa1b..3e2f46f 100644 --- a/space-crush-app/src/render/mod.rs +++ b/space-crush-app/src/render/mod.rs @@ -1,7 +1,7 @@ mod debug; mod init; -mod test; +mod planets; pub use debug::Debug; pub use init::Init; -pub use test::Test; +pub use planets::Planets; diff --git a/space-crush-app/src/render/planets.rs b/space-crush-app/src/render/planets.rs new file mode 100644 index 0000000..f00e82e --- /dev/null +++ b/space-crush-app/src/render/planets.rs @@ -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 { + let program = world.load_program(vec![ + (Type::Vertex, "resources/shader/planet.vert"), + (Type::Fragment, "resources/shader/planet.frag"), + ])?; + program.bind(); + world.resource::()?.bind(0)?; + world.resource::()?.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); + } +} diff --git a/space-crush-app/src/render/test.rs b/space-crush-app/src/render/test.rs deleted file mode 100644 index 9313375..0000000 --- a/space-crush-app/src/render/test.rs +++ /dev/null @@ -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 { - 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::()?.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); - } -} diff --git a/space-crush-app/src/resources/mod.rs b/space-crush-app/src/resources/mod.rs index 580cc16..3e78906 100644 --- a/space-crush-app/src/resources/mod.rs +++ b/space-crush-app/src/resources/mod.rs @@ -2,8 +2,10 @@ mod camera; mod config; mod geometry; mod state; +mod uniform; pub use camera::Camera; pub use config::Config; pub use geometry::Geometry; pub use state::State; +pub use uniform::Uniform; diff --git a/space-crush-app/src/resources/uniform.rs b/space-crush-app/src/resources/uniform.rs new file mode 100644 index 0000000..4572b36 --- /dev/null +++ b/space-crush-app/src/resources/uniform.rs @@ -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 { + 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::(true)?; + + data[0].time = time; + + Ok(()) + } + + pub fn bind(&self, index: gl::GLuint) -> Result<(), Error> { + Error::checked(|| self.buffer.bind_buffer_base(index)) + } +} diff --git a/space-crush-common/src/components/mod.rs b/space-crush-common/src/components/mod.rs index 2fa936e..0911c30 100644 --- a/space-crush-common/src/components/mod.rs +++ b/space-crush-common/src/components/mod.rs @@ -1,3 +1,7 @@ +mod planet; mod position; +mod velocity; +pub use planet::Planet; pub use position::Position; +pub use velocity::Velocity; diff --git a/space-crush-common/src/components/planet.rs b/space-crush-common/src/components/planet.rs new file mode 100644 index 0000000..8a9e4e7 --- /dev/null +++ b/space-crush-common/src/components/planet.rs @@ -0,0 +1,8 @@ +use specs::{Component, NullStorage}; + +#[derive(Default)] +pub struct Planet; + +impl Component for Planet { + type Storage = NullStorage; +} diff --git a/space-crush-common/src/components/position.rs b/space-crush-common/src/components/position.rs index 58f2e25..a52ae7f 100644 --- a/space-crush-common/src/components/position.rs +++ b/space-crush-common/src/components/position.rs @@ -1,24 +1,11 @@ -use std::ops::{Deref, DerefMut}; - use glc::vector::Vector2f; use specs::{Component, VecStorage}; -pub struct Position(pub Vector2f); +pub struct Position { + pub pos: Vector2f, + pub size: f32, +} impl Component for Position { type Storage = VecStorage; } - -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 - } -}