| @@ -306,9 +306,9 @@ where | |||
| } | |||
| #[inline] | |||
| pub fn multiply(&self, other: &Self) -> Self { | |||
| pub fn multiply<M: Borrow<Self>>(&self, other: M) -> Self { | |||
| let m1 = self; | |||
| let m2 = other; | |||
| let m2 = other.borrow(); | |||
| macro_rules! mul { | |||
| ($x:tt, $y:tt) => { | |||
| @@ -440,9 +440,9 @@ where | |||
| } | |||
| #[inline] | |||
| pub fn multiply(&self, other: &Self) -> Self { | |||
| pub fn multiply<M: Borrow<Self>>(&self, other: M) -> Self { | |||
| let m1 = self; | |||
| let m2 = other; | |||
| let m2 = other.borrow(); | |||
| macro_rules! mul { | |||
| ($x:tt, $y:tt) => { | |||
| @@ -462,8 +462,9 @@ where | |||
| } | |||
| #[inline] | |||
| pub fn transform(&self, v: &Vector4<T>) -> Vector4<T> { | |||
| pub fn transform<V: Into<Vector4<T>>>(&self, v: V) -> Vector4<T> { | |||
| let m = self; | |||
| let v = V::into(v); | |||
| macro_rules! mul { | |||
| ($i:tt) => { | |||
| @@ -629,7 +630,7 @@ macro_rules! impl_mul_mat4 { | |||
| type Output = $output; | |||
| fn mul(self, rhs: $input) -> Self::Output { | |||
| self.$func(&rhs) | |||
| self.$func(rhs) | |||
| } | |||
| } | |||
| @@ -640,7 +641,7 @@ macro_rules! impl_mul_mat4 { | |||
| type Output = $output; | |||
| fn mul(self, rhs: $input) -> Self::Output { | |||
| self.$func(&rhs) | |||
| self.$func(rhs) | |||
| } | |||
| } | |||
| }; | |||
| @@ -649,7 +650,6 @@ macro_rules! impl_mul_mat4 { | |||
| 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 { | |||
| @@ -2,19 +2,45 @@ use glc::vector::{Angle, Vector2f, Vector4f}; | |||
| use space_crush_common::{ | |||
| components::{Fleet, Position}, | |||
| constants::SHIP_ORBIT_DISTANCE_MAX, | |||
| misc::LogResult, | |||
| }; | |||
| use specs::{prelude::*, ReadStorage, System, World, WriteExpect}; | |||
| use crate::resources::Geometry; | |||
| use crate::{ | |||
| components::FleetInfo, | |||
| misc::{HorizontalAlign, Text, TextManager, VerticalAlign}, | |||
| resources::{Camera, Geometry}, | |||
| Error, | |||
| }; | |||
| #[derive(Default)] | |||
| pub struct Fleets; | |||
| pub struct Fleets { | |||
| text: Text, | |||
| } | |||
| #[derive(SystemData)] | |||
| pub struct FleetData<'a> { | |||
| geometry: WriteExpect<'a, Geometry>, | |||
| position: ReadStorage<'a, Position>, | |||
| fleet: ReadStorage<'a, Fleet>, | |||
| camera: ReadExpect<'a, Camera>, | |||
| positions: ReadStorage<'a, Position>, | |||
| fleets: ReadStorage<'a, Fleet>, | |||
| fleet_infos: ReadStorage<'a, FleetInfo>, | |||
| } | |||
| impl Fleets { | |||
| pub fn new(manager: &TextManager) -> Result<Self, Error> { | |||
| let cache = manager.create_cache()?; | |||
| let text = cache | |||
| .new_text() | |||
| .scale(20.0) | |||
| .font("resources/fonts/DroidSansMono.ttf") | |||
| .color(0.7, 0.7, 0.7, 1.0) | |||
| .vert_align(VerticalAlign::Center) | |||
| .horz_align(HorizontalAlign::Center) | |||
| .text("") | |||
| .build()?; | |||
| Ok(Self { text }) | |||
| } | |||
| } | |||
| impl<'a> System<'a> for Fleets { | |||
| @@ -23,28 +49,47 @@ impl<'a> System<'a> for Fleets { | |||
| fn run(&mut self, data: Self::SystemData) { | |||
| let FleetData { | |||
| mut geometry, | |||
| position, | |||
| fleet, | |||
| camera, | |||
| positions, | |||
| fleets, | |||
| fleet_infos, | |||
| } = data; | |||
| gl::enable(gl::BLEND); | |||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
| for (p, f) in (&position, &fleet).join() { | |||
| for (position, fleet, fleet_info) in (&positions, &fleets, &fleet_infos).join() { | |||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE); | |||
| gl::blend_equation(gl::FUNC_ADD); | |||
| geometry.render_lines( | |||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
| &create_circle(p.pos, f.orbit_min), | |||
| &create_circle(position.pos, fleet.orbit_min), | |||
| ); | |||
| geometry.render_lines( | |||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
| &create_circle(p.pos, f.orbit_max), | |||
| &create_circle(position.pos, fleet.orbit_max), | |||
| ); | |||
| geometry.render_lines( | |||
| Vector4f::new(0.5, 0.5, 0.5, 0.05), | |||
| &create_circle(p.pos, SHIP_ORBIT_DISTANCE_MAX * f.orbit_max), | |||
| &create_circle(position.pos, SHIP_ORBIT_DISTANCE_MAX * fleet.orbit_max), | |||
| ); | |||
| gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); | |||
| gl::blend_equation(gl::FUNC_SUBTRACT); | |||
| self.text | |||
| .update( | |||
| 0, | |||
| format!( | |||
| "F:{}\nB:{}\nT:{}", | |||
| fleet_info.count.fighter, | |||
| fleet_info.count.bomber, | |||
| fleet_info.count.transporter | |||
| ), | |||
| ) | |||
| .panic("Unable to update text") | |||
| .render_offset(&camera.world_to_window(position.pos)); | |||
| } | |||
| gl::blend_equation(gl::FUNC_ADD); | |||
| gl::disable(gl::BLEND); | |||
| } | |||
| } | |||
| @@ -53,8 +53,8 @@ impl<'a, 'b> App<'a, 'b> { | |||
| .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(DebugShips::default()) | |||
| .with_thread_local(DebugFleets::new(&text_manager)?) | |||
| .with_thread_local(DebugSummary::new(&text_manager)?) | |||
| .build(); | |||
| dispatcher.setup(world); | |||
| @@ -83,7 +83,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(500.0, -500.0), | |||
| pos: Vector2f::new(1000.0, -1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| @@ -100,7 +100,7 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(500.0, 500.0), | |||
| pos: Vector2f::new(1000.0, 1000.0), | |||
| shape: Shape::Circle(100.0), | |||
| }) | |||
| .with(Fleet { | |||
| @@ -129,7 +129,22 @@ fn create_world(world: &mut World, player_id: Entity) { | |||
| .with(Planet {}) | |||
| .build(); | |||
| for i in 0..10 { | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| .with(Position { | |||
| pos: Vector2f::new(2000.0, 0.0), | |||
| shape: Shape::Circle(250.0), | |||
| }) | |||
| .with(Fleet { | |||
| orbit_min: 325.0, | |||
| orbit_max: 425.0, | |||
| }) | |||
| .with(FleetInfo::default()) | |||
| .with(Planet {}) | |||
| .build(); | |||
| for i in 0..30 { | |||
| world | |||
| .create_entity() | |||
| .marked::<<PersistWorld as Persistence>::Marker>() | |||
| @@ -513,11 +513,7 @@ impl SelectFleet { | |||
| let text_offset = self.ring1 + FLEET_SELECT_TEXT_OFFSET / self.zoom.sqrt(); | |||
| let pos = fleet_pos + T::into(dir).normalize() * text_offset; | |||
| let mut pos = (d.camera.view() * pos.into_vec4()).into_vec2(); | |||
| pos.x += d.input_state.resolution.0 as f32 / 2.0; | |||
| pos.y = d.input_state.resolution.1 as f32 / 2.0 - pos.y; | |||
| pos | |||
| d.camera.world_to_window(pos) | |||
| } | |||
| } | |||
| @@ -88,4 +88,12 @@ impl Camera { | |||
| pub fn bind(&self, index: gl::GLuint) -> Result<(), Error> { | |||
| Error::checked(|| self.buffer.bind_buffer_base(index)) | |||
| } | |||
| pub fn world_to_window<T: Into<Vector2f>>(&self, pos: T) -> Vector2f { | |||
| let mut pos = self.data.view.transform(T::into(pos)).into_vec2(); | |||
| pos.x += self.data.size.x / 2.0; | |||
| pos.y = self.data.size.y / 2.0 - pos.y; | |||
| pos | |||
| } | |||
| } | |||