Browse Source

Implemented simple fleet selection menu

master
Bergmann89 3 years ago
parent
commit
264fc63f1e
32 changed files with 969 additions and 81 deletions
  1. +0
    -2
      Cargo.lock
  2. +225
    -0
      glc/src/animation.rs
  3. +1
    -0
      glc/src/lib.rs
  4. +6
    -4
      glc/src/math.rs
  5. +28
    -9
      glc/src/matrix.rs
  6. +11
    -0
      glc/src/numeric.rs
  7. +5
    -1
      glc/src/shader.rs
  8. +1
    -1
      glc/src/vector.rs
  9. +0
    -1
      space-crush-app/Cargo.toml
  10. +96
    -0
      space-crush-app/resources/shader/fleet_select/simple/frag.glsl
  11. +3
    -0
      space-crush-app/resources/shader/fleet_select/simple/shared.glsl
  12. +15
    -0
      space-crush-app/resources/shader/fleet_select/simple/vert.glsl
  13. +3
    -2
      space-crush-app/resources/shader/text/frag.glsl
  14. +3
    -1
      space-crush-app/resources/shader/text/vert.glsl
  15. +9
    -4
      space-crush-app/src/constants.rs
  16. +6
    -1
      space-crush-app/src/debug/summary.rs
  17. +2
    -1
      space-crush-app/src/lib.rs
  18. +1
    -1
      space-crush-app/src/misc/mod.rs
  19. +75
    -17
      space-crush-app/src/misc/text.rs
  20. +1
    -1
      space-crush-app/src/render/asteroids.rs
  21. +2
    -0
      space-crush-app/src/render/mod.rs
  22. +1
    -1
      space-crush-app/src/render/planets.rs
  23. +337
    -0
      space-crush-app/src/render/select_fleet.rs
  24. +1
    -1
      space-crush-app/src/render/ships.rs
  25. +55
    -24
      space-crush-app/src/resources/camera.rs
  26. +7
    -0
      space-crush-app/src/resources/config.rs
  27. +1
    -1
      space-crush-app/src/resources/mod.rs
  28. +11
    -1
      space-crush-app/src/resources/player_state.rs
  29. +0
    -1
      space-crush-common/Cargo.toml
  30. +7
    -0
      space-crush-common/src/components/position.rs
  31. +55
    -2
      space-crush-common/src/components/ship.rs
  32. +1
    -4
      space-crush-common/src/constants.rs

+ 0
- 2
Cargo.lock View File

@@ -1510,7 +1510,6 @@ dependencies = [
"glc",
"glutin",
"glyph_brush",
"lazy_static",
"log",
"log4rs",
"ordered-float 2.0.1",
@@ -1531,7 +1530,6 @@ version = "0.1.0"
dependencies = [
"glc",
"hibitset",
"lazy_static",
"log",
"log4rs",
"rand",


+ 225
- 0
glc/src/animation.rs View File

@@ -0,0 +1,225 @@
use super::{angle::Angle, numeric::Float};

pub fn animate<T, F>(low: T, high: T, value: T, f: F) -> T
where
T: Float,
F: FnOnce(T) -> T,
{
low + f(value) * (high - low)
}

pub fn zero<T: Float>(_: T) -> T {
T::zero()
}

pub fn one<T: Float>(_: T) -> T {
T::one()
}

pub fn step<T: Float + Ord>(value: T) -> T {
if value < T::new(0.5) {
T::zero()
} else {
T::one()
}
}

pub fn linear<T: Float>(value: T) -> T {
value
}

pub fn invert<T: Float>(value: T) -> T {
T::one() - value
}

pub fn sinus<T: Float>(value: T) -> T {
Angle::Deg(T::new(360.0) * value)
.into_rad()
.into_inner()
.sin()
}

pub fn cosinus<T: Float>(value: T) -> T {
Angle::Deg(T::new(360.0) * value)
.into_rad()
.into_inner()
.cos()
}

pub fn ease_in_quadric<T: Float>(value: T) -> T {
value * value
}

pub fn ease_in_cubic<T: Float>(value: T) -> T {
value * value * value
}

pub fn ease_in_quartic<T: Float>(value: T) -> T {
value * value * value * value
}

pub fn ease_in_quintic<T: Float>(value: T) -> T {
value * value * value * value * value
}

pub fn ease_in_sinusoidal<T: Float>(value: T) -> T {
Angle::Deg(T::new(90.0) * value + T::one())
.into_rad()
.into_inner()
.cos()
.neg()
}

pub fn ease_in_circular<T: Float + Ord>(value: T) -> T {
if value > T::one() {
T::one()
} else {
T::one() - (T::one() - value * value)
}
}

pub fn ease_in_back_cubic<T: Float>(value: T) -> T {
T::new(4.0) * value * value * value - T::new(3.0) * value * value
}

pub fn ease_in_back_quadric<T: Float>(value: T) -> T {
T::new(2.0) * value * value * value * value + T::new(2.0) * value * value * value
- T::new(3.0) * value * value
}

pub fn ease_in_elastic_small<T: Float>(value: T) -> T {
T::new(33.0) * value * value * value * value * value
- T::new(59.0) * value * value * value * value
+ T::new(32.0) * value * value * value
- T::new(5.0) * value * value
}

pub fn ease_in_elastic_big<T: Float>(value: T) -> T {
T::new(56.0) * value * value * value * value * value
- T::new(105.0) * value * value * value * value
+ T::new(60.0) * value * value * value
- T::new(10.0) * value * value
}

pub fn ease_out_quadric<T: Float>(value: T) -> T {
-value * (value - T::new(2.0))
}

pub fn ease_out_cubic<T: Float>(value: T) -> T {
let value = value - T::one();

value * value * value + T::one()
}

pub fn ease_out_quartic<T: Float>(value: T) -> T {
let value = value - T::one();

-value * value * value * value + T::one()
}

pub fn ease_out_quintic<T: Float>(value: T) -> T {
let value = value - T::one();

value * value * value * value * value + T::one()
}

pub fn ease_out_sinusoidal<T: Float>(value: T) -> T {
Angle::Deg(T::new(90.0) * value)
.into_rad()
.into_inner()
.sin()
}

pub fn ease_out_circular<T: Float + Ord>(value: T) -> T {
if value < T::zero() {
T::zero()
} else {
let value = value - T::one();

(T::one() - value * value).sqrt()
}
}

pub fn ease_out_back_cubic<T: Float>(value: T) -> T {
T::new(4.0) * value * value * value - T::new(9.0) * value * value + T::new(6.0) * value
}

pub fn ease_out_back_quadric<T: Float>(value: T) -> T {
-T::new(2.0) * value * value * value * value + T::new(10.0) * value * value * value
- T::new(15.0) * value * value
+ T::new(8.0) * value
}

pub fn ease_out_elastic_small<T: Float>(value: T) -> T {
T::new(33.0) * value * value * value * value * value
- T::new(106.0) * value * value * value * value
+ T::new(126.0) * value * value * value
- T::new(67.0) * value * value
+ T::new(15.0) * value
}

pub fn ease_out_elastic_big<T: Float>(value: T) -> T {
T::new(56.0) * value * value * value * value * value
- T::new(175.0) * value * value * value * value
+ T::new(200.0) * value * value * value
- T::new(100.0) * value * value
+ T::new(20.0) * value
}

pub fn ease_in_out_quadric<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_quadric, ease_out_quadric)
}

pub fn ease_in_out_cubic<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_cubic, ease_out_cubic)
}

pub fn ease_in_out_quartic<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_quartic, ease_out_quartic)
}

pub fn ease_in_out_quintic<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_quintic, ease_out_quintic)
}

pub fn ease_in_out_sinusodial<T: Float>(value: T) -> T {
-T::new(0.5)
* (Angle::Deg(T::new(180.0) * value)
.into_rad()
.into_inner()
.cos()
- T::one())
}

pub fn ease_in_out_circular<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_circular, ease_out_circular)
}

pub fn ease_in_out_back_cubic<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_back_cubic, ease_out_back_cubic)
}

pub fn ease_in_out_back_quadtric<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_back_quadric, ease_out_back_quadric)
}

pub fn ease_in_out_elasic_small<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_elastic_small, ease_out_elastic_small)
}

pub fn ease_in_out_elasic_big<T: Float + Ord>(value: T) -> T {
ease_in_out(value, ease_in_elastic_big, ease_out_elastic_big)
}

pub fn ease_in_out<T, F1, F2>(value: T, f1: F1, f2: F2) -> T
where
T: Float + Ord,
F1: FnOnce(T) -> T,
F2: FnOnce(T) -> T,
{
if value < T::new(0.5) {
T::new(0.5) * f1(T::new(2.0) * value)
} else {
T::new(0.5) * f2(T::new(2.0) * (value - T::new(0.5))) + T::new(0.5)
}
}

+ 1
- 0
glc/src/lib.rs View File

@@ -1,4 +1,5 @@
pub mod angle;
pub mod animation;
pub mod array_buffer;
pub mod error;
pub mod math;


+ 6
- 4
glc/src/math.rs View File

@@ -1,5 +1,7 @@
use std::cmp::PartialOrd;
use std::ops::{Div, Mul, Sub};
use std::ops::{Mul, Sub};

use super::numeric::Float;

#[inline]
pub fn min<T>(a: T, b: T) -> T
@@ -44,15 +46,15 @@ where
#[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>,
T: Float + PartialOrd,
{
clamp(low, high, (value - low) / (high - low))
clamp(T::zero(), T::one(), (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>,
T: Float + PartialOrd,
f32: Mul<T, Output = T> + Sub<T, Output = T>,
{
let x = linear_step(low, high, value);


+ 28
- 9
glc/src/matrix.rs View File

@@ -620,18 +620,37 @@ where
}
}

impl<T, M> Mul<M> for Matrix4<T>
where
T: Float,
M: Borrow<Matrix4<T>>,
{
type Output = Self;
macro_rules! impl_mul_mat4 {
($input:ty, $output:ty, $func:ident) => {
impl<T> Mul<$input> for Matrix4<T>
where
T: Float,
{
type Output = $output;

fn mul(self, rhs: M) -> Self::Output {
self.multiply(rhs.borrow())
}
fn mul(self, rhs: $input) -> Self::Output {
self.$func(&rhs)
}
}

impl<T> Mul<$input> for &Matrix4<T>
where
T: Float,
{
type Output = $output;

fn mul(self, rhs: $input) -> Self::Output {
self.$func(&rhs)
}
}
};
}

impl_mul_mat4!(Matrix4<T>, Matrix4<T>, multiply);
impl_mul_mat4!(&Matrix4<T>, Matrix4<T>, multiply);
impl_mul_mat4!(Vector4<T>, Vector4<T>, transform);
impl_mul_mat4!(&Vector4<T>, Vector4<T>, transform);

#[cfg(test)]
mod tests {
use super::*;


+ 11
- 0
glc/src/numeric.rs View File

@@ -21,6 +21,7 @@ pub trait Numeric:
}

pub trait Float: Numeric {
fn new(value: f64) -> Self;
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
@@ -68,6 +69,11 @@ impl Numeric for gl::GLfloat {
}

impl Float for gl::GLfloat {
#[inline]
fn new(value: f64) -> Self {
value as _
}

#[inline]
fn sin(self) -> Self {
f32::sin(self)
@@ -142,6 +148,11 @@ impl Numeric for gl::GLdouble {
}

impl Float for gl::GLdouble {
#[inline]
fn new(value: f64) -> Self {
value as _
}

#[inline]
fn sin(self) -> Self {
f64::sin(self)


+ 5
- 1
glc/src/shader.rs View File

@@ -9,7 +9,7 @@ use crate::{
error::Error,
matrix::Matrix4f,
misc::{AsEnum, Bindable},
vector::Vector4f,
vector::{Vector2f, Vector4f},
};

/* Programm */
@@ -84,6 +84,8 @@ impl Program {
match uniform {
Uniform::Matrix4f(v) => gl::uniform_matrix_4fv(location, 1, gl::FALSE, v.as_ptr()),
Uniform::Vector4f(v) => gl::uniform_4fv(location, 1, v.as_ptr()),
Uniform::Vector2f(v) => gl::uniform_2fv(location, 1, v.as_ptr()),
Uniform::Float(v) => gl::uniform_1f(location, v),
Uniform::Texture(i) => gl::uniform_1i(location, i),
}

@@ -280,6 +282,8 @@ impl AsEnum for Type {
pub enum Uniform<'a> {
Matrix4f(&'a Matrix4f),
Vector4f(&'a Vector4f),
Vector2f(&'a Vector2f),
Float(f32),
Texture(gl::GLint),
}



+ 1
- 1
glc/src/vector.rs View File

@@ -30,7 +30,7 @@ macro_rules! define_vec {

impl<T> $Name<T> {
#[inline]
pub fn new($($f: T,)+) -> Self {
pub const fn new($($f: T,)+) -> Self {
Self { $($f,)+ }
}



+ 0
- 1
space-crush-app/Cargo.toml View File

@@ -9,7 +9,6 @@ gl = { version = "0.1", features = [ "use_log_crate" ] }
glc = "0.1"
glutin = { version = "0.25", features = [ "serde" ] }
glyph_brush = "0.7"
lazy_static = "1.4"
log = { version = "0.4", features = [ "max_level_trace", "release_max_level_warn" ] }
log4rs = "0.13"
ordered-float = "2.0"


+ 96
- 0
space-crush-app/resources/shader/fleet_select/simple/frag.glsl View File

@@ -0,0 +1,96 @@
#version 450 core

#pragma include ./shared.glsl
#pragma include ../../misc/camera.glsl

in FragmentData fragmentData;

uniform vec2 uRings;
uniform vec2 uMarker;
uniform float uProgress;
uniform float uSize;
uniform float uValue;

out vec4 outColor;

const float RING_STRENGTH = 0.0000;
const float RING_BLUR = 0.0020;
const float MARKER_STRENGTH = 0.0000;
const float MARKER_BLUR = 0.0020;
const vec2 VEC_HORZ = vec2(1.0, 0.0);
const float PI = 3.1415926535897932384626433832795;

float smoothstep2(float b0, float b1, float b2, float b3, float v) {
return smoothstep(b0, b1, v) * (1.0 - smoothstep(b2, b3, v));
}

float calcLine(float actual, float expected, float strength, float blur)
{
return smoothstep2(
expected - strength - blur,
expected - strength,
expected + strength,
expected + strength + blur,
actual);
}

float calcAngle(vec2 v1, vec2 v2)
{
v1 = -v1;
return -atan(
v1.x * v2.y - v1.y * v2.x,
v1.x * v2.x + v1.y * v2.y);
}

float random(float seed) {
return fract(sin(dot(vec4(seed), vec4(12.9898, 78.233, 45.164, 53.1324))) * 43758.5453);
}

float animate(float v0, float v1, float t) {
float x = t * t * (3.0 - 2.0 * t);

return v0 + x * (v1 - v0);
}

void main() {
float ts = clamp(uProgress, 0.0, 1.0);
float te = clamp(uProgress - 1.0, 0.0, 1.0);
float r = length(fragmentData.texCoords);
float zoom = pow(length(uCamera.view[0].xyz), 0.5);
float angle = calcAngle(fragmentData.texCoords, VEC_HORZ);

/* marker */
float m = calcAngle(uMarker, VEC_HORZ);
m = calcLine(angle, m, MARKER_STRENGTH / r / zoom, MARKER_BLUR / r / zoom);
m *= smoothstep2(
ts * (uRings[0] - 0.25),
ts * uRings[0],
ts * uRings[1],
ts * (uRings[1] + 0.10),
r);
m *= 0.75;

/* rings */
float f = angle / PI + 1.0;
f = f * 2.0;
f = fract(f + 0.5);
f = smoothstep2(
0.0 - (ts - 0.5),
0.2 - (ts - 0.5),
0.8 + (ts - 0.5),
1.0 + (ts - 0.5),
f);

float r0 = 0.25 * f * calcLine(r, animate(uSize, uRings[0], ts), RING_STRENGTH / zoom, RING_BLUR / zoom);
float r1 = 0.25 * f * calcLine(r, animate(uSize, uRings[1], ts), RING_STRENGTH / zoom, RING_BLUR / zoom);
float v = 0.75 * f * calcLine(r, animate(uSize, uValue, ts), RING_STRENGTH / zoom, RING_BLUR / zoom);

/* alpha */
float as = step(1.0 - ts, random(uProgress));
float ae = (1.0 - te) * step(te, random(uProgress));
float a = as * ae;

/* put together */
vec3 rgb = vec3(1.0);
outColor = vec4(rgb, (v + r0 + r1 + m) * a + uSize * 0.0001);
}

+ 3
- 0
space-crush-app/resources/shader/fleet_select/simple/shared.glsl View File

@@ -0,0 +1,3 @@
struct FragmentData {
vec2 texCoords;
};

+ 15
- 0
space-crush-app/resources/shader/fleet_select/simple/vert.glsl View File

@@ -0,0 +1,15 @@
#version 450 core

#pragma include ./shared.glsl
#pragma include ../../misc/camera.glsl

in vec3 inPosition;

uniform mat4 uModel;

out FragmentData fragmentData;

void main() {
fragmentData.texCoords = 2.0 * inPosition.xy;
gl_Position = uCamera.projection * uCamera.view * uModel * vec4(2.0 * inPosition, 1.0);
}

+ 3
- 2
space-crush-app/resources/shader/text/frag.glsl View File

@@ -4,7 +4,8 @@

in FragmentData fragmentData;

uniform sampler2D uTexture;
layout (location = 0) uniform sampler2D uTexture;
layout (location = 1) uniform vec4 uColor;

out vec4 outColor;

@@ -15,5 +16,5 @@ void main() {
discard;
}

outColor = fragmentData.color * vec4(1.0, 1.0, 1.0, alpha);
outColor = fragmentData.color * uColor * vec4(1.0, 1.0, 1.0, alpha);
}

+ 3
- 1
space-crush-app/resources/shader/text/vert.glsl View File

@@ -9,6 +9,8 @@ layout (location = 2) in vec2 inTexMin;
layout (location = 3) in vec2 inTexMax;
layout (location = 4) in vec4 inColor;

layout (location = 2) uniform vec2 uOffset;

out FragmentData fragmentData;

void main() {
@@ -44,7 +46,7 @@ void main() {
vec4(-1.0, 1.0, 1.0, 1.0)
);

gl_Position = ortho * vec4(position, 0.0, 1.0);
gl_Position = ortho * vec4(position + uOffset, 0.0, 1.0);

fragmentData.texCoords = texCoords;
fragmentData.color = inColor;


+ 9
- 4
space-crush-app/src/constants.rs View File

@@ -1,5 +1,6 @@
use std::time::Duration;

use glc::vector::Vector4f;
use lazy_static::lazy_static;

pub const UNIFORM_BUFFER_INDEX_CAMERA: gl::GLuint = 0;
pub const UNIFORM_BUFFER_INDEX_UNIFORM: gl::GLuint = 1;
@@ -8,6 +9,10 @@ pub const SHIP_SIZE: f32 = 15.0;
pub const PLANET_SIZE: f32 = 200.0;
pub const ASTEROID_SIZE: f32 = 100.0;

lazy_static! {
pub static ref PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1);
}
pub const FLEET_SELECT_DETAIL_TIMEOUT: Duration = Duration::from_millis(500);
pub const FLEET_SELECT_OFFSET: f32 = 25.0;
pub const FLEET_SELECT_WIDTH: f32 = 125.0;
pub const FLEET_SELECT_TEXT_OFFSET: f32 = 50.0;
pub const FLEET_SELECT_ANIMATION_TIME: f32 = 0.500;

pub const PLAYER_COLOR_DEFAULT: Vector4f = Vector4f::new(1.0, 1.0, 1.0, 0.1);

+ 6
- 1
space-crush-app/src/debug/summary.rs View File

@@ -101,7 +101,12 @@ impl<'a> System<'a> for Summary {
);
drop(guard);

self.text.render(true);
gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

self.text.render();

gl::disable(gl::BLEND);
}
}



+ 2
- 1
space-crush-app/src/lib.rs View File

@@ -13,7 +13,7 @@ pub use error::Error;

use debug::{Fleets as DebugFleets, Ships as DebugShips, Summary as DebugSummary};
use misc::{Events, TextManager, Window};
use render::{Asteroids, Init, Planets, Ships};
use render::{Asteroids, Init, Planets, SelectFleet, Ships};
use resources::{Camera, Config, Geometry, InputState, PlayerState, Uniform};
use systems::{FleetInfoUpdate, StateUpdate};

@@ -52,6 +52,7 @@ impl<'a, 'b> App<'a, 'b> {
.with_thread_local(Planets::new(world)?)
.with_thread_local(Asteroids::new(world)?)
.with_thread_local(Ships::new(world)?)
.with_thread_local(SelectFleet::new(world, &text_manager)?)
.with_thread_local(DebugShips::default())
.with_thread_local(DebugFleets::default())
.with_thread_local(DebugSummary::new(&text_manager)?)


+ 1
- 1
space-crush-app/src/misc/mod.rs View File

@@ -6,6 +6,6 @@ mod world;
pub use events::{
ControlEvent, Events, KeyboardEvent, MouseButton, MouseEvent, VirtualKeyCode, WindowEvent,
};
pub use text::{Text, TextCache, TextManager};
pub use text::{HorizontalAlign, Text, TextCache, TextManager, VerticalAlign};
pub use window::Window;
pub use world::WorldHelper;

+ 75
- 17
space-crush-app/src/misc/text.rs View File

@@ -14,15 +14,15 @@ use glc::{
array_buffer::{ArrayBuffer, Target, Usage},
error::Error as GlcError,
misc::BindGuard,
shader::{Program, Type},
shader::{Program, Type, Uniform},
texture::{FilterMag, FilterMin, Target as TextureTarget, Texture, Wrap},
vector::{Vector2f, Vector4f},
vertex_array::{DataType, VertexArray},
};
use glyph_brush::{
ab_glyph::{FontArc, FontVec},
BrushAction, BrushError, FontId, GlyphBrush, GlyphBrushBuilder, GlyphVertex, OwnedSection,
OwnedText, Rectangle,
BrushAction, BrushError, FontId, GlyphBrush, GlyphBrushBuilder, GlyphVertex, Layout,
OwnedSection, OwnedText, Rectangle,
};
use ordered_float::OrderedFloat;
use space_crush_common::misc::{LogResult, Vfs, WorldHelper as _};
@@ -30,6 +30,8 @@ use specs::World;

use crate::{misc::WorldHelper, Error};

pub use glyph_brush::{HorizontalAlign, VerticalAlign};

/* TextManager */

#[derive(Clone)]
@@ -391,6 +393,10 @@ enum BuilderItem {
Font(String),
Text(String),
Scale(f32),
VertAlign(VerticalAlign),
HorzAlign(HorizontalAlign),
Wrap,
NoWrap,
}

impl TextBuilder {
@@ -408,6 +414,9 @@ impl TextBuilder {
let mut font_id = None;
let mut color = Vector4f::new(0.0, 0.0, 0.0, 1.0);
let mut position = None;
let mut wrap = true;
let mut vert_align = VerticalAlign::Top;
let mut horz_align = HorizontalAlign::Left;

for item in self.items {
match item {
@@ -418,12 +427,20 @@ impl TextBuilder {
.with_scale(scale)
.with_font_id(font_id);

let layout = if wrap {
Layout::default_wrap()
} else {
Layout::default_single_line()
};
let layout = layout.v_align(vert_align).h_align(horz_align);

match (sections.pop(), position.take()) {
(Some(section), Some(pos)) => {
sections.push(section);
sections.push(
OwnedSection::default()
.with_screen_position(pos)
.with_layout(layout)
.add_text(text),
);
}
@@ -431,19 +448,23 @@ impl TextBuilder {
sections.push(section.add_text(text));
}
(None, Some(pos)) => sections.push(
OwnedSection::<Extra>::default()
OwnedSection::default()
.with_screen_position(pos)
.with_layout(layout)
.add_text(text),
),
(None, None) => {
sections.push(OwnedSection::<Extra>::default().add_text(text))
}
(None, None) => sections
.push(OwnedSection::default().with_layout(layout).add_text(text)),
};
}
BuilderItem::Position(x, y) => position = Some((x, y)),
BuilderItem::Color(c) => color = c,
BuilderItem::Scale(s) => scale = s,
BuilderItem::Font(path) => font_id = Some(self.cache.font(&path)?),
BuilderItem::VertAlign(value) => vert_align = value,
BuilderItem::HorzAlign(value) => horz_align = value,
BuilderItem::Wrap => wrap = true,
BuilderItem::NoWrap => wrap = false,
}
}

@@ -494,6 +515,30 @@ impl TextBuilder {

self
}

pub fn wrap(mut self) -> Self {
self.items.push(BuilderItem::Wrap);

self
}

pub fn nowrap(mut self) -> Self {
self.items.push(BuilderItem::NoWrap);

self
}

pub fn vert_align(mut self, value: VerticalAlign) -> Self {
self.items.push(BuilderItem::VertAlign(value));

self
}

pub fn horz_align(mut self, value: HorizontalAlign) -> Self {
self.items.push(BuilderItem::HorzAlign(value));

self
}
}

/* Text */
@@ -509,6 +554,7 @@ struct TextInner {
texture: Rc<RefCell<Texture>>,
sections: Vec<OwnedSection<Extra>>,
vertex_count: usize,
color: Vector4f,
}

#[derive(Default, Debug, Clone)]
@@ -570,6 +616,7 @@ impl Text {
texture,
sections,
vertex_count: 0,
color: Vector4f::new(1.0, 1.0, 1.0, 1.0),
};
let text = Text {
cache,
@@ -579,23 +626,28 @@ impl Text {
Ok(text)
}

pub fn render(&self, enable_blending: bool) {
pub fn render(&self) {
self.render_offset(&Vector2f::new(0.0, 0.0));
}

pub fn render_offset(&self, pos: &Vector2f) {
let inner = self.inner.borrow();
let texture = inner.texture.borrow();

if enable_blending {
gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
}

let _guard = BindGuard::new(&*texture);
let _guard = BindGuard::new(&inner.array);
let _guard = BindGuard::new(&*inner.program);
gl::draw_arrays_instanced(gl::TRIANGLE_STRIP, 0, 4, inner.vertex_count as _);

if enable_blending {
gl::disable(gl::BLEND);
}
inner
.program
.uniform(1, Uniform::Vector4f(&inner.color))
.warn("Unable to update color");
inner
.program
.uniform(2, Uniform::Vector2f(pos))
.warn("Unable to update text offset");

gl::draw_arrays_instanced(gl::TRIANGLE_STRIP, 0, 4, inner.vertex_count as _);
}

pub fn update<S>(&mut self, mut index: usize, text: S) -> Result<(), Error>
@@ -618,6 +670,12 @@ impl Text {

self.cache.update()
}

pub fn color(&self, color: Vector4f) {
let mut inner = self.inner.borrow_mut();

inner.color = color;
}
}

impl TextInner {


+ 1
- 1
space-crush-app/src/render/asteroids.rs View File

@@ -97,7 +97,7 @@ impl<'a> System<'a> for Asteroids {

let c = match owned.and_then(|owned| player.get(owned.owner)) {
Some(pv) => &pv.color,
None => &*PLAYER_COLOR_DEFAULT,
None => &PLAYER_COLOR_DEFAULT,
};

let m = Matrix4f::new(


+ 2
- 0
space-crush-app/src/render/mod.rs View File

@@ -1,9 +1,11 @@
mod asteroids;
mod init;
mod planets;
mod select_fleet;
mod ships;

pub use asteroids::Asteroids;
pub use init::Init;
pub use planets::Planets;
pub use select_fleet::SelectFleet;
pub use ships::Ships;

+ 1
- 1
space-crush-app/src/render/planets.rs View File

@@ -90,7 +90,7 @@ impl<'a> System<'a> for Planets {

let c = match owned.and_then(|owned| player.get(owned.owner)) {
Some(pv) => &pv.color,
None => &*PLAYER_COLOR_DEFAULT,
None => &PLAYER_COLOR_DEFAULT,
};

let m = Matrix4f::new(


+ 337
- 0
space-crush-app/src/render/select_fleet.rs View File

@@ -0,0 +1,337 @@
use std::time::Instant;

use glc::{
animation::{animate, linear},
math::{clamp, linear_step},
matrix::Matrix4f,
misc::BindGuard,
shader::{Program, Type, Uniform},
vector::{Vector2f, Vector4f},
};
use log::debug;
use shrev::{EventChannel, ReaderId};
use space_crush_common::{
components::{Fleet, Position, ShipCount},
misc::{LogResult, WorldHelper as _},
resources::Global,
};
use specs::{prelude::*, Entities, ReadExpect, ReadStorage, System, World};

use crate::{
components::FleetInfo,
constants::{
FLEET_SELECT_ANIMATION_TIME, FLEET_SELECT_DETAIL_TIMEOUT, FLEET_SELECT_OFFSET,
FLEET_SELECT_TEXT_OFFSET, FLEET_SELECT_WIDTH, UNIFORM_BUFFER_INDEX_CAMERA,
},
misc::{
HorizontalAlign, MouseEvent, Text, TextCache, TextManager, VerticalAlign, WorldHelper as _,
},
resources::{Camera, Config, Geometry, InputState, PlayerState, Selection},
Error,
};

pub struct SelectFleet {
program: Program,
location_model: gl::GLint,
location_rings: gl::GLint,
location_progress: gl::GLint,
location_marker: gl::GLint,
location_size: gl::GLint,
location_value: gl::GLint,
cache: TextCache,
text_total: Text,
select_mode: SelectMode,
mouse_event_id: ReaderId<MouseEvent>,
}

#[derive(Copy, Clone, Debug)]
enum SelectMode {
None,
Init { detail_timeout: Instant },
Simple { progress: f32 },
SimpleClose { progress: f32, mouse_pos: Vector2f },
Detail,
}

#[derive(SystemData)]
pub struct SelectFleetData<'a> {
player_state: WriteExpect<'a, PlayerState>,
camera: WriteExpect<'a, Camera>,

entities: Entities<'a>,
mouse_events: ReadExpect<'a, EventChannel<MouseEvent>>,
input_state: ReadExpect<'a, InputState>,
geometry: ReadExpect<'a, Geometry>,
global: ReadExpect<'a, Global>,
config: ReadExpect<'a, Config>,

fleet_infos: ReadStorage<'a, FleetInfo>,
positions: ReadStorage<'a, Position>,
fleets: ReadStorage<'a, Fleet>,
}

impl SelectFleet {
pub fn new(world: &World, text_manager: &TextManager) -> Result<Self, Error> {
let program = world.load_program(vec![
(
Type::Vertex,
"resources/shader/fleet_select/simple/vert.glsl",
),
(
Type::Fragment,
"resources/shader/fleet_select/simple/frag.glsl",
),
])?;
program.uniform_block_binding("Camera", UNIFORM_BUFFER_INDEX_CAMERA)?;

let location_model = program.uniform_location("uModel")?;
let location_rings = program.uniform_location("uRings")?;
let location_progress = program.uniform_location("uProgress")?;
let location_marker = program.uniform_location("uMarker")?;
let location_size = program.uniform_location("uSize")?;
let location_value = program.uniform_location("uValue")?;

let cache = text_manager.create_cache()?;
let text_total = cache
.new_text()
.scale(32.0)
.font("resources/fonts/DroidSansMono.ttf")
.color(1.0, 1.0, 1.0, 0.75)
.nowrap()
.vert_align(VerticalAlign::Center)
.horz_align(HorizontalAlign::Center)
.text("-")
.build()?;

let select_mode = SelectMode::None;
let mouse_event_id = world.register_event_reader::<MouseEvent>()?;

Ok(Self {
program,
location_model,
location_rings,
location_progress,
location_marker,
location_size,
location_value,
cache,
text_total,
select_mode,
mouse_event_id,
})
}

fn handle_events(&mut self, d: &mut SelectFleetData<'_>) {
let events = d.mouse_events.read(&mut self.mouse_event_id);
for event in events {
match event {
MouseEvent::ButtonDown(button) if button == &d.config.input.fleet_select_button => {
let pos = d.input_state.mouse_pos;
let pos = Vector4f::new(pos.0, pos.1, 0.0, 1.0);
let pos = d.camera.view_inverted() * pos;
let pos = Vector2f::new(pos.x, pos.y);

self.select_mode = SelectMode::Init {
detail_timeout: Instant::now() + FLEET_SELECT_DETAIL_TIMEOUT,
};

let selection = d.player_state.selection.take();
for (id, position, fleet) in (&d.entities, &d.positions, &d.fleets).join() {
let r = fleet.orbit_max * fleet.orbit_max;
if (position.pos - pos).length_sqr() <= r {
d.player_state.selection = match selection {
Some(s) if s.fleet == id => {
debug!("Re-selected fleet: {:?}", id);

Some(s)
}
_ => {
debug!("Selected fleet: {:?}", id);

Some(Selection {
fleet: id,
count: ShipCount::all(),
})
}
};
break;
}
}
}
MouseEvent::ButtonUp(button) if button == &d.config.input.fleet_select_button => {
self.select_mode = match self.select_mode {
SelectMode::Simple { progress, .. } => SelectMode::SimpleClose {
progress,
mouse_pos: d.input_state.mouse_pos.into(),
},
_ => SelectMode::None,
}
}
MouseEvent::Move(_, _) if self.select_mode.is_init() => {
self.select_mode = SelectMode::Simple { progress: 0.0 }
}
_ => (),
}
}
}

fn render_simple(
&mut self,
progress: f32,
mouse_pos: Vector2f,
d: &mut SelectFleetData<'_>,
) -> Option<()> {
let selection = d.player_state.selection.as_mut()?;
let fleet_info = d.fleet_infos.get(selection.fleet)?;
let position = d.positions.get(selection.fleet)?;

let mut axis_x = d.camera.view().axis_x;
axis_x.w = 1.0;

let zoom = axis_x.length();
let shape_size = position.shape.radius().unwrap_or(0.0);
let ring0 = shape_size + FLEET_SELECT_OFFSET / zoom;
let ring1 = ring0 + FLEET_SELECT_WIDTH / zoom;
let size = ring1 + 50.0;
let px = position.pos.x;
let py = position.pos.y;
let m = Matrix4f::new(
Vector4f::new(size, 0.0, 0.0, 0.0),
Vector4f::new(0.0, size, 0.0, 0.0),
Vector4f::new(0.0, 0.0, size, 0.0),
Vector4f::new(px, py, 0.0, 1.0),
);

let rings = Vector2f::new(ring0 / size, ring1 / size);

let marker = mouse_pos;
let marker = Vector4f::new(marker.x, marker.y, 0.0, 1.0);
let marker = d.camera.view_inverted() * marker;
let marker = Vector2f::new(marker.x, marker.y);
let marker = marker - position.pos;

let value = marker.length();
let value = if value > ring1 {
selection.count = ShipCount::all();

let _guard = self.cache.begin_update();
self.text_total
.update(0, fleet_info.count.total().to_string())
.error("Unable to update text");

ring1
} else if value > shape_size {
let value = linear_step(ring0, ring1, value);

selection.count = fleet_info.count * value;

let _guard = self.cache.begin_update();
self.text_total
.update(0, selection.count.total().to_string())
.error("Unable to update text");

animate(ring0, ring1, value, linear)
} else {
let value = selection.count.total() as f32 / fleet_info.count.total() as f32;
let value = clamp(0.0, 1.0, value);

animate(ring0, ring1, value, linear)
};

let guard = BindGuard::new(&self.program);
self.program
.uniform(self.location_model, Uniform::Matrix4f(&m))
.error("Error while updating model matrix");
self.program
.uniform(self.location_rings, Uniform::Vector2f(&rings))
.error("Error while updating rings");
self.program
.uniform(self.location_progress, Uniform::Float(progress))
.error("Error while updating progress");
self.program
.uniform(self.location_marker, Uniform::Vector2f(&marker))
.error("Error while updating marker");
self.program
.uniform(self.location_value, Uniform::Float(value / size))
.error("Error while updating value");
self.program
.uniform(self.location_size, Uniform::Float(shape_size / size))
.error("Error while updating size");

d.geometry.render_quad();
drop(guard);

let pos = marker.normalize() * (ring1 + FLEET_SELECT_TEXT_OFFSET / zoom.sqrt());
let pos = position.pos + pos;
let pos = Vector4f::new(pos.x, pos.y, 0.0, 1.0);
let pos = d.camera.view() * pos;
let mut pos = Vector2f::new(pos.x, pos.y);
pos.x += d.input_state.resolution.0 as f32 / 2.0;
pos.y = d.input_state.resolution.1 as f32 / 2.0 - pos.y;

if progress <= 1.0 {
self.text_total
.color(Vector4f::new(1.0, 1.0, 1.0, progress));
} else {
self.text_total
.color(Vector4f::new(1.0, 1.0, 1.0, 1.0 - (progress - 1.0)));
}

self.text_total.render_offset(&pos);

Some(())
}
}

impl SelectMode {
pub fn is_init(&self) -> bool {
matches!(self, Self::Init { .. })
}
}

impl<'a> System<'a> for SelectFleet {
type SystemData = SelectFleetData<'a>;

fn run(&mut self, mut data: Self::SystemData) {
self.handle_events(&mut data);

gl::enable(gl::BLEND);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

match &mut self.select_mode {
SelectMode::None => (),
SelectMode::Init { detail_timeout } => {
if *detail_timeout < Instant::now() {
self.select_mode = SelectMode::Detail;
}
}
SelectMode::Simple { progress } => {
let delta = data.global.delta / FLEET_SELECT_ANIMATION_TIME;
*progress = clamp(0.0, 1.0, *progress + delta);

let progress = *progress;
self.render_simple(progress, data.input_state.mouse_pos.into(), &mut data);
}
SelectMode::SimpleClose {
progress,
mouse_pos,
} => {
let mouse_pos = *mouse_pos;
*progress += data.global.delta / FLEET_SELECT_ANIMATION_TIME;

let progress = if *progress > 2.0 {
self.select_mode = SelectMode::None;

2.0
} else {
*progress
};

self.render_simple(progress, mouse_pos, &mut data);
}
SelectMode::Detail => (),
}

gl::disable(gl::BLEND);
}
}

+ 1
- 1
space-crush-app/src/render/ships.rs View File

@@ -98,7 +98,7 @@ impl<'a> System<'a> for Ships {

let c = match owned.and_then(|owned| player.get(owned.owner)) {
Some(pv) => &pv.color,
None => &*PLAYER_COLOR_DEFAULT,
None => &PLAYER_COLOR_DEFAULT,
};

let p_x = p.pos.x;


+ 55
- 24
space-crush-app/src/resources/camera.rs View File

@@ -9,36 +9,72 @@ use glc::{

pub struct Camera {
buffer: ArrayBuffer,
data: Data,
view_inverted: Option<Matrix4f>,
}

#[repr(C, packed)]
#[derive(Clone)]
struct Data {
projection: Matrix4f,
view: Matrix4f,
size: Vector2f,
}

impl Camera {
pub fn new() -> Result<Self, Error> {
let data = Data {
projection: Matrix4f::identity(),
view: Matrix4f::identity(),
size: Vector2f::default(),
};
let mut buffer = ArrayBuffer::new(Target::UniformBuffer)?;
buffer.buffer_data(
Usage::StaticDraw,
&[Data {
projection: Matrix4f::identity(),
view: Matrix4f::identity(),
size: Vector2f::default(),
}],
)?;

Ok(Self { buffer })
buffer.buffer_data(Usage::StaticDraw, &[data.clone()])?;

Ok(Self {
buffer,
data,
view_inverted: None,
})
}

pub fn projection(&self) -> &Matrix4f {
&self.data.projection
}

pub fn view(&self) -> &Matrix4f {
&self.data.view
}

pub fn view_inverted(&mut self) -> &Matrix4f {
if self.view_inverted.is_none() {
self.view_inverted = Some(self.data.view.invert());
}

self.view_inverted.as_ref().unwrap()
}

pub fn size(&self) -> &Vector2f {
&self.data.size
}

pub fn resize(&mut self, w: f32, h: f32) -> Result<(), Error> {
let mut data = self.buffer.map_mut::<Data>(true)?;
self.data.projection = Matrix4f::ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0);
self.data.size = (w, h).into();

data[0].projection = Matrix4f::ortho(-w / 2.0, w / 2.0, -h / 2.0, h / 2.0, -100.0, 100.0);
data[0].size = (w, h).into();
let mut data = self.buffer.map_mut::<Data>(true)?;
data[0].projection = self.data.projection;
data[0].size = self.data.size;

Ok(())
}

pub fn update(&mut self, view: Matrix4f) -> Result<(), Error> {
let mut data = self.buffer.map_mut::<Data>(true)?;
self.view_inverted = None;
self.data.view = self.data.view * view;

data[0].view = data[0].view * view;
let mut data = self.buffer.map_mut::<Data>(true)?;
data[0].view = view;

Ok(())
}
@@ -47,9 +83,11 @@ impl Camera {
where
F: FnOnce(&Matrix4f) -> Matrix4f,
{
let mut data = self.buffer.map_mut::<Data>(true)?;
self.view_inverted = None;
self.data.view = f(&self.data.view);

data[0].view = f(&data[0].view);
let mut data = self.buffer.map_mut::<Data>(true)?;
data[0].view = self.data.view;

Ok(())
}
@@ -58,10 +96,3 @@ impl Camera {
Error::checked(|| self.buffer.bind_buffer_base(index))
}
}

#[repr(C, packed)]
struct Data {
projection: Matrix4f,
view: Matrix4f,
size: Vector2f,
}

+ 7
- 0
space-crush-app/src/resources/config.rs View File

@@ -44,6 +44,8 @@ pub struct Input {

#[serde(default = "defaults::camera_move_button")]
pub camera_move_button: MouseButton,
#[serde(default = "defaults::fleet_select_button")]
pub fleet_select_button: MouseButton,

#[serde(default = "defaults::camera_move_speed")]
pub camera_move_speed: f32,
@@ -92,6 +94,7 @@ impl Default for Input {
camera_move_key_right: defaults::camera_move_key_right(),

camera_move_button: defaults::camera_move_button(),
fleet_select_button: defaults::fleet_select_button(),

camera_move_speed: defaults::camera_move_speed(),
camera_zoom_speed: defaults::camera_zoom_speed(),
@@ -138,6 +141,10 @@ mod defaults {
MouseButton::Right
}

pub fn fleet_select_button() -> MouseButton {
MouseButton::Left
}

pub fn camera_move_speed() -> f32 {
500.0
}


+ 1
- 1
space-crush-app/src/resources/mod.rs View File

@@ -9,5 +9,5 @@ pub use camera::Camera;
pub use config::Config;
pub use geometry::Geometry;
pub use input_state::InputState;
pub use player_state::PlayerState;
pub use player_state::{PlayerState, Selection};
pub use uniform::Uniform;

+ 11
- 1
space-crush-app/src/resources/player_state.rs View File

@@ -1,13 +1,23 @@
#![allow(dead_code)]

use space_crush_common::components::ShipCount;
use specs::Entity;

pub struct PlayerState {
pub player_id: Entity,
pub selection: Option<Selection>,
}

pub struct Selection {
pub fleet: Entity,
pub count: ShipCount,
}

impl PlayerState {
pub fn new(player_id: Entity) -> Self {
Self { player_id }
Self {
player_id,
selection: None,
}
}
}

+ 0
- 1
space-crush-common/Cargo.toml View File

@@ -8,7 +8,6 @@ edition = "2018"
glc = { version = "0.1", features = [ "serde" ] }
hibitset = "0.6"
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"


+ 7
- 0
space-crush-common/src/components/position.rs View File

@@ -19,6 +19,13 @@ impl Component for Position {
}

impl Shape {
pub fn radius(&self) -> Option<f32> {
match self {
Self::Dot => Some(0.0),
Self::Circle(r) => Some(*r),
}
}

pub fn circle(&self) -> Option<f32> {
if let Self::Circle(r) = &self {
Some(*r)


+ 55
- 2
space-crush-common/src/components/ship.rs View File

@@ -1,4 +1,4 @@
use std::ops::{Index, IndexMut};
use std::ops::{Index, IndexMut, Mul};

use glc::{matrix::Angle, vector::Vector2f};
use serde::{Deserialize, Serialize};
@@ -12,7 +12,7 @@ pub struct Ship {
pub target_dir: Vector2f,
}

#[derive(Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default)]
pub struct Count {
pub fighter: usize,
pub bomber: usize,
@@ -30,6 +30,22 @@ impl Component for Ship {
type Storage = VecStorage<Self>;
}

impl Count {
pub fn all() -> Self {
Self {
fighter: usize::MAX,
bomber: usize::MAX,
transporter: usize::MAX,
}
}

pub fn total(&self) -> usize {
self.fighter
.saturating_add(self.bomber)
.saturating_add(self.transporter)
}
}

impl Index<Type> for Count {
type Output = usize;

@@ -51,3 +67,40 @@ impl IndexMut<Type> for Count {
}
}
}

impl Mul<f32> for Count {
type Output = Count;

#[allow(unused_assignments)]
fn mul(self, rhs: f32) -> Self::Output {
let expected = self.total() as f32;
let expected = (rhs * expected).ceil() as usize;

let mut fighter = (rhs * self.fighter as f32) as usize;
let mut bomber = (rhs * self.bomber as f32) as usize;
let mut transporter = (rhs * self.transporter as f32) as usize;

let mut actual = fighter.saturating_add(bomber).saturating_add(transporter);

if actual < expected && fighter < self.fighter {
fighter += 1;
actual += 1;
}

if actual < expected && bomber < self.bomber {
bomber += 1;
actual += 1;
}

if actual < expected && transporter < self.transporter {
transporter += 1;
actual += 1;
}

Count {
fighter,
bomber,
transporter,
}
}
}

+ 1
- 4
space-crush-common/src/constants.rs View File

@@ -1,5 +1,4 @@
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;
@@ -13,6 +12,4 @@ 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);
}
pub const VECTOR_2F_POS_X: Vector2f = Vector2f::new(1.0, 0.0);

Loading…
Cancel
Save