Added basic radar

master
Mark 2023-12-31 17:58:17 -08:00
parent 8fbbf7f110
commit fa8a0f5761
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
12 changed files with 186 additions and 43 deletions

View File

@ -209,6 +209,12 @@ impl Content {
} }
} }
/// Get a texture from a handle
pub fn get_texture_handle(&self, name: &str) -> TextureHandle {
return *self.texture_index.get(name).unwrap();
// TODO: handle errors
}
/// Get a texture from a handle /// Get a texture from a handle
pub fn get_texture(&self, h: TextureHandle) -> &Texture { pub fn get_texture(&self, h: TextureHandle) -> &Texture {
// In theory, this could fail if h has a bad index, but that shouldn't ever happen. // In theory, this could fail if h has a bad index, but that shouldn't ever happen.

View File

@ -1,4 +1,4 @@
use cgmath::Point2; use cgmath::{Deg, InnerSpace, Point2};
use galactica_content::FactionHandle; use galactica_content::FactionHandle;
use std::time::Instant; use std::time::Instant;
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode}; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
@ -9,10 +9,61 @@ use crate::{
content::Content, content::Content,
inputstatus::InputStatus, inputstatus::InputStatus,
physics::{util, Physics, ShipHandle}, physics::{util, Physics, ShipHandle},
render::Sprite, render::{AnchoredUiPosition, ObjectSprite, UiSprite},
shipbehavior::{self, ShipBehavior}, shipbehavior::{self, ShipBehavior},
}; };
struct Ui {}
impl Ui {
fn new() -> Self {
Self {}
}
fn build_radar(&self, player: &ShipHandle, physics: &Physics, ct: &Content) -> Vec<UiSprite> {
let mut out = Vec::new();
let radar_range = 2000.0;
let radar_size = 300.0;
out.push(UiSprite {
texture: ct.get_texture_handle("ui::radar"),
pos: AnchoredUiPosition::NorthWest(Point2 { x: 10.0, y: -10.0 }),
dimensions: Point2 {
x: radar_size,
y: radar_size,
},
angle: Deg(0.0),
});
let (_, pr) = physics.get_ship_body(player).unwrap();
let pr = util::rigidbody_position(pr);
for (s, r) in physics.iter_ship_body() {
if s.physics_handle == *player {
continue;
}
let r = util::rigidbody_position(r);
let d = r - pr;
let m = d.magnitude() / radar_range;
if m < 0.8 {
out.push(UiSprite {
texture: ct.get_texture_handle("ui::blip"),
pos: AnchoredUiPosition::NorthWest(
Point2 {
x: radar_size / 2.0 + 10.0,
y: radar_size / -2.0 - 10.0,
} + (d / radar_range * 150.0),
),
dimensions: Point2 { x: 1.0, y: 1.0 } * 5.0f32.min((0.8 - m) * 50.0),
angle: Deg(0.0),
});
}
}
return out;
}
}
pub struct Game { pub struct Game {
pub input: InputStatus, pub input: InputStatus,
pub last_update: Instant, pub last_update: Instant,
@ -22,6 +73,7 @@ pub struct Game {
paused: bool, paused: bool,
pub time_scale: f32, pub time_scale: f32,
ui: Ui,
physics: Physics, physics: Physics,
shipbehaviors: Vec<Box<dyn ShipBehavior>>, shipbehaviors: Vec<Box<dyn ShipBehavior>>,
content: Content, content: Content,
@ -84,6 +136,7 @@ impl Game {
physics, physics,
shipbehaviors, shipbehaviors,
content: ct, content: ct,
ui: Ui::new(),
} }
} }
@ -140,8 +193,8 @@ impl Game {
self.last_update = Instant::now(); self.last_update = Instant::now();
} }
pub fn get_sprites(&self) -> Vec<Sprite> { pub fn get_object_sprites(&self) -> Vec<ObjectSprite> {
let mut sprites: Vec<Sprite> = Vec::new(); let mut sprites: Vec<ObjectSprite> = Vec::new();
sprites.append(&mut self.system.get_sprites()); sprites.append(&mut self.system.get_sprites());
sprites.extend(self.physics.get_ship_sprites()); sprites.extend(self.physics.get_ship_sprites());
@ -158,4 +211,10 @@ impl Game {
return sprites; return sprites;
} }
pub fn get_ui_sprites(&self) -> Vec<UiSprite> {
return self
.ui
.build_radar(&self.player, &self.physics, &self.content);
}
} }

View File

@ -1,7 +1,7 @@
use cgmath::{Deg, Point3}; use cgmath::{Deg, Point3};
use content::{OutfitSpace, TextureHandle}; use content::{OutfitSpace, TextureHandle};
use crate::{content, render::SubSprite}; use crate::{content, render::ObjectSubSprite};
/// Represents a gun attached to a specific ship at a certain gunpoint. /// Represents a gun attached to a specific ship at a certain gunpoint.
#[derive(Debug)] #[derive(Debug)]
@ -77,7 +77,7 @@ pub struct ShipOutfits {
// Minor performance optimization, since we // Minor performance optimization, since we
// rarely need to re-compute these. // rarely need to re-compute these.
engine_flare_sprites: Vec<SubSprite>, engine_flare_sprites: Vec<ObjectSubSprite>,
} }
impl<'a> ShipOutfits { impl<'a> ShipOutfits {
@ -177,7 +177,7 @@ impl<'a> ShipOutfits {
self.engine_flare_sprites = self self.engine_flare_sprites = self
.enginepoints .enginepoints
.iter() .iter()
.map(|p| SubSprite { .map(|p| ObjectSubSprite {
pos: Point3 { pos: Point3 {
x: p.pos.x, x: p.pos.x,
y: p.pos.y, y: p.pos.y,
@ -190,7 +190,7 @@ impl<'a> ShipOutfits {
.collect(); .collect();
} }
pub fn get_engine_flares(&self) -> Vec<SubSprite> { pub fn get_engine_flares(&self) -> Vec<ObjectSubSprite> {
return self.engine_flare_sprites.clone(); return self.engine_flare_sprites.clone();
} }
} }

View File

@ -2,7 +2,7 @@ use cgmath::{Point3, Vector2};
use rand::{self, Rng}; use rand::{self, Rng};
use super::SystemObject; use super::SystemObject;
use crate::{consts, content, render::Sprite}; use crate::{consts, content, render::ObjectSprite};
pub struct StarfieldStar { pub struct StarfieldStar {
/// Star coordinates, in world space. /// Star coordinates, in world space.
@ -59,7 +59,7 @@ impl System {
return s; return s;
} }
pub fn get_sprites(&self) -> Vec<Sprite> { pub fn get_sprites(&self) -> Vec<ObjectSprite> {
return self.bodies.iter().map(|x| x.get_sprite()).collect(); return self.bodies.iter().map(|x| x.get_sprite()).collect();
} }
} }

View File

@ -1,7 +1,7 @@
use cgmath::{Deg, Point3}; use cgmath::{Deg, Point3};
use galactica_content::TextureHandle; use galactica_content::TextureHandle;
use crate::render::Sprite; use crate::render::ObjectSprite;
pub struct SystemObject { pub struct SystemObject {
pub sprite_texture: TextureHandle, pub sprite_texture: TextureHandle,
@ -11,8 +11,8 @@ pub struct SystemObject {
} }
impl SystemObject { impl SystemObject {
pub(super) fn get_sprite(&self) -> Sprite { pub(super) fn get_sprite(&self) -> ObjectSprite {
return Sprite { return ObjectSprite {
texture: self.sprite_texture, texture: self.sprite_texture,
pos: self.pos, pos: self.pos,
angle: self.angle, angle: self.angle,

View File

@ -5,7 +5,7 @@ use rapier2d::{
geometry::{ColliderBuilder, ColliderHandle}, geometry::{ColliderBuilder, ColliderHandle},
}; };
use crate::{physics::util, render::Sprite}; use crate::{physics::util, render::ObjectSprite};
pub struct ProjectileBuilder { pub struct ProjectileBuilder {
pub rigid_body: RigidBodyBuilder, pub rigid_body: RigidBodyBuilder,
@ -51,14 +51,14 @@ impl Projectile {
return self.lifetime < 0.0; return self.lifetime < 0.0;
} }
pub fn get_sprite(&self, r: &RigidBody) -> Sprite { pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let pos = util::rigidbody_position(r); let pos = util::rigidbody_position(r);
let rot = util::rigidbody_rotation(r); let rot = util::rigidbody_rotation(r);
// Sprites point north at 0 degrees // Sprites point north at 0 degrees
let ang: Deg<f32> = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into(); let ang: Deg<f32> = rot.angle(Vector2 { x: 1.0, y: 0.0 }).into();
Sprite { ObjectSprite {
texture: self.sprite_texture, texture: self.sprite_texture,
pos: Point3 { pos: Point3 {
x: pos.x, x: pos.x,

View File

@ -13,7 +13,7 @@ use crate::{
content, content,
game::outfits, game::outfits,
physics::{util, ShipHandle}, physics::{util, ShipHandle},
render::Sprite, render::ObjectSprite,
}; };
pub struct ShipTickResult { pub struct ShipTickResult {
@ -157,14 +157,14 @@ impl Ship {
return ShipTickResult { projectiles: p }; return ShipTickResult { projectiles: p };
} }
pub fn get_sprite(&self, r: &RigidBody) -> Sprite { pub fn get_sprite(&self, r: &RigidBody) -> ObjectSprite {
let ship_pos = util::rigidbody_position(r); let ship_pos = util::rigidbody_position(r);
let ship_rot = util::rigidbody_rotation(r); let ship_rot = util::rigidbody_rotation(r);
// Sprites point north at 0 degrees // Sprites point north at 0 degrees
let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into(); let ship_ang: Deg<f32> = ship_rot.angle(Vector2 { x: 0.0, y: 1.0 }).into();
Sprite { ObjectSprite {
pos: (ship_pos.x, ship_pos.y, 1.0).into(), pos: (ship_pos.x, ship_pos.y, 1.0).into(),
texture: self.sprite_texture, texture: self.sprite_texture,
angle: -ship_ang, angle: -ship_ang,

View File

@ -6,5 +6,5 @@ pub use physics::Physics;
use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle}; use rapier2d::{dynamics::RigidBodyHandle, geometry::ColliderHandle};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct ShipHandle(pub(super) RigidBodyHandle, ColliderHandle); pub struct ShipHandle(pub(super) RigidBodyHandle, ColliderHandle);

View File

@ -10,7 +10,7 @@ use rapier2d::{
use std::{collections::HashMap, f32::consts::PI}; use std::{collections::HashMap, f32::consts::PI};
use super::{wrapper::Wrapper, ShipHandle}; use super::{wrapper::Wrapper, ShipHandle};
use crate::{content, game::outfits, objects, render::Sprite}; use crate::{content, game::outfits, objects, render::ObjectSprite};
/// Keeps track of all objects in the world that we can interact with. /// Keeps track of all objects in the world that we can interact with.
/// Also wraps our physics engine /// Also wraps our physics engine
@ -205,13 +205,13 @@ impl Physics {
}) })
} }
pub fn get_ship_sprites(&self) -> impl Iterator<Item = Sprite> + '_ { pub fn get_ship_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
self.ships self.ships
.values() .values()
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0])) .map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.physics_handle.0]))
} }
pub fn get_projectile_sprites(&self) -> impl Iterator<Item = Sprite> + '_ { pub fn get_projectile_sprites(&self) -> impl Iterator<Item = ObjectSprite> + '_ {
self.projectiles self.projectiles
.values() .values()
.map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.rigid_body])) .map(|x| x.get_sprite(&self.wrapper.rigid_body_set[x.rigid_body]))

View File

@ -4,20 +4,24 @@ use cgmath::{Deg, EuclideanSpace, Matrix4, Point2, Vector2, Vector3};
use galactica_content::Content; use galactica_content::Content;
use std::{iter, rc::Rc}; use std::{iter, rc::Rc};
use wgpu; use wgpu;
use winit::{self, dpi::PhysicalSize, window::Window}; use winit::{
self,
dpi::{LogicalSize, PhysicalSize},
window::Window,
};
use super::{ use super::{
consts::{OPENGL_TO_WGPU_MATRIX, SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT}, consts::{OPENGL_TO_WGPU_MATRIX, SPRITE_INSTANCE_LIMIT, STARFIELD_INSTANCE_LIMIT},
globaldata::{GlobalData, GlobalDataContent}, globaldata::{GlobalData, GlobalDataContent},
pipeline::PipelineBuilder, pipeline::PipelineBuilder,
sprite::SubSprite, sprite::ObjectSubSprite,
texturearray::TextureArray, texturearray::TextureArray,
vertexbuffer::{ vertexbuffer::{
consts::{SPRITE_INDICES, SPRITE_VERTICES}, consts::{SPRITE_INDICES, SPRITE_VERTICES},
types::{SpriteInstance, StarfieldInstance, TexturedVertex}, types::{SpriteInstance, StarfieldInstance, TexturedVertex},
VertexBuffer, VertexBuffer,
}, },
Sprite, ObjectSprite, UiSprite,
}; };
use crate::{consts, game::Game}; use crate::{consts, game::Game};
@ -197,15 +201,15 @@ impl GPUState {
self.update_starfield_buffer(game) self.update_starfield_buffer(game)
} }
/// Create a SpriteInstance for s and add it to instances. /// Create a SpriteInstance for an object and add it to `instances`.
/// Also handles child sprites. /// Also handles child sprites.
fn push_sprite( fn push_object_sprite(
&self, &self,
game: &Game, game: &Game,
instances: &mut Vec<SpriteInstance>, instances: &mut Vec<SpriteInstance>,
clip_ne: Point2<f32>, clip_ne: Point2<f32>,
clip_sw: Point2<f32>, clip_sw: Point2<f32>,
s: Sprite, s: ObjectSprite,
) { ) {
// Position adjusted for parallax // Position adjusted for parallax
// TODO: adjust parallax for zoom? // TODO: adjust parallax for zoom?
@ -284,18 +288,18 @@ impl GPUState {
// Add children // Add children
if let Some(children) = s.children { if let Some(children) = s.children {
for c in children { for c in children {
self.push_subsprite(game, instances, c, pos, s.angle); self.push_object_subsprite(game, instances, c, pos, s.angle);
} }
} }
} }
/// Add a sprite's subsprite to instance. /// Add an object sprite's subsprite to `instances`.
/// Only called by push_sprite. /// Only called by `self.push_object_sprite`.
fn push_subsprite( fn push_object_subsprite(
&self, &self,
game: &Game, game: &Game,
instances: &mut Vec<SpriteInstance>, instances: &mut Vec<SpriteInstance>,
s: SubSprite, s: ObjectSubSprite,
parent_pos: Point2<f32>, parent_pos: Point2<f32>,
parent_angle: Deg<f32>, parent_angle: Deg<f32>,
) { ) {
@ -332,6 +336,41 @@ impl GPUState {
}); });
} }
/// Create a SpriteInstance for a ui sprite and add it to `instances`
fn push_ui_sprite(&self, instances: &mut Vec<SpriteInstance>, s: UiSprite) {
let logical_size: LogicalSize<f32> =
self.window_size.to_logical(self.window.scale_factor());
let texture = self.texture_array.get_texture(s.texture);
let width = s.dimensions.x;
let height = s.dimensions.y;
let scale = Matrix4::from_nonuniform_scale(
width / logical_size.width,
height / logical_size.height,
1.0,
);
let rotate = Matrix4::from_angle_z(s.angle);
let translate = Matrix4::from_translation(match s.pos {
super::AnchoredUiPosition::NorthWest(p) => Vector3 {
// Note the signs. Positive y points north!
x: -1.0 + (width / 2.0 + p.x) / (logical_size.width / 2.0),
y: 1.0 - (height / 2.0 - p.y) / (logical_size.height / 2.0),
z: 0.0,
},
_ => Vector3 {
x: 0.0,
y: 0.0,
z: 0.0,
},
});
instances.push(SpriteInstance {
transform: (OPENGL_TO_WGPU_MATRIX * translate * rotate * scale).into(),
texture_index: texture.index,
});
}
/// Make a SpriteInstance for each of the game's visible sprites. /// Make a SpriteInstance for each of the game's visible sprites.
/// Will panic if SPRITE_INSTANCE_LIMIT is exceeded. /// Will panic if SPRITE_INSTANCE_LIMIT is exceeded.
/// ///
@ -344,8 +383,12 @@ impl GPUState {
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * game.camera.zoom; let clip_ne = Point2::from((-self.window_aspect, 1.0)) * game.camera.zoom;
let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom; let clip_sw = Point2::from((self.window_aspect, -1.0)) * game.camera.zoom;
for s in game.get_sprites() { for s in game.get_object_sprites() {
self.push_sprite(game, &mut instances, clip_ne, clip_sw, s); self.push_object_sprite(game, &mut instances, clip_ne, clip_sw, s);
}
for s in game.get_ui_sprites() {
self.push_ui_sprite(&mut instances, s);
} }
// Enforce sprite limit // Enforce sprite limit

View File

@ -7,4 +7,4 @@ mod texturearray;
mod vertexbuffer; mod vertexbuffer;
pub use gpustate::GPUState; pub use gpustate::GPUState;
pub use sprite::{Sprite, SubSprite}; pub use sprite::{AnchoredUiPosition, ObjectSprite, ObjectSubSprite, UiSprite};

View File

@ -1,12 +1,46 @@
use cgmath::{Deg, Point3}; use cgmath::{Deg, Point2, Point3};
use galactica_content::TextureHandle; use galactica_content::TextureHandle;
/// The location of a UI element, in one of a few
/// possible coordinate systems.
///
/// Positive Y always points up,
/// positive X always points right.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Sprite { pub enum AnchoredUiPosition {
/// The sprite texture to draw /// Position of this sprite's nw corner,
/// relative to the nw corner of the window.
NorthWest(Point2<f32>),
/// Position of this sprite's sw corner,
/// relative to the sw corner of the window.
SouthWest(Point2<f32>),
}
/// A sprite that represents a ui element
#[derive(Debug, Clone)]
pub struct UiSprite {
/// The texture to use for this sprite
pub texture: TextureHandle, pub texture: TextureHandle,
/// This object's position, in world coordinates. /// This object's position, in logical (dpi-adjusted) pixels
pub pos: AnchoredUiPosition,
/// The size of this sprite, in logical (dpi-adjusted) pixels
pub dimensions: Point2<f32>,
/// This sprite's rotation, measured ccw
pub angle: Deg<f32>,
}
/// A sprite that represents a world object:
/// Ships, planets, debris, etc
#[derive(Debug, Clone)]
pub struct ObjectSprite {
/// The texture to use for this sprite
pub texture: TextureHandle,
/// This object's center, in world coordinates.
pub pos: Point3<f32>, pub pos: Point3<f32>,
/// The size of this sprite, /// The size of this sprite,
@ -15,14 +49,15 @@ pub struct Sprite {
/// This sprite's rotation /// This sprite's rotation
/// (relative to north, measured ccw) /// (relative to north, measured ccw)
/// Note that this is different from the angle used by our physics system.
pub angle: Deg<f32>, pub angle: Deg<f32>,
/// Sprites that should be drawn relative to this sprite. /// Sprites that should be drawn relative to this sprite.
pub children: Option<Vec<SubSprite>>, pub children: Option<Vec<ObjectSubSprite>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SubSprite { pub struct ObjectSubSprite {
/// The sprite texture to draw /// The sprite texture to draw
pub texture: TextureHandle, pub texture: TextureHandle,