| @@ -4,6 +4,10 @@ use lazy_static::lazy_static; | |||||
| pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0; | pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0; | ||||
| pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1; | pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1; | ||||
| pub const SHIP_SIZE: f32 = 15.0; | |||||
| pub const PLANET_SIZE: f32 = 200.0; | |||||
| pub const ASTEROID_SIZE: f32 = 100.0; | |||||
| lazy_static! { | lazy_static! { | ||||
| pub static ref PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1); | pub static ref PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1); | ||||
| } | } | ||||
| @@ -1,7 +1,9 @@ | |||||
| mod fleets; | mod fleets; | ||||
| mod raster; | |||||
| mod ships; | mod ships; | ||||
| mod summary; | mod summary; | ||||
| pub use fleets::Fleets; | pub use fleets::Fleets; | ||||
| pub use raster::Raster; | |||||
| pub use ships::Ships; | pub use ships::Ships; | ||||
| pub use summary::Summary; | pub use summary::Summary; | ||||
| @@ -0,0 +1,52 @@ | |||||
| use glc::vector::{Vector2f, Vector4f}; | |||||
| use space_crush_common::resources::Raster as RasterResource; | |||||
| use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; | |||||
| use crate::resources::Geometry; | |||||
| #[derive(Default)] | |||||
| pub struct Raster; | |||||
| #[derive(SystemData)] | |||||
| pub struct FleetData<'a> { | |||||
| geometry: WriteExpect<'a, Geometry>, | |||||
| raster: ReadExpect<'a, RasterResource>, | |||||
| } | |||||
| impl<'a> System<'a> for Raster { | |||||
| type SystemData = FleetData<'a>; | |||||
| fn run(&mut self, data: Self::SystemData) { | |||||
| let FleetData { | |||||
| mut geometry, | |||||
| raster, | |||||
| } = data; | |||||
| let s = raster.size(); | |||||
| gl::enable(gl::BLEND); | |||||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||||
| for (x, y, q) in raster.quads() { | |||||
| let a = if q.is_empty() { 0.05 } else { 0.5 }; | |||||
| geometry.render_lines( | |||||
| Vector4f::new(1.0, 1.0, 1.0, a), | |||||
| &create_quad(x as _, y as _, s), | |||||
| ); | |||||
| } | |||||
| gl::disable(gl::BLEND); | |||||
| } | |||||
| } | |||||
| fn create_quad(x: f32, y: f32, s: f32) -> Vec<Vector2f> { | |||||
| let f = s / 2.0; | |||||
| vec![ | |||||
| Vector2f::new(x * s + f, y * s + f), | |||||
| Vector2f::new(x * s + f, y * s - f), | |||||
| Vector2f::new(x * s - f, y * s - f), | |||||
| Vector2f::new(x * s - f, y * s + f), | |||||
| Vector2f::new(x * s + f, y * s + f), | |||||
| ] | |||||
| } | |||||
| @@ -11,7 +11,9 @@ use specs::{Dispatcher, DispatcherBuilder, Entity, World}; | |||||
| pub use error::Error; | pub use error::Error; | ||||
| use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | |||||
| use debug::{ | |||||
| Fleets as DebugFleets, Raster as RasterDebug, Ships as DebugShips, Summary as DebugSummary, | |||||
| }; | |||||
| use misc::{Events, TextManager, Window}; | use misc::{Events, TextManager, Window}; | ||||
| use render::{Asteroids, Init, Planets, Ships}; | use render::{Asteroids, Init, Planets, Ships}; | ||||
| use resources::{Camera, Config, Geometry, PlayerState, State, Uniform}; | use resources::{Camera, Config, Geometry, PlayerState, State, Uniform}; | ||||
| @@ -47,11 +49,12 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(StateUpdate::new(world)?, "state_update", &[]) | .with(StateUpdate::new(world)?, "state_update", &[]) | ||||
| .with(FleetInfoUpdate::new(world)?, "fleet_info_update", &[]) | |||||
| .with(FleetInfoUpdate::new(world), "fleet_info_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(Asteroids::new(world)?) | .with_thread_local(Asteroids::new(world)?) | ||||
| .with_thread_local(Ships::new(world)?) | .with_thread_local(Ships::new(world)?) | ||||
| .with_thread_local(RasterDebug::default()) | |||||
| .with_thread_local(DebugShips::default()) | .with_thread_local(DebugShips::default()) | ||||
| .with_thread_local(DebugFleets::default()) | .with_thread_local(DebugFleets::default()) | ||||
| .with_thread_local(DebugSummary::new(&text_manager)?) | .with_thread_local(DebugSummary::new(&text_manager)?) | ||||
| @@ -60,8 +60,8 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| use space_crush_app::components::FleetInfo; | use space_crush_app::components::FleetInfo; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{ | components::{ | ||||
| Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, Ship, | |||||
| ShipType, Velocity, | |||||
| Asteroid, AsteroidType, Fleet, FleetOwned, Planet, Player, PlayerOwned, Position, | |||||
| Shape, Ship, ShipType, Velocity, | |||||
| }, | }, | ||||
| misc::{PersistWorld, Persistence}, | misc::{PersistWorld, Persistence}, | ||||
| }; | }; | ||||
| @@ -84,7 +84,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(500.0, -500.0), | pos: Vector2f::new(500.0, -500.0), | ||||
| size: 100.0, | |||||
| shape: Shape::Circle(100.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 125.0, | orbit_min: 125.0, | ||||
| @@ -101,7 +101,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::new(500.0, 500.0), | pos: Vector2f::new(500.0, 500.0), | ||||
| size: 100.0, | |||||
| shape: Shape::Circle(100.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 125.0, | orbit_min: 125.0, | ||||
| @@ -119,7 +119,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .with(PlayerOwned { owner: player_id }) | .with(PlayerOwned { owner: player_id }) | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::default(), | pos: Vector2f::default(), | ||||
| size: 250.0, | |||||
| shape: Shape::Circle(250.0), | |||||
| }) | }) | ||||
| .with(Fleet { | .with(Fleet { | ||||
| orbit_min: 325.0, | orbit_min: 325.0, | ||||
| @@ -129,7 +129,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| .with(Planet {}) | .with(Planet {}) | ||||
| .build(); | .build(); | ||||
| for i in 0..10 { | |||||
| for i in 0..100 { | |||||
| world | world | ||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| @@ -139,7 +139,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||||
| 500.0 * random::<f32>() - 250.0, | 500.0 * random::<f32>() - 250.0, | ||||
| 500.0 * random::<f32>() - 250.0, | 500.0 * random::<f32>() - 250.0, | ||||
| ), | ), | ||||
| size: 15.0, | |||||
| shape: Shape::Dot, | |||||
| }) | }) | ||||
| .with(Velocity { | .with(Velocity { | ||||
| dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | ||||
| @@ -12,7 +12,10 @@ use space_crush_common::{ | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
| use crate::{ | use crate::{ | ||||
| constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||||
| constants::{ | |||||
| ASTEROID_SIZE, PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, | |||||
| UNIFORM_BUFFER_INDEX_UNIFORM, | |||||
| }, | |||||
| misc::WorldHelper, | misc::WorldHelper, | ||||
| resources::Geometry, | resources::Geometry, | ||||
| Error, | Error, | ||||
| @@ -85,7 +88,7 @@ impl<'a> System<'a> for Asteroids { | |||||
| for (position, asteroid, owned) in (&position, &asteroid, owned.maybe()).join() { | for (position, asteroid, owned) in (&position, &asteroid, owned.maybe()).join() { | ||||
| let p_x = position.pos.x; | let p_x = position.pos.x; | ||||
| let p_y = position.pos.y; | let p_y = position.pos.y; | ||||
| let s = position.size; | |||||
| let s = position.shape.circle().unwrap_or(ASTEROID_SIZE); | |||||
| let _guard = match asteroid.type_ { | let _guard = match asteroid.type_ { | ||||
| AsteroidType::Metal => BindGuard::new(&self.texture_metal), | AsteroidType::Metal => BindGuard::new(&self.texture_metal), | ||||
| @@ -12,7 +12,10 @@ use space_crush_common::{ | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
| use crate::{ | use crate::{ | ||||
| constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||||
| constants::{ | |||||
| PLANET_SIZE, PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, | |||||
| UNIFORM_BUFFER_INDEX_UNIFORM, | |||||
| }, | |||||
| misc::WorldHelper, | misc::WorldHelper, | ||||
| resources::Geometry, | resources::Geometry, | ||||
| Error, | Error, | ||||
| @@ -83,7 +86,7 @@ impl<'a> System<'a> for Planets { | |||||
| for (p, _, owned) in (&position, &planet, owned.maybe()).join() { | for (p, _, owned) in (&position, &planet, owned.maybe()).join() { | ||||
| let p_x = p.pos.x; | let p_x = p.pos.x; | ||||
| let p_y = p.pos.y; | let p_y = p.pos.y; | ||||
| let s = p.size; | |||||
| let s = p.shape.circle().unwrap_or(PLANET_SIZE); | |||||
| let c = match owned.and_then(|owned| player.get(owned.owner)) { | let c = match owned.and_then(|owned| player.get(owned.owner)) { | ||||
| Some(pv) => &pv.color, | Some(pv) => &pv.color, | ||||
| @@ -12,7 +12,9 @@ use space_crush_common::{ | |||||
| use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | use specs::{prelude::*, ReadExpect, ReadStorage, System, World}; | ||||
| use crate::{ | use crate::{ | ||||
| constants::{PLAYER_COLOR_DEFAULT, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM}, | |||||
| constants::{ | |||||
| PLAYER_COLOR_DEFAULT, SHIP_SIZE, UNIFORM_BUFFER_INDEX_CAMERA, UNIFORM_BUFFER_INDEX_UNIFORM, | |||||
| }, | |||||
| misc::WorldHelper, | misc::WorldHelper, | ||||
| resources::Geometry, | resources::Geometry, | ||||
| Error, | Error, | ||||
| @@ -103,7 +105,7 @@ impl<'a> System<'a> for Ships { | |||||
| let p_y = p.pos.y; | let p_y = p.pos.y; | ||||
| let d_x = v.dir.x; | let d_x = v.dir.x; | ||||
| let d_y = v.dir.y; | let d_y = v.dir.y; | ||||
| let s = p.size; | |||||
| let s = SHIP_SIZE; | |||||
| let m = Matrix4f::new( | let m = Matrix4f::new( | ||||
| Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0), | Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0), | ||||
| @@ -10,7 +10,7 @@ use specs::{ | |||||
| World, WriteStorage, | World, WriteStorage, | ||||
| }; | }; | ||||
| use crate::{components::FleetInfo, resources::PlayerState, Error}; | |||||
| use crate::{components::FleetInfo, resources::PlayerState}; | |||||
| pub struct FleetInfoUpdate { | pub struct FleetInfoUpdate { | ||||
| modified: HashMap<Index, Modified>, | modified: HashMap<Index, Modified>, | ||||
| @@ -26,7 +26,7 @@ struct Modified { | |||||
| } | } | ||||
| impl FleetInfoUpdate { | impl FleetInfoUpdate { | ||||
| pub fn new(world: &mut World) -> Result<Self, Error> { | |||||
| pub fn new(world: &mut World) -> Self { | |||||
| let modified = HashMap::new(); | let modified = HashMap::new(); | ||||
| let need_update = BitSet::new(); | let need_update = BitSet::new(); | ||||
| @@ -50,12 +50,12 @@ impl FleetInfoUpdate { | |||||
| .register_reader() | .register_reader() | ||||
| }; | }; | ||||
| Ok(Self { | |||||
| Self { | |||||
| modified, | modified, | ||||
| need_update, | need_update, | ||||
| fleet_owned_id, | fleet_owned_id, | ||||
| player_owned_id, | player_owned_id, | ||||
| }) | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,6 +10,6 @@ pub use asteroid::{Asteroid, Type as AsteroidType}; | |||||
| pub use fleet::{Fleet, Owned as FleetOwned}; | pub use fleet::{Fleet, Owned as FleetOwned}; | ||||
| pub use planet::Planet; | pub use planet::Planet; | ||||
| pub use player::{Owned as PlayerOwned, Player}; | pub use player::{Owned as PlayerOwned, Player}; | ||||
| pub use position::Position; | |||||
| pub use position::{Position, Shape}; | |||||
| pub use ship::{Count as ShipCount, Ship, Type as ShipType}; | pub use ship::{Count as ShipCount, Ship, Type as ShipType}; | ||||
| pub use velocity::Velocity; | pub use velocity::Velocity; | ||||
| @@ -2,12 +2,36 @@ use glc::vector::Vector2f; | |||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||
| use specs::{Component, VecStorage}; | use specs::{Component, VecStorage}; | ||||
| use crate::misc::FlaggedStorage; | |||||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||||
| pub struct Position { | pub struct Position { | ||||
| pub pos: Vector2f, | pub pos: Vector2f, | ||||
| pub size: f32, | |||||
| pub shape: Shape, | |||||
| } | |||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||||
| pub enum Shape { | |||||
| Dot, | |||||
| Circle(f32), | |||||
| } | } | ||||
| impl Component for Position { | impl Component for Position { | ||||
| type Storage = VecStorage<Self>; | |||||
| type Storage = FlaggedStorage<Self, VecStorage<Self>>; | |||||
| } | |||||
| impl Shape { | |||||
| pub fn circle(&self) -> Option<f32> { | |||||
| if let Self::Circle(r) = &self { | |||||
| Some(*r) | |||||
| } else { | |||||
| None | |||||
| } | |||||
| } | |||||
| } | |||||
| impl Default for Shape { | |||||
| fn default() -> Self { | |||||
| Self::Dot | |||||
| } | |||||
| } | } | ||||
| @@ -2,8 +2,8 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | |||||
| systems::{Fleets, Movement, Process}, | |||||
| resources::{Global, Raster}, | |||||
| systems::{Fleets, Movement, Process, RasterUpdate}, | |||||
| }; | }; | ||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| @@ -13,6 +13,7 @@ pub struct Dispatcher<'a, 'b> { | |||||
| impl<'a, 'b> Dispatcher<'a, 'b> { | impl<'a, 'b> Dispatcher<'a, 'b> { | ||||
| pub fn new(world: &mut World) -> Self { | pub fn new(world: &mut World) -> Self { | ||||
| world.insert(Global::default()); | world.insert(Global::default()); | ||||
| world.insert(Raster::new(250.0)); | |||||
| world.register::<Player>(); | world.register::<Player>(); | ||||
| @@ -20,6 +21,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | .with(Movement::default(), "movement", &[]) | ||||
| .with(Fleets::default(), "fleets", &[]) | .with(Fleets::default(), "fleets", &[]) | ||||
| .with(RasterUpdate::new(world), "raster_update", &[]) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,3 +1,5 @@ | |||||
| mod global; | mod global; | ||||
| mod raster; | |||||
| pub use global::Global; | pub use global::Global; | ||||
| pub use raster::Raster; | |||||
| @@ -0,0 +1,210 @@ | |||||
| #![allow(dead_code)] | |||||
| use std::collections::HashSet; | |||||
| use glc::vector::Vector2f; | |||||
| use specs::world::Index; | |||||
| use crate::components::{Position, Shape}; | |||||
| pub struct Raster { | |||||
| size: f32, | |||||
| quads: OffsetVec<OffsetVec<Quad>>, | |||||
| } | |||||
| #[derive(Default)] | |||||
| struct OffsetVec<T> | |||||
| where | |||||
| T: Default, | |||||
| { | |||||
| origin: usize, | |||||
| data: Vec<T>, | |||||
| } | |||||
| #[derive(Default)] | |||||
| pub struct Quad { | |||||
| entities: HashSet<Index>, | |||||
| } | |||||
| impl Raster { | |||||
| pub fn new(size: f32) -> Self { | |||||
| Self { | |||||
| size, | |||||
| quads: OffsetVec::new(), | |||||
| } | |||||
| } | |||||
| pub fn size(&self) -> f32 { | |||||
| self.size | |||||
| } | |||||
| pub fn insert(&mut self, id: Index, position: &Position) -> bool { | |||||
| self.update_quads(position, |q| { | |||||
| q.entities.insert(id); | |||||
| }) | |||||
| } | |||||
| pub fn remove(&mut self, id: &Index, position: &Position) -> bool { | |||||
| self.update_quads(position, |q| { | |||||
| q.entities.remove(id); | |||||
| }) | |||||
| } | |||||
| pub fn quads(&self) -> impl Iterator<Item = (isize, isize, &Quad)> { | |||||
| self.quads.data.iter().enumerate().flat_map(move |(x, vy)| { | |||||
| vy.data.iter().enumerate().map(move |(y, q)| { | |||||
| ( | |||||
| x as isize - self.quads.origin as isize, | |||||
| y as isize - vy.origin as isize, | |||||
| q, | |||||
| ) | |||||
| }) | |||||
| }) | |||||
| } | |||||
| fn update_quads<F>(&mut self, position: &Position, mut f: F) -> bool | |||||
| where | |||||
| F: FnMut(&mut Quad), | |||||
| { | |||||
| let p = position.pos; | |||||
| match position.shape { | |||||
| Shape::Dot => { | |||||
| let x = (p.x / self.size).round() as isize; | |||||
| let y = (p.y / self.size).round() as isize; | |||||
| f(self.quads.get_mut(x).get_mut(y)); | |||||
| true | |||||
| } | |||||
| Shape::Circle(r) => { | |||||
| let s = self.size; | |||||
| let min_x = ((p.x - r) / s).round() as isize - 1; | |||||
| let max_x = ((p.x + r) / s).round() as isize + 1; | |||||
| let min_y = ((p.y - r) / s).round() as isize - 1; | |||||
| let max_y = ((p.y + r) / s).round() as isize + 1; | |||||
| let sr = r * r; | |||||
| let s = self.size / 2.0; | |||||
| let mut ret = false; | |||||
| for x in min_x..=max_x { | |||||
| for y in min_y..=max_y { | |||||
| let fx = x as f32 * self.size; | |||||
| let fy = y as f32 * self.size; | |||||
| let quad = self.quads.get_mut(x).get_mut(y); | |||||
| if (Vector2f::new(fx - s, fy - s) - p).length_sqr() < sr | |||||
| || (Vector2f::new(fx + s, fy - s) - p).length_sqr() < sr | |||||
| || (Vector2f::new(fx - s, fy + s) - p).length_sqr() < sr | |||||
| || (Vector2f::new(fx + s, fy + s) - p).length_sqr() < sr | |||||
| { | |||||
| f(quad); | |||||
| ret = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| ret | |||||
| } | |||||
| } | |||||
| } | |||||
| fn get_quad(&self, x: isize, y: isize) -> Option<&Quad> { | |||||
| let row = self.quads.get(x)?; | |||||
| let quad = row.get(y)?; | |||||
| Some(quad) | |||||
| } | |||||
| fn get_quad_mut(&mut self, x: isize, y: isize) -> &mut Quad { | |||||
| self.quads.get_mut(x).get_mut(y) | |||||
| } | |||||
| } | |||||
| impl<T> OffsetVec<T> | |||||
| where | |||||
| T: Default, | |||||
| { | |||||
| pub fn new() -> Self { | |||||
| Self { | |||||
| origin: 0, | |||||
| data: Vec::new(), | |||||
| } | |||||
| } | |||||
| pub fn get(&self, i: isize) -> Option<&T> { | |||||
| let low = (0usize - self.origin) as isize; | |||||
| let high = (self.data.len() - self.origin) as isize - 1; | |||||
| if i < low || i > high { | |||||
| None | |||||
| } else { | |||||
| Some(&self.data[(i - low) as usize]) | |||||
| } | |||||
| } | |||||
| pub fn get_mut(&mut self, i: isize) -> &mut T { | |||||
| let low = 0isize - self.origin as isize; | |||||
| let high = (self.data.len() - self.origin) as isize - 1; | |||||
| if i > high { | |||||
| let add = (i - low + 1) as usize; | |||||
| self.data.resize_with(add, Default::default); | |||||
| } else if i < low { | |||||
| let add = (low - i) as usize; | |||||
| let mut prefix = Vec::new(); | |||||
| prefix.resize_with(add, Default::default); | |||||
| prefix.append(&mut self.data); | |||||
| self.data = prefix; | |||||
| self.origin += add; | |||||
| } | |||||
| &mut self.data[(i + self.origin as isize) as usize] | |||||
| } | |||||
| } | |||||
| impl Quad { | |||||
| pub fn is_empty(&self) -> bool { | |||||
| self.entities.is_empty() | |||||
| } | |||||
| } | |||||
| #[cfg(test)] | |||||
| mod tests { | |||||
| use super::*; | |||||
| #[test] | |||||
| fn vec_get_mut() { | |||||
| let mut vec = OffsetVec::<usize>::new(); | |||||
| assert_eq!(vec.origin, 0); | |||||
| assert_eq!(vec.data, Vec::<usize>::new()); | |||||
| *vec.get_mut(0) = 100; | |||||
| assert_eq!(vec.origin, 0); | |||||
| assert_eq!(vec.data, vec![100]); | |||||
| *vec.get_mut(1) = 101; | |||||
| assert_eq!(vec.origin, 0); | |||||
| assert_eq!(vec.data, vec![100, 101]); | |||||
| *vec.get_mut(-2) = 98; | |||||
| assert_eq!(vec.origin, 2); | |||||
| assert_eq!(vec.data, vec![98, 0, 100, 101]); | |||||
| *vec.get_mut(4) = 104; | |||||
| assert_eq!(vec.origin, 2); | |||||
| assert_eq!(vec.data, vec![98, 0, 100, 101, 0, 0, 104]); | |||||
| *vec.get_mut(-4) = 96; | |||||
| assert_eq!(vec.origin, 4); | |||||
| assert_eq!(vec.data, vec![96, 0, 98, 0, 100, 101, 0, 0, 104]); | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,9 @@ | |||||
| mod fleets; | mod fleets; | ||||
| mod movement; | mod movement; | ||||
| mod process; | mod process; | ||||
| mod raster_update; | |||||
| pub use fleets::Fleets; | pub use fleets::Fleets; | ||||
| pub use movement::Movement; | pub use movement::Movement; | ||||
| pub use process::Process; | pub use process::Process; | ||||
| pub use raster_update::RasterUpdate; | |||||
| @@ -1,6 +1,6 @@ | |||||
| #![allow(dead_code)] | #![allow(dead_code)] | ||||
| use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage}; | |||||
| use specs::{prelude::*, Read, ReadStorage, System, WriteStorage}; | |||||
| use crate::{ | use crate::{ | ||||
| components::{Position, Velocity}, | components::{Position, Velocity}, | ||||
| @@ -27,10 +27,8 @@ impl<'a> System<'a> for Movement { | |||||
| global, | global, | ||||
| } = data; | } = data; | ||||
| (&mut position, &velocity) | |||||
| .par_join() | |||||
| .for_each(|(position, velocity)| { | |||||
| position.pos = position.pos + velocity.dir * velocity.speed * global.delta; | |||||
| }); | |||||
| for (position, velocity) in (&mut position, &velocity).join() { | |||||
| position.pos = position.pos + velocity.dir * velocity.speed * global.delta; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,74 @@ | |||||
| use shrev::ReaderId; | |||||
| use specs::{hibitset::BitSetLike, prelude::*, Entities, ReadStorage, System, World, WriteStorage}; | |||||
| use crate::{components::Position, misc::ComponentEvent, resources::Raster}; | |||||
| pub struct RasterUpdate { | |||||
| need_update: BitSet, | |||||
| position_id: ReaderId<ComponentEvent<Position>>, | |||||
| } | |||||
| impl RasterUpdate { | |||||
| pub fn new(world: &mut World) -> Self { | |||||
| let need_update = BitSet::new(); | |||||
| let position_id = unsafe { | |||||
| WriteStorage::<Position>::setup(world); | |||||
| let mut position = world.write_component::<Position>(); | |||||
| position | |||||
| .unprotected_storage_mut() | |||||
| .channel_mut() | |||||
| .register_reader() | |||||
| }; | |||||
| Self { | |||||
| need_update, | |||||
| position_id, | |||||
| } | |||||
| } | |||||
| } | |||||
| #[derive(SystemData)] | |||||
| pub struct RasterUpdateData<'a> { | |||||
| raster: WriteExpect<'a, Raster>, | |||||
| entities: Entities<'a>, | |||||
| positions: ReadStorage<'a, Position>, | |||||
| } | |||||
| impl<'a> System<'a> for RasterUpdate { | |||||
| type SystemData = RasterUpdateData<'a>; | |||||
| fn run(&mut self, data: Self::SystemData) { | |||||
| let RasterUpdateData { | |||||
| mut raster, | |||||
| entities, | |||||
| positions, | |||||
| } = data; | |||||
| self.need_update.clear(); | |||||
| let events = positions | |||||
| .unprotected_storage() | |||||
| .channel() | |||||
| .read(&mut self.position_id); | |||||
| for event in events { | |||||
| match event { | |||||
| ComponentEvent::Inserted(id) => { | |||||
| self.need_update.add(*id); | |||||
| } | |||||
| ComponentEvent::Modified(id, position) | ComponentEvent::Removed(id, position) => { | |||||
| self.need_update.add(*id); | |||||
| raster.remove(id, position); | |||||
| } | |||||
| } | |||||
| } | |||||
| if self.need_update.is_empty() { | |||||
| return; | |||||
| } | |||||
| for (e, position) in (&entities, &positions).join() { | |||||
| raster.insert(e.id(), position); | |||||
| } | |||||
| } | |||||
| } | |||||