| @@ -304,6 +304,18 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<T> Mul<T> for Vector2<T> | |||||
| where | |||||
| T: Element, | |||||
| { | |||||
| type Output = Self; | |||||
| #[inline] | |||||
| fn mul(self, rhs: T) -> Self::Output { | |||||
| self.multiply(rhs) | |||||
| } | |||||
| } | |||||
| /* Vector3 */ | /* Vector3 */ | ||||
| impl<T> Vector3<T> | impl<T> Vector3<T> | ||||
| @@ -403,6 +415,18 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<T> Mul<T> for Vector3<T> | |||||
| where | |||||
| T: Element, | |||||
| { | |||||
| type Output = Self; | |||||
| #[inline] | |||||
| fn mul(self, rhs: T) -> Self::Output { | |||||
| self.multiply(rhs) | |||||
| } | |||||
| } | |||||
| /* Vector4 */ | /* Vector4 */ | ||||
| impl<T> Vector4<T> | impl<T> Vector4<T> | ||||
| @@ -513,6 +537,18 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<T> Mul<T> for Vector4<T> | |||||
| where | |||||
| T: Element, | |||||
| { | |||||
| type Output = Self; | |||||
| #[inline] | |||||
| fn mul(self, rhs: T) -> Self::Output { | |||||
| self.multiply(rhs) | |||||
| } | |||||
| } | |||||
| impl<T> From<(Vector3<T>, T)> for Vector4<T> { | impl<T> From<(Vector3<T>, T)> for Vector4<T> { | ||||
| fn from((vec3, w): (Vector3<T>, T)) -> Self { | fn from((vec3, w): (Vector3<T>, T)) -> Self { | ||||
| Self { | Self { | ||||
| @@ -8,7 +8,7 @@ const GlowArgs GLOW_ARGS = { | |||||
| /* step0 */ 0.480, | /* step0 */ 0.480, | ||||
| /* step1 */ 0.975, | /* step1 */ 0.975, | ||||
| /* pulseSize0 */ 0.010, | /* pulseSize0 */ 0.010, | ||||
| /* pulseSize1 */ 0.025, | |||||
| /* pulseSize1 */ 0.050, | |||||
| /* pulseTime */ 2.000, | /* pulseTime */ 2.000, | ||||
| }; | }; | ||||
| @@ -0,0 +1,28 @@ | |||||
| #version 450 core | |||||
| #pragma include ./shared.glsl | |||||
| #pragma include ../misc/glow.glsl | |||||
| #pragma include ../misc/global.glsl | |||||
| const GlowArgs GLOW_ARGS = { | |||||
| /* step0 */ 0.100, | |||||
| /* step1 */ 1.900, | |||||
| /* pulseSize0 */ 0.050, | |||||
| /* pulseSize1 */ 0.200, | |||||
| /* pulseTime */ 2.000, | |||||
| }; | |||||
| in FragmentData fragmentData; | |||||
| uniform vec4 uGlowColor; | |||||
| uniform sampler2D uTexture; | |||||
| out vec4 outColor; | |||||
| void main() { | |||||
| float alpha = glow(GLOW_ARGS, fragmentData.texCoords - vec2(0.5), uGlobal.time); | |||||
| vec4 glow = vec4(uGlowColor.rgb, uGlowColor.a * alpha); | |||||
| vec4 tex = texture(uTexture, fragmentData.texCoords); | |||||
| outColor = tex * tex.a + glow * (1.0 - tex.a); | |||||
| } | |||||
| @@ -0,0 +1,3 @@ | |||||
| struct FragmentData { | |||||
| vec2 texCoords; | |||||
| }; | |||||
| @@ -0,0 +1,17 @@ | |||||
| #version 450 core | |||||
| #pragma include ./shared.glsl | |||||
| #pragma include ../misc/camera.glsl | |||||
| const float GLOW_SIZE_FACTOR = 4.00; | |||||
| in vec3 inPosition; | |||||
| uniform mat4 uModel; | |||||
| out FragmentData fragmentData; | |||||
| void main() { | |||||
| fragmentData.texCoords = inPosition.xy * GLOW_SIZE_FACTOR + vec2(0.5); | |||||
| gl_Position = uCamera.projection * uCamera.view * uModel * vec4(inPosition * GLOW_SIZE_FACTOR, 1.0); | |||||
| } | |||||
| @@ -1 +1,94 @@ | |||||
| [{"marker":[0],"components":[{"pos":[0.0,0.0],"size":500.0},{}]}] | |||||
| [ | |||||
| { | |||||
| "marker": [ | |||||
| 0 | |||||
| ], | |||||
| "components": [ | |||||
| { | |||||
| "pos": [ | |||||
| 0.0, | |||||
| 0.0 | |||||
| ], | |||||
| "size": 500.0 | |||||
| }, | |||||
| null, | |||||
| {}, | |||||
| null | |||||
| ] | |||||
| }, | |||||
| { | |||||
| "marker": [ | |||||
| 1 | |||||
| ], | |||||
| "components": [ | |||||
| { | |||||
| "pos": [ | |||||
| 250.0, | |||||
| 250.0 | |||||
| ], | |||||
| "size": 30.0 | |||||
| }, | |||||
| { | |||||
| "dir": [ | |||||
| -0.70710677, | |||||
| 0.70710677 | |||||
| ], | |||||
| "speed": 0.0 | |||||
| }, | |||||
| null, | |||||
| { | |||||
| "type_": "Fighter" | |||||
| } | |||||
| ] | |||||
| }, | |||||
| { | |||||
| "marker": [ | |||||
| 2 | |||||
| ], | |||||
| "components": [ | |||||
| { | |||||
| "pos": [ | |||||
| -250.0, | |||||
| 250.0 | |||||
| ], | |||||
| "size": 30.0 | |||||
| }, | |||||
| { | |||||
| "dir": [ | |||||
| 0.0, | |||||
| 1.0 | |||||
| ], | |||||
| "speed": 0.0 | |||||
| }, | |||||
| null, | |||||
| { | |||||
| "type_": "Bomber" | |||||
| } | |||||
| ] | |||||
| }, | |||||
| { | |||||
| "marker": [ | |||||
| 3 | |||||
| ], | |||||
| "components": [ | |||||
| { | |||||
| "pos": [ | |||||
| 250.0, | |||||
| -250.0 | |||||
| ], | |||||
| "size": 30.0 | |||||
| }, | |||||
| { | |||||
| "dir": [ | |||||
| 0.70710677, | |||||
| 0.70710677 | |||||
| ], | |||||
| "speed": 0.0 | |||||
| }, | |||||
| null, | |||||
| { | |||||
| "type_": "Transporter" | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| @@ -10,7 +10,7 @@ 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, Planets}; | |||||
| use render::{Debug, Init, Planets, Ships}; | |||||
| use resources::{Camera, Config, Geometry, State, Uniform}; | use resources::{Camera, Config, Geometry, State, Uniform}; | ||||
| use systems::StateUpdate; | use systems::StateUpdate; | ||||
| @@ -39,6 +39,7 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| .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(Planets::new(world)?) | .with_thread_local(Planets::new(world)?) | ||||
| .with_thread_local(Ships::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,7 +33,9 @@ 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)?; | ||||
| load_world(&mut world, WORLD_FILEPATH)?; | |||||
| if !load_world(&mut world, WORLD_FILEPATH)? { | |||||
| create_world(&mut world); | |||||
| } | |||||
| while app.is_running() { | while app.is_running() { | ||||
| world.maintain(); | world.maintain(); | ||||
| @@ -47,19 +49,69 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| fn load_world(world: &mut World, path: &str) -> Result<(), Error> { | |||||
| fn create_world(world: &mut World) { | |||||
| use glc::vector::Vector2f; | |||||
| use space_crush_common::{ | |||||
| components::{Planet, Position, Ship, ShipType, Velocity}, | |||||
| misc::{PersistWorld, Persistence}, | |||||
| }; | |||||
| use specs::{saveload::MarkedBuilder, Builder}; | |||||
| world | |||||
| .create_entity() | |||||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||||
| .with(Position { | |||||
| pos: Vector2f::default(), | |||||
| size: 500.0, | |||||
| }) | |||||
| .with(Planet {}) | |||||
| .build(); | |||||
| for i in 0..3 { | |||||
| let x = if i & 1 == 0 { 1.0 } else { -1.0 }; | |||||
| let y = if i & 2 == 0 { 1.0 } else { -1.0 }; | |||||
| world | |||||
| .create_entity() | |||||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||||
| .with(Position { | |||||
| pos: Vector2f::new(250.0 * x, 250.0 * y), | |||||
| size: 100.0, | |||||
| }) | |||||
| .with(Velocity { | |||||
| dir: Vector2f::new(i as f32 - 1.0, 1.0).normalize(), | |||||
| speed: 0.0, | |||||
| }) | |||||
| .with(Ship { | |||||
| type_: match i { | |||||
| 0 => ShipType::Fighter, | |||||
| 1 => ShipType::Bomber, | |||||
| 2 => ShipType::Transporter, | |||||
| _ => unreachable!(), | |||||
| }, | |||||
| }) | |||||
| .build(); | |||||
| } | |||||
| } | |||||
| fn load_world(world: &mut World, path: &str) -> Result<bool, Error> { | |||||
| use serde_json::de::{Deserializer, IoRead}; | use serde_json::de::{Deserializer, IoRead}; | ||||
| use space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; | use space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; | ||||
| PersistWorld::setup(world); | PersistWorld::setup(world); | ||||
| let vfs = world.resource::<Vfs>()?; | let vfs = world.resource::<Vfs>()?; | ||||
| let mut file = vfs.join(path)?.open_file()?; | |||||
| let path = vfs.join(path)?; | |||||
| if !path.exists() { | |||||
| return Ok(false); | |||||
| } | |||||
| let mut file = path.open_file()?; | |||||
| let mut read = IoRead::new(&mut file); | let mut read = IoRead::new(&mut file); | ||||
| let mut deserializer = Deserializer::new(&mut read); | let mut deserializer = Deserializer::new(&mut read); | ||||
| world.deserialize(PersistWorld, &mut deserializer)?; | world.deserialize(PersistWorld, &mut deserializer)?; | ||||
| Ok(()) | |||||
| Ok(true) | |||||
| } | } | ||||
| fn save_world(world: &mut World, path: &str) -> Result<(), Error> { | fn save_world(world: &mut World, path: &str) -> Result<(), Error> { | ||||
| @@ -7,9 +7,7 @@ use glutin::{ | |||||
| }; | }; | ||||
| use log::{error, info}; | use log::{error, info}; | ||||
| use crate::Error; | |||||
| use super::super::resources::Config; | |||||
| use crate::{resources::Config, Error}; | |||||
| pub struct Window { | pub struct Window { | ||||
| context: ContextWrapper<PossiblyCurrent, GlutinWindow>, | context: ContextWrapper<PossiblyCurrent, GlutinWindow>, | ||||
| @@ -1,7 +1,9 @@ | |||||
| mod debug; | mod debug; | ||||
| mod init; | mod init; | ||||
| mod planets; | mod planets; | ||||
| mod ships; | |||||
| pub use debug::Debug; | pub use debug::Debug; | ||||
| pub use init::Init; | pub use init::Init; | ||||
| pub use planets::Planets; | pub use planets::Planets; | ||||
| pub use ships::Ships; | |||||
| @@ -0,0 +1,116 @@ | |||||
| use glc::{ | |||||
| matrix::Matrix4f, | |||||
| misc::{BindGuard, Bindable}, | |||||
| shader::{Program, Type, Uniform}, | |||||
| texture::Texture, | |||||
| vector::Vector4f, | |||||
| }; | |||||
| use log::error; | |||||
| use space_crush_common::components::{Position, Ship, ShipType, Velocity}; | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||||
| use crate::{ | |||||
| constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||||
| misc::WorldHelper, | |||||
| resources::Geometry, | |||||
| Error, | |||||
| }; | |||||
| pub struct Ships { | |||||
| program: Program, | |||||
| texture_fighter: Texture, | |||||
| texture_bomber: Texture, | |||||
| texture_transporter: Texture, | |||||
| model_location: gl::GLint, | |||||
| } | |||||
| impl Ships { | |||||
| pub fn new(world: &World) -> Result<Self, Error> { | |||||
| let program = world.load_program(vec![ | |||||
| (Type::Vertex, "resources/shader/ship/vert.glsl"), | |||||
| (Type::Fragment, "resources/shader/ship/frag.glsl"), | |||||
| ])?; | |||||
| program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; | |||||
| program.uniform_block_binding("Global", UNIFORM_BUFFER_INDEX_UNIFORM)?; | |||||
| let glow_color = Vector4f::new(1.0, 1.0, 1.0, 0.02); | |||||
| let glow_color = Uniform::Vector4f(&glow_color); | |||||
| let model_location = program.uniform_location("uModel")?; | |||||
| program.bind(); | |||||
| program.uniform("uTexture", Uniform::Texture(0))?; | |||||
| program.uniform("uGlowColor", glow_color)?; | |||||
| program.unbind(); | |||||
| let texture_fighter = world.load_texture("resources/textures/ship_fighter.png")?; | |||||
| let texture_bomber = world.load_texture("resources/textures/ship_bomber.png")?; | |||||
| let texture_transporter = world.load_texture("resources/textures/ship_transporter.png")?; | |||||
| Ok(Self { | |||||
| program, | |||||
| texture_fighter, | |||||
| texture_bomber, | |||||
| texture_transporter, | |||||
| model_location, | |||||
| }) | |||||
| } | |||||
| } | |||||
| #[derive(SystemData)] | |||||
| pub struct ShipsData<'a> { | |||||
| geometry: ReadExpect<'a, Geometry>, | |||||
| position: ReadStorage<'a, Position>, | |||||
| velocity: ReadStorage<'a, Velocity>, | |||||
| ship: ReadStorage<'a, Ship>, | |||||
| } | |||||
| impl<'a> System<'a> for Ships { | |||||
| type SystemData = ShipsData<'a>; | |||||
| fn run(&mut self, data: Self::SystemData) { | |||||
| let ShipsData { | |||||
| geometry, | |||||
| position, | |||||
| velocity, | |||||
| ship, | |||||
| } = data; | |||||
| gl::enable(gl::BLEND); | |||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | |||||
| let _guard = BindGuard::new(&self.program); | |||||
| for (p, v, ship) in (&position, &velocity, &ship).join() { | |||||
| let _guard = match ship.type_ { | |||||
| ShipType::Fighter => BindGuard::new(&self.texture_fighter), | |||||
| ShipType::Bomber => BindGuard::new(&self.texture_bomber), | |||||
| ShipType::Transporter => BindGuard::new(&self.texture_transporter), | |||||
| }; | |||||
| let p_x = p.pos.x; | |||||
| let p_y = p.pos.y; | |||||
| let d_x = v.dir.x; | |||||
| let d_y = v.dir.y; | |||||
| let s = p.size; | |||||
| let m = Matrix4f::new( | |||||
| Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0), | |||||
| Vector4f::new(-s * d_x, -s * d_y, 0.0, 0.0), | |||||
| Vector4f::new(0.0, 0.0, s, 0.0), | |||||
| Vector4f::new(p_x, p_y, 0.0, 1.0), | |||||
| ); | |||||
| if let Err(err) = self | |||||
| .program | |||||
| .uniform(self.model_location, Uniform::Matrix4f(&m)) | |||||
| { | |||||
| error!("Error while updating model matrix: {}", err); | |||||
| } | |||||
| geometry.render_quad(); | |||||
| } | |||||
| gl::disable(gl::BLEND); | |||||
| } | |||||
| } | |||||
| @@ -3,7 +3,7 @@ | |||||
| use std::collections::HashSet; | use std::collections::HashSet; | ||||
| use std::iter::IntoIterator; | use std::iter::IntoIterator; | ||||
| use super::super::misc::{MouseButton, VirtualKeyCode}; | |||||
| use crate::misc::{MouseButton, VirtualKeyCode}; | |||||
| #[derive(Default)] | #[derive(Default)] | ||||
| pub struct State { | pub struct State { | ||||
| @@ -1,7 +1,9 @@ | |||||
| mod planet; | mod planet; | ||||
| mod position; | mod position; | ||||
| mod ship; | |||||
| mod velocity; | mod velocity; | ||||
| pub use planet::Planet; | pub use planet::Planet; | ||||
| pub use position::Position; | pub use position::Position; | ||||
| pub use ship::{Ship, Type as ShipType}; | |||||
| pub use velocity::Velocity; | pub use velocity::Velocity; | ||||
| @@ -0,0 +1,18 @@ | |||||
| use serde::{Deserialize, Serialize}; | |||||
| use specs::{Component, VecStorage}; | |||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||||
| pub struct Ship { | |||||
| pub type_: Type, | |||||
| } | |||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||||
| pub enum Type { | |||||
| Fighter, | |||||
| Bomber, | |||||
| Transporter, | |||||
| } | |||||
| impl Component for Ship { | |||||
| type Storage = VecStorage<Self>; | |||||
| } | |||||
| @@ -1,24 +1,13 @@ | |||||
| use std::ops::{Deref, DerefMut}; | |||||
| use glc::vector::Vector2f; | use glc::vector::Vector2f; | ||||
| use serde::{Deserialize, Serialize}; | |||||
| use specs::{Component, VecStorage}; | use specs::{Component, VecStorage}; | ||||
| pub struct Velocity(pub Vector2f); | |||||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||||
| pub struct Velocity { | |||||
| pub dir: Vector2f, | |||||
| pub speed: f32, | |||||
| } | |||||
| impl Component for Velocity { | impl Component for Velocity { | ||||
| type Storage = VecStorage<Self>; | type Storage = VecStorage<Self>; | ||||
| } | } | ||||
| impl Deref for Velocity { | |||||
| type Target = Vector2f; | |||||
| fn deref(&self) -> &Self::Target { | |||||
| &self.0 | |||||
| } | |||||
| } | |||||
| impl DerefMut for Velocity { | |||||
| fn deref_mut(&mut self) -> &mut Self::Target { | |||||
| &mut self.0 | |||||
| } | |||||
| } | |||||
| @@ -1,6 +1,9 @@ | |||||
| use specs::{Dispatcher as Inner, DispatcherBuilder, World}; | use specs::{Dispatcher as Inner, DispatcherBuilder, World}; | ||||
| use crate::{resources::Global, systems::Process}; | |||||
| use crate::{ | |||||
| resources::Global, | |||||
| systems::{Movement, Process}, | |||||
| }; | |||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| dispatcher: Inner<'a, 'b>, | dispatcher: Inner<'a, 'b>, | ||||
| @@ -12,6 +15,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,6 +1,6 @@ | |||||
| use specs::saveload::SimpleMarker; | use specs::saveload::SimpleMarker; | ||||
| use crate::components::{Planet, Position}; | |||||
| use crate::components::{Planet, Position, Ship, Velocity}; | |||||
| use super::Persistence; | use super::Persistence; | ||||
| @@ -9,5 +9,5 @@ pub struct PersistWorldMarker; | |||||
| impl Persistence for PersistWorld { | impl Persistence for PersistWorld { | ||||
| type Marker = SimpleMarker<PersistWorldMarker>; | type Marker = SimpleMarker<PersistWorldMarker>; | ||||
| type Components = (Position, Planet); | |||||
| type Components = (Position, Velocity, Planet, Ship); | |||||
| } | } | ||||
| @@ -1,3 +1,5 @@ | |||||
| mod movement; | |||||
| mod process; | mod process; | ||||
| pub use movement::Movement; | |||||
| pub use process::Process; | pub use process::Process; | ||||
| @@ -0,0 +1,36 @@ | |||||
| #![allow(dead_code)] | |||||
| use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | |||||
| use crate::{ | |||||
| components::{Position, Velocity}, | |||||
| resources::Global, | |||||
| }; | |||||
| #[derive(Default)] | |||||
| pub struct Movement; | |||||
| #[derive(SystemData)] | |||||
| pub struct MovementData<'a> { | |||||
| position: WriteStorage<'a, Position>, | |||||
| velocity: ReadStorage<'a, Velocity>, | |||||
| global: Read<'a, Global>, | |||||
| } | |||||
| impl<'a> System<'a> for Movement { | |||||
| type SystemData = MovementData<'a>; | |||||
| fn run(&mut self, data: Self::SystemData) { | |||||
| let MovementData { | |||||
| mut position, | |||||
| velocity, | |||||
| global, | |||||
| } = data; | |||||
| (&mut position, &velocity) | |||||
| .par_join() | |||||
| .for_each(|(position, velocity)| { | |||||
| position.pos = position.pos + velocity.dir * velocity.speed * global.delta; | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -5,7 +5,7 @@ use std::time::Instant; | |||||
| use specs::{System, Write}; | use specs::{System, Write}; | ||||
| use super::super::resources::Global; | |||||
| use crate::resources::Global; | |||||
| pub struct Process { | pub struct Process { | ||||
| last_frame: Instant, | last_frame: Instant, | ||||