| @@ -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", | |||
| @@ -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" | |||
| @@ -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 error; | |||
| pub mod math; | |||
| pub mod matrix; | |||
| pub mod misc; | |||
| pub mod numeric; | |||
| pub mod shader; | |||
| pub mod texture; | |||
| 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")] | |||
| use serde::{ | |||
| de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor}, | |||
| ser::{Serialize, SerializeStruct, Serializer}, | |||
| de::{Deserializer, Error, MapAccess, SeqAccess, Visitor}, | |||
| ser::{SerializeStruct, Serializer}, | |||
| Deserialize, Serialize, | |||
| }; | |||
| use super::vector::{Element, Vector2, Vector3, Vector4}; | |||
| use super::vector::{Vector2, Vector3, Vector4}; | |||
| pub use super::{ | |||
| angle::Angle, | |||
| numeric::{Float, Numeric}, | |||
| }; | |||
| macro_rules! first_ptr { | |||
| ($this:ident, $first:ident $(, $other:ident)*) => { | |||
| @@ -39,7 +45,7 @@ macro_rules! define_mat { | |||
| impl<T> Default for $Name<T> | |||
| where | |||
| T: Element | |||
| T: Numeric | |||
| { | |||
| #[inline] | |||
| fn default() -> Self { | |||
| @@ -248,7 +254,7 @@ pub type Matrix4i = Matrix4<gl::GLint>; | |||
| impl<T> Matrix2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn identity() -> Self { | |||
| @@ -263,7 +269,7 @@ where | |||
| impl<T> Matrix3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn identity() -> Self { | |||
| @@ -277,6 +283,18 @@ where | |||
| ) | |||
| } | |||
| #[inline] | |||
| pub fn translate(v: Vector2<T>) -> Self { | |||
| let one = T::one(); | |||
| let zro = T::zero(); | |||
| Self::new( | |||
| Vector3::new(one, zro, zro), | |||
| Vector3::new(zro, one, zro), | |||
| Vector3::new(v.x, v.y, one), | |||
| ) | |||
| } | |||
| #[inline] | |||
| pub fn determinant(&self) -> T { | |||
| let m = self; | |||
| @@ -286,13 +304,60 @@ where | |||
| - m[1][0] * m[0][1] * m[2][2] | |||
| - m[0][0] * m[2][1] * m[1][2] | |||
| } | |||
| #[inline] | |||
| pub fn multiply(&self, other: &Self) -> Self { | |||
| let m1 = self; | |||
| let m2 = other; | |||
| macro_rules! mul { | |||
| ($x:tt, $y:tt) => { | |||
| m1[0][$y] * m2[$x][0] + m1[1][$y] * m2[$x][1] + m1[2][$y] * m2[$x][2] | |||
| }; | |||
| } | |||
| Self::new( | |||
| Vector3::new(mul!(0, 0), mul!(0, 1), mul!(0, 2)), | |||
| Vector3::new(mul!(1, 0), mul!(1, 1), mul!(1, 2)), | |||
| Vector3::new(mul!(2, 0), mul!(2, 1), mul!(2, 2)), | |||
| ) | |||
| } | |||
| } | |||
| impl<T> Matrix3<T> | |||
| where | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn rotate(a: Angle<T>) -> Self { | |||
| let one = T::one(); | |||
| let zro = T::zero(); | |||
| Self::new( | |||
| Vector3::new(a.cos(), -a.sin(), zro), | |||
| Vector3::new(a.sin(), a.cos(), zro), | |||
| Vector3::new(zro, zro, one), | |||
| ) | |||
| } | |||
| } | |||
| impl<T, M> Mul<M> for Matrix3<T> | |||
| where | |||
| T: Float, | |||
| M: Borrow<Matrix3<T>>, | |||
| { | |||
| type Output = Self; | |||
| fn mul(self, rhs: M) -> Self::Output { | |||
| self.multiply(rhs.borrow()) | |||
| } | |||
| } | |||
| /* Matrix4 */ | |||
| impl<T> Matrix4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn identity() -> Self { | |||
| @@ -306,7 +371,12 @@ where | |||
| Vector4::new(zero, zero, zero, one), | |||
| ) | |||
| } | |||
| } | |||
| impl<T> Matrix4<T> | |||
| where | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn translate(v: Vector3<T>) -> Self { | |||
| let one = T::one(); | |||
| @@ -504,7 +574,7 @@ where | |||
| impl<T> Matrix4<T> | |||
| where | |||
| T: Element<AsFloat = T>, | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn ortho(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self { | |||
| @@ -552,7 +622,7 @@ where | |||
| impl<T, M> Mul<M> for Matrix4<T> | |||
| where | |||
| T: Element, | |||
| T: Float, | |||
| M: Borrow<Matrix4<T>>, | |||
| { | |||
| type Output = Self; | |||
| @@ -562,39 +632,6 @@ where | |||
| } | |||
| } | |||
| /* Angle */ | |||
| pub enum Angle<T> { | |||
| Deg(T), | |||
| Rad(T), | |||
| } | |||
| impl<T> Angle<T> | |||
| where | |||
| T: Element, | |||
| { | |||
| pub fn into_inner(self) -> T { | |||
| match self { | |||
| Self::Deg(v) => v, | |||
| Self::Rad(v) => v, | |||
| } | |||
| } | |||
| pub fn into_deg(self) -> Self { | |||
| match self { | |||
| Self::Deg(v) => Self::Deg(v), | |||
| Self::Rad(v) => Self::Rad(T::to_degrees(v)), | |||
| } | |||
| } | |||
| pub fn into_rad(self) -> Self { | |||
| match self { | |||
| Self::Deg(v) => Self::Deg(T::to_radians(v)), | |||
| Self::Rad(v) => Self::Rad(v), | |||
| } | |||
| } | |||
| } | |||
| #[cfg(test)] | |||
| mod tests { | |||
| use super::*; | |||
| @@ -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::fmt::{Debug, Formatter, Result as FmtResult}; | |||
| use std::ops::{Add, Deref, DerefMut, Div, Mul, Neg, Sub}; | |||
| use std::ops::{Add, Deref, DerefMut, Mul, Sub}; | |||
| #[cfg(feature = "serde")] | |||
| use serde::{ | |||
| @@ -10,6 +10,11 @@ use serde::{ | |||
| ser::{Serialize, Serializer}, | |||
| }; | |||
| pub use super::{ | |||
| angle::Angle, | |||
| numeric::{Float, Numeric}, | |||
| }; | |||
| macro_rules! first_ptr { | |||
| ($this:ident, $first:ident $(, $other:ident)*) => { | |||
| unsafe { &$this.$first } | |||
| @@ -214,7 +219,7 @@ pub type Vector4i = Vector4<gl::GLint>; | |||
| impl<T> Vector2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn multiply(mut self, v: T) -> Self { | |||
| @@ -224,9 +229,14 @@ where | |||
| self | |||
| } | |||
| #[inline] | |||
| pub fn length_sqr(&self) -> T { | |||
| self.x * self.x + self.y * self.y | |||
| } | |||
| #[inline] | |||
| pub fn length(&self) -> T { | |||
| Element::sqrt(self.x * self.x + self.y * self.y) | |||
| Numeric::sqrt(self.length_sqr()) | |||
| } | |||
| #[inline] | |||
| @@ -243,28 +253,33 @@ where | |||
| pub fn scalar(&self, other: &Self) -> T { | |||
| self.x * other.x + self.y * other.y | |||
| } | |||
| } | |||
| impl<T> Vector2<T> | |||
| where | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn angle(&self, other: &Self) -> T::AsFloat { | |||
| pub fn angle(&self, other: &Self) -> Angle<T> { | |||
| let s = self.scalar(other); | |||
| let l1 = self.length(); | |||
| let l2 = self.length(); | |||
| Element::acos(s / (l1 + l2)) | |||
| Angle::Rad(T::acos(s / (l1 + l2))) | |||
| } | |||
| #[inline] | |||
| pub fn angle2(&self, other: &Self) -> T::AsFloat { | |||
| T::atan2( | |||
| pub fn angle2(&self, other: &Self) -> Angle<T> { | |||
| Angle::Rad(T::atan2( | |||
| other.x * self.y - other.y * self.x, | |||
| other.x * self.x + other.y * self.y, | |||
| ) | |||
| )) | |||
| } | |||
| } | |||
| impl<T> Add for Vector2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -279,7 +294,7 @@ where | |||
| impl<T> Sub for Vector2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -294,7 +309,7 @@ where | |||
| impl<T> Mul for Vector2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = T; | |||
| @@ -306,7 +321,7 @@ where | |||
| impl<T> Mul<T> for Vector2<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -320,7 +335,7 @@ where | |||
| impl<T> Vector3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn multiply(mut self, v: T) -> Self { | |||
| @@ -331,9 +346,14 @@ where | |||
| self | |||
| } | |||
| #[inline] | |||
| pub fn length_sqr(&self) -> T { | |||
| self.x * self.x + self.y * self.y + self.z * self.z | |||
| } | |||
| #[inline] | |||
| pub fn length(&self) -> T { | |||
| Element::sqrt(self.x * self.x + self.y * self.y + self.z * self.z) | |||
| Numeric::sqrt(self.length_sqr()) | |||
| } | |||
| #[inline] | |||
| @@ -360,20 +380,25 @@ where | |||
| z: self.x * other.y - self.y * other.x, | |||
| } | |||
| } | |||
| } | |||
| impl<T> Vector3<T> | |||
| where | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn angle(&self, other: &Self) -> T::AsFloat { | |||
| pub fn angle(&self, other: &Self) -> Angle<T> { | |||
| let s = self.scalar(other); | |||
| let l1 = self.length(); | |||
| let l2 = self.length(); | |||
| Element::acos(s / (l1 + l2)) | |||
| Angle::Rad(T::acos(s / (l1 + l2))) | |||
| } | |||
| } | |||
| impl<T> Add for Vector3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -389,7 +414,7 @@ where | |||
| impl<T> Sub for Vector3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -405,7 +430,7 @@ where | |||
| impl<T> Mul for Vector3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = T; | |||
| @@ -417,7 +442,7 @@ where | |||
| impl<T> Mul<T> for Vector3<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -431,7 +456,7 @@ where | |||
| impl<T> Vector4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| #[inline] | |||
| pub fn multiply(mut self, v: T) -> Self { | |||
| @@ -442,6 +467,11 @@ where | |||
| self | |||
| } | |||
| #[inline] | |||
| pub fn length_sqr(self) -> T { | |||
| self.xyz().length_sqr() | |||
| } | |||
| #[inline] | |||
| pub fn length(self) -> T { | |||
| self.xyz().length() | |||
| @@ -451,20 +481,20 @@ where | |||
| pub fn normalize(mut self) -> Self { | |||
| let len = self.length(); | |||
| if unsafe { !Element::is_zero(&self.w) } { | |||
| if unsafe { !Numeric::is_zero(&self.w) } { | |||
| self.x = self.x / self.w; | |||
| self.y = self.y / self.w; | |||
| self.z = self.z / self.w; | |||
| } else { | |||
| self.x = Element::zero(); | |||
| self.y = Element::zero(); | |||
| self.z = Element::zero(); | |||
| self.x = Numeric::zero(); | |||
| self.y = Numeric::zero(); | |||
| self.z = Numeric::zero(); | |||
| } | |||
| self.x = self.x / len; | |||
| self.y = self.y / len; | |||
| self.z = self.z / len; | |||
| self.w = Element::one(); | |||
| self.w = Numeric::one(); | |||
| self | |||
| } | |||
| @@ -476,21 +506,16 @@ where | |||
| #[inline] | |||
| pub fn cross(&self, other: &Self) -> Self { | |||
| (self.xyz().cross(&other.xyz()), Element::one()).into() | |||
| } | |||
| #[inline] | |||
| pub fn angle(&self, other: &Self) -> T::AsFloat { | |||
| self.xyz().angle(&other.xyz()) | |||
| (self.xyz().cross(&other.xyz()), Numeric::one()).into() | |||
| } | |||
| #[inline] | |||
| pub fn xyz(self) -> Vector3<T> { | |||
| if unsafe { Element::is_zero(&self.w) } { | |||
| if unsafe { Numeric::is_zero(&self.w) } { | |||
| Vector3 { | |||
| x: Element::zero(), | |||
| y: Element::zero(), | |||
| z: Element::zero(), | |||
| x: Numeric::zero(), | |||
| y: Numeric::zero(), | |||
| z: Numeric::zero(), | |||
| } | |||
| } else { | |||
| Vector3 { | |||
| @@ -502,9 +527,19 @@ where | |||
| } | |||
| } | |||
| impl<T> Vector4<T> | |||
| where | |||
| T: Float, | |||
| { | |||
| #[inline] | |||
| pub fn angle(&self, other: &Self) -> Angle<T> { | |||
| self.xyz().angle(&other.xyz()) | |||
| } | |||
| } | |||
| impl<T> Add for Vector4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -516,7 +551,7 @@ where | |||
| impl<T> Sub for Vector4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -528,7 +563,7 @@ where | |||
| impl<T> Mul for Vector4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = T; | |||
| @@ -539,7 +574,7 @@ where | |||
| impl<T> Mul<T> for Vector4<T> | |||
| where | |||
| T: Element, | |||
| T: Numeric, | |||
| { | |||
| type Output = Self; | |||
| @@ -560,225 +595,6 @@ impl<T> From<(Vector3<T>, T)> for Vector4<T> { | |||
| } | |||
| } | |||
| /* Element */ | |||
| pub trait Element: | |||
| Sized | |||
| + Neg<Output = Self> | |||
| + Add<Self, Output = Self> | |||
| + Sub<Self, Output = Self> | |||
| + Mul<Self, Output = Self> | |||
| + Div<Self, Output = Self> | |||
| + Copy | |||
| { | |||
| type AsFloat: Element; | |||
| fn one() -> Self; | |||
| fn zero() -> Self; | |||
| fn sin(self) -> Self; | |||
| fn cos(self) -> Self; | |||
| fn sqrt(self) -> Self; | |||
| fn tan(self) -> Self::AsFloat; | |||
| fn acos(self) -> Self::AsFloat; | |||
| fn atan(self) -> Self::AsFloat; | |||
| fn atan2(a: Self, b: Self) -> Self::AsFloat; | |||
| fn is_zero(&self) -> bool; | |||
| fn to_degrees(self) -> Self; | |||
| fn to_radians(self) -> Self; | |||
| } | |||
| impl Element for gl::GLfloat { | |||
| type AsFloat = f32; | |||
| #[inline] | |||
| fn one() -> Self { | |||
| 1.0 | |||
| } | |||
| #[inline] | |||
| fn zero() -> Self { | |||
| 0.0 | |||
| } | |||
| #[inline] | |||
| fn sin(self) -> Self { | |||
| f32::sin(self) | |||
| } | |||
| #[inline] | |||
| fn cos(self) -> Self { | |||
| f32::cos(self) | |||
| } | |||
| #[inline] | |||
| fn sqrt(self) -> Self { | |||
| f32::sqrt(self) | |||
| } | |||
| #[inline] | |||
| fn tan(self) -> Self::AsFloat { | |||
| f32::tan(self) | |||
| } | |||
| #[inline] | |||
| fn acos(self) -> Self::AsFloat { | |||
| f32::acos(self) | |||
| } | |||
| #[inline] | |||
| fn atan(self) -> Self::AsFloat { | |||
| f32::atan(self) | |||
| } | |||
| #[inline] | |||
| fn atan2(a: Self, b: Self) -> Self::AsFloat { | |||
| f32::atan2(a, b) | |||
| } | |||
| #[inline] | |||
| fn is_zero(&self) -> bool { | |||
| PartialEq::<f32>::eq(self, &0.0) | |||
| } | |||
| #[inline] | |||
| fn to_degrees(self) -> Self { | |||
| f32::to_degrees(self) | |||
| } | |||
| #[inline] | |||
| fn to_radians(self) -> Self { | |||
| f32::to_radians(self) | |||
| } | |||
| } | |||
| impl Element for gl::GLdouble { | |||
| type AsFloat = f64; | |||
| #[inline] | |||
| fn one() -> Self { | |||
| 1.0 | |||
| } | |||
| #[inline] | |||
| fn zero() -> Self { | |||
| 0.0 | |||
| } | |||
| #[inline] | |||
| fn sin(self) -> Self { | |||
| f64::sin(self) | |||
| } | |||
| #[inline] | |||
| fn cos(self) -> Self { | |||
| f64::cos(self) | |||
| } | |||
| #[inline] | |||
| fn sqrt(self) -> Self { | |||
| f64::sqrt(self) | |||
| } | |||
| #[inline] | |||
| fn tan(self) -> Self::AsFloat { | |||
| f64::tan(self) | |||
| } | |||
| #[inline] | |||
| fn acos(self) -> Self::AsFloat { | |||
| f64::acos(self) | |||
| } | |||
| #[inline] | |||
| fn atan(self) -> Self::AsFloat { | |||
| f64::atan(self) | |||
| } | |||
| #[inline] | |||
| fn atan2(a: Self, b: Self) -> Self::AsFloat { | |||
| f64::atan2(a, b) | |||
| } | |||
| #[inline] | |||
| fn is_zero(&self) -> bool { | |||
| PartialEq::<f64>::eq(self, &0.0) | |||
| } | |||
| #[inline] | |||
| fn to_degrees(self) -> Self { | |||
| f64::to_degrees(self) | |||
| } | |||
| #[inline] | |||
| fn to_radians(self) -> Self { | |||
| f64::to_radians(self) | |||
| } | |||
| } | |||
| impl Element for gl::GLint { | |||
| type AsFloat = f32; | |||
| #[inline] | |||
| fn one() -> Self { | |||
| 1 | |||
| } | |||
| #[inline] | |||
| fn zero() -> Self { | |||
| 0 | |||
| } | |||
| #[inline] | |||
| fn sin(self) -> Self { | |||
| f32::sin(self as f32) as i32 | |||
| } | |||
| #[inline] | |||
| fn cos(self) -> Self { | |||
| f32::cos(self as f32) as i32 | |||
| } | |||
| #[inline] | |||
| fn sqrt(self) -> Self { | |||
| f64::sqrt(self as f64) as i32 | |||
| } | |||
| #[inline] | |||
| fn tan(self) -> Self::AsFloat { | |||
| f32::tan(self as f32) | |||
| } | |||
| #[inline] | |||
| fn acos(self) -> Self::AsFloat { | |||
| f32::acos(self as f32) | |||
| } | |||
| #[inline] | |||
| fn atan(self) -> Self::AsFloat { | |||
| f32::atan(self as f32) | |||
| } | |||
| #[inline] | |||
| fn atan2(a: Self, b: Self) -> Self::AsFloat { | |||
| f32::atan2(a as f32, b as f32) | |||
| } | |||
| #[inline] | |||
| fn is_zero(&self) -> bool { | |||
| PartialEq::<i32>::eq(self, &0) | |||
| } | |||
| #[inline] | |||
| fn to_degrees(self) -> Self { | |||
| f32::to_degrees(self as f32) as i32 | |||
| } | |||
| #[inline] | |||
| fn to_radians(self) -> Self { | |||
| f32::to_radians(self as f32) as i32 | |||
| } | |||
| } | |||
| #[cfg(test)] | |||
| mod tests { | |||
| use super::*; | |||
| @@ -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" ] } | |||
| @@ -25,7 +25,7 @@ | |||
| "D", | |||
| "Right" | |||
| ], | |||
| "speed_camera_move": 1.0, | |||
| "speed_camera_move": 500.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() { | |||
| 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() { | |||
| 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 space_crush_common::{misc::LogResult, resources::Global}; | |||
| use specs::{ReadExpect, System}; | |||
| use specs::{prelude::*, Entities, ReadExpect, System}; | |||
| use crate::{ | |||
| misc::{Text, TextCache, TextManager}, | |||
| @@ -9,17 +9,18 @@ use crate::{ | |||
| Error, | |||
| }; | |||
| /* Debug */ | |||
| /* Summary */ | |||
| pub struct Debug { | |||
| pub struct Summary { | |||
| cache: TextCache, | |||
| text: Text, | |||
| fps: usize, | |||
| resolution: (u32, u32), | |||
| mouse_pos: (f32, f32), | |||
| entity_count: usize, | |||
| } | |||
| impl Debug { | |||
| impl Summary { | |||
| pub fn new(text_manager: &TextManager) -> Result<Self, Error> { | |||
| let cache = text_manager.create_cache()?; | |||
| let text = cache | |||
| @@ -35,6 +36,8 @@ impl Debug { | |||
| .text("1280 | 720") | |||
| .text("\nmouse_pos: ") | |||
| .text("0.00 | 0.00") | |||
| .text("\nentities: ") | |||
| .text("0") | |||
| .build()?; | |||
| Ok(Self { | |||
| @@ -43,14 +46,30 @@ impl Debug { | |||
| fps: 0, | |||
| resolution: (0, 0), | |||
| mouse_pos: (0.0, 0.0), | |||
| entity_count: 0, | |||
| }) | |||
| } | |||
| } | |||
| impl<'a> System<'a> for Debug { | |||
| type SystemData = (ReadExpect<'a, Global>, ReadExpect<'a, State>); | |||
| #[derive(SystemData)] | |||
| pub struct SummaryData<'a> { | |||
| entities: Entities<'a>, | |||
| global: ReadExpect<'a, Global>, | |||
| state: ReadExpect<'a, State>, | |||
| } | |||
| impl<'a> System<'a> for Summary { | |||
| type SystemData = SummaryData<'a>; | |||
| fn run(&mut self, data: Self::SystemData) { | |||
| let SummaryData { | |||
| entities, | |||
| global, | |||
| state, | |||
| } = data; | |||
| let entity_count = entities.par_join().count(); | |||
| fn run(&mut self, (global, state): Self::SystemData) { | |||
| let guard = self.cache.begin_update(); | |||
| update_text( | |||
| &mut self.text, | |||
| @@ -73,6 +92,13 @@ impl<'a> System<'a> for Debug { | |||
| &state.mouse_pos, | |||
| |(x, y)| format!("{:.2} | {:.2}", x, y), | |||
| ); | |||
| update_text( | |||
| &mut self.text, | |||
| 8, | |||
| &mut self.entity_count, | |||
| &entity_count, | |||
| |x| format!("{}", x), | |||
| ); | |||
| drop(guard); | |||
| self.text.render(true); | |||
| @@ -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); | |||
| @@ -1,4 +1,5 @@ | |||
| use log::{error, info}; | |||
| use rand::random; | |||
| use space_crush_app::{App, Error}; | |||
| use space_crush_common::{ | |||
| misc::{init_logger, Vfs}, | |||
| @@ -50,9 +51,12 @@ fn run(vfs: Vfs) -> Result<(), Error> { | |||
| } | |||
| fn create_world(world: &mut World) { | |||
| use glc::vector::{Vector2f, Vector4f}; | |||
| use glc::{ | |||
| matrix::Angle, | |||
| vector::{Vector2f, Vector4f}, | |||
| }; | |||
| use space_crush_common::{ | |||
| components::{Owned, Planet, Player, Position, Ship, ShipType, Velocity}, | |||
| components::{Fleet, Owned, Planet, Player, Position, Ship, ShipType, Velocity}, | |||
| misc::{PersistWorld, Persistence}, | |||
| }; | |||
| use specs::{saveload::MarkedBuilder, Builder}; | |||
| @@ -64,40 +68,48 @@ fn create_world(world: &mut World) { | |||
| }) | |||
| .build(); | |||
| world | |||
| let planet = world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Owned { owner: player1 }) | |||
| .with(Position { | |||
| pos: Vector2f::default(), | |||
| size: 500.0, | |||
| size: 250.0, | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(Planet {}) | |||
| .build(); | |||
| for i in 0..3 { | |||
| let x = if i & 1 == 0 { 1.0 } else { -1.0 }; | |||
| let y = if i & 2 == 0 { 1.0 } else { -1.0 }; | |||
| for i in 0..10 { | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Owned { owner: player1 }) | |||
| .with(Position { | |||
| pos: Vector2f::new(250.0 * x, 250.0 * y), | |||
| size: 30.0, | |||
| pos: Vector2f::new( | |||
| 500.0 * random::<f32>() - 250.0, | |||
| 500.0 * random::<f32>() - 250.0, | |||
| ), | |||
| size: 15.0, | |||
| }) | |||
| .with(Velocity { | |||
| dir: Vector2f::new(i as f32 - 1.0, 1.0).normalize(), | |||
| speed: 0.0, | |||
| dir: Vector2f::new(random::<f32>() - 0.5, random::<f32>() - 0.5).normalize(), | |||
| speed: 100.0, | |||
| }) | |||
| .with(Ship { | |||
| type_: match i { | |||
| type_: match i % 3 { | |||
| 0 => ShipType::Fighter, | |||
| 1 => ShipType::Bomber, | |||
| 2 => ShipType::Transporter, | |||
| _ => unreachable!(), | |||
| }, | |||
| fleet: planet, | |||
| agility: Angle::Deg(360.0), | |||
| target_pos: Default::default(), | |||
| target_dir: Default::default(), | |||
| }) | |||
| .build(); | |||
| } | |||
| @@ -5,7 +5,10 @@ use glc::{ | |||
| vector::Vector3f, | |||
| }; | |||
| use shrev::{EventChannel, ReaderId}; | |||
| use space_crush_common::misc::{LogResult, WorldHelper as _}; | |||
| use space_crush_common::{ | |||
| misc::{LogResult, WorldHelper as _}, | |||
| resources::Global, | |||
| }; | |||
| use specs::{prelude::*, ReadExpect, System, World, WriteExpect}; | |||
| use crate::{ | |||
| @@ -50,6 +53,7 @@ pub struct InitData<'a> { | |||
| camera: WriteExpect<'a, Camera>, | |||
| uniform: WriteExpect<'a, Uniform>, | |||
| state: ReadExpect<'a, State>, | |||
| global: ReadExpect<'a, Global>, | |||
| config: ReadExpect<'a, Config>, | |||
| geometry: ReadExpect<'a, Geometry>, | |||
| mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>, | |||
| @@ -63,6 +67,7 @@ impl<'a> System<'a> for Init { | |||
| mut camera, | |||
| mut uniform, | |||
| state, | |||
| global, | |||
| config, | |||
| geometry, | |||
| mouse_events, | |||
| @@ -102,7 +107,7 @@ impl<'a> System<'a> for Init { | |||
| let right = state.key_state(&config.input.key_camera_move_right); | |||
| if up || down || left || right { | |||
| let s = config.input.speed_camera_move; | |||
| let s = config.input.speed_camera_move * global.delta; | |||
| let translate = Vector3f::new( | |||
| if left { s } else { 0.0 } + if right { -s } else { 0.0 }, | |||
| if up { -s } else { 0.0 } + if down { s } else { 0.0 }, | |||
| @@ -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; | |||
| @@ -127,7 +127,7 @@ mod defaults { | |||
| } | |||
| pub fn speed_camera_move() -> f32 { | |||
| 2.0 | |||
| 500.0 | |||
| } | |||
| pub fn speed_camera_zoom() -> f32 { | |||
| @@ -1,49 +1,81 @@ | |||
| #![allow(dead_code)] | |||
| use std::mem::size_of; | |||
| use glc::{ | |||
| array_buffer::{ArrayBuffer, Target, Usage}, | |||
| misc::Bindable, | |||
| shader::{Program, Type, Uniform}, | |||
| vector::{Vector2f, Vector4f}, | |||
| vertex_array::{DataType, VertexArray}, | |||
| }; | |||
| use space_crush_common::misc::LogResult; | |||
| use specs::World; | |||
| use crate::Error; | |||
| use crate::{constants::UNIFORM_BUFFER_INDEX_CAMERA, misc::WorldHelper, Error}; | |||
| pub struct Geometry { | |||
| quad: VertexArray, | |||
| vertex_array_quad: VertexArray, | |||
| vertex_array_line: VertexArray, | |||
| program_line: Program, | |||
| location_line_color: gl::GLint, | |||
| } | |||
| impl Geometry { | |||
| pub fn new() -> Result<Self, Error> { | |||
| pub fn new(world: &mut World) -> Result<Self, Error> { | |||
| let vertex_array_quad = create_array_quad()?; | |||
| let vertex_array_line = create_array_line()?; | |||
| let program_line = world.load_program(vec![ | |||
| (Type::Vertex, "resources/shader/line/vert.glsl"), | |||
| (Type::Fragment, "resources/shader/line/frag.glsl"), | |||
| ])?; | |||
| program_line.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?; | |||
| let location_line_color = program_line.uniform_location("uColor")?; | |||
| Ok(Self { | |||
| quad: create_quad_array()?, | |||
| vertex_array_quad, | |||
| vertex_array_line, | |||
| program_line, | |||
| location_line_color, | |||
| }) | |||
| } | |||
| pub fn render_quad(&self) { | |||
| self.quad.bind(); | |||
| self.vertex_array_quad.bind(); | |||
| gl::draw_arrays(gl::TRIANGLE_FAN, 0, 4); | |||
| self.quad.unbind(); | |||
| self.vertex_array_quad.unbind(); | |||
| } | |||
| } | |||
| #[repr(C, packed)] | |||
| #[derive(Debug, Copy, Clone, PartialEq)] | |||
| struct Vertex { | |||
| pub x: f32, | |||
| pub y: f32, | |||
| pub fn render_lines(&mut self, c: Vector4f, points: &[Vector2f]) { | |||
| self.vertex_array_line.buffers_mut()[0] | |||
| .buffer_data(Usage::StaticDraw, points) | |||
| .unwrap(); | |||
| self.vertex_array_line.bind(); | |||
| self.program_line.bind(); | |||
| self.program_line | |||
| .uniform(self.location_line_color, Uniform::Vector4f(&c)) | |||
| .warn("Unable to set line color"); | |||
| gl::draw_arrays(gl::LINE_STRIP, 0, points.len() as _); | |||
| self.program_line.unbind(); | |||
| self.vertex_array_line.unbind(); | |||
| } | |||
| } | |||
| fn create_quad_array() -> Result<VertexArray, Error> { | |||
| fn create_array_quad() -> Result<VertexArray, Error> { | |||
| let vertices = &[ | |||
| Vertex { x: -0.5, y: -0.5 }, | |||
| Vertex { x: 0.5, y: -0.5 }, | |||
| Vertex { x: 0.5, y: 0.5 }, | |||
| Vertex { x: -0.5, y: 0.5 }, | |||
| Vector2f::new(-0.5, -0.5), | |||
| Vector2f::new(0.5, -0.5), | |||
| Vector2f::new(0.5, 0.5), | |||
| Vector2f::new(-0.5, 0.5), | |||
| ]; | |||
| const STRIDE_POS: gl::GLsizei = size_of::<Vertex>() as gl::GLsizei; | |||
| const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei; | |||
| const OFFSET_POS: gl::GLsizei = 0; | |||
| let mut array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?; | |||
| @@ -56,3 +88,17 @@ fn create_quad_array() -> Result<VertexArray, Error> { | |||
| Ok(vertex_array) | |||
| } | |||
| fn create_array_line() -> Result<VertexArray, Error> { | |||
| const STRIDE_POS: gl::GLsizei = size_of::<Vector2f>() as gl::GLsizei; | |||
| const OFFSET_POS: gl::GLsizei = 0; | |||
| let array_buffer = ArrayBuffer::new(Target::ArrayBuffer)?; | |||
| let vertex_array = VertexArray::builder() | |||
| .bind_buffer(array_buffer) | |||
| .vertex_attrib_pointer(0, 2, DataType::Float, false, STRIDE_POS, OFFSET_POS)? | |||
| .build()?; | |||
| Ok(vertex_array) | |||
| } | |||
| @@ -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" ] } | |||
| @@ -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 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; | |||
| @@ -1,9 +1,18 @@ | |||
| use glc::{matrix::Angle, vector::Vector2f}; | |||
| use serde::{Deserialize, Serialize}; | |||
| use specs::{Component, VecStorage}; | |||
| use specs::{ | |||
| error::NoError, | |||
| saveload::{ConvertSaveload, Marker}, | |||
| Component, Entity, VecStorage, | |||
| }; | |||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||
| #[derive(Clone, Debug)] | |||
| pub struct Ship { | |||
| pub type_: Type, | |||
| pub fleet: Entity, | |||
| pub agility: Angle<f32>, | |||
| pub target_pos: Vector2f, | |||
| pub target_dir: Vector2f, | |||
| } | |||
| #[derive(Clone, Debug, Serialize, Deserialize)] | |||
| @@ -13,6 +22,67 @@ pub enum Type { | |||
| Transporter, | |||
| } | |||
| #[derive(Serialize, Deserialize)] | |||
| pub struct ShipData<M> { | |||
| pub type_: Type, | |||
| pub fleet: M, | |||
| pub agility: Angle<f32>, | |||
| pub target_pos: Vector2f, | |||
| pub target_dir: Vector2f, | |||
| } | |||
| impl Component for Ship { | |||
| type Storage = VecStorage<Self>; | |||
| } | |||
| impl<M> ConvertSaveload<M> for Ship | |||
| where | |||
| for<'de> M: Marker + Serialize + Deserialize<'de>, | |||
| { | |||
| type Data = ShipData<M>; | |||
| type Error = NoError; | |||
| fn convert_into<F>(&self, mut ids: F) -> Result<Self::Data, Self::Error> | |||
| where | |||
| F: FnMut(Entity) -> Option<M>, | |||
| { | |||
| let Ship { | |||
| type_, | |||
| fleet, | |||
| agility, | |||
| target_pos, | |||
| target_dir, | |||
| } = self.clone(); | |||
| let fleet = ids(fleet).unwrap(); | |||
| Ok(ShipData { | |||
| type_, | |||
| fleet, | |||
| agility, | |||
| target_pos, | |||
| target_dir, | |||
| }) | |||
| } | |||
| fn convert_from<F>(data: Self::Data, mut ids: F) -> Result<Self, Self::Error> | |||
| where | |||
| F: FnMut(M) -> Option<Entity>, | |||
| { | |||
| let ShipData { | |||
| type_, | |||
| fleet, | |||
| agility, | |||
| target_pos, | |||
| target_dir, | |||
| } = data; | |||
| let fleet = ids(fleet).unwrap(); | |||
| Ok(Ship { | |||
| type_, | |||
| fleet, | |||
| agility, | |||
| target_pos, | |||
| target_dir, | |||
| }) | |||
| } | |||
| } | |||
| @@ -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::{ | |||
| components::Player, | |||
| resources::Global, | |||
| systems::{Movement, Process}, | |||
| systems::{Fleets, Movement, Process}, | |||
| }; | |||
| pub struct Dispatcher<'a, 'b> { | |||
| @@ -19,6 +19,7 @@ impl<'a, 'b> Dispatcher<'a, 'b> { | |||
| let mut dispatcher = DispatcherBuilder::new() | |||
| .with(Process::default(), "process", &[]) | |||
| .with(Movement::default(), "movement", &[]) | |||
| .with(Fleets::default(), "fleets", &[]) | |||
| .build(); | |||
| dispatcher.setup(world); | |||
| @@ -1,4 +1,5 @@ | |||
| pub mod components; | |||
| pub mod constants; | |||
| pub mod dispatcher; | |||
| pub mod error; | |||
| pub mod misc; | |||
| @@ -5,7 +5,7 @@ use specs::{ | |||
| Component, Entities, ReadStorage, World, WorldExt, Write, WriteStorage, | |||
| }; | |||
| use crate::components::{Owned, Planet, Player, Position, Ship, Velocity}; | |||
| use crate::components::{Fleet, Owned, Planet, Player, Position, Ship, Velocity}; | |||
| /* PersistWorld */ | |||
| @@ -14,7 +14,7 @@ pub struct PersistWorldMarker; | |||
| impl Persistence for PersistWorld { | |||
| type Marker = SimpleMarker<PersistWorldMarker>; | |||
| type Components = (Position, Velocity, Planet, Ship, Owned, Player); | |||
| type Components = (Position, Velocity, Planet, Ship, Owned, Player, Fleet); | |||
| } | |||
| /* Persistence */ | |||
| @@ -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 process; | |||
| pub use fleets::Fleets; | |||
| pub use movement::Movement; | |||
| pub use process::Process; | |||