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