| @@ -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), | |||
| } | |||
| @@ -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 | |||
| 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); | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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; | |||
| @@ -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; | |||
| } | |||
| @@ -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); | |||
| @@ -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(); | |||
| } | |||
| @@ -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) | |||
| } | |||
| @@ -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(); | |||
| @@ -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; | |||
| @@ -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 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; | |||
| @@ -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 velocity; | |||
| pub use planet::Planet; | |||
| 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 specs::{Component, VecStorage}; | |||
| pub struct Position(pub Vector2f); | |||
| pub struct Position { | |||
| pub pos: Vector2f, | |||
| pub size: f32, | |||
| } | |||
| impl Component for Position { | |||
| 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 | |||
| } | |||
| } | |||