From b750209346abbd7d4ca872fc9a77e58604b426d6 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Thu, 10 Dec 2020 05:21:28 +0100 Subject: [PATCH] Implemented fleet component and needed systems --- Cargo.lock | 3 + glc/Cargo.toml | 2 +- glc/src/angle.rs | 130 +++++++ glc/src/lib.rs | 3 + glc/src/math.rs | 61 ++++ glc/src/matrix.rs | 121 ++++--- glc/src/numeric.rs | 216 +++++++++++ glc/src/vector.rs | 334 ++++-------------- space-crush-app/Cargo.toml | 1 + space-crush-app/config.json | 2 +- .../resources/shader/line/frag.glsl | 9 + .../resources/shader/line/vert.glsl | 9 + .../resources/shader/planet/vert.glsl | 2 +- .../resources/shader/ship/vert.glsl | 2 +- space-crush-app/resources/world.json | 143 -------- space-crush-app/src/debug/fleets.rs | 63 ++++ space-crush-app/src/debug/mod.rs | 7 + space-crush-app/src/debug/ships.rs | 46 +++ .../src/{render/debug.rs => debug/summary.rs} | 40 ++- space-crush-app/src/lib.rs | 21 +- space-crush-app/src/main.rs | 38 +- space-crush-app/src/render/init.rs | 9 +- space-crush-app/src/render/mod.rs | 2 - space-crush-app/src/resources/config.rs | 2 +- space-crush-app/src/resources/geometry.rs | 82 ++++- space-crush-common/Cargo.toml | 2 + space-crush-common/src/components/fleet.rs | 12 + space-crush-common/src/components/mod.rs | 2 + space-crush-common/src/components/ship.rs | 74 +++- space-crush-common/src/constants.rs | 18 + space-crush-common/src/dispatcher.rs | 3 +- space-crush-common/src/lib.rs | 1 + space-crush-common/src/misc/persistence.rs | 4 +- space-crush-common/src/systems/fleets.rs | 123 +++++++ space-crush-common/src/systems/mod.rs | 2 + 35 files changed, 1087 insertions(+), 502 deletions(-) create mode 100644 glc/src/angle.rs create mode 100644 glc/src/math.rs create mode 100644 glc/src/numeric.rs create mode 100644 space-crush-app/resources/shader/line/frag.glsl create mode 100644 space-crush-app/resources/shader/line/vert.glsl delete mode 100644 space-crush-app/resources/world.json create mode 100644 space-crush-app/src/debug/fleets.rs create mode 100644 space-crush-app/src/debug/mod.rs create mode 100644 space-crush-app/src/debug/ships.rs rename space-crush-app/src/{render/debug.rs => debug/summary.rs} (70%) create mode 100644 space-crush-common/src/components/fleet.rs create mode 100644 space-crush-common/src/constants.rs create mode 100644 space-crush-common/src/systems/fleets.rs diff --git a/Cargo.lock b/Cargo.lock index c4b33a5..c346b14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/glc/Cargo.toml b/glc/Cargo.toml index d5e74b1..3029d71 100644 --- a/glc/Cargo.toml +++ b/glc/Cargo.toml @@ -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" diff --git a/glc/src/angle.rs b/glc/src/angle.rs new file mode 100644 index 0000000..f9d0bba --- /dev/null +++ b/glc/src/angle.rs @@ -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 { + Deg(T), + Rad(T), +} + +impl Angle +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 Angle +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 Neg for Angle +where + T: Neg + Numeric, +{ + type Output = Angle; + + fn neg(self) -> Self::Output { + match self { + Self::Deg(value) => Self::Deg(-value), + Self::Rad(value) => Self::Rad(-value), + } + } +} + +impl Add for Angle +where + T: Add + Numeric, +{ + type Output = Angle; + + 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 Sub for Angle +where + T: Sub + Numeric, +{ + type Output = Angle; + + 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 Mul for Angle +where + T: Mul + Numeric, +{ + type Output = Angle; + + fn mul(self, factor: T) -> Self::Output { + match self { + Self::Deg(value) => Self::Deg(value * factor), + Self::Rad(value) => Self::Rad(value * factor), + } + } +} + +impl Div for Angle +where + T: Div + Numeric, +{ + type Output = Angle; + + fn div(self, factor: T) -> Self::Output { + match self { + Self::Deg(value) => Self::Deg(value / factor), + Self::Rad(value) => Self::Rad(value / factor), + } + } +} diff --git a/glc/src/lib.rs b/glc/src/lib.rs index c11f4fa..b0b6a7c 100644 --- a/glc/src/lib.rs +++ b/glc/src/lib.rs @@ -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; diff --git a/glc/src/math.rs b/glc/src/math.rs new file mode 100644 index 0000000..ff9643f --- /dev/null +++ b/glc/src/math.rs @@ -0,0 +1,61 @@ +use std::cmp::PartialOrd; +use std::ops::{Div, Mul, Sub}; + +#[inline] +pub fn min(a: T, b: T) -> T +where + T: PartialOrd, +{ + if a.lt(&b) { + a + } else { + b + } +} + +#[inline] +pub fn max(a: T, b: T) -> T +where + T: PartialOrd, +{ + if a.gt(&b) { + a + } else { + b + } +} + +#[inline] +pub fn sqr(a: T) -> T +where + T: Mul + Copy, +{ + a * a +} + +#[inline] +pub fn clamp(low: T, high: T, value: T) -> T +where + T: PartialOrd, +{ + min(max(value, low), high) +} + +#[inline] +pub fn linear_step(low: T, high: T, value: T) -> T +where + T: PartialOrd + Copy + Sub + Div, +{ + clamp(low, high, (value - low) / (high - low)) +} + +#[inline] +pub fn smooth_step(low: T, high: T, value: T) -> T +where + T: PartialOrd + Copy + Sub + Div + Mul, + f32: Mul + Sub, +{ + let x = linear_step(low, high, value); + + x * x * (3.0 - 2.0 * x) +} diff --git a/glc/src/matrix.rs b/glc/src/matrix.rs index 62e9249..85d76df 100644 --- a/glc/src/matrix.rs +++ b/glc/src/matrix.rs @@ -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 Default for $Name where - T: Element + T: Numeric { #[inline] fn default() -> Self { @@ -248,7 +254,7 @@ pub type Matrix4i = Matrix4; impl Matrix2 where - T: Element, + T: Numeric, { #[inline] pub fn identity() -> Self { @@ -263,7 +269,7 @@ where impl Matrix3 where - T: Element, + T: Numeric, { #[inline] pub fn identity() -> Self { @@ -277,6 +283,18 @@ where ) } + #[inline] + pub fn translate(v: Vector2) -> 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 Matrix3 +where + T: Float, +{ + #[inline] + pub fn rotate(a: Angle) -> 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 Mul for Matrix3 +where + T: Float, + M: Borrow>, +{ + type Output = Self; + + fn mul(self, rhs: M) -> Self::Output { + self.multiply(rhs.borrow()) + } } /* Matrix4 */ impl Matrix4 where - T: Element, + T: Numeric, { #[inline] pub fn identity() -> Self { @@ -306,7 +371,12 @@ where Vector4::new(zero, zero, zero, one), ) } +} +impl Matrix4 +where + T: Float, +{ #[inline] pub fn translate(v: Vector3) -> Self { let one = T::one(); @@ -504,7 +574,7 @@ where impl Matrix4 where - T: Element, + 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 Mul for Matrix4 where - T: Element, + T: Float, M: Borrow>, { type Output = Self; @@ -562,39 +632,6 @@ where } } -/* Angle */ - -pub enum Angle { - Deg(T), - Rad(T), -} - -impl Angle -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::*; diff --git a/glc/src/numeric.rs b/glc/src/numeric.rs new file mode 100644 index 0000000..1544826 --- /dev/null +++ b/glc/src/numeric.rs @@ -0,0 +1,216 @@ +use std::ops::{Add, Div, Mul, Neg, Sub}; + +/* Numeric */ + +pub trait Numeric: + Sized + + Neg + + Add + + Sub + + Mul + + Div + + 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::::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::::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::::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 + } +} diff --git a/glc/src/vector.rs b/glc/src/vector.rs index 8392d4e..1103cfc 100644 --- a/glc/src/vector.rs +++ b/glc/src/vector.rs @@ -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; impl Vector2 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 Vector2 +where + T: Float, +{ #[inline] - pub fn angle(&self, other: &Self) -> T::AsFloat { + pub fn angle(&self, other: &Self) -> Angle { 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 { + Angle::Rad(T::atan2( other.x * self.y - other.y * self.x, other.x * self.x + other.y * self.y, - ) + )) } } impl Add for Vector2 where - T: Element, + T: Numeric, { type Output = Self; @@ -279,7 +294,7 @@ where impl Sub for Vector2 where - T: Element, + T: Numeric, { type Output = Self; @@ -294,7 +309,7 @@ where impl Mul for Vector2 where - T: Element, + T: Numeric, { type Output = T; @@ -306,7 +321,7 @@ where impl Mul for Vector2 where - T: Element, + T: Numeric, { type Output = Self; @@ -320,7 +335,7 @@ where impl Vector3 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 Vector3 +where + T: Float, +{ #[inline] - pub fn angle(&self, other: &Self) -> T::AsFloat { + pub fn angle(&self, other: &Self) -> Angle { 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 Add for Vector3 where - T: Element, + T: Numeric, { type Output = Self; @@ -389,7 +414,7 @@ where impl Sub for Vector3 where - T: Element, + T: Numeric, { type Output = Self; @@ -405,7 +430,7 @@ where impl Mul for Vector3 where - T: Element, + T: Numeric, { type Output = T; @@ -417,7 +442,7 @@ where impl Mul for Vector3 where - T: Element, + T: Numeric, { type Output = Self; @@ -431,7 +456,7 @@ where impl Vector4 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 { - 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 Vector4 +where + T: Float, +{ + #[inline] + pub fn angle(&self, other: &Self) -> Angle { + self.xyz().angle(&other.xyz()) + } +} + impl Add for Vector4 where - T: Element, + T: Numeric, { type Output = Self; @@ -516,7 +551,7 @@ where impl Sub for Vector4 where - T: Element, + T: Numeric, { type Output = Self; @@ -528,7 +563,7 @@ where impl Mul for Vector4 where - T: Element, + T: Numeric, { type Output = T; @@ -539,7 +574,7 @@ where impl Mul for Vector4 where - T: Element, + T: Numeric, { type Output = Self; @@ -560,225 +595,6 @@ impl From<(Vector3, T)> for Vector4 { } } -/* Element */ - -pub trait Element: - Sized - + Neg - + Add - + Sub - + Mul - + Div - + 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::::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::::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::::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::*; diff --git a/space-crush-app/Cargo.toml b/space-crush-app/Cargo.toml index 834d0e6..fc76efd 100644 --- a/space-crush-app/Cargo.toml +++ b/space-crush-app/Cargo.toml @@ -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" ] } diff --git a/space-crush-app/config.json b/space-crush-app/config.json index 7564821..f62ba44 100644 --- a/space-crush-app/config.json +++ b/space-crush-app/config.json @@ -25,7 +25,7 @@ "D", "Right" ], - "speed_camera_move": 1.0, + "speed_camera_move": 500.0, "speed_camera_zoom": 100.0 } } diff --git a/space-crush-app/resources/shader/line/frag.glsl b/space-crush-app/resources/shader/line/frag.glsl new file mode 100644 index 0000000..3f65931 --- /dev/null +++ b/space-crush-app/resources/shader/line/frag.glsl @@ -0,0 +1,9 @@ +#version 450 core + +uniform vec4 uColor; + +out vec4 outColor; + +void main() { + outColor = uColor; +} diff --git a/space-crush-app/resources/shader/line/vert.glsl b/space-crush-app/resources/shader/line/vert.glsl new file mode 100644 index 0000000..e5c5556 --- /dev/null +++ b/space-crush-app/resources/shader/line/vert.glsl @@ -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); +} diff --git a/space-crush-app/resources/shader/planet/vert.glsl b/space-crush-app/resources/shader/planet/vert.glsl index 2be4de5..d629730 100644 --- a/space-crush-app/resources/shader/planet/vert.glsl +++ b/space-crush-app/resources/shader/planet/vert.glsl @@ -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); } diff --git a/space-crush-app/resources/shader/ship/vert.glsl b/space-crush-app/resources/shader/ship/vert.glsl index 88fad03..7baf06f 100644 --- a/space-crush-app/resources/shader/ship/vert.glsl +++ b/space-crush-app/resources/shader/ship/vert.glsl @@ -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); } diff --git a/space-crush-app/resources/world.json b/space-crush-app/resources/world.json deleted file mode 100644 index 56e3180..0000000 --- a/space-crush-app/resources/world.json +++ /dev/null @@ -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 - ] - } - ] - } -] diff --git a/space-crush-app/src/debug/fleets.rs b/space-crush-app/src/debug/fleets.rs new file mode 100644 index 0000000..b87deef --- /dev/null +++ b/space-crush-app/src/debug/fleets.rs @@ -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 { + 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 +} diff --git a/space-crush-app/src/debug/mod.rs b/space-crush-app/src/debug/mod.rs new file mode 100644 index 0000000..3b244c0 --- /dev/null +++ b/space-crush-app/src/debug/mod.rs @@ -0,0 +1,7 @@ +mod fleets; +mod ships; +mod summary; + +pub use fleets::Fleets; +pub use ships::Ships; +pub use summary::Summary; diff --git a/space-crush-app/src/debug/ships.rs b/space-crush-app/src/debug/ships.rs new file mode 100644 index 0000000..a493410 --- /dev/null +++ b/space-crush-app/src/debug/ships.rs @@ -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); + } +} diff --git a/space-crush-app/src/render/debug.rs b/space-crush-app/src/debug/summary.rs similarity index 70% rename from space-crush-app/src/render/debug.rs rename to space-crush-app/src/debug/summary.rs index 2add89e..1fddddc 100644 --- a/space-crush-app/src/render/debug.rs +++ b/space-crush-app/src/debug/summary.rs @@ -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 { 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); diff --git a/space-crush-app/src/lib.rs b/space-crush-app/src/lib.rs index 6971566..08a0166 100644 --- a/space-crush-app/src/lib.rs +++ b/space-crush-app/src/lib.rs @@ -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); diff --git a/space-crush-app/src/main.rs b/space-crush-app/src/main.rs index 31a6d5e..23d18e3 100644 --- a/space-crush-app/src/main.rs +++ b/space-crush-app/src/main.rs @@ -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::<::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::<::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::() - 250.0, + 500.0 * random::() - 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::() - 0.5, random::() - 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(); } diff --git a/space-crush-app/src/render/init.rs b/space-crush-app/src/render/init.rs index bdb51a5..454b9da 100644 --- a/space-crush-app/src/render/init.rs +++ b/space-crush-app/src/render/init.rs @@ -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>, @@ -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 }, diff --git a/space-crush-app/src/render/mod.rs b/space-crush-app/src/render/mod.rs index b04910b..e049e93 100644 --- a/space-crush-app/src/render/mod.rs +++ b/space-crush-app/src/render/mod.rs @@ -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; diff --git a/space-crush-app/src/resources/config.rs b/space-crush-app/src/resources/config.rs index 6d35453..c0f14ae 100644 --- a/space-crush-app/src/resources/config.rs +++ b/space-crush-app/src/resources/config.rs @@ -127,7 +127,7 @@ mod defaults { } pub fn speed_camera_move() -> f32 { - 2.0 + 500.0 } pub fn speed_camera_zoom() -> f32 { diff --git a/space-crush-app/src/resources/geometry.rs b/space-crush-app/src/resources/geometry.rs index ac1f2c1..4026030 100644 --- a/space-crush-app/src/resources/geometry.rs +++ b/space-crush-app/src/resources/geometry.rs @@ -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 { + pub fn new(world: &mut World) -> Result { + 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 { +fn create_array_quad() -> Result { 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::() as gl::GLsizei; + const STRIDE_POS: gl::GLsizei = size_of::() 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 { Ok(vertex_array) } + +fn create_array_line() -> Result { + const STRIDE_POS: gl::GLsizei = size_of::() 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) +} diff --git a/space-crush-common/Cargo.toml b/space-crush-common/Cargo.toml index 5b95348..c6b64a4 100644 --- a/space-crush-common/Cargo.toml +++ b/space-crush-common/Cargo.toml @@ -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" ] } diff --git a/space-crush-common/src/components/fleet.rs b/space-crush-common/src/components/fleet.rs new file mode 100644 index 0000000..23d5078 --- /dev/null +++ b/space-crush-common/src/components/fleet.rs @@ -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; +} diff --git a/space-crush-common/src/components/mod.rs b/space-crush-common/src/components/mod.rs index 71ca45f..d63623e 100644 --- a/space-crush-common/src/components/mod.rs +++ b/space-crush-common/src/components/mod.rs @@ -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; diff --git a/space-crush-common/src/components/ship.rs b/space-crush-common/src/components/ship.rs index 8d79774..4a7582a 100644 --- a/space-crush-common/src/components/ship.rs +++ b/space-crush-common/src/components/ship.rs @@ -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, + 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 { + pub type_: Type, + pub fleet: M, + pub agility: Angle, + pub target_pos: Vector2f, + pub target_dir: Vector2f, +} + impl Component for Ship { type Storage = VecStorage; } + +impl ConvertSaveload for Ship +where + for<'de> M: Marker + Serialize + Deserialize<'de>, +{ + type Data = ShipData; + type Error = NoError; + + fn convert_into(&self, mut ids: F) -> Result + where + F: FnMut(Entity) -> Option, + { + 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(data: Self::Data, mut ids: F) -> Result + where + F: FnMut(M) -> Option, + { + let ShipData { + type_, + fleet, + agility, + target_pos, + target_dir, + } = data; + let fleet = ids(fleet).unwrap(); + + Ok(Ship { + type_, + fleet, + agility, + target_pos, + target_dir, + }) + } +} diff --git a/space-crush-common/src/constants.rs b/space-crush-common/src/constants.rs new file mode 100644 index 0000000..7fa641c --- /dev/null +++ b/space-crush-common/src/constants.rs @@ -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 = Angle::Deg(7000.0); + +/// Random angle between old and new target position in orbit +pub const SHIP_ORBIT_ANGLE_DELTA_RND: Angle = Angle::Deg(4000.0); + +/// Agility of ships inside orbit +pub const SHIP_ORBIT_AGILITY: Angle = Angle::Deg(90.0); + +lazy_static! { + pub static ref VECTOR_2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0); +} diff --git a/space-crush-common/src/dispatcher.rs b/space-crush-common/src/dispatcher.rs index 2b3641c..77b9600 100644 --- a/space-crush-common/src/dispatcher.rs +++ b/space-crush-common/src/dispatcher.rs @@ -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); diff --git a/space-crush-common/src/lib.rs b/space-crush-common/src/lib.rs index ac3bbea..d072147 100644 --- a/space-crush-common/src/lib.rs +++ b/space-crush-common/src/lib.rs @@ -1,4 +1,5 @@ pub mod components; +pub mod constants; pub mod dispatcher; pub mod error; pub mod misc; diff --git a/space-crush-common/src/misc/persistence.rs b/space-crush-common/src/misc/persistence.rs index ec5d6d2..77dbeef 100644 --- a/space-crush-common/src/misc/persistence.rs +++ b/space-crush-common/src/misc/persistence.rs @@ -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; - type Components = (Position, Velocity, Planet, Ship, Owned, Player); + type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); } /* Persistence */ diff --git a/space-crush-common/src/systems/fleets.rs b/space-crush-common/src/systems/fleets.rs new file mode 100644 index 0000000..260bf68 --- /dev/null +++ b/space-crush-common/src/systems/fleets.rs @@ -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::(); + + 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); + } +} diff --git a/space-crush-common/src/systems/mod.rs b/space-crush-common/src/systems/mod.rs index e2bdf5f..dc3c6de 100644 --- a/space-crush-common/src/systems/mod.rs +++ b/space-crush-common/src/systems/mod.rs @@ -1,5 +1,7 @@ +mod fleets; mod movement; mod process; +pub use fleets::Fleets; pub use movement::Movement; pub use process::Process;