@@ -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, | |||