322 lines
9.3 KiB
Rust
322 lines
9.3 KiB
Rust
use galactica_system::data::ShipState;
|
|
use galactica_util::{constants::UI_SPRITE_INSTANCE_LIMIT, to_radians};
|
|
use nalgebra::{Point2, Vector2};
|
|
|
|
use crate::{
|
|
datastructs::RenderState,
|
|
vertexbuffer::{types::UiInstance, BufferObject},
|
|
PositionAnchor, RenderInput,
|
|
};
|
|
|
|
pub(super) struct Radar {
|
|
last_player_position: Point2<f32>,
|
|
}
|
|
|
|
impl Radar {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
last_player_position: Point2::new(0.0, 0.0),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Radar {
|
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
|
let radar_range = 4000.0;
|
|
let radar_size = 300.0;
|
|
let hide_range = 0.85;
|
|
let shrink_distance = 20.0;
|
|
let system_object_scale = 1.0 / 600.0;
|
|
let ship_scale = 1.0 / 10.0;
|
|
|
|
// TODO: maybe a cleaner solution for last posititon?
|
|
// This is necessary because the player may be dead or landed
|
|
let player_ship = input
|
|
.systemsim
|
|
.get_ship(&galactica_system::phys::PhysSimShipHandle(
|
|
input.player.ship.unwrap(),
|
|
))
|
|
.unwrap();
|
|
|
|
match player_ship.data.get_state() {
|
|
ShipState::Dead => {}
|
|
|
|
ShipState::UnLanding { .. } => {
|
|
let pos = player_ship
|
|
.data
|
|
.get_state()
|
|
.unlanding_position(&input.ct)
|
|
.unwrap();
|
|
self.last_player_position = Point2::new(pos.x, pos.y)
|
|
}
|
|
|
|
ShipState::Landed { target } => {
|
|
let landed_body = input.ct.get_system_object(*target);
|
|
self.last_player_position = Point2::new(landed_body.pos.x, landed_body.pos.y);
|
|
}
|
|
|
|
ShipState::Landing { .. } | ShipState::Flying { .. } | ShipState::Collapsing { .. } => {
|
|
let player_body = input
|
|
.systemsim
|
|
.get_rigid_body(player_ship.rigid_body)
|
|
.unwrap();
|
|
|
|
self.last_player_position = (*player_body.translation()).into();
|
|
}
|
|
};
|
|
|
|
// TODO: don't hard-code these, add config options
|
|
let planet_sprite = input.ct.get_sprite_handle("ui::planetblip");
|
|
let ship_sprite = input.ct.get_sprite_handle("ui::shipblip");
|
|
let arrow_sprite = input.ct.get_sprite_handle("ui::centerarrow");
|
|
|
|
// Enforce buffer limit
|
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
|
// TODO: no panic, handle this better.
|
|
panic!("UI limit exceeded!")
|
|
}
|
|
|
|
// Push this object's instance
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwNw.to_int(),
|
|
position: [10.0, -10.0],
|
|
angle: 0.0,
|
|
size: radar_size,
|
|
color: [1.0, 1.0, 1.0, 1.0],
|
|
sprite_index: input.ct.get_sprite_handle("ui::radar").get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
|
|
// Draw system objects
|
|
let system = input.ct.get_system(input.current_system);
|
|
for o in &system.objects {
|
|
let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
|
|
let p = Point2::new(o.pos.x, o.pos.y);
|
|
let d = (p - self.last_player_position) / radar_range;
|
|
// Add half the blip sprite's height to distance
|
|
let m = d.magnitude() + (size / (2.0 * radar_size));
|
|
if m < hide_range {
|
|
// Shrink blips as they get closeto the edge
|
|
let size = size.min((hide_range - m) * size * shrink_distance);
|
|
if size <= 2.0 {
|
|
// Don't draw super tiny sprites, they flicker
|
|
continue;
|
|
}
|
|
|
|
// Enforce buffer limit
|
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
|
// TODO: no panic, handle this better.
|
|
panic!("UI limit exceeded!")
|
|
}
|
|
|
|
// Push this object's instance
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwC.to_int(),
|
|
position: (Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
|
+ (d * (radar_size / 2.0)))
|
|
.into(),
|
|
angle: o.angle,
|
|
size,
|
|
color: [0.5, 0.5, 0.5, 1.0],
|
|
sprite_index: planet_sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
}
|
|
}
|
|
|
|
// Draw ships
|
|
for (s, r) in input.systemsim.iter_ship_body() {
|
|
let ship = input.ct.get_ship(s.data.get_content());
|
|
let (color, z_scale) = match s.data.get_state() {
|
|
ShipState::Dead | ShipState::Landed { .. } => {
|
|
continue;
|
|
}
|
|
|
|
// TODO: different color for landing?
|
|
// TODO: scale blip for ship z-position
|
|
ShipState::Landing { .. } => ([0.2, 0.2, 0.2, 1.0], 1.0),
|
|
|
|
ShipState::UnLanding { .. } => ([0.2, 0.2, 0.2, 1.0], 1.0),
|
|
|
|
ShipState::Collapsing { .. } => {
|
|
// TODO: configurable
|
|
([0.2, 0.2, 0.2, 1.0], 1.0)
|
|
}
|
|
ShipState::Flying { .. } => {
|
|
let c = input.ct.get_faction(s.data.get_faction()).color;
|
|
([c[0], c[1], c[2], 1.0], 1.0)
|
|
}
|
|
};
|
|
let size = (ship.size * ship.sprite.aspect) * ship_scale * z_scale;
|
|
let p: Point2<f32> = {
|
|
if s.collider == input.player.ship.unwrap() {
|
|
self.last_player_position
|
|
} else {
|
|
(*r.translation()).into()
|
|
}
|
|
};
|
|
let d = (p - self.last_player_position) / radar_range;
|
|
let m = d.magnitude() + (size / (2.0 * radar_size));
|
|
if m < hide_range {
|
|
let size = size.min((hide_range - m) * size * shrink_distance);
|
|
if size < 2.0 {
|
|
continue;
|
|
}
|
|
let angle = r.rotation().angle();
|
|
|
|
let position = Point2::new(radar_size / 2.0 + 10.0, radar_size / -2.0 - 10.0)
|
|
+ (d * (radar_size / 2.0));
|
|
|
|
// Enforce buffer limit
|
|
// TODO: cleaner solution. don't do this everywhere.
|
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
|
// TODO: no panic, handle this better.
|
|
panic!("UI limit exceeded!")
|
|
}
|
|
|
|
// Push this object's instance
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwC.to_int(),
|
|
position: position.into(),
|
|
angle: -angle, // TODO: consistent angles
|
|
size,
|
|
color,
|
|
sprite_index: ship_sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
}
|
|
}
|
|
|
|
// Draw viewport frame
|
|
let d = Vector2::new(
|
|
(input.camera_zoom / 2.0) * state.window_aspect,
|
|
input.camera_zoom / 2.0,
|
|
) / radar_range;
|
|
let m = d.magnitude();
|
|
let d = d * (radar_size / 2.0);
|
|
let color = [0.3, 0.3, 0.3, 1.0];
|
|
if m < 0.8 {
|
|
let sprite = input.ct.get_sprite_handle("ui::radarframe");
|
|
let size = 7.0f32.min((0.8 - m) * 70.0);
|
|
|
|
// Enforce buffer limit (this section adds four items)
|
|
if state.vertex_buffers.ui_counter as u64 + 4 > UI_SPRITE_INSTANCE_LIMIT {
|
|
// TODO: no panic, handle this better.
|
|
panic!("UI limit exceeded!")
|
|
}
|
|
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwNw.to_int(),
|
|
position: Point2::new(
|
|
(radar_size / 2.0 + 10.0) - d.x,
|
|
(radar_size / -2.0 - 10.0) + d.y,
|
|
)
|
|
.into(),
|
|
angle: to_radians(90.0),
|
|
size,
|
|
color,
|
|
sprite_index: sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwSw.to_int(),
|
|
position: Point2::new(
|
|
(radar_size / 2.0 + 10.0) - d.x,
|
|
(radar_size / -2.0 - 10.0) - d.y,
|
|
)
|
|
.into(),
|
|
angle: to_radians(180.0),
|
|
size,
|
|
color,
|
|
sprite_index: sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwSe.to_int(),
|
|
position: Point2::new(
|
|
(radar_size / 2.0 + 10.0) + d.x,
|
|
(radar_size / -2.0 - 10.0) - d.y,
|
|
)
|
|
.into(),
|
|
angle: to_radians(270.0),
|
|
size,
|
|
color,
|
|
sprite_index: sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwNe.to_int(),
|
|
position: Point2::new(
|
|
(radar_size / 2.0 + 10.0) + d.x,
|
|
(radar_size / -2.0 - 10.0) + d.y,
|
|
)
|
|
.into(),
|
|
angle: to_radians(0.0),
|
|
size,
|
|
color,
|
|
sprite_index: sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
}
|
|
|
|
// Arrow to center of system
|
|
let q = Point2::new(0.0, 0.0) - self.last_player_position;
|
|
let m = q.magnitude();
|
|
if m > 200.0 {
|
|
let angle = clockwise_angle(&Vector2::new(1.0, 0.0), &q);
|
|
let position = Point2::new(10.0 + (radar_size / 2.0), -10.0 - (radar_size / 2.0))
|
|
+ Rotation2::new(angle) * Vector2::new(0.915 * (radar_size / 2.0), 0.0);
|
|
|
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
|
// TODO: no panic, handle this better.
|
|
panic!("UI limit exceeded!")
|
|
}
|
|
|
|
state.queue.write_buffer(
|
|
&state.vertex_buffers.ui.instances,
|
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
|
bytemuck::cast_slice(&[UiInstance {
|
|
anchor: PositionAnchor::NwC.to_int(),
|
|
position: position.into(),
|
|
angle,
|
|
size: 10.0,
|
|
color: [1.0, 1.0, 1.0, 1f32.min((m - 200.0) / 400.0)],
|
|
sprite_index: arrow_sprite.get_index(),
|
|
}]),
|
|
);
|
|
state.vertex_buffers.ui_counter += 1;
|
|
}
|
|
}
|
|
}
|