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