| @@ -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 */ | |||
| 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 */ | |||
| 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> { | |||
| fn from((vec3, w): (Vector3<T>, T)) -> Self { | |||
| Self { | |||
| @@ -8,7 +8,7 @@ const GlowArgs GLOW_ARGS = { | |||
| /* step0 */ 0.480, | |||
| /* step1 */ 0.975, | |||
| /* pulseSize0 */ 0.010, | |||
| /* pulseSize1 */ 0.025, | |||
| /* pulseSize1 */ 0.050, | |||
| /* 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; | |||
| use misc::{Events, TextManager, Window}; | |||
| use render::{Debug, Init, Planets}; | |||
| use render::{Debug, Init, Planets, Ships}; | |||
| use resources::{Camera, Config, Geometry, State, Uniform}; | |||
| use systems::StateUpdate; | |||
| @@ -39,6 +39,7 @@ impl<'a, 'b> App<'a, 'b> { | |||
| .with(StateUpdate::new(world)?, "state_update", &[]) | |||
| .with_thread_local(Init::new(world)?) | |||
| .with_thread_local(Planets::new(world)?) | |||
| .with_thread_local(Ships::new(world)?) | |||
| .with_thread_local(Debug::new(&text_manager)?) | |||
| .build(); | |||
| dispatcher.setup(world); | |||
| @@ -33,7 +33,9 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
| let mut common = Dispatcher::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() { | |||
| world.maintain(); | |||
| @@ -47,19 +49,69 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
| 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 space_crush_common::misc::{PersistWorld, Persistence, WorldHelper}; | |||
| PersistWorld::setup(world); | |||
| 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 deserializer = Deserializer::new(&mut read); | |||
| world.deserialize(PersistWorld, &mut deserializer)?; | |||
| Ok(()) | |||
| Ok(true) | |||
| } | |||
| fn save_world(world: &mut World, path: &str) -> Result<(), Error> { | |||
| @@ -7,9 +7,7 @@ use glutin::{ | |||
| }; | |||
| use log::{error, info}; | |||
| use crate::Error; | |||
| use super::super::resources::Config; | |||
| use crate::{resources::Config, Error}; | |||
| pub struct Window { | |||
| context: ContextWrapper<PossiblyCurrent, GlutinWindow>, | |||
| @@ -1,7 +1,9 @@ | |||
| mod debug; | |||
| mod init; | |||
| mod planets; | |||
| mod ships; | |||
| pub use debug::Debug; | |||
| pub use init::Init; | |||
| 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::iter::IntoIterator; | |||
| use super::super::misc::{MouseButton, VirtualKeyCode}; | |||
| use crate::misc::{MouseButton, VirtualKeyCode}; | |||
| #[derive(Default)] | |||
| pub struct State { | |||
| @@ -1,7 +1,9 @@ | |||
| mod planet; | |||
| mod position; | |||
| mod ship; | |||
| mod velocity; | |||
| pub use planet::Planet; | |||
| pub use position::Position; | |||
| pub use ship::{Ship, Type as ShipType}; | |||
| 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 serde::{Deserialize, Serialize}; | |||
| 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 { | |||
| 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 crate::{resources::Global, systems::Process}; | |||
| use crate::{ | |||
| resources::Global, | |||
| systems::{Movement, Process}, | |||
| }; | |||
| pub struct Dispatcher<'a, 'b> { | |||
| dispatcher: Inner<'a, 'b>, | |||
| @@ -12,6 +15,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
| let mut dispatcher = DispatcherBuilder::new() | |||
| .with(Process::default(), "process", &[]) | |||
| .with(Movement::default(), "movement", &[]) | |||
| .build(); | |||
| dispatcher.setup(world); | |||
| @@ -1,6 +1,6 @@ | |||
| use specs::saveload::SimpleMarker; | |||
| use crate::components::{Planet, Position}; | |||
| use crate::components::{Planet, Position, Ship, Velocity}; | |||
| use super::Persistence; | |||
| @@ -9,5 +9,5 @@ pub struct PersistWorldMarker; | |||
| impl Persistence for PersistWorld { | |||
| type Marker = SimpleMarker<PersistWorldMarker>; | |||
| type Components = (Position, Planet); | |||
| type Components = (Position, Velocity, Planet, Ship); | |||
| } | |||
| @@ -1,3 +1,5 @@ | |||
| mod movement; | |||
| mod process; | |||
| pub use movement::Movement; | |||
| 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 super::super::resources::Global; | |||
| use crate::resources::Global; | |||
| pub struct Process { | |||
| last_frame: Instant, | |||