@@ -1510,6 +1510,7 @@ dependencies = [ | |||
"glc", | |||
"glutin", | |||
"glyph_brush", | |||
"lazy_static", | |||
"log", | |||
"log4rs", | |||
"ordered-float 2.0.1", | |||
@@ -9,6 +9,7 @@ gl = { version = "0.1", features = [ "use_log_crate" ] } | |||
glc = "0.1" | |||
glutin = { version = "0.25", features = [ "serde" ] } | |||
glyph_brush = "0.7" | |||
lazy_static = "1.4" | |||
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | |||
log4rs = "0.13" | |||
ordered-float = "2.0" | |||
@@ -4,6 +4,7 @@ | |||
#pragma include ../misc/glow.glsl | |||
#pragma include ../misc/global.glsl | |||
const float GLOW_ALPHA = 0.20; | |||
const GlowArgs GLOW_ARGS = { | |||
/* step0 */ 0.100, | |||
/* step1 */ 1.900, | |||
@@ -21,7 +22,7 @@ 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 glow = vec4(uGlowColor.rgb, uGlowColor.a * alpha * GLOW_ALPHA); | |||
vec4 tex = texture(uTexture, fragmentData.texCoords); | |||
outColor = tex * tex.a + glow * (1.0 - tex.a); | |||
@@ -13,6 +13,13 @@ | |||
}, | |||
null, | |||
{}, | |||
null, | |||
{ | |||
"owner": [ | |||
4 | |||
] | |||
}, | |||
null, | |||
null | |||
] | |||
}, | |||
@@ -38,7 +45,14 @@ | |||
null, | |||
{ | |||
"type_": "Fighter" | |||
} | |||
}, | |||
{ | |||
"owner": [ | |||
4 | |||
] | |||
}, | |||
null, | |||
null | |||
] | |||
}, | |||
{ | |||
@@ -63,7 +77,14 @@ | |||
null, | |||
{ | |||
"type_": "Bomber" | |||
} | |||
}, | |||
{ | |||
"owner": [ | |||
4 | |||
] | |||
}, | |||
null, | |||
null | |||
] | |||
}, | |||
{ | |||
@@ -88,6 +109,34 @@ | |||
null, | |||
{ | |||
"type_": "Transporter" | |||
}, | |||
{ | |||
"owner": [ | |||
4 | |||
] | |||
}, | |||
null, | |||
null | |||
] | |||
}, | |||
{ | |||
"marker": [ | |||
4 | |||
], | |||
"components": [ | |||
null, | |||
null, | |||
null, | |||
null, | |||
null, | |||
{}, | |||
{ | |||
"color": [ | |||
0.0, | |||
0.5, | |||
1.0, | |||
0.1 | |||
] | |||
} | |||
] | |||
} | |||
@@ -0,0 +1,3 @@ | |||
mod player_visual; | |||
pub use player_visual::PlayerVisual; |
@@ -0,0 +1,12 @@ | |||
use glc::vector::Vector4f; | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, HashMapStorage}; | |||
#[derive(Clone, Debug, Serialize, Deserialize)] | |||
pub struct PlayerVisual { | |||
pub color: Vector4f, | |||
} | |||
impl Component for PlayerVisual { | |||
type Storage = HashMapStorage<Self>; | |||
} |
@@ -1,2 +1,9 @@ | |||
use glc::vector::Vector4f; | |||
use lazy_static::lazy_static; | |||
pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0; | |||
pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1; | |||
lazy_static! { | |||
pub static ref PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1); | |||
} |
@@ -1,9 +1,10 @@ | |||
mod constants; | |||
mod error; | |||
mod misc; | |||
mod render; | |||
mod resources; | |||
mod systems; | |||
pub mod components; | |||
pub mod constants; | |||
pub mod error; | |||
pub mod misc; | |||
pub mod render; | |||
pub mod resources; | |||
pub mod systems; | |||
use specs::{Dispatcher, DispatcherBuilder, World}; | |||
@@ -50,16 +50,26 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
} | |||
fn create_world(world: &mut World) { | |||
use glc::vector::Vector2f; | |||
use glc::vector::{Vector2f, Vector4f}; | |||
use space_crush_app::{components::PlayerVisual, misc::PersistWorld}; | |||
use space_crush_common::{ | |||
components::{Planet, Position, Ship, ShipType, Velocity}, | |||
misc::{PersistWorld, Persistence}, | |||
components::{Owned, Planet, Player, Position, Ship, ShipType, Velocity}, | |||
misc::Persistence, | |||
}; | |||
use specs::{saveload::MarkedBuilder, Builder}; | |||
let player1 = world | |||
.create_entity() | |||
.with(Player {}) | |||
.with(PlayerVisual { | |||
color: Vector4f::new(0.0, 0.5, 1.0, 0.1), | |||
}) | |||
.build(); | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(Owned { owner: player1 }) | |||
.with(Position { | |||
pos: Vector2f::default(), | |||
size: 500.0, | |||
@@ -74,9 +84,10 @@ fn create_world(world: &mut World) { | |||
world | |||
.create_entity() | |||
.marked::<<PersistWorld as Persistence>::Marker>() | |||
.with(Owned { owner: player1 }) | |||
.with(Position { | |||
pos: Vector2f::new(250.0 * x, 250.0 * y), | |||
size: 100.0, | |||
size: 30.0, | |||
}) | |||
.with(Velocity { | |||
dir: Vector2f::new(i as f32 - 1.0, 1.0).normalize(), | |||
@@ -96,7 +107,8 @@ fn create_world(world: &mut World) { | |||
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}; | |||
use space_crush_app::misc::PersistWorld; | |||
use space_crush_common::misc::{Persistence, WorldHelper}; | |||
PersistWorld::setup(world); | |||
@@ -116,7 +128,8 @@ fn load_world(world: &mut World, path: &str) -> Result<bool, Error> { | |||
fn save_world(world: &mut World, path: &str) -> Result<(), Error> { | |||
use serde_json::Serializer; | |||
use space_crush_common::misc::{PersistWorld, WorldHelper}; | |||
use space_crush_app::misc::PersistWorld; | |||
use space_crush_common::misc::WorldHelper; | |||
let vfs = world.resource::<Vfs>()?; | |||
let mut file = vfs.join(path)?.create_file()?; | |||
@@ -6,4 +6,4 @@ mod world; | |||
pub use events::{Events, KeyboardEvent, MouseButton, MouseEvent, VirtualKeyCode, WindowEvent}; | |||
pub use text::{Text, TextCache, TextManager}; | |||
pub use window::Window; | |||
pub use world::WorldHelper; | |||
pub use world::{PersistWorld, WorldHelper}; |
@@ -5,10 +5,33 @@ use glc::{ | |||
shader::{Program, Shader, Type}, | |||
texture::{Data, FilterMag, FilterMin, Target, Texture, Wrap}, | |||
}; | |||
use space_crush_common::misc::Vfs; | |||
use specs::World; | |||
use space_crush_common::{ | |||
components::{Owned, Planet, Player, Position, Ship, Velocity}, | |||
misc::{Persistence, Vfs}, | |||
}; | |||
use specs::{saveload::SimpleMarker, World}; | |||
use crate::{components::PlayerVisual, Error}; | |||
/* PersistWorld */ | |||
pub struct PersistWorld; | |||
pub struct PersistWorldMarker; | |||
impl Persistence for PersistWorld { | |||
type Marker = SimpleMarker<PersistWorldMarker>; | |||
type Components = ( | |||
Position, | |||
Velocity, | |||
Planet, | |||
Ship, | |||
Owned, | |||
Player, | |||
PlayerVisual, | |||
); | |||
} | |||
use crate::Error; | |||
/* WorldHelper */ | |||
pub trait WorldHelper { | |||
fn load_program<I>(&self, iter: I) -> Result<Program, Error> | |||
@@ -1,16 +1,17 @@ | |||
use glc::{ | |||
matrix::Matrix4f, | |||
misc::Bindable, | |||
misc::{BindGuard, Bindable}, | |||
shader::{Program, Type, Uniform}, | |||
texture::Texture, | |||
vector::Vector4f, | |||
}; | |||
use log::error; | |||
use space_crush_common::components::{Planet, Position}; | |||
use space_crush_common::components::{Owned, Planet, Position}; | |||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
use crate::{ | |||
constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
components::PlayerVisual, | |||
constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
misc::WorldHelper, | |||
resources::Geometry, | |||
Error, | |||
@@ -19,7 +20,8 @@ use crate::{ | |||
pub struct Planets { | |||
program: Program, | |||
texture: Texture, | |||
model_location: gl::GLint, | |||
location_model: gl::GLint, | |||
location_glow_color: gl::GLint, | |||
} | |||
impl Planets { | |||
@@ -32,13 +34,11 @@ impl Planets { | |||
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.1); | |||
let glow_color = Uniform::Vector4f(&glow_color); | |||
let model_location = program.uniform_location("uModel")?; | |||
let location_model = program.uniform_location("uModel")?; | |||
let location_glow_color = program.uniform_location("uGlowColor")?; | |||
program.bind(); | |||
program.uniform("uTexture", Uniform::Texture(0))?; | |||
program.uniform("uGlowColor", glow_color)?; | |||
program.unbind(); | |||
let texture = world.load_texture("resources/textures/planet01.png")?; | |||
@@ -46,7 +46,8 @@ impl Planets { | |||
Ok(Self { | |||
program, | |||
texture, | |||
model_location, | |||
location_model, | |||
location_glow_color, | |||
}) | |||
} | |||
} | |||
@@ -56,6 +57,8 @@ pub struct PlanetsData<'a> { | |||
geometry: ReadExpect<'a, Geometry>, | |||
position: ReadStorage<'a, Position>, | |||
planet: ReadStorage<'a, Planet>, | |||
owned: ReadStorage<'a, Owned>, | |||
player_visual: ReadStorage<'a, PlayerVisual>, | |||
} | |||
impl<'a> System<'a> for Planets { | |||
@@ -66,29 +69,43 @@ impl<'a> System<'a> for Planets { | |||
geometry, | |||
position, | |||
planet, | |||
owned, | |||
player_visual, | |||
} = data; | |||
gl::enable(gl::BLEND); | |||
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | |||
self.texture.bind(); | |||
self.program.bind(); | |||
let _guard = BindGuard::new(&self.program); | |||
let _guard = BindGuard::new(&self.texture); | |||
for (p, _) in (&position, &planet).join() { | |||
let x = p.pos.x; | |||
let y = p.pos.y; | |||
for (p, _, owned) in (&position, &planet, owned.maybe()).join() { | |||
let p_x = p.pos.x; | |||
let p_y = p.pos.y; | |||
let s = p.size; | |||
let c = match owned.and_then(|owned| player_visual.get(owned.owner)) { | |||
Some(pv) => &pv.color, | |||
None => &*PLAYER_COLOR_DEFAULT, | |||
}; | |||
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), | |||
Vector4f::new(p_x, p_y, 0.0, 1.0), | |||
); | |||
if let Err(err) = self | |||
.program | |||
.uniform(self.model_location, Uniform::Matrix4f(&m)) | |||
.uniform(self.location_glow_color, Uniform::Vector4f(&c)) | |||
{ | |||
error!("Error while updating glow color: {}", err); | |||
} | |||
if let Err(err) = self | |||
.program | |||
.uniform(self.location_model, Uniform::Matrix4f(&m)) | |||
{ | |||
error!("Error while updating model matrix: {}", err); | |||
} | |||
@@ -96,9 +113,6 @@ impl<'a> System<'a> for Planets { | |||
geometry.render_quad(); | |||
} | |||
self.program.unbind(); | |||
self.texture.unbind(); | |||
gl::disable(gl::BLEND); | |||
} | |||
} |
@@ -6,11 +6,12 @@ use glc::{ | |||
vector::Vector4f, | |||
}; | |||
use log::error; | |||
use space_crush_common::components::{Position, Ship, ShipType, Velocity}; | |||
use space_crush_common::components::{Owned, Position, Ship, ShipType, Velocity}; | |||
use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | |||
use crate::{ | |||
constants::{UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
components::PlayerVisual, | |||
constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||
misc::WorldHelper, | |||
resources::Geometry, | |||
Error, | |||
@@ -18,10 +19,11 @@ use crate::{ | |||
pub struct Ships { | |||
program: Program, | |||
texture_fighter: Texture, | |||
texture_bomber: Texture, | |||
texture_fighter: Texture, | |||
texture_transporter: Texture, | |||
model_location: gl::GLint, | |||
location_model: gl::GLint, | |||
location_glow_color: gl::GLint, | |||
} | |||
impl Ships { | |||
@@ -34,25 +36,24 @@ impl Ships { | |||
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")?; | |||
let location_model = program.uniform_location("uModel")?; | |||
let location_glow_color = program.uniform_location("uGlowColor")?; | |||
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_fighter = world.load_texture("resources/textures/ship_fighter.png")?; | |||
let texture_transporter = world.load_texture("resources/textures/ship_transporter.png")?; | |||
Ok(Self { | |||
program, | |||
texture_fighter, | |||
texture_bomber, | |||
texture_fighter, | |||
texture_transporter, | |||
model_location, | |||
location_model, | |||
location_glow_color, | |||
}) | |||
} | |||
} | |||
@@ -63,6 +64,8 @@ pub struct ShipsData<'a> { | |||
position: ReadStorage<'a, Position>, | |||
velocity: ReadStorage<'a, Velocity>, | |||
ship: ReadStorage<'a, Ship>, | |||
owned: ReadStorage<'a, Owned>, | |||
player_visual: ReadStorage<'a, PlayerVisual>, | |||
} | |||
impl<'a> System<'a> for Ships { | |||
@@ -74,6 +77,8 @@ impl<'a> System<'a> for Ships { | |||
position, | |||
velocity, | |||
ship, | |||
owned, | |||
player_visual, | |||
} = data; | |||
gl::enable(gl::BLEND); | |||
@@ -81,13 +86,18 @@ impl<'a> System<'a> for Ships { | |||
let _guard = BindGuard::new(&self.program); | |||
for (p, v, ship) in (&position, &velocity, &ship).join() { | |||
for (p, v, ship, owned) in (&position, &velocity, &ship, owned.maybe()).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 c = match owned.and_then(|owned| player_visual.get(owned.owner)) { | |||
Some(pv) => &pv.color, | |||
None => &*PLAYER_COLOR_DEFAULT, | |||
}; | |||
let p_x = p.pos.x; | |||
let p_y = p.pos.y; | |||
let d_x = v.dir.x; | |||
@@ -103,7 +113,14 @@ impl<'a> System<'a> for Ships { | |||
if let Err(err) = self | |||
.program | |||
.uniform(self.model_location, Uniform::Matrix4f(&m)) | |||
.uniform(self.location_glow_color, Uniform::Vector4f(&c)) | |||
{ | |||
error!("Error while updating glow color: {}", err); | |||
} | |||
if let Err(err) = self | |||
.program | |||
.uniform(self.location_model, Uniform::Matrix4f(&m)) | |||
{ | |||
error!("Error while updating model matrix: {}", err); | |||
} | |||
@@ -1,9 +1,13 @@ | |||
mod owned; | |||
mod planet; | |||
mod player; | |||
mod position; | |||
mod ship; | |||
mod velocity; | |||
pub use owned::Owned; | |||
pub use planet::Planet; | |||
pub use player::Player; | |||
pub use position::Position; | |||
pub use ship::{Ship, Type as ShipType}; | |||
pub use velocity::Velocity; |
@@ -0,0 +1,46 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{ConvertSaveload, Marker}, | |||
Component, Entity, VecStorage, | |||
}; | |||
#[derive(Clone, Debug)] | |||
pub struct Owned { | |||
pub owner: Entity, | |||
} | |||
#[derive(Serialize, Deserialize)] | |||
pub struct OwnedData<M> { | |||
pub owner: M, | |||
} | |||
impl Component for Owned { | |||
type Storage = VecStorage<Self>; | |||
} | |||
impl<M> ConvertSaveload<M> for Owned | |||
where | |||
for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
{ | |||
type Data = OwnedData<M>; | |||
type Error = NoError; | |||
fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||
where | |||
F: FnMut(Entity) -> Option<M>, | |||
{ | |||
let owner = ids(self.owner).unwrap(); | |||
Ok(OwnedData { owner }) | |||
} | |||
fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||
where | |||
F: FnMut(M) -> Option<Entity>, | |||
{ | |||
let owner = ids(data.owner).unwrap(); | |||
Ok(Owned { owner }) | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
use serde::{Deserialize, Serialize}; | |||
use specs::{Component, NullStorage}; | |||
#[derive(Clone, Debug, Default, Serialize, Deserialize)] | |||
pub struct Player {} | |||
impl Component for Player { | |||
type Storage = NullStorage<Self>; | |||
} |
@@ -1,6 +1,7 @@ | |||
use specs::{Dispatcher as Inner, DispatcherBuilder, World}; | |||
use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||
use crate::{ | |||
components::Player, | |||
resources::Global, | |||
systems::{Movement, Process}, | |||
}; | |||
@@ -13,6 +14,8 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
pub fn new(world: &mut World) -> Self { | |||
world.insert(Global::default()); | |||
world.register::<Player>(); | |||
let mut dispatcher = DispatcherBuilder::new() | |||
.with(Process::default(), "process", &[]) | |||
.with(Movement::default(), "movement", &[]) | |||
@@ -1,9 +1,7 @@ | |||
mod log; | |||
mod persistence; | |||
mod vfs; | |||
mod world; | |||
pub use self::log::init as init_logger; | |||
pub use self::vfs::{Vfs, VfsError}; | |||
pub use persistence::PersistWorld; | |||
pub use world::{Persistence, WorldHelper}; |
@@ -1,13 +0,0 @@ | |||
use specs::saveload::SimpleMarker; | |||
use crate::components::{Planet, Position, Ship, Velocity}; | |||
use super::Persistence; | |||
pub struct PersistWorld; | |||
pub struct PersistWorldMarker; | |||
impl Persistence for PersistWorld { | |||
type Marker = SimpleMarker<PersistWorldMarker>; | |||
type Components = (Position, Velocity, Planet, Ship); | |||
} |
@@ -1,14 +1,11 @@ | |||
use std::any::type_name; | |||
use serde::{ | |||
de::{DeserializeOwned, Deserializer}, | |||
ser::{Serialize, Serializer}, | |||
}; | |||
use serde::{de::Deserializer, ser::Serializer}; | |||
use shred::{Fetch, FetchMut, Resource}; | |||
use shrev::{Event, EventChannel, ReaderId}; | |||
use specs::{ | |||
error::NoError, | |||
saveload::{DeserializeComponents, Marker, SerializeComponents}, | |||
saveload::{ConvertSaveload, DeserializeComponents, Marker, SerializeComponents}, | |||
Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | |||
}; | |||
@@ -133,16 +130,31 @@ macro_rules! define_persistence_components { | |||
where | |||
M: Marker, | |||
M::Allocator: Default, | |||
$($T: Component + Serialize + DeserializeOwned + Clone,)+ | |||
$($T: Component + ConvertSaveload<M, Error = NoError>,)+ | |||
{ | |||
fn serialize<S>(world: &World, serializer: S) -> Result<(), S::Error> | |||
where | |||
S: Serializer, | |||
{ | |||
let (entities, marker, $($T,)+) = | |||
world.system_data::<(Entities, ReadStorage<M>, $(ReadStorage<$T>,)+)>(); | |||
let ( | |||
entities, | |||
mut marker, | |||
mut allocator, | |||
$($T,)+ | |||
) = world.system_data::<( | |||
Entities, | |||
WriteStorage<M>, | |||
Write<M::Allocator>, | |||
$(ReadStorage<$T>, | |||
)+)>(); | |||
SerializeComponents::<NoError, M>::serialize(&($($T,)+), &entities, &marker, serializer)?; | |||
SerializeComponents::<NoError, M>::serialize_recursive | |||
(&($($T,)+), | |||
&entities, | |||
&mut marker, | |||
&mut allocator, | |||
serializer, | |||
)?; | |||
Ok(()) | |||
} | |||
@@ -151,7 +163,12 @@ macro_rules! define_persistence_components { | |||
where | |||
D: Deserializer<'de>, | |||
{ | |||
let (entities, mut marker, mut allocator, $($T,)+) = world.system_data::<( | |||
let ( | |||
entities, | |||
mut marker, | |||
mut allocator, | |||
$($T,)+ | |||
) = world.system_data::<( | |||
Entities, | |||
WriteStorage<M>, | |||
Write<M::Allocator>, | |||