Browse Source

Implemented first shoot of raster

raster
Bergmann89 3 years ago
parent
commit
6304c6b9d7
17 changed files with 411 additions and 30 deletions
  1. +4
    -0
      space-crush-app/src/constants.rs
  2. +2
    -0
      space-crush-app/src/debug/mod.rs
  3. +52
    -0
      space-crush-app/src/debug/raster.rs
  4. +5
    -2
      space-crush-app/src/lib.rs
  5. +7
    -7
      space-crush-app/src/main.rs
  6. +5
    -2
      space-crush-app/src/render/asteroids.rs
  7. +5
    -2
      space-crush-app/src/render/planets.rs
  8. +4
    -2
      space-crush-app/src/render/ships.rs
  9. +4
    -4
      space-crush-app/src/systems/fleet_info_update.rs
  10. +1
    -1
      space-crush-common/src/components/mod.rs
  11. +26
    -2
      space-crush-common/src/components/position.rs
  12. +4
    -2
      space-crush-common/src/dispatcher.rs
  13. +2
    -0
      space-crush-common/src/resources/mod.rs
  14. +210
    -0
      space-crush-common/src/resources/raster.rs
  15. +2
    -0
      space-crush-common/src/systems/mod.rs
  16. +4
    -6
      space-crush-common/src/systems/movement.rs
  17. +74
    -0
      space-crush-common/src/systems/raster_update.rs

+ 4
- 0
space-crush-app/src/constants.rs View File

@@ -4,6 +4,10 @@ use lazy_static::lazy_static;
pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0;
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! {
pub static ref PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1);
}

+ 2
- 0
space-crush-app/src/debug/mod.rs View File

@@ -1,7 +1,9 @@
mod fleets;
mod raster;
mod ships;
mod summary;

pub use fleets::Fleets;
pub use raster::Raster;
pub use ships::Ships;
pub use summary::Summary;

+ 52
- 0
space-crush-app/src/debug/raster.rs View File

@@ -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),
]
}

+ 5
- 2
space-crush-app/src/lib.rs View File

@@ -11,7 +11,9 @@ use specs::{Dispatcher, DispatcherBuilder, Entity, World};

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 render::{Asteroids, Init, Planets, Ships};
use resources::{Camera, Config, Geometry, PlayerState, State, Uniform};
@@ -47,11 +49,12 @@ impl<'a, 'b> App<'a, 'b> {

let mut dispatcher = DispatcherBuilder::new()
.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(Planets::new(world)?)
.with_thread_local(Asteroids::new(world)?)
.with_thread_local(Ships::new(world)?)
.with_thread_local(RasterDebug::default())
.with_thread_local(DebugShips::default())
.with_thread_local(DebugFleets::default())
.with_thread_local(DebugSummary::new(&text_manager)?)


+ 7
- 7
space-crush-app/src/main.rs View File

@@ -60,8 +60,8 @@ fn create_world(world: &mut World, player_id: Entity) {
use space_crush_app::components::FleetInfo;
use space_crush_common::{
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},
};
@@ -84,7 +84,7 @@ fn create_world(world: &mut World, player_id: Entity) {
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(500.0, -500.0),
size: 100.0,
shape: Shape::Circle(100.0),
})
.with(Fleet {
orbit_min: 125.0,
@@ -101,7 +101,7 @@ fn create_world(world: &mut World, player_id: Entity) {
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Position {
pos: Vector2f::new(500.0, 500.0),
size: 100.0,
shape: Shape::Circle(100.0),
})
.with(Fleet {
orbit_min: 125.0,
@@ -119,7 +119,7 @@ fn create_world(world: &mut World, player_id: Entity) {
.with(PlayerOwned { owner: player_id })
.with(Position {
pos: Vector2f::default(),
size: 250.0,
shape: Shape::Circle(250.0),
})
.with(Fleet {
orbit_min: 325.0,
@@ -129,7 +129,7 @@ fn create_world(world: &mut World, player_id: Entity) {
.with(Planet {})
.build();

for i in 0..10 {
for i in 0..100 {
world
.create_entity()
.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,
),
size: 15.0,
shape: Shape::Dot,
})
.with(Velocity {
dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(),


+ 5
- 2
space-crush-app/src/render/asteroids.rs View File

@@ -12,7 +12,10 @@ use space_crush_common::{
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};

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,
resources::Geometry,
Error,
@@ -85,7 +88,7 @@ impl<'a> System<'a> for Asteroids {
for (position, asteroid, owned) in (&position, &asteroid, owned.maybe()).join() {
let p_x = position.pos.x;
let p_y = position.pos.y;
let s = position.size;
let s = position.shape.circle().unwrap_or(ASTEROID_SIZE);

let _guard = match asteroid.type_ {
AsteroidType::Metal => BindGuard::new(&self.texture_metal),


+ 5
- 2
space-crush-app/src/render/planets.rs View File

@@ -12,7 +12,10 @@ use space_crush_common::{
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};

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,
resources::Geometry,
Error,
@@ -83,7 +86,7 @@ impl<'a> System<'a> for Planets {
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 s = p.shape.circle().unwrap_or(PLANET_SIZE);

let c = match owned.and_then(|owned| player.get(owned.owner)) {
Some(pv) => &pv.color,


+ 4
- 2
space-crush-app/src/render/ships.rs View File

@@ -12,7 +12,9 @@ use space_crush_common::{
use specs::{prelude::*, ReadExpect, ReadStorage, System, World};

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,
resources::Geometry,
Error,
@@ -103,7 +105,7 @@ impl<'a> System<'a> for Ships {
let p_y = p.pos.y;
let d_x = v.dir.x;
let d_y = v.dir.y;
let s = p.size;
let s = SHIP_SIZE;

let m = Matrix4f::new(
Vector4f::new(-s * d_y, s * d_x, 0.0, 0.0),


+ 4
- 4
space-crush-app/src/systems/fleet_info_update.rs View File

@@ -10,7 +10,7 @@ use specs::{
World, WriteStorage,
};

use crate::{components::FleetInfo, resources::PlayerState, Error};
use crate::{components::FleetInfo, resources::PlayerState};

pub struct FleetInfoUpdate {
modified: HashMap<Index, Modified>,
@@ -26,7 +26,7 @@ struct Modified {
}

impl FleetInfoUpdate {
pub fn new(world: &mut World) -> Result<Self, Error> {
pub fn new(world: &mut World) -> Self {
let modified = HashMap::new();
let need_update = BitSet::new();

@@ -50,12 +50,12 @@ impl FleetInfoUpdate {
.register_reader()
};

Ok(Self {
Self {
modified,
need_update,
fleet_owned_id,
player_owned_id,
})
}
}
}



+ 1
- 1
space-crush-common/src/components/mod.rs View File

@@ -10,6 +10,6 @@ pub use asteroid::{Asteroid, Type as AsteroidType};
pub use fleet::{Fleet, Owned as FleetOwned};
pub use planet::Planet;
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 velocity::Velocity;

+ 26
- 2
space-crush-common/src/components/position.rs View File

@@ -2,12 +2,36 @@ use glc::vector::Vector2f;
use serde::{Deserialize, Serialize};
use specs::{Component, VecStorage};

use crate::misc::FlaggedStorage;

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Position {
pub pos: Vector2f,
pub size: f32,
pub shape: Shape,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Shape {
Dot,
Circle(f32),
}

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
}
}

+ 4
- 2
space-crush-common/src/dispatcher.rs View File

@@ -2,8 +2,8 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt};

use crate::{
components::Player,
resources::Global,
systems::{Fleets, Movement, Process},
resources::{Global, Raster},
systems::{Fleets, Movement, Process, RasterUpdate},
};

pub struct Dispatcher<'a, 'b> {
@@ -13,6 +13,7 @@ pub struct Dispatcher<'a, 'b> {
impl<'a, 'b> Dispatcher<'a, 'b> {
pub fn new(world: &mut World) -> Self {
world.insert(Global::default());
world.insert(Raster::new(250.0));

world.register::<Player>();

@@ -20,6 +21,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> {
.with(Process::default(), "process", &[])
.with(Movement::default(), "movement", &[])
.with(Fleets::default(), "fleets", &[])
.with(RasterUpdate::new(world), "raster_update", &[])
.build();
dispatcher.setup(world);



+ 2
- 0
space-crush-common/src/resources/mod.rs View File

@@ -1,3 +1,5 @@
mod global;
mod raster;

pub use global::Global;
pub use raster::Raster;

+ 210
- 0
space-crush-common/src/resources/raster.rs View File

@@ -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]);
}
}

+ 2
- 0
space-crush-common/src/systems/mod.rs View File

@@ -1,7 +1,9 @@
mod fleets;
mod movement;
mod process;
mod raster_update;

pub use fleets::Fleets;
pub use movement::Movement;
pub use process::Process;
pub use raster_update::RasterUpdate;

+ 4
- 6
space-crush-common/src/systems/movement.rs View File

@@ -1,6 +1,6 @@
#![allow(dead_code)]

use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage};
use specs::{prelude::*, Read, ReadStorage, System, WriteStorage};

use crate::{
components::{Position, Velocity},
@@ -27,10 +27,8 @@ impl<'a> System<'a> for Movement {
global,
} = 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;
}
}
}

+ 74
- 0
space-crush-common/src/systems/raster_update.rs View File

@@ -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);
}
}
}

Loading…
Cancel
Save