| @@ -1514,6 +1514,7 @@ dependencies = [ | |||||
| "log", | "log", | ||||
| "log4rs", | "log4rs", | ||||
| "ordered-float 2.0.1", | "ordered-float 2.0.1", | ||||
| "rand", | |||||
| "serde", | "serde", | ||||
| "serde_json", | "serde_json", | ||||
| "shred", | "shred", | ||||
| @@ -1529,8 +1530,10 @@ name = "space-crush-common" | |||||
| version = "0.1.0" | version = "0.1.0" | ||||
| dependencies = [ | dependencies = [ | ||||
| "glc", | "glc", | ||||
| "lazy_static", | |||||
| "log", | "log", | ||||
| "log4rs", | "log4rs", | ||||
| "rand", | |||||
| "serde", | "serde", | ||||
| "serde_yaml", | "serde_yaml", | ||||
| "shred", | "shred", | ||||
| @@ -10,5 +10,5 @@ default = [ ] | |||||
| [dependencies] | [dependencies] | ||||
| gl = { version = "0.1", features = [ "generate_global" ] } | gl = { version = "0.1", features = [ "generate_global" ] } | ||||
| imagefmt = "4.0" | imagefmt = "4.0" | ||||
| serde = { version = "1.0", optional = true } | |||||
| serde = { version = "1.0", optional = true, features = [ "derive" ] } | |||||
| thiserror = "1.0" | thiserror = "1.0" | ||||
| @@ -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), | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,10 @@ | |||||
| pub mod angle; | |||||
| pub mod array_buffer; | pub mod array_buffer; | ||||
| pub mod error; | pub mod error; | ||||
| pub mod math; | |||||
| pub mod matrix; | pub mod matrix; | ||||
| pub mod misc; | pub mod misc; | ||||
| pub mod numeric; | |||||
| pub mod shader; | pub mod shader; | ||||
| pub mod texture; | pub mod texture; | ||||
| pub mod vector; | pub mod vector; | ||||
| @@ -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) | |||||
| } | |||||
| @@ -7,11 +7,17 @@ use std::ops::{Deref, DerefMut, Mul}; | |||||
| #[cfg(feature = "serde")] | #[cfg(feature = "serde")] | ||||
| use 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 { | macro_rules! first_ptr { | ||||
| ($this:ident, $first:ident $(, $other:ident)*) => { | ($this:ident, $first:ident $(, $other:ident)*) => { | ||||
| @@ -39,7 +45,7 @@ macro_rules! define_mat { | |||||
| impl<T> Default for $Name<T> | impl<T> Default for $Name<T> | ||||
| where | where | ||||
| T: Element | |||||
| T: Numeric | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| fn default() -> Self { | fn default() -> Self { | ||||
| @@ -248,7 +254,7 @@ pub type Matrix4i = Matrix4<gl::GLint>; | |||||
| impl<T> Matrix2<T> | impl<T> Matrix2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn identity() -> Self { | pub fn identity() -> Self { | ||||
| @@ -263,7 +269,7 @@ where | |||||
| impl<T> Matrix3<T> | impl<T> Matrix3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn identity() -> Self { | 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] | #[inline] | ||||
| pub fn determinant(&self) -> T { | pub fn determinant(&self) -> T { | ||||
| let m = self; | let m = self; | ||||
| @@ -286,13 +304,60 @@ where | |||||
| - m[1][0] * m[0][1] * m[2][2] | - m[1][0] * m[0][1] * m[2][2] | ||||
| - m[0][0] * m[2][1] * m[1][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 */ | /* Matrix4 */ | ||||
| impl<T> Matrix4<T> | impl<T> Matrix4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn identity() -> Self { | pub fn identity() -> Self { | ||||
| @@ -306,7 +371,12 @@ where | |||||
| Vector4::new(zero, zero, zero, one), | Vector4::new(zero, zero, zero, one), | ||||
| ) | ) | ||||
| } | } | ||||
| } | |||||
| impl<T> Matrix4<T> | |||||
| where | |||||
| T: Float, | |||||
| { | |||||
| #[inline] | #[inline] | ||||
| pub fn translate(v: Vector3<T>) -> Self { | pub fn translate(v: Vector3<T>) -> Self { | ||||
| let one = T::one(); | let one = T::one(); | ||||
| @@ -504,7 +574,7 @@ where | |||||
| impl<T> Matrix4<T> | impl<T> Matrix4<T> | ||||
| where | where | ||||
| T: Element<AsFloat = T>, | |||||
| T: Float, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { | 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> | impl<T, M> Mul<M> for Matrix4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Float, | |||||
| M: Borrow<Matrix4<T>>, | M: Borrow<Matrix4<T>>, | ||||
| { | { | ||||
| type Output = Self; | 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)] | #[cfg(test)] | ||||
| mod tests { | mod tests { | ||||
| use super::*; | use super::*; | ||||
| @@ -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 | |||||
| } | |||||
| } | |||||
| @@ -2,7 +2,7 @@ | |||||
| use std::convert::{AsMut, AsRef}; | use std::convert::{AsMut, AsRef}; | ||||
| use std::fmt::{Debug, Formatter, Result as FmtResult}; | 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")] | #[cfg(feature = "serde")] | ||||
| use serde::{ | use serde::{ | ||||
| @@ -10,6 +10,11 @@ use serde::{ | |||||
| ser::{Serialize, Serializer}, | ser::{Serialize, Serializer}, | ||||
| }; | }; | ||||
| pub use super::{ | |||||
| angle::Angle, | |||||
| numeric::{Float, Numeric}, | |||||
| }; | |||||
| macro_rules! first_ptr { | macro_rules! first_ptr { | ||||
| ($this:ident, $first:ident $(, $other:ident)*) => { | ($this:ident, $first:ident $(, $other:ident)*) => { | ||||
| unsafe { &$this.$first } | unsafe { &$this.$first } | ||||
| @@ -214,7 +219,7 @@ pub type Vector4i = Vector4<gl::GLint>; | |||||
| impl<T> Vector2<T> | impl<T> Vector2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn multiply(mut self, v: T) -> Self { | pub fn multiply(mut self, v: T) -> Self { | ||||
| @@ -224,9 +229,14 @@ where | |||||
| self | self | ||||
| } | } | ||||
| #[inline] | |||||
| pub fn length_sqr(&self) -> T { | |||||
| self.x * self.x + self.y * self.y | |||||
| } | |||||
| #[inline] | #[inline] | ||||
| pub fn length(&self) -> T { | pub fn length(&self) -> T { | ||||
| Element::sqrt(self.x * self.x + self.y * self.y) | |||||
| Numeric::sqrt(self.length_sqr()) | |||||
| } | } | ||||
| #[inline] | #[inline] | ||||
| @@ -243,28 +253,33 @@ where | |||||
| pub fn scalar(&self, other: &Self) -> T { | pub fn scalar(&self, other: &Self) -> T { | ||||
| self.x * other.x + self.y * other.y | self.x * other.x + self.y * other.y | ||||
| } | } | ||||
| } | |||||
| impl<T> Vector2<T> | |||||
| where | |||||
| T: Float, | |||||
| { | |||||
| #[inline] | #[inline] | ||||
| pub fn angle(&self, other: &Self) -> T::AsFloat { | |||||
| pub fn angle(&self, other: &Self) -> Angle<T> { | |||||
| let s = self.scalar(other); | let s = self.scalar(other); | ||||
| let l1 = self.length(); | let l1 = self.length(); | ||||
| let l2 = self.length(); | let l2 = self.length(); | ||||
| Element::acos(s / (l1 + l2)) | |||||
| Angle::Rad(T::acos(s / (l1 + l2))) | |||||
| } | } | ||||
| #[inline] | #[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.y - other.y * self.x, | ||||
| other.x * self.x + other.y * self.y, | other.x * self.x + other.y * self.y, | ||||
| ) | |||||
| )) | |||||
| } | } | ||||
| } | } | ||||
| impl<T> Add for Vector2<T> | impl<T> Add for Vector2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -279,7 +294,7 @@ where | |||||
| impl<T> Sub for Vector2<T> | impl<T> Sub for Vector2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -294,7 +309,7 @@ where | |||||
| impl<T> Mul for Vector2<T> | impl<T> Mul for Vector2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = T; | type Output = T; | ||||
| @@ -306,7 +321,7 @@ where | |||||
| impl<T> Mul<T> for Vector2<T> | impl<T> Mul<T> for Vector2<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -320,7 +335,7 @@ where | |||||
| impl<T> Vector3<T> | impl<T> Vector3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn multiply(mut self, v: T) -> Self { | pub fn multiply(mut self, v: T) -> Self { | ||||
| @@ -331,9 +346,14 @@ where | |||||
| self | self | ||||
| } | } | ||||
| #[inline] | |||||
| pub fn length_sqr(&self) -> T { | |||||
| self.x * self.x + self.y * self.y + self.z * self.z | |||||
| } | |||||
| #[inline] | #[inline] | ||||
| pub fn length(&self) -> T { | 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] | #[inline] | ||||
| @@ -360,20 +380,25 @@ where | |||||
| z: self.x * other.y - self.y * other.x, | z: self.x * other.y - self.y * other.x, | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| impl<T> Vector3<T> | |||||
| where | |||||
| T: Float, | |||||
| { | |||||
| #[inline] | #[inline] | ||||
| pub fn angle(&self, other: &Self) -> T::AsFloat { | |||||
| pub fn angle(&self, other: &Self) -> Angle<T> { | |||||
| let s = self.scalar(other); | let s = self.scalar(other); | ||||
| let l1 = self.length(); | let l1 = self.length(); | ||||
| let l2 = self.length(); | let l2 = self.length(); | ||||
| Element::acos(s / (l1 + l2)) | |||||
| Angle::Rad(T::acos(s / (l1 + l2))) | |||||
| } | } | ||||
| } | } | ||||
| impl<T> Add for Vector3<T> | impl<T> Add for Vector3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -389,7 +414,7 @@ where | |||||
| impl<T> Sub for Vector3<T> | impl<T> Sub for Vector3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -405,7 +430,7 @@ where | |||||
| impl<T> Mul for Vector3<T> | impl<T> Mul for Vector3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = T; | type Output = T; | ||||
| @@ -417,7 +442,7 @@ where | |||||
| impl<T> Mul<T> for Vector3<T> | impl<T> Mul<T> for Vector3<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -431,7 +456,7 @@ where | |||||
| impl<T> Vector4<T> | impl<T> Vector4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| #[inline] | #[inline] | ||||
| pub fn multiply(mut self, v: T) -> Self { | pub fn multiply(mut self, v: T) -> Self { | ||||
| @@ -442,6 +467,11 @@ where | |||||
| self | self | ||||
| } | } | ||||
| #[inline] | |||||
| pub fn length_sqr(self) -> T { | |||||
| self.xyz().length_sqr() | |||||
| } | |||||
| #[inline] | #[inline] | ||||
| pub fn length(self) -> T { | pub fn length(self) -> T { | ||||
| self.xyz().length() | self.xyz().length() | ||||
| @@ -451,20 +481,20 @@ where | |||||
| pub fn normalize(mut self) -> Self { | pub fn normalize(mut self) -> Self { | ||||
| let len = self.length(); | 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.x = self.x / self.w; | ||||
| self.y = self.y / self.w; | self.y = self.y / self.w; | ||||
| self.z = self.z / self.w; | self.z = self.z / self.w; | ||||
| } else { | } 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.x = self.x / len; | ||||
| self.y = self.y / len; | self.y = self.y / len; | ||||
| self.z = self.z / len; | self.z = self.z / len; | ||||
| self.w = Element::one(); | |||||
| self.w = Numeric::one(); | |||||
| self | self | ||||
| } | } | ||||
| @@ -476,21 +506,16 @@ where | |||||
| #[inline] | #[inline] | ||||
| pub fn cross(&self, other: &Self) -> Self { | 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] | #[inline] | ||||
| pub fn xyz(self) -> Vector3<T> { | pub fn xyz(self) -> Vector3<T> { | ||||
| if unsafe { Element::is_zero(&self.w) } { | |||||
| if unsafe { Numeric::is_zero(&self.w) } { | |||||
| Vector3 { | Vector3 { | ||||
| x: Element::zero(), | |||||
| y: Element::zero(), | |||||
| z: Element::zero(), | |||||
| x: Numeric::zero(), | |||||
| y: Numeric::zero(), | |||||
| z: Numeric::zero(), | |||||
| } | } | ||||
| } else { | } else { | ||||
| Vector3 { | 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> | impl<T> Add for Vector4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -516,7 +551,7 @@ where | |||||
| impl<T> Sub for Vector4<T> | impl<T> Sub for Vector4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | type Output = Self; | ||||
| @@ -528,7 +563,7 @@ where | |||||
| impl<T> Mul for Vector4<T> | impl<T> Mul for Vector4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = T; | type Output = T; | ||||
| @@ -539,7 +574,7 @@ where | |||||
| impl<T> Mul<T> for Vector4<T> | impl<T> Mul<T> for Vector4<T> | ||||
| where | where | ||||
| T: Element, | |||||
| T: Numeric, | |||||
| { | { | ||||
| type Output = Self; | 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)] | #[cfg(test)] | ||||
| mod tests { | mod tests { | ||||
| use super::*; | use super::*; | ||||
| @@ -13,6 +13,7 @@ lazy_static = "1.4" | |||||
| log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | ||||
| log4rs = "0.13" | log4rs = "0.13" | ||||
| ordered-float = "2.0" | ordered-float = "2.0" | ||||
| rand = "0.7" | |||||
| serde = { version = "1.0", features = [ "derive" ] } | serde = { version = "1.0", features = [ "derive" ] } | ||||
| serde_json = "1.0" | serde_json = "1.0" | ||||
| shred = { version = "0.10", features = [ "shred-derive" ] } | shred = { version = "0.10", features = [ "shred-derive" ] } | ||||
| @@ -25,7 +25,7 @@ | |||||
| "D", | "D", | ||||
| "Right" | "Right" | ||||
| ], | ], | ||||
| "speed_camera_move": 1.0, | |||||
| "speed_camera_move": 500.0, | |||||
| "speed_camera_zoom": 100.0 | "speed_camera_zoom": 100.0 | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,9 @@ | |||||
| #version 450 core | |||||
| uniform vec4 uColor; | |||||
| out vec4 outColor; | |||||
| void main() { | |||||
| outColor = uColor; | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| @@ -13,5 +13,5 @@ out FragmentData fragmentData; | |||||
| void main() { | void main() { | ||||
| fragmentData.texCoords = inPosition.xy * GLOW_SIZE_FACTOR + vec2(0.5); | 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); | |||||
| } | } | ||||
| @@ -13,5 +13,5 @@ out FragmentData fragmentData; | |||||
| void main() { | void main() { | ||||
| fragmentData.texCoords = inPosition.xy * GLOW_SIZE_FACTOR + vec2(0.5); | 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,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 | |||||
| ] | |||||
| } | |||||
| ] | |||||
| } | |||||
| ] | |||||
| @@ -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 | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| mod fleets; | |||||
| mod ships; | |||||
| mod summary; | |||||
| pub use fleets::Fleets; | |||||
| pub use ships::Ships; | |||||
| pub use summary::Summary; | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -1,7 +1,7 @@ | |||||
| use std::string::ToString; | use std::string::ToString; | ||||
| use space_crush_common::{misc::LogResult, resources::Global}; | use space_crush_common::{misc::LogResult, resources::Global}; | ||||
| use specs::{ReadExpect, System}; | |||||
| use specs::{prelude::*, Entities, ReadExpect, System}; | |||||
| use crate::{ | use crate::{ | ||||
| misc::{Text, TextCache, TextManager}, | misc::{Text, TextCache, TextManager}, | ||||
| @@ -9,17 +9,18 @@ use crate::{ | |||||
| Error, | Error, | ||||
| }; | }; | ||||
| /* Debug */ | |||||
| /* Summary */ | |||||
| pub struct Debug { | |||||
| pub struct Summary { | |||||
| cache: TextCache, | cache: TextCache, | ||||
| text: Text, | text: Text, | ||||
| fps: usize, | fps: usize, | ||||
| resolution: (u32, u32), | resolution: (u32, u32), | ||||
| mouse_pos: (f32, f32), | mouse_pos: (f32, f32), | ||||
| entity_count: usize, | |||||
| } | } | ||||
| impl Debug { | |||||
| impl Summary { | |||||
| pub fn new(text_manager: &TextManager) -> Result<Self, Error> { | pub fn new(text_manager: &TextManager) -> Result<Self, Error> { | ||||
| let cache = text_manager.create_cache()?; | let cache = text_manager.create_cache()?; | ||||
| let text = cache | let text = cache | ||||
| @@ -35,6 +36,8 @@ impl Debug { | |||||
| .text("1280 | 720") | .text("1280 | 720") | ||||
| .text("\nmouse_pos: ") | .text("\nmouse_pos: ") | ||||
| .text("0.00 | 0.00") | .text("0.00 | 0.00") | ||||
| .text("\nentities: ") | |||||
| .text("0") | |||||
| .build()?; | .build()?; | ||||
| Ok(Self { | Ok(Self { | ||||
| @@ -43,14 +46,30 @@ impl Debug { | |||||
| fps: 0, | fps: 0, | ||||
| resolution: (0, 0), | resolution: (0, 0), | ||||
| mouse_pos: (0.0, 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(); | let guard = self.cache.begin_update(); | ||||
| update_text( | update_text( | ||||
| &mut self.text, | &mut self.text, | ||||
| @@ -73,6 +92,13 @@ impl<'a> System<'a> for Debug { | |||||
| &state.mouse_pos, | &state.mouse_pos, | ||||
| |(x, y)| format!("{:.2} | {:.2}", x, y), | |(x, y)| format!("{:.2} | {:.2}", x, y), | ||||
| ); | ); | ||||
| update_text( | |||||
| &mut self.text, | |||||
| 8, | |||||
| &mut self.entity_count, | |||||
| &entity_count, | |||||
| |x| format!("{}", x), | |||||
| ); | |||||
| drop(guard); | drop(guard); | ||||
| self.text.render(true); | self.text.render(true); | ||||
| @@ -1,4 +1,5 @@ | |||||
| mod constants; | mod constants; | ||||
| mod debug; | |||||
| mod error; | mod error; | ||||
| mod misc; | mod misc; | ||||
| mod render; | mod render; | ||||
| @@ -9,8 +10,9 @@ use specs::{Dispatcher, DispatcherBuilder, World}; | |||||
| pub use error::Error; | pub use error::Error; | ||||
| use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary}; | |||||
| use misc::{Events, TextManager, Window}; | use misc::{Events, TextManager, Window}; | ||||
| use render::{Debug, Init, Planets, Ships}; | |||||
| use render::{Init, Planets, Ships}; | |||||
| use resources::{Camera, Config, Geometry, State, Uniform}; | use resources::{Camera, Config, Geometry, State, Uniform}; | ||||
| use systems::StateUpdate; | use systems::StateUpdate; | ||||
| @@ -27,11 +29,16 @@ impl<'a, 'b> App<'a, 'b> { | |||||
| let events = Events::new(world); | let events = Events::new(world); | ||||
| let window = Window::new(events.handle(), &config)?; | 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(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)?; | 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(Init::new(world)?) | ||||
| .with_thread_local(Planets::new(world)?) | .with_thread_local(Planets::new(world)?) | ||||
| .with_thread_local(Ships::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(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,4 +1,5 @@ | |||||
| use log::{error, info}; | use log::{error, info}; | ||||
| use rand::random; | |||||
| use space_crush_app::{App, Error}; | use space_crush_app::{App, Error}; | ||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| misc::{init_logger, Vfs}, | misc::{init_logger, Vfs}, | ||||
| @@ -50,9 +51,12 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||||
| } | } | ||||
| fn create_world(world: &mut World) { | fn create_world(world: &mut World) { | ||||
| use glc::vector::{Vector2f, Vector4f}; | |||||
| use glc::{ | |||||
| matrix::Angle, | |||||
| vector::{Vector2f, Vector4f}, | |||||
| }; | |||||
| use space_crush_common::{ | use space_crush_common::{ | ||||
| components::{Owned, Planet, Player, Position, Ship, ShipType, Velocity}, | |||||
| components::{Fleet, Owned, Planet, Player, Position, Ship, ShipType, Velocity}, | |||||
| misc::{PersistWorld, Persistence}, | misc::{PersistWorld, Persistence}, | ||||
| }; | }; | ||||
| use specs::{saveload::MarkedBuilder, Builder}; | use specs::{saveload::MarkedBuilder, Builder}; | ||||
| @@ -64,40 +68,48 @@ fn create_world(world: &mut World) { | |||||
| }) | }) | ||||
| .build(); | .build(); | ||||
| world | |||||
| let planet = world | |||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Owned { owner: player1 }) | .with(Owned { owner: player1 }) | ||||
| .with(Position { | .with(Position { | ||||
| pos: Vector2f::default(), | pos: Vector2f::default(), | ||||
| size: 500.0, | |||||
| size: 250.0, | |||||
| }) | |||||
| .with(Fleet { | |||||
| orbit_min: 325.0, | |||||
| orbit_max: 425.0, | |||||
| }) | }) | ||||
| .with(Planet {}) | .with(Planet {}) | ||||
| .build(); | .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 | world | ||||
| .create_entity() | .create_entity() | ||||
| .marked::<<PersistWorld as Persistence>::Marker>() | .marked::<<PersistWorld as Persistence>::Marker>() | ||||
| .with(Owned { owner: player1 }) | .with(Owned { owner: player1 }) | ||||
| .with(Position { | .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 { | .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 { | .with(Ship { | ||||
| type_: match i { | |||||
| type_: match i % 3 { | |||||
| 0 => ShipType::Fighter, | 0 => ShipType::Fighter, | ||||
| 1 => ShipType::Bomber, | 1 => ShipType::Bomber, | ||||
| 2 => ShipType::Transporter, | 2 => ShipType::Transporter, | ||||
| _ => unreachable!(), | _ => unreachable!(), | ||||
| }, | }, | ||||
| fleet: planet, | |||||
| agility: Angle::Deg(360.0), | |||||
| target_pos: Default::default(), | |||||
| target_dir: Default::default(), | |||||
| }) | }) | ||||
| .build(); | .build(); | ||||
| } | } | ||||
| @@ -5,7 +5,10 @@ use glc::{ | |||||
| vector::Vector3f, | vector::Vector3f, | ||||
| }; | }; | ||||
| use shrev::{EventChannel, ReaderId}; | 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 specs::{prelude::*, ReadExpect, System, World, WriteExpect}; | ||||
| use crate::{ | use crate::{ | ||||
| @@ -50,6 +53,7 @@ pub struct InitData<'a> { | |||||
| camera: WriteExpect<'a, Camera>, | camera: WriteExpect<'a, Camera>, | ||||
| uniform: WriteExpect<'a, Uniform>, | uniform: WriteExpect<'a, Uniform>, | ||||
| state: ReadExpect<'a, State>, | state: ReadExpect<'a, State>, | ||||
| global: ReadExpect<'a, Global>, | |||||
| config: ReadExpect<'a, Config>, | config: ReadExpect<'a, Config>, | ||||
| geometry: ReadExpect<'a, Geometry>, | geometry: ReadExpect<'a, Geometry>, | ||||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | ||||
| @@ -63,6 +67,7 @@ impl<'a> System<'a> for Init { | |||||
| mut camera, | mut camera, | ||||
| mut uniform, | mut uniform, | ||||
| state, | state, | ||||
| global, | |||||
| config, | config, | ||||
| geometry, | geometry, | ||||
| mouse_events, | mouse_events, | ||||
| @@ -102,7 +107,7 @@ impl<'a> System<'a> for Init { | |||||
| let right = state.key_state(&config.input.key_camera_move_right); | let right = state.key_state(&config.input.key_camera_move_right); | ||||
| if up || down || left || 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( | let translate = Vector3f::new( | ||||
| if left { s } else { 0.0 } + if right { -s } else { 0.0 }, | if left { s } else { 0.0 } + if right { -s } else { 0.0 }, | ||||
| if up { -s } else { 0.0 } + if down { s } else { 0.0 }, | if up { -s } else { 0.0 } + if down { s } else { 0.0 }, | ||||
| @@ -1,9 +1,7 @@ | |||||
| mod debug; | |||||
| mod init; | mod init; | ||||
| mod planets; | mod planets; | ||||
| mod ships; | mod ships; | ||||
| pub use debug::Debug; | |||||
| pub use init::Init; | pub use init::Init; | ||||
| pub use planets::Planets; | pub use planets::Planets; | ||||
| pub use ships::Ships; | pub use ships::Ships; | ||||
| @@ -127,7 +127,7 @@ mod defaults { | |||||
| } | } | ||||
| pub fn speed_camera_move() -> f32 { | pub fn speed_camera_move() -> f32 { | ||||
| 2.0 | |||||
| 500.0 | |||||
| } | } | ||||
| pub fn speed_camera_zoom() -> f32 { | pub fn speed_camera_zoom() -> f32 { | ||||
| @@ -1,49 +1,81 @@ | |||||
| #![allow(dead_code)] | |||||
| use std::mem::size_of; | use std::mem::size_of; | ||||
| use glc::{ | use glc::{ | ||||
| array_buffer::{ArrayBuffer, Target, Usage}, | array_buffer::{ArrayBuffer, Target, Usage}, | ||||
| misc::Bindable, | misc::Bindable, | ||||
| shader::{Program, Type, Uniform}, | |||||
| vector::{Vector2f, Vector4f}, | |||||
| vertex_array::{DataType, VertexArray}, | 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 { | pub struct Geometry { | ||||
| quad: VertexArray, | |||||
| vertex_array_quad: VertexArray, | |||||
| vertex_array_line: VertexArray, | |||||
| program_line: Program, | |||||
| location_line_color: gl::GLint, | |||||
| } | } | ||||
| impl Geometry { | 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 { | Ok(Self { | ||||
| quad: create_quad_array()?, | |||||
| vertex_array_quad, | |||||
| vertex_array_line, | |||||
| program_line, | |||||
| location_line_color, | |||||
| }) | }) | ||||
| } | } | ||||
| pub fn render_quad(&self) { | pub fn render_quad(&self) { | ||||
| self.quad.bind(); | |||||
| self.vertex_array_quad.bind(); | |||||
| gl::draw_arrays(gl::TRIANGLE_FAN, 0, 4); | 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 = &[ | 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; | const OFFSET_POS: gl::GLsizei = 0; | ||||
| let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?; | let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?; | ||||
| @@ -56,3 +88,17 @@ fn create_quad_array() -> Result<VertexArray, Error> { | |||||
| Ok(vertex_array) | 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) | |||||
| } | |||||
| @@ -7,7 +7,9 @@ edition = "2018" | |||||
| [dependencies] | [dependencies] | ||||
| glc = { version = "0.1", features = [ "serde" ] } | glc = { version = "0.1", features = [ "serde" ] } | ||||
| log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] } | ||||
| lazy_static = "1.4" | |||||
| log4rs = "0.13" | log4rs = "0.13" | ||||
| rand = "0.7" | |||||
| serde = "1.0" | serde = "1.0" | ||||
| serde_yaml = "0.8" | serde_yaml = "0.8" | ||||
| shred = { version = "0.10", features = [ "shred-derive" ] } | shred = { version = "0.10", features = [ "shred-derive" ] } | ||||
| @@ -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>; | |||||
| } | |||||
| @@ -1,3 +1,4 @@ | |||||
| mod fleet; | |||||
| mod owned; | mod owned; | ||||
| mod planet; | mod planet; | ||||
| mod player; | mod player; | ||||
| @@ -5,6 +6,7 @@ mod position; | |||||
| mod ship; | mod ship; | ||||
| mod velocity; | mod velocity; | ||||
| pub use fleet::Fleet; | |||||
| pub use owned::Owned; | pub use owned::Owned; | ||||
| pub use planet::Planet; | pub use planet::Planet; | ||||
| pub use player::Player; | pub use player::Player; | ||||
| @@ -1,9 +1,18 @@ | |||||
| use glc::{matrix::Angle, vector::Vector2f}; | |||||
| use serde::{Deserialize, Serialize}; | 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 struct Ship { | ||||
| pub type_: Type, | pub type_: Type, | ||||
| pub fleet: Entity, | |||||
| pub agility: Angle<f32>, | |||||
| pub target_pos: Vector2f, | |||||
| pub target_dir: Vector2f, | |||||
| } | } | ||||
| #[derive(Clone, Debug, Serialize, Deserialize)] | #[derive(Clone, Debug, Serialize, Deserialize)] | ||||
| @@ -13,6 +22,67 @@ pub enum Type { | |||||
| Transporter, | 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 { | impl Component for Ship { | ||||
| type Storage = VecStorage<Self>; | 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, | |||||
| }) | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| @@ -3,7 +3,7 @@ use specs::{Dispatcher as Inner, DispatcherBuilder, World, WorldExt}; | |||||
| use crate::{ | use crate::{ | ||||
| components::Player, | components::Player, | ||||
| resources::Global, | resources::Global, | ||||
| systems::{Movement, Process}, | |||||
| systems::{Fleets, Movement, Process}, | |||||
| }; | }; | ||||
| pub struct Dispatcher<'a, 'b> { | pub struct Dispatcher<'a, 'b> { | ||||
| @@ -19,6 +19,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||||
| let mut dispatcher = DispatcherBuilder::new() | let mut dispatcher = DispatcherBuilder::new() | ||||
| .with(Process::default(), "process", &[]) | .with(Process::default(), "process", &[]) | ||||
| .with(Movement::default(), "movement", &[]) | .with(Movement::default(), "movement", &[]) | ||||
| .with(Fleets::default(), "fleets", &[]) | |||||
| .build(); | .build(); | ||||
| dispatcher.setup(world); | dispatcher.setup(world); | ||||
| @@ -1,4 +1,5 @@ | |||||
| pub mod components; | pub mod components; | ||||
| pub mod constants; | |||||
| pub mod dispatcher; | pub mod dispatcher; | ||||
| pub mod error; | pub mod error; | ||||
| pub mod misc; | pub mod misc; | ||||
| @@ -5,7 +5,7 @@ use specs::{ | |||||
| Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | 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 */ | /* PersistWorld */ | ||||
| @@ -14,7 +14,7 @@ pub struct PersistWorldMarker; | |||||
| impl Persistence for PersistWorld { | impl Persistence for PersistWorld { | ||||
| type Marker = SimpleMarker<PersistWorldMarker>; | type Marker = SimpleMarker<PersistWorldMarker>; | ||||
| type Components = (Position, Velocity, Planet, Ship, Owned, Player); | |||||
| type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); | |||||
| } | } | ||||
| /* Persistence */ | /* Persistence */ | ||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,7 @@ | |||||
| mod fleets; | |||||
| mod movement; | mod movement; | ||||
| mod process; | mod process; | ||||
| pub use fleets::Fleets; | |||||
| pub use movement::Movement; | pub use movement::Movement; | ||||
| pub use process::Process; | pub use process::Process; | ||||