Browse Source

Implemented fleet component and needed systems

raster
Bergmann89 3 years ago
parent
commit
b750209346
35 changed files with 1087 additions and 502 deletions
  1. +3
    -0
      Cargo.lock
  2. +1
    -1
      glc/Cargo.toml
  3. +130
    -0
      glc/src/angle.rs
  4. +3
    -0
      glc/src/lib.rs
  5. +61
    -0
      glc/src/math.rs
  6. +79
    -42
      glc/src/matrix.rs
  7. +216
    -0
      glc/src/numeric.rs
  8. +75
    -259
      glc/src/vector.rs
  9. +1
    -0
      space-crush-app/Cargo.toml
  10. +1
    -1
      space-crush-app/config.json
  11. +9
    -0
      space-crush-app/resources/shader/line/frag.glsl
  12. +9
    -0
      space-crush-app/resources/shader/line/vert.glsl
  13. +1
    -1
      space-crush-app/resources/shader/planet/vert.glsl
  14. +1
    -1
      space-crush-app/resources/shader/ship/vert.glsl
  15. +0
    -143
      space-crush-app/resources/world.json
  16. +63
    -0
      space-crush-app/src/debug/fleets.rs
  17. +7
    -0
      space-crush-app/src/debug/mod.rs
  18. +46
    -0
      space-crush-app/src/debug/ships.rs
  19. +33
    -7
      space-crush-app/src/debug/summary.rs
  20. +15
    -6
      space-crush-app/src/lib.rs
  21. +25
    -13
      space-crush-app/src/main.rs
  22. +7
    -2
      space-crush-app/src/render/init.rs
  23. +0
    -2
      space-crush-app/src/render/mod.rs
  24. +1
    -1
      space-crush-app/src/resources/config.rs
  25. +64
    -18
      space-crush-app/src/resources/geometry.rs
  26. +2
    -0
      space-crush-common/Cargo.toml
  27. +12
    -0
      space-crush-common/src/components/fleet.rs
  28. +2
    -0
      space-crush-common/src/components/mod.rs
  29. +72
    -2
      space-crush-common/src/components/ship.rs
  30. +18
    -0
      space-crush-common/src/constants.rs
  31. +2
    -1
      space-crush-common/src/dispatcher.rs
  32. +1
    -0
      space-crush-common/src/lib.rs
  33. +2
    -2
      space-crush-common/src/misc/persistence.rs
  34. +123
    -0
      space-crush-common/src/systems/fleets.rs
  35. +2
    -0
      space-crush-common/src/systems/mod.rs

+ 3
- 0
Cargo.lock View File

@@ -1514,6 +1514,7 @@ dependencies = [
"log",
"log4rs",
"ordered-float 2.0.1",
"rand",
"serde",
"serde_json",
"shred",
@@ -1529,8 +1530,10 @@ name = "space-crush-common"
version = "0.1.0"
dependencies = [
"glc",
"lazy_static",
"log",
"log4rs",
"rand",
"serde",
"serde_yaml",
"shred",


+ 1
- 1
glc/Cargo.toml View File

@@ -10,5 +10,5 @@ default = [ ]
[dependencies]
gl = { version = "0.1", features = [ "generate_global" ] }
imagefmt = "4.0"
serde = { version = "1.0", optional = true }
serde = { version = "1.0", optional = true, features = [ "derive" ] }
thiserror = "1.0"

+ 130
- 0
glc/src/angle.rs View File

@@ -0,0 +1,130 @@
use std::ops::{Add, Div, Mul, Neg, Sub};

use serde::{Deserialize, Serialize};

pub use super::numeric::{Float, Numeric};

/* Angle */

#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Angle<T> {
Deg(T),
Rad(T),
}

impl<T> Angle<T>
where
T: Numeric,
{
pub fn into_inner(self) -> T {
match self {
Self::Deg(v) => v,
Self::Rad(v) => v,
}
}

pub fn into_deg(self) -> Self {
match self {
Self::Deg(v) => Self::Deg(v),
Self::Rad(v) => Self::Rad(T::to_degrees(v)),
}
}

pub fn into_rad(self) -> Self {
match self {
Self::Deg(v) => Self::Deg(T::to_radians(v)),
Self::Rad(v) => Self::Rad(v),
}
}

pub fn abs(self) -> Self {
match self {
Self::Deg(value) => Self::Deg(value.abs()),
Self::Rad(value) => Self::Rad(value.abs()),
}
}
}

impl<T> Angle<T>
where
T: Float,
{
pub fn sin(self) -> T {
T::sin(self.into_rad().into_inner())
}

pub fn cos(self) -> T {
T::cos(self.into_rad().into_inner())
}
}

impl<T> Neg for Angle<T>
where
T: Neg + Numeric,
{
type Output = Angle<T>;

fn neg(self) -> Self::Output {
match self {
Self::Deg(value) => Self::Deg(-value),
Self::Rad(value) => Self::Rad(-value),
}
}
}

impl<T> Add for Angle<T>
where
T: Add<T, Output = T> + Numeric,
{
type Output = Angle<T>;

fn add(self, v: Self) -> Self::Output {
match self {
Self::Deg(value) => Self::Deg(value + v.into_deg().into_inner()),
Self::Rad(value) => Self::Rad(value + v.into_rad().into_inner()),
}
}
}

impl<T> Sub for Angle<T>
where
T: Sub<T, Output = T> + Numeric,
{
type Output = Angle<T>;

fn sub(self, v: Self) -> Self::Output {
match self {
Self::Deg(value) => Self::Deg(value - v.into_deg().into_inner()),
Self::Rad(value) => Self::Rad(value - v.into_rad().into_inner()),
}
}
}

impl<T> Mul<T> for Angle<T>
where
T: Mul<T, Output = T> + Numeric,
{
type Output = Angle<T>;

fn mul(self, factor: T) -> Self::Output {
match self {
Self::Deg(value) => Self::Deg(value * factor),
Self::Rad(value) => Self::Rad(value * factor),
}
}
}

impl<T> Div<T> for Angle<T>
where
T: Div<T, Output = T> + Numeric,
{
type Output = Angle<T>;

fn div(self, factor: T) -> Self::Output {
match self {
Self::Deg(value) => Self::Deg(value / factor),
Self::Rad(value) => Self::Rad(value / factor),
}
}
}

+ 3
- 0
glc/src/lib.rs View File

@@ -1,7 +1,10 @@
pub mod angle;
pub mod array_buffer;
pub mod error;
pub mod math;
pub mod matrix;
pub mod misc;
pub mod numeric;
pub mod shader;
pub mod texture;
pub mod vector;


+ 61
- 0
glc/src/math.rs View File

@@ -0,0 +1,61 @@
use std::cmp::PartialOrd;
use std::ops::{Div, Mul, Sub};

#[inline]
pub fn min<T>(a: T, b: T) -> T
where
T: PartialOrd,
{
if a.lt(&b) {
a
} else {
b
}
}

#[inline]
pub fn max<T>(a: T, b: T) -> T
where
T: PartialOrd,
{
if a.gt(&b) {
a
} else {
b
}
}

#[inline]
pub fn sqr<T>(a: T) -> T
where
T: Mul<T, Output = T> + Copy,
{
a * a
}

#[inline]
pub fn clamp<T>(low: T, high: T, value: T) -> T
where
T: PartialOrd,
{
min(max(value, low), high)
}

#[inline]
pub fn linear_step<T>(low: T, high: T, value: T) -> T
where
T: PartialOrd + Copy + Sub<T, Output = T> + Div<T, Output = T>,
{
clamp(low, high, (value - low) / (high - low))
}

#[inline]
pub fn smooth_step<T>(low: T, high: T, value: T) -> T
where
T: PartialOrd + Copy + Sub<T, Output = T> + Div<T, Output = T> + Mul<T, Output = T>,
f32: Mul<T, Output = T> + Sub<T, Output = T>,
{
let x = linear_step(low, high, value);

x * x * (3.0 - 2.0 * x)
}

+ 79
- 42
glc/src/matrix.rs View File

@@ -7,11 +7,17 @@ use std::ops::{Deref, DerefMut, Mul};

#[cfg(feature = "serde")]
use serde::{
de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor},
ser::{Serialize, SerializeStruct, Serializer},
de::{Deserializer, Error, MapAccess, SeqAccess, Visitor},
ser::{SerializeStruct, Serializer},
Deserialize, Serialize,
};

use super::vector::{Element, Vector2, Vector3, Vector4};
use super::vector::{Vector2, Vector3, Vector4};

pub use super::{
angle::Angle,
numeric::{Float, Numeric},
};

macro_rules! first_ptr {
($this:ident, $first:ident $(, $other:ident)*) => {
@@ -39,7 +45,7 @@ macro_rules! define_mat {

impl<T> Default for $Name<T>
where
T: Element
T: Numeric
{
#[inline]
fn default() -> Self {
@@ -248,7 +254,7 @@ pub type Matrix4i = Matrix4<gl::GLint>;

impl<T> Matrix2<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn identity() -> Self {
@@ -263,7 +269,7 @@ where

impl<T> Matrix3<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn identity() -> Self {
@@ -277,6 +283,18 @@ where
)
}

#[inline]
pub fn translate(v: Vector2<T>) -> Self {
let one = T::one();
let zro = T::zero();

Self::new(
Vector3::new(one, zro, zro),
Vector3::new(zro, one, zro),
Vector3::new(v.x, v.y, one),
)
}

#[inline]
pub fn determinant(&self) -> T {
let m = self;
@@ -286,13 +304,60 @@ where
- m[1][0] * m[0][1] * m[2][2]
- m[0][0] * m[2][1] * m[1][2]
}

#[inline]
pub fn multiply(&self, other: &Self) -> Self {
let m1 = self;
let m2 = other;

macro_rules! mul {
($x:tt, $y:tt) => {
m1[0][$y] * m2[$x][0] + m1[1][$y] * m2[$x][1] + m1[2][$y] * m2[$x][2]
};
}

Self::new(
Vector3::new(mul!(0, 0), mul!(0, 1), mul!(0, 2)),
Vector3::new(mul!(1, 0), mul!(1, 1), mul!(1, 2)),
Vector3::new(mul!(2, 0), mul!(2, 1), mul!(2, 2)),
)
}
}

impl<T> Matrix3<T>
where
T: Float,
{
#[inline]
pub fn rotate(a: Angle<T>) -> Self {
let one = T::one();
let zro = T::zero();

Self::new(
Vector3::new(a.cos(), -a.sin(), zro),
Vector3::new(a.sin(), a.cos(), zro),
Vector3::new(zro, zro, one),
)
}
}

impl<T, M> Mul<M> for Matrix3<T>
where
T: Float,
M: Borrow<Matrix3<T>>,
{
type Output = Self;

fn mul(self, rhs: M) -> Self::Output {
self.multiply(rhs.borrow())
}
}

/* Matrix4 */

impl<T> Matrix4<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn identity() -> Self {
@@ -306,7 +371,12 @@ where
Vector4::new(zero, zero, zero, one),
)
}
}

impl<T> Matrix4<T>
where
T: Float,
{
#[inline]
pub fn translate(v: Vector3<T>) -> Self {
let one = T::one();
@@ -504,7 +574,7 @@ where

impl<T> Matrix4<T>
where
T: Element<AsFloat = T>,
T: Float,
{
#[inline]
pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
@@ -552,7 +622,7 @@ where

impl<T, M> Mul<M> for Matrix4<T>
where
T: Element,
T: Float,
M: Borrow<Matrix4<T>>,
{
type Output = Self;
@@ -562,39 +632,6 @@ where
}
}

/* Angle */

pub enum Angle<T> {
Deg(T),
Rad(T),
}

impl<T> Angle<T>
where
T: Element,
{
pub fn into_inner(self) -> T {
match self {
Self::Deg(v) => v,
Self::Rad(v) => v,
}
}

pub fn into_deg(self) -> Self {
match self {
Self::Deg(v) => Self::Deg(v),
Self::Rad(v) => Self::Rad(T::to_degrees(v)),
}
}

pub fn into_rad(self) -> Self {
match self {
Self::Deg(v) => Self::Deg(T::to_radians(v)),
Self::Rad(v) => Self::Rad(v),
}
}
}

#[cfg(test)]
mod tests {
use super::*;


+ 216
- 0
glc/src/numeric.rs View File

@@ -0,0 +1,216 @@
use std::ops::{Add, Div, Mul, Neg, Sub};

/* Numeric */

pub trait Numeric:
Sized
+ Neg<Output = Self>
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ Copy
{
fn one() -> Self;
fn zero() -> Self;
fn abs(self) -> Self;
fn sqrt(self) -> Self;
fn is_zero(&self) -> bool;
fn to_degrees(self) -> Self;
fn to_radians(self) -> Self;
}

pub trait Float: Numeric {
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
fn asin(self) -> Self;
fn acos(self) -> Self;
fn atan(self) -> Self;
fn atan2(a: Self, b: Self) -> Self;
}

impl Numeric for gl::GLfloat {
#[inline]
fn one() -> Self {
1.0
}

#[inline]
fn zero() -> Self {
0.0
}

#[inline]
fn abs(self) -> Self {
self.abs()
}

#[inline]
fn sqrt(self) -> Self {
f32::sqrt(self)
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<f32>::eq(self, &0.0)
}

#[inline]
fn to_degrees(self) -> Self {
f32::to_degrees(self)
}

#[inline]
fn to_radians(self) -> Self {
f32::to_radians(self)
}
}

impl Float for gl::GLfloat {
#[inline]
fn sin(self) -> Self {
f32::sin(self)
}

#[inline]
fn cos(self) -> Self {
f32::cos(self)
}

#[inline]
fn tan(self) -> Self {
f32::tan(self)
}

#[inline]
fn asin(self) -> Self {
f32::acos(self)
}

#[inline]
fn acos(self) -> Self {
f32::acos(self)
}

#[inline]
fn atan(self) -> Self {
f32::atan(self)
}

#[inline]
fn atan2(a: Self, b: Self) -> Self {
f32::atan2(a, b)
}
}

impl Numeric for gl::GLdouble {
#[inline]
fn one() -> Self {
1.0
}

#[inline]
fn zero() -> Self {
0.0
}

#[inline]
fn abs(self) -> Self {
self.abs()
}

#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self)
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<f64>::eq(self, &0.0)
}

#[inline]
fn to_degrees(self) -> Self {
f64::to_degrees(self)
}

#[inline]
fn to_radians(self) -> Self {
f64::to_radians(self)
}
}

impl Float for gl::GLdouble {
#[inline]
fn sin(self) -> Self {
f64::sin(self)
}

#[inline]
fn cos(self) -> Self {
f64::cos(self)
}

#[inline]
fn tan(self) -> Self {
f64::tan(self)
}

#[inline]
fn asin(self) -> Self {
f64::acos(self)
}

#[inline]
fn acos(self) -> Self {
f64::acos(self)
}

#[inline]
fn atan(self) -> Self {
f64::atan(self)
}

#[inline]
fn atan2(a: Self, b: Self) -> Self {
f64::atan2(a, b)
}
}

impl Numeric for gl::GLint {
#[inline]
fn one() -> Self {
1
}

#[inline]
fn zero() -> Self {
0
}

#[inline]
fn abs(self) -> Self {
self.abs()
}

#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self as f64) as i32
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<i32>::eq(self, &0)
}

#[inline]
fn to_degrees(self) -> Self {
f32::to_degrees(self as f32) as i32
}

#[inline]
fn to_radians(self) -> Self {
f32::to_radians(self as f32) as i32
}
}

+ 75
- 259
glc/src/vector.rs View File

@@ -2,7 +2,7 @@

use std::convert::{AsMut, AsRef};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::ops::{Add, Deref, DerefMut, Div, Mul, Neg, Sub};
use std::ops::{Add, Deref, DerefMut, Mul, Sub};

#[cfg(feature = "serde")]
use serde::{
@@ -10,6 +10,11 @@ use serde::{
ser::{Serialize, Serializer},
};

pub use super::{
angle::Angle,
numeric::{Float, Numeric},
};

macro_rules! first_ptr {
($this:ident, $first:ident $(, $other:ident)*) => {
unsafe { &$this.$first }
@@ -214,7 +219,7 @@ pub type Vector4i = Vector4<gl::GLint>;

impl<T> Vector2<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn multiply(mut self, v: T) -> Self {
@@ -224,9 +229,14 @@ where
self
}

#[inline]
pub fn length_sqr(&self) -> T {
self.x * self.x + self.y * self.y
}

#[inline]
pub fn length(&self) -> T {
Element::sqrt(self.x * self.x + self.y * self.y)
Numeric::sqrt(self.length_sqr())
}

#[inline]
@@ -243,28 +253,33 @@ where
pub fn scalar(&self, other: &Self) -> T {
self.x * other.x + self.y * other.y
}
}

impl<T> Vector2<T>
where
T: Float,
{
#[inline]
pub fn angle(&self, other: &Self) -> T::AsFloat {
pub fn angle(&self, other: &Self) -> Angle<T> {
let s = self.scalar(other);
let l1 = self.length();
let l2 = self.length();

Element::acos(s / (l1 + l2))
Angle::Rad(T::acos(s / (l1 + l2)))
}

#[inline]
pub fn angle2(&self, other: &Self) -> T::AsFloat {
T::atan2(
pub fn angle2(&self, other: &Self) -> Angle<T> {
Angle::Rad(T::atan2(
other.x * self.y - other.y * self.x,
other.x * self.x + other.y * self.y,
)
))
}
}

impl<T> Add for Vector2<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -279,7 +294,7 @@ where

impl<T> Sub for Vector2<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -294,7 +309,7 @@ where

impl<T> Mul for Vector2<T>
where
T: Element,
T: Numeric,
{
type Output = T;

@@ -306,7 +321,7 @@ where

impl<T> Mul<T> for Vector2<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -320,7 +335,7 @@ where

impl<T> Vector3<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn multiply(mut self, v: T) -> Self {
@@ -331,9 +346,14 @@ where
self
}

#[inline]
pub fn length_sqr(&self) -> T {
self.x * self.x + self.y * self.y + self.z * self.z
}

#[inline]
pub fn length(&self) -> T {
Element::sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
Numeric::sqrt(self.length_sqr())
}

#[inline]
@@ -360,20 +380,25 @@ where
z: self.x * other.y - self.y * other.x,
}
}
}

impl<T> Vector3<T>
where
T: Float,
{
#[inline]
pub fn angle(&self, other: &Self) -> T::AsFloat {
pub fn angle(&self, other: &Self) -> Angle<T> {
let s = self.scalar(other);
let l1 = self.length();
let l2 = self.length();

Element::acos(s / (l1 + l2))
Angle::Rad(T::acos(s / (l1 + l2)))
}
}

impl<T> Add for Vector3<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -389,7 +414,7 @@ where

impl<T> Sub for Vector3<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -405,7 +430,7 @@ where

impl<T> Mul for Vector3<T>
where
T: Element,
T: Numeric,
{
type Output = T;

@@ -417,7 +442,7 @@ where

impl<T> Mul<T> for Vector3<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -431,7 +456,7 @@ where

impl<T> Vector4<T>
where
T: Element,
T: Numeric,
{
#[inline]
pub fn multiply(mut self, v: T) -> Self {
@@ -442,6 +467,11 @@ where
self
}

#[inline]
pub fn length_sqr(self) -> T {
self.xyz().length_sqr()
}

#[inline]
pub fn length(self) -> T {
self.xyz().length()
@@ -451,20 +481,20 @@ where
pub fn normalize(mut self) -> Self {
let len = self.length();

if unsafe { !Element::is_zero(&self.w) } {
if unsafe { !Numeric::is_zero(&self.w) } {
self.x = self.x / self.w;
self.y = self.y / self.w;
self.z = self.z / self.w;
} else {
self.x = Element::zero();
self.y = Element::zero();
self.z = Element::zero();
self.x = Numeric::zero();
self.y = Numeric::zero();
self.z = Numeric::zero();
}

self.x = self.x / len;
self.y = self.y / len;
self.z = self.z / len;
self.w = Element::one();
self.w = Numeric::one();

self
}
@@ -476,21 +506,16 @@ where

#[inline]
pub fn cross(&self, other: &Self) -> Self {
(self.xyz().cross(&other.xyz()), Element::one()).into()
}

#[inline]
pub fn angle(&self, other: &Self) -> T::AsFloat {
self.xyz().angle(&other.xyz())
(self.xyz().cross(&other.xyz()), Numeric::one()).into()
}

#[inline]
pub fn xyz(self) -> Vector3<T> {
if unsafe { Element::is_zero(&self.w) } {
if unsafe { Numeric::is_zero(&self.w) } {
Vector3 {
x: Element::zero(),
y: Element::zero(),
z: Element::zero(),
x: Numeric::zero(),
y: Numeric::zero(),
z: Numeric::zero(),
}
} else {
Vector3 {
@@ -502,9 +527,19 @@ where
}
}

impl<T> Vector4<T>
where
T: Float,
{
#[inline]
pub fn angle(&self, other: &Self) -> Angle<T> {
self.xyz().angle(&other.xyz())
}
}

impl<T> Add for Vector4<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -516,7 +551,7 @@ where

impl<T> Sub for Vector4<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -528,7 +563,7 @@ where

impl<T> Mul for Vector4<T>
where
T: Element,
T: Numeric,
{
type Output = T;

@@ -539,7 +574,7 @@ where

impl<T> Mul<T> for Vector4<T>
where
T: Element,
T: Numeric,
{
type Output = Self;

@@ -560,225 +595,6 @@ impl<T> From<(Vector3<T>, T)> for Vector4<T> {
}
}

/* Element */

pub trait Element:
Sized
+ Neg<Output = Self>
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ Copy
{
type AsFloat: Element;

fn one() -> Self;
fn zero() -> Self;
fn sin(self) -> Self;
fn cos(self) -> Self;
fn sqrt(self) -> Self;
fn tan(self) -> Self::AsFloat;
fn acos(self) -> Self::AsFloat;
fn atan(self) -> Self::AsFloat;
fn atan2(a: Self, b: Self) -> Self::AsFloat;
fn is_zero(&self) -> bool;
fn to_degrees(self) -> Self;
fn to_radians(self) -> Self;
}

impl Element for gl::GLfloat {
type AsFloat = f32;

#[inline]
fn one() -> Self {
1.0
}

#[inline]
fn zero() -> Self {
0.0
}

#[inline]
fn sin(self) -> Self {
f32::sin(self)
}

#[inline]
fn cos(self) -> Self {
f32::cos(self)
}

#[inline]
fn sqrt(self) -> Self {
f32::sqrt(self)
}

#[inline]
fn tan(self) -> Self::AsFloat {
f32::tan(self)
}

#[inline]
fn acos(self) -> Self::AsFloat {
f32::acos(self)
}

#[inline]
fn atan(self) -> Self::AsFloat {
f32::atan(self)
}

#[inline]
fn atan2(a: Self, b: Self) -> Self::AsFloat {
f32::atan2(a, b)
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<f32>::eq(self, &0.0)
}

#[inline]
fn to_degrees(self) -> Self {
f32::to_degrees(self)
}

#[inline]
fn to_radians(self) -> Self {
f32::to_radians(self)
}
}

impl Element for gl::GLdouble {
type AsFloat = f64;

#[inline]
fn one() -> Self {
1.0
}

#[inline]
fn zero() -> Self {
0.0
}

#[inline]
fn sin(self) -> Self {
f64::sin(self)
}

#[inline]
fn cos(self) -> Self {
f64::cos(self)
}

#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self)
}

#[inline]
fn tan(self) -> Self::AsFloat {
f64::tan(self)
}

#[inline]
fn acos(self) -> Self::AsFloat {
f64::acos(self)
}

#[inline]
fn atan(self) -> Self::AsFloat {
f64::atan(self)
}

#[inline]
fn atan2(a: Self, b: Self) -> Self::AsFloat {
f64::atan2(a, b)
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<f64>::eq(self, &0.0)
}

#[inline]
fn to_degrees(self) -> Self {
f64::to_degrees(self)
}

#[inline]
fn to_radians(self) -> Self {
f64::to_radians(self)
}
}

impl Element for gl::GLint {
type AsFloat = f32;

#[inline]
fn one() -> Self {
1
}

#[inline]
fn zero() -> Self {
0
}

#[inline]
fn sin(self) -> Self {
f32::sin(self as f32) as i32
}

#[inline]
fn cos(self) -> Self {
f32::cos(self as f32) as i32
}

#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self as f64) as i32
}

#[inline]
fn tan(self) -> Self::AsFloat {
f32::tan(self as f32)
}

#[inline]
fn acos(self) -> Self::AsFloat {
f32::acos(self as f32)
}

#[inline]
fn atan(self) -> Self::AsFloat {
f32::atan(self as f32)
}

#[inline]
fn atan2(a: Self, b: Self) -> Self::AsFloat {
f32::atan2(a as f32, b as f32)
}

#[inline]
fn is_zero(&self) -> bool {
PartialEq::<i32>::eq(self, &0)
}

#[inline]
fn to_degrees(self) -> Self {
f32::to_degrees(self as f32) as i32
}

#[inline]
fn to_radians(self) -> Self {
f32::to_radians(self as f32) as i32
}
}

#[cfg(test)]
mod tests {
use super::*;


+ 1
- 0
space-crush-app/Cargo.toml View File

@@ -13,6 +13,7 @@ lazy_static = "1.4"
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] }
log4rs = "0.13"
ordered-float = "2.0"
rand = "0.7"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
shred = { version = "0.10", features = [ "shred-derive" ] }


+ 1
- 1
space-crush-app/config.json View File

@@ -25,7 +25,7 @@
"D",
"Right"
],
"speed_camera_move": 1.0,
"speed_camera_move": 500.0,
"speed_camera_zoom": 100.0
}
}

+ 9
- 0
space-crush-app/resources/shader/line/frag.glsl View File

@@ -0,0 +1,9 @@
#version 450 core

uniform vec4 uColor;

out vec4 outColor;

void main() {
outColor = uColor;
}

+ 9
- 0
space-crush-app/resources/shader/line/vert.glsl View File

@@ -0,0 +1,9 @@
#version 450 core

#pragma include ../misc/camera.glsl

in vec3 inPosition;

void main() {
gl_Position = uCamera.projection * uCamera.view * vec4(inPosition, 1.0);
}

+ 1
- 1
space-crush-app/resources/shader/planet/vert.glsl View File

@@ -13,5 +13,5 @@ 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);
gl_Position = uCamera.projection * uCamera.view * uModel * vec4(2.0 * inPosition * GLOW_SIZE_FACTOR, 1.0);
}

+ 1
- 1
space-crush-app/resources/shader/ship/vert.glsl View File

@@ -13,5 +13,5 @@ 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);
gl_Position = uCamera.projection * uCamera.view * uModel * vec4(2.0 * inPosition * GLOW_SIZE_FACTOR, 1.0);
}

+ 0
- 143
space-crush-app/resources/world.json View File

@@ -1,143 +0,0 @@
[
{
"marker": [
0
],
"components": [
{
"pos": [
0.0,
0.0
],
"size": 500.0
},
null,
{},
null,
{
"owner": [
4
]
},
null,
null
]
},
{
"marker": [
1
],
"components": [
{
"pos": [
250.0,
250.0
],
"size": 30.0
},
{
"dir": [
-0.70710677,
0.70710677
],
"speed": 0.0
},
null,
{
"type_": "Fighter"
},
{
"owner": [
4
]
},
null,
null
]
},
{
"marker": [
2
],
"components": [
{
"pos": [
-250.0,
250.0
],
"size": 30.0
},
{
"dir": [
0.0,
1.0
],
"speed": 0.0
},
null,
{
"type_": "Bomber"
},
{
"owner": [
4
]
},
null,
null
]
},
{
"marker": [
3
],
"components": [
{
"pos": [
250.0,
-250.0
],
"size": 30.0
},
{
"dir": [
0.70710677,
0.70710677
],
"speed": 0.0
},
null,
{
"type_": "Transporter"
},
{
"owner": [
4
]
},
null,
null
]
},
{
"marker": [
4
],
"components": [
null,
null,
null,
null,
null,
{},
{
"color": [
0.0,
0.5,
1.0,
0.1
]
}
]
}
]

+ 63
- 0
space-crush-app/src/debug/fleets.rs View File

@@ -0,0 +1,63 @@
use glc::vector::{Angle, Vector2f, Vector4f};
use space_crush_common::{
components::{Fleet, Position},
constants::SHIP_ORBIT_DISTANCE_MAX,
};
use specs::{prelude::*, ReadStorage, System, World, WriteExpect};

use crate::resources::Geometry;

#[derive(Default)]
pub struct Fleets;

#[derive(SystemData)]
pub struct FleetData<'a> {
geometry: WriteExpect<'a, Geometry>,
position: ReadStorage<'a, Position>,
fleet: ReadStorage<'a, Fleet>,
}

impl<'a> System<'a> for Fleets {
type SystemData = FleetData<'a>;

fn run(&mut self, data: Self::SystemData) {
let FleetData {
mut geometry,
position,
fleet,
} = data;

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE);

for (p, f) in (&position, &fleet).join() {
geometry.render_lines(
Vector4f::new(0.5, 0.5, 0.5, 0.2),
&create_circle(p.pos, f.orbit_min),
);
geometry.render_lines(
Vector4f::new(0.5, 0.5, 0.5, 0.2),
&create_circle(p.pos, f.orbit_max),
);
geometry.render_lines(
Vector4f::new(0.5, 0.5, 0.5, 0.1),
&create_circle(p.pos, SHIP_ORBIT_DISTANCE_MAX * f.orbit_max),
);
}

gl::disable(gl::BLEND);
}
}

fn create_circle(p: Vector2f, r: f32) -> Vec<Vector2f> {
let mut points = Vec::new();

for i in 0..=180 {
points.push(Vector2f::new(
p.x + r * Angle::Deg(i as f32 * 2.0).cos(),
p.y + r * Angle::Deg(i as f32 * 2.0).sin(),
));
}

points
}

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

@@ -0,0 +1,7 @@
mod fleets;
mod ships;
mod summary;

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

+ 46
- 0
space-crush-app/src/debug/ships.rs View File

@@ -0,0 +1,46 @@
use glc::vector::Vector4f;
use space_crush_common::components::{Position, Ship, Velocity};
use specs::{prelude::*, ReadStorage, System, World, WriteExpect};

use crate::resources::Geometry;

#[derive(Default)]
pub struct Ships;

#[derive(SystemData)]
pub struct ShipsData<'a> {
geometry: WriteExpect<'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 {
mut geometry,
position,
velocity,
ship,
} = data;

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE);

for (p, v, s) in (&position, &velocity, &ship).join() {
geometry.render_lines(
Vector4f::new(0.0, 0.0, 1.0, 0.2),
&[p.pos, p.pos + v.dir * v.speed],
);
geometry.render_lines(
Vector4f::new(1.0, 0.0, 0.0, 0.2),
&[p.pos, p.pos + s.target_dir * 100.0],
);
geometry.render_lines(Vector4f::new(1.0, 1.0, 1.0, 0.2), &[p.pos, s.target_pos]);
}

gl::disable(gl::BLEND);
}
}

space-crush-app/src/render/debug.rs → space-crush-app/src/debug/summary.rs View File

@@ -1,7 +1,7 @@
use std::string::ToString;

use space_crush_common::{misc::LogResult, resources::Global};
use specs::{ReadExpect, System};
use specs::{prelude::*, Entities, ReadExpect, System};

use crate::{
misc::{Text, TextCache, TextManager},
@@ -9,17 +9,18 @@ use crate::{
Error,
};

/* Debug */
/* Summary */

pub struct Debug {
pub struct Summary {
cache: TextCache,
text: Text,
fps: usize,
resolution: (u32, u32),
mouse_pos: (f32, f32),
entity_count: usize,
}

impl Debug {
impl Summary {
pub fn new(text_manager: &TextManager) -> Result<Self, Error> {
let cache = text_manager.create_cache()?;
let text = cache
@@ -35,6 +36,8 @@ impl Debug {
.text("1280 | 720")
.text("\nmouse_pos: ")
.text("0.00 | 0.00")
.text("\nentities: ")
.text("0")
.build()?;

Ok(Self {
@@ -43,14 +46,30 @@ impl Debug {
fps: 0,
resolution: (0, 0),
mouse_pos: (0.0, 0.0),
entity_count: 0,
})
}
}

impl<'a> System<'a> for Debug {
type SystemData = (ReadExpect<'a, Global>, ReadExpect<'a, State>);
#[derive(SystemData)]
pub struct SummaryData<'a> {
entities: Entities<'a>,
global: ReadExpect<'a, Global>,
state: ReadExpect<'a, State>,
}

impl<'a> System<'a> for Summary {
type SystemData = SummaryData<'a>;

fn run(&mut self, data: Self::SystemData) {
let SummaryData {
entities,
global,
state,
} = data;

let entity_count = entities.par_join().count();

fn run(&mut self, (global, state): Self::SystemData) {
let guard = self.cache.begin_update();
update_text(
&mut self.text,
@@ -73,6 +92,13 @@ impl<'a> System<'a> for Debug {
&state.mouse_pos,
|(x, y)| format!("{:.2} | {:.2}", x, y),
);
update_text(
&mut self.text,
8,
&mut self.entity_count,
&entity_count,
|x| format!("{}", x),
);
drop(guard);

self.text.render(true);

+ 15
- 6
space-crush-app/src/lib.rs View File

@@ -1,4 +1,5 @@
mod constants;
mod debug;
mod error;
mod misc;
mod render;
@@ -9,8 +10,9 @@ use specs::{Dispatcher, DispatcherBuilder, World};

pub use error::Error;

use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary};
use misc::{Events, TextManager, Window};
use render::{Debug, Init, Planets, Ships};
use render::{Init, Planets, Ships};
use resources::{Camera, Config, Geometry, State, Uniform};
use systems::StateUpdate;

@@ -27,11 +29,16 @@ impl<'a, 'b> App<'a, 'b> {
let events = Events::new(world);
let window = Window::new(events.handle(), &config)?;

let state = State::default();
let camera = Camera::new()?;
let uniform = Uniform::new()?;
let geometry = Geometry::new(world)?;

world.insert(state);
world.insert(config);
world.insert(Camera::new()?);
world.insert(Uniform::new()?);
world.insert(Geometry::new()?);
world.insert(State::default());
world.insert(camera);
world.insert(uniform);
world.insert(geometry);

let text_manager = TextManager::new(world)?;

@@ -40,7 +47,9 @@ impl<'a, 'b> App<'a, 'b> {
.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)?)
.with_thread_local(DebugShips::default())
.with_thread_local(DebugFleets::default())
.with_thread_local(DebugSummary::new(&text_manager)?)
.build();
dispatcher.setup(world);



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

@@ -1,4 +1,5 @@
use log::{error, info};
use rand::random;
use space_crush_app::{App, Error};
use space_crush_common::{
misc::{init_logger, Vfs},
@@ -50,9 +51,12 @@ fn run(vfs: Vfs) -> Result<(), Error> {
}

fn create_world(world: &mut World) {
use glc::vector::{Vector2f, Vector4f};
use glc::{
matrix::Angle,
vector::{Vector2f, Vector4f},
};
use space_crush_common::{
components::{Owned, Planet, Player, Position, Ship, ShipType, Velocity},
components::{Fleet, Owned, Planet, Player, Position, Ship, ShipType, Velocity},
misc::{PersistWorld, Persistence},
};
use specs::{saveload::MarkedBuilder, Builder};
@@ -64,40 +68,48 @@ fn create_world(world: &mut World) {
})
.build();

world
let planet = world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Owned { owner: player1 })
.with(Position {
pos: Vector2f::default(),
size: 500.0,
size: 250.0,
})
.with(Fleet {
orbit_min: 325.0,
orbit_max: 425.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 };

for i in 0..10 {
world
.create_entity()
.marked::<<PersistWorld as Persistence>::Marker>()
.with(Owned { owner: player1 })
.with(Position {
pos: Vector2f::new(250.0 * x, 250.0 * y),
size: 30.0,
pos: Vector2f::new(
500.0 * random::<f32>() - 250.0,
500.0 * random::<f32>() - 250.0,
),
size: 15.0,
})
.with(Velocity {
dir: Vector2f::new(i as f32 - 1.0, 1.0).normalize(),
speed: 0.0,
dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(),
speed: 100.0,
})
.with(Ship {
type_: match i {
type_: match i % 3 {
0 => ShipType::Fighter,
1 => ShipType::Bomber,
2 => ShipType::Transporter,
_ => unreachable!(),
},
fleet: planet,
agility: Angle::Deg(360.0),
target_pos: Default::default(),
target_dir: Default::default(),
})
.build();
}


+ 7
- 2
space-crush-app/src/render/init.rs View File

@@ -5,7 +5,10 @@ use glc::{
vector::Vector3f,
};
use shrev::{EventChannel, ReaderId};
use space_crush_common::misc::{LogResult, WorldHelper as _};
use space_crush_common::{
misc::{LogResult, WorldHelper as _},
resources::Global,
};
use specs::{prelude::*, ReadExpect, System, World, WriteExpect};

use crate::{
@@ -50,6 +53,7 @@ pub struct InitData<'a> {
camera: WriteExpect<'a, Camera>,
uniform: WriteExpect<'a, Uniform>,
state: ReadExpect<'a, State>,
global: ReadExpect<'a, Global>,
config: ReadExpect<'a, Config>,
geometry: ReadExpect<'a, Geometry>,
mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>,
@@ -63,6 +67,7 @@ impl<'a> System<'a> for Init {
mut camera,
mut uniform,
state,
global,
config,
geometry,
mouse_events,
@@ -102,7 +107,7 @@ impl<'a> System<'a> for Init {
let right = state.key_state(&config.input.key_camera_move_right);

if up || down || left || right {
let s = config.input.speed_camera_move;
let s = config.input.speed_camera_move * global.delta;
let translate = Vector3f::new(
if left { s } else { 0.0 } + if right { -s } else { 0.0 },
if up { -s } else { 0.0 } + if down { s } else { 0.0 },


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

@@ -1,9 +1,7 @@
mod debug;
mod init;
mod planets;
mod ships;

pub use debug::Debug;
pub use init::Init;
pub use planets::Planets;
pub use ships::Ships;

+ 1
- 1
space-crush-app/src/resources/config.rs View File

@@ -127,7 +127,7 @@ mod defaults {
}

pub fn speed_camera_move() -> f32 {
2.0
500.0
}

pub fn speed_camera_zoom() -> f32 {


+ 64
- 18
space-crush-app/src/resources/geometry.rs View File

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

use std::mem::size_of;

use glc::{
array_buffer::{ArrayBuffer, Target, Usage},
misc::Bindable,
shader::{Program, Type, Uniform},
vector::{Vector2f, Vector4f},
vertex_array::{DataType, VertexArray},
};
use space_crush_common::misc::LogResult;
use specs::World;

use crate::Error;
use crate::{constants::UNIFORM_BUFFER_INDEX_CAMERA, misc::WorldHelper, Error};

pub struct Geometry {
quad: VertexArray,
vertex_array_quad: VertexArray,
vertex_array_line: VertexArray,
program_line: Program,
location_line_color: gl::GLint,
}

impl Geometry {
pub fn new() -> Result<Self, Error> {
pub fn new(world: &mut World) -> Result<Self, Error> {
let vertex_array_quad = create_array_quad()?;
let vertex_array_line = create_array_line()?;

let program_line = world.load_program(vec![
(Type::Vertex, "resources/shader/line/vert.glsl"),
(Type::Fragment, "resources/shader/line/frag.glsl"),
])?;
program_line.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;

let location_line_color = program_line.uniform_location("uColor")?;

Ok(Self {
quad: create_quad_array()?,
vertex_array_quad,
vertex_array_line,
program_line,
location_line_color,
})
}

pub fn render_quad(&self) {
self.quad.bind();
self.vertex_array_quad.bind();

gl::draw_arrays(gl::TRIANGLE_FAN, 0, 4);

self.quad.unbind();
self.vertex_array_quad.unbind();
}
}

#[repr(C, packed)]
#[derive(Debug, Copy, Clone, PartialEq)]
struct Vertex {
pub x: f32,
pub y: f32,
pub fn render_lines(&mut self, c: Vector4f, points: &[Vector2f]) {
self.vertex_array_line.buffers_mut()[0]
.buffer_data(Usage::StaticDraw, points)
.unwrap();
self.vertex_array_line.bind();
self.program_line.bind();
self.program_line
.uniform(self.location_line_color, Uniform::Vector4f(&c))
.warn("Unable to set line color");

gl::draw_arrays(gl::LINE_STRIP, 0, points.len() as _);

self.program_line.unbind();
self.vertex_array_line.unbind();
}
}

fn create_quad_array() -> Result<VertexArray, Error> {
fn create_array_quad() -> Result<VertexArray, Error> {
let vertices = &[
Vertex { x: -0.5, y: -0.5 },
Vertex { x: 0.5, y: -0.5 },
Vertex { x: 0.5, y: 0.5 },
Vertex { x: -0.5, y: 0.5 },
Vector2f::new(-0.5, -0.5),
Vector2f::new(0.5, -0.5),
Vector2f::new(0.5, 0.5),
Vector2f::new(-0.5, 0.5),
];

const STRIDE_POS: gl::GLsizei = size_of::<Vertex>() as gl::GLsizei;
const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei;
const OFFSET_POS: gl::GLsizei = 0;

let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?;
@@ -56,3 +88,17 @@ fn create_quad_array() -> Result<VertexArray, Error> {

Ok(vertex_array)
}

fn create_array_line() -> Result<VertexArray, Error> {
const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei;
const OFFSET_POS: gl::GLsizei = 0;

let array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?;

let vertex_array = VertexArray::builder()
.bind_buffer(array_buffer)
.vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE_POS, OFFSET_POS)?
.build()?;

Ok(vertex_array)
}

+ 2
- 0
space-crush-common/Cargo.toml View File

@@ -7,7 +7,9 @@ edition = "2018"
[dependencies]
glc = { version = "0.1", features = [ "serde" ] }
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] }
lazy_static = "1.4"
log4rs = "0.13"
rand = "0.7"
serde = "1.0"
serde_yaml = "0.8"
shred = { version = "0.10", features = [ "shred-derive" ] }


+ 12
- 0
space-crush-common/src/components/fleet.rs View File

@@ -0,0 +1,12 @@
use serde::{Deserialize, Serialize};
use specs::{Component, HashMapStorage};

#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Fleet {
pub orbit_min: f32,
pub orbit_max: f32,
}

impl Component for Fleet {
type Storage = HashMapStorage<Self>;
}

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

@@ -1,3 +1,4 @@
mod fleet;
mod owned;
mod planet;
mod player;
@@ -5,6 +6,7 @@ mod position;
mod ship;
mod velocity;

pub use fleet::Fleet;
pub use owned::Owned;
pub use planet::Planet;
pub use player::Player;


+ 72
- 2
space-crush-common/src/components/ship.rs View File

@@ -1,9 +1,18 @@
use glc::{matrix::Angle, vector::Vector2f};
use serde::{Deserialize, Serialize};
use specs::{Component, VecStorage};
use specs::{
error::NoError,
saveload::{ConvertSaveload, Marker},
Component, Entity, VecStorage,
};

#[derive(Clone, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug)]
pub struct Ship {
pub type_: Type,
pub fleet: Entity,
pub agility: Angle<f32>,
pub target_pos: Vector2f,
pub target_dir: Vector2f,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -13,6 +22,67 @@ pub enum Type {
Transporter,
}

#[derive(Serialize, Deserialize)]
pub struct ShipData<M> {
pub type_: Type,
pub fleet: M,
pub agility: Angle<f32>,
pub target_pos: Vector2f,
pub target_dir: Vector2f,
}

impl Component for Ship {
type Storage = VecStorage<Self>;
}

impl<M> ConvertSaveload<M> for Ship
where
for<'de> M: Marker + Serialize + Deserialize<'de>,
{
type Data = ShipData<M>;
type Error = NoError;

fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error>
where
F: FnMut(Entity) -> Option<M>,
{
let Ship {
type_,
fleet,
agility,
target_pos,
target_dir,
} = self.clone();
let fleet = ids(fleet).unwrap();

Ok(ShipData {
type_,
fleet,
agility,
target_pos,
target_dir,
})
}

fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error>
where
F: FnMut(M) -> Option<Entity>,
{
let ShipData {
type_,
fleet,
agility,
target_pos,
target_dir,
} = data;
let fleet = ids(fleet).unwrap();

Ok(Ship {
type_,
fleet,
agility,
target_pos,
target_dir,
})
}
}

+ 18
- 0
space-crush-common/src/constants.rs View File

@@ -0,0 +1,18 @@
use glc::{matrix::Angle, vector::Vector2f};
use lazy_static::lazy_static;

/// Distance to orbit before ship is handled as "in orbit" in %
pub const SHIP_ORBIT_DISTANCE_MAX: f32 = 1.10;

/// Minimum angle between old and new target position in orbit
pub const SHIP_ORBIT_ANGLE_DELTA_MIN: Angle<f32> = Angle::Deg(7000.0);

/// Random angle between old and new target position in orbit
pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle<f32> = Angle::Deg(4000.0);

/// Agility of ships inside orbit
pub const SHIP_ORBIT_AGILITY: Angle<f32> = Angle::Deg(90.0);

lazy_static! {
pub static ref VECTOR_2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0);
}

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

@@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt};
use crate::{
components::Player,
resources::Global,
systems::{Movement, Process},
systems::{Fleets, Movement, Process},
};

pub struct Dispatcher<'a, 'b> {
@@ -19,6 +19,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> {
let mut dispatcher = DispatcherBuilder::new()
.with(Process::default(), "process", &[])
.with(Movement::default(), "movement", &[])
.with(Fleets::default(), "fleets", &[])
.build();
dispatcher.setup(world);



+ 1
- 0
space-crush-common/src/lib.rs View File

@@ -1,4 +1,5 @@
pub mod components;
pub mod constants;
pub mod dispatcher;
pub mod error;
pub mod misc;


+ 2
- 2
space-crush-common/src/misc/persistence.rs View File

@@ -5,7 +5,7 @@ use specs::{
Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage,
};

use crate::components::{Owned, Planet, Player, Position, Ship, Velocity};
use crate::components::{Fleet, Owned, Planet, Player, Position, Ship, Velocity};

/* PersistWorld */

@@ -14,7 +14,7 @@ pub struct PersistWorldMarker;

impl Persistence for PersistWorld {
type Marker = SimpleMarker<PersistWorldMarker>;
type Components = (Position, Velocity, Planet, Ship, Owned, Player);
type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet);
}

/* Persistence */


+ 123
- 0
space-crush-common/src/systems/fleets.rs View File

@@ -0,0 +1,123 @@
use glc::{
math::{linear_step, sqr},
matrix::Matrix3f,
vector::{Vector2f, Vector3f},
};
use rand::random;
use specs::{prelude::*, ParJoin, Read, ReadStorage, System, WriteStorage};

use crate::{
components::{Fleet, Position, Ship, Velocity},
constants::{
SHIP_ORBIT_AGILITY, SHIP_ORBIT_ANGLE_DELTA_MIN, SHIP_ORBIT_ANGLE_DELTA_RND,
SHIP_ORBIT_DISTANCE_MAX, VECTOR_2F_POS_X,
},
resources::Global,
};

#[derive(Default)]
pub struct Fleets;

#[derive(SystemData)]
pub struct FleetsData<'a> {
ship: WriteStorage<'a, Ship>,
velocity: WriteStorage<'a, Velocity>,
position: ReadStorage<'a, Position>,
fleet: ReadStorage<'a, Fleet>,
global: Read<'a, Global>,
}

impl<'a> System<'a> for Fleets {
type SystemData = FleetsData<'a>;

fn run(&mut self, data: Self::SystemData) {
let FleetsData {
mut ship,
mut velocity,
position,
fleet,
global,
} = data;

(&mut ship, &mut velocity, &position)
.par_join()
.for_each(|(ship, vel, pos)| {
progress_ship(&position, &fleet, ship, vel, pos, global.delta)
});
}
}

fn progress_ship<'a>(
positions: &ReadStorage<'a, Position>,
fleets: &ReadStorage<'a, Fleet>,
ship: &mut Ship,
velocity: &mut Velocity,
position: &Position,
delta: f32,
) {
let (orbit_pos, orbit_min, orbit_max): (Vector2f, f32, f32) =
match (positions.get(ship.fleet), fleets.get(ship.fleet)) {
(Some(position), Some(fleet)) => (position.pos, fleet.orbit_min, fleet.orbit_max),
(_, _) => return,
};

let orbit_to_target = ship.target_pos - orbit_pos;
let orbit_to_ship = position.pos - orbit_pos;
let ship_to_target = ship.target_pos - position.pos;

let target_radius = orbit_to_target.length_sqr();
let ship_radius = orbit_to_ship.length_sqr();

let target_in_orbit = (target_radius <= sqr(orbit_max)) && (target_radius >= sqr(orbit_min));
let ship_in_orbit = ship_radius < sqr(SHIP_ORBIT_DISTANCE_MAX * orbit_max);

/* check and update target posistion */
if ship.target_dir.length_sqr() == 0.0
|| ship_in_orbit != target_in_orbit
|| ship.target_dir * ship_to_target <= 0.0
{
if ship_in_orbit && orbit_max > 0.0 {
let orbit_to_ship_vec3 = Vector3f::new(orbit_to_ship.x, orbit_to_ship.y, 0.0);
let ship_dir_vec3 = Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0);

let dir = if orbit_to_ship_vec3.cross(&ship_dir_vec3).z > 0.0 {
1.0
} else {
-1.0
};

let angle = orbit_to_ship.angle2(&VECTOR_2F_POS_X);
let angle = angle
+ (SHIP_ORBIT_ANGLE_DELTA_MIN + SHIP_ORBIT_ANGLE_DELTA_RND * random()) * dir
/ orbit_max;
let radius = orbit_min + (orbit_max - orbit_min) * random::<f32>();

ship.target_pos.x = orbit_pos.x + radius * angle.cos();
ship.target_pos.y = orbit_pos.y + radius * angle.sin();
} else {
ship.target_pos = orbit_pos;
}
ship.target_dir = (ship.target_pos - position.pos).normalize();
}

/* update ship direction */
let angle = ship_to_target.angle2(&velocity.dir);
if angle.into_inner().abs() > 0.0001 {
let dir = angle.into_inner() / angle.abs().into_inner();
let agility = if ship_in_orbit {
SHIP_ORBIT_AGILITY
} else {
ship.agility
};
let rot_speed = agility * linear_step(0.0, 45.0, angle.abs().into_deg().into_inner());

let m = Matrix3f::new(
Vector3f::new(velocity.dir.y, -velocity.dir.x, 0.0),
Vector3f::new(velocity.dir.x, velocity.dir.y, 0.0),
Vector3f::new(0.0, 0.0, 1.0),
);
let m = m * Matrix3f::rotate(rot_speed * -dir * delta);

velocity.dir = Vector2f::new(m.axis_y.x, m.axis_y.y);
}
}

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

@@ -1,5 +1,7 @@
mod fleets;
mod movement;
mod process;

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

Loading…
Cancel
Save