2024-03-16 20:00:40 +00:00
|
|
|
use bevy::prelude::*;
|
|
|
|
use bevy::input::mouse::MouseMotion;
|
2024-03-16 23:24:47 +00:00
|
|
|
use bevy::window::PrimaryWindow;
|
2024-03-29 18:41:46 +00:00
|
|
|
use bevy::core_pipeline::bloom::{BloomCompositeMode, BloomSettings};
|
2024-03-30 14:50:36 +00:00
|
|
|
use bevy::core_pipeline::tonemapping::Tonemapping;
|
2024-04-01 16:06:41 +00:00
|
|
|
use bevy::pbr::CascadeShadowConfigBuilder;
|
2024-03-30 17:50:53 +00:00
|
|
|
use bevy::transform::TransformSystem;
|
2024-04-03 10:26:56 +00:00
|
|
|
use bevy::math::{DVec3, DQuat};
|
2024-03-29 15:33:12 +00:00
|
|
|
use bevy_xpbd_3d::prelude::*;
|
2024-04-01 16:06:41 +00:00
|
|
|
use std::f32::consts::PI;
|
2024-04-05 18:57:21 +00:00
|
|
|
use crate::{actor, audio, hud, settings};
|
2024-03-16 20:00:40 +00:00
|
|
|
|
2024-03-31 20:08:26 +00:00
|
|
|
pub struct CameraPlugin;
|
2024-03-16 20:00:40 +00:00
|
|
|
|
2024-03-31 20:08:26 +00:00
|
|
|
impl Plugin for CameraPlugin {
|
2024-03-16 20:00:40 +00:00
|
|
|
fn build(&self, app: &mut App) {
|
2024-03-29 18:41:46 +00:00
|
|
|
app.add_systems(Startup, setup_camera);
|
|
|
|
app.add_systems(Update, handle_input);
|
2024-03-30 17:56:24 +00:00
|
|
|
app.add_systems(Update, manage_player_actor.after(handle_input));
|
2024-03-30 17:50:53 +00:00
|
|
|
app.add_systems(PostUpdate, sync_camera_to_player
|
|
|
|
.after(PhysicsSet::Sync)
|
2024-03-30 18:51:41 +00:00
|
|
|
.after(apply_input_to_player)
|
2024-03-30 17:50:53 +00:00
|
|
|
.before(TransformSystem::TransformPropagate));
|
2024-03-30 15:41:22 +00:00
|
|
|
app.add_systems(Update, update_fov);
|
2024-03-30 18:49:47 +00:00
|
|
|
app.add_systems(PostUpdate, apply_input_to_player
|
|
|
|
.after(PhysicsSet::Sync)
|
|
|
|
.before(TransformSystem::TransformPropagate));
|
2024-03-16 20:00:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-30 21:36:04 +00:00
|
|
|
pub fn setup_camera(
|
2024-03-29 18:41:46 +00:00
|
|
|
mut commands: Commands,
|
|
|
|
) {
|
|
|
|
// Add player
|
|
|
|
commands.spawn((
|
|
|
|
Camera3dBundle {
|
|
|
|
camera: Camera {
|
|
|
|
hdr: true, // HDR is required for bloom
|
2024-03-31 20:08:26 +00:00
|
|
|
clear_color: ClearColorConfig::Custom(Color::BLACK),
|
2024-03-29 18:41:46 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-03-30 14:50:36 +00:00
|
|
|
tonemapping: Tonemapping::TonyMcMapface,
|
2024-03-29 18:41:46 +00:00
|
|
|
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
BloomSettings {
|
|
|
|
composite_mode: BloomCompositeMode::EnergyConserving,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
));
|
2024-04-01 16:06:41 +00:00
|
|
|
|
|
|
|
// Add Light from the Sun
|
|
|
|
commands.spawn(DirectionalLightBundle {
|
|
|
|
directional_light: DirectionalLight {
|
|
|
|
illuminance: 1000.0,
|
|
|
|
shadows_enabled: false,
|
|
|
|
..default()
|
|
|
|
},
|
|
|
|
transform: Transform::from_rotation(Quat::from_rotation_y(PI/2.0)),
|
|
|
|
cascade_shadow_config: CascadeShadowConfigBuilder {
|
|
|
|
first_cascade_far_bound: 7.0,
|
|
|
|
maximum_distance: 25.0,
|
|
|
|
..default()
|
|
|
|
}
|
|
|
|
.into(),
|
|
|
|
..default()
|
|
|
|
});
|
2024-03-29 18:41:46 +00:00
|
|
|
}
|
|
|
|
|
2024-03-30 15:18:49 +00:00
|
|
|
pub fn sync_camera_to_player(
|
|
|
|
settings: Res<settings::Settings>,
|
2024-04-05 00:58:02 +00:00
|
|
|
mut q_camera: Query<&mut Transform, (With<Camera>, Without<actor::PlayerCamera>)>,
|
2024-03-30 16:05:32 +00:00
|
|
|
q_playercam: Query<(&actor::Actor, &Transform), (With<actor::PlayerCamera>, Without<Camera>)>,
|
2024-03-30 15:18:49 +00:00
|
|
|
) {
|
|
|
|
if q_camera.is_empty() || q_playercam.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let mut camera_transform = q_camera.get_single_mut().unwrap();
|
|
|
|
let (actor, player_transform) = q_playercam.get_single().unwrap();
|
|
|
|
|
|
|
|
// Rotation
|
|
|
|
camera_transform.rotation = player_transform.rotation * Quat::from_array([0.0, -1.0, 0.0, 0.0]);
|
|
|
|
|
|
|
|
// Translation
|
|
|
|
if settings.third_person {
|
|
|
|
camera_transform.translation = player_transform.translation + camera_transform.rotation * (actor.camdistance * Vec3::new(0.0, 0.2, 1.0));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
camera_transform.translation = player_transform.translation;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-30 15:41:22 +00:00
|
|
|
pub fn update_fov(
|
|
|
|
q_player: Query<&actor::LifeForm, With<actor::Player>>,
|
2024-04-05 21:38:20 +00:00
|
|
|
mouse_input: Res<ButtonInput<MouseButton>>,
|
|
|
|
mut settings: ResMut<settings::Settings>,
|
2024-03-30 15:41:22 +00:00
|
|
|
mut q_camera: Query<&mut Projection, With<Camera>>,
|
|
|
|
) {
|
|
|
|
if let (Ok(lifeform), Ok(mut projection)) = (q_player.get_single(), q_camera.get_single_mut())
|
|
|
|
{
|
2024-04-05 21:38:20 +00:00
|
|
|
let fov: f32;
|
2024-04-05 21:38:27 +00:00
|
|
|
if settings.hud_active && mouse_input.pressed(settings.key_zoom) {
|
2024-04-05 21:38:20 +00:00
|
|
|
fov = settings.zoom_fov_radians;
|
|
|
|
if !settings.is_zooming {
|
|
|
|
settings.is_zooming = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fov = (lifeform.adrenaline.powf(3.0) * 45.0 + 45.0).to_radians();
|
|
|
|
if settings.is_zooming {
|
|
|
|
settings.is_zooming = false;
|
|
|
|
}
|
|
|
|
};
|
2024-03-30 15:41:22 +00:00
|
|
|
*projection = Projection::Perspective(PerspectiveProjection { fov: fov, ..default() });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-29 18:41:46 +00:00
|
|
|
pub fn handle_input(
|
|
|
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
|
|
|
mut settings: ResMut<settings::Settings>,
|
|
|
|
) {
|
|
|
|
if keyboard_input.just_pressed(settings.key_camera) {
|
|
|
|
settings.third_person ^= true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn manage_player_actor(
|
|
|
|
settings: Res<settings::Settings>,
|
2024-03-30 14:37:51 +00:00
|
|
|
mut q_playercam: Query<&mut Visibility, With<actor::PlayerCamera>>,
|
2024-04-03 10:26:56 +00:00
|
|
|
mut q_hiddenplayer: Query<(&mut Visibility, &mut Position, &mut Rotation, &mut LinearVelocity, &mut AngularVelocity), (With<actor::Player>, Without<actor::PlayerCamera>)>,
|
|
|
|
q_ride: Query<(&Transform, &Position, &Rotation, &LinearVelocity, &AngularVelocity), (With<actor::PlayerDrivesThis>, Without<actor::Player>)>,
|
2024-03-29 18:41:46 +00:00
|
|
|
) {
|
|
|
|
for mut vis in &mut q_playercam {
|
|
|
|
if settings.third_person {
|
|
|
|
*vis = Visibility::Inherited;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*vis = Visibility::Hidden;
|
|
|
|
}
|
|
|
|
}
|
2024-04-03 10:26:56 +00:00
|
|
|
for (mut vis, mut pos, mut rot, mut v, mut angv) in &mut q_hiddenplayer {
|
2024-04-03 11:53:49 +00:00
|
|
|
// If we are riding a vehicle, place the player at the position where
|
|
|
|
// it would be after exiting the vehicle.
|
|
|
|
// I would rather place it in the center of the vehicle, but at the time
|
|
|
|
// of writing, I couldn't set the position/rotation of the player *during*
|
|
|
|
// exiting the vehicle, so I'm doing it here instead, as a workaround.
|
2024-03-30 17:48:19 +00:00
|
|
|
*vis = Visibility::Hidden;
|
2024-04-03 10:26:56 +00:00
|
|
|
if let Ok((ride_trans, ride_pos, ride_rot, ride_v, ride_angv)) = q_ride.get_single() {
|
2024-04-03 11:53:49 +00:00
|
|
|
pos.0 = ride_pos.0 + DVec3::from(ride_trans.rotation * Vec3::new(0.0, 0.0, ride_trans.scale.z * 2.0));
|
2024-04-03 10:26:56 +00:00
|
|
|
rot.0 = ride_rot.0 * DQuat::from_array([-1.0, 0.0, 0.0, 0.0]);
|
2024-03-30 21:31:07 +00:00
|
|
|
*v = ride_v.clone();
|
|
|
|
*angv = ride_angv.clone();
|
|
|
|
}
|
2024-03-30 17:48:19 +00:00
|
|
|
}
|
2024-03-29 18:41:46 +00:00
|
|
|
}
|
|
|
|
|
2024-03-16 20:00:40 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2024-04-05 20:16:01 +00:00
|
|
|
pub fn apply_input_to_player(
|
2024-03-16 20:00:40 +00:00
|
|
|
time: Res<Time>,
|
2024-03-18 03:10:08 +00:00
|
|
|
settings: Res<settings::Settings>,
|
2024-03-16 23:24:47 +00:00
|
|
|
mut windows: Query<&mut Window, With<PrimaryWindow>>,
|
2024-03-16 20:00:40 +00:00
|
|
|
mut mouse_events: EventReader<MouseMotion>,
|
|
|
|
key_input: Res<ButtonInput<KeyCode>>,
|
2024-03-16 23:41:06 +00:00
|
|
|
thruster_sound_controller: Query<&AudioSink, With<audio::ComponentThrusterSound>>,
|
2024-03-28 13:10:10 +00:00
|
|
|
rocket_sound_controller: Query<&AudioSink, With<audio::ComponentRocketSound>>,
|
2024-03-29 03:36:20 +00:00
|
|
|
ion_sound_controller: Query<&AudioSink, With<audio::ComponentIonSound>>,
|
2024-03-31 00:07:35 +00:00
|
|
|
electricmotor_sound_controller: Query<&AudioSink, With<audio::ComponentElectricMotorSound>>,
|
2024-04-05 18:57:21 +00:00
|
|
|
q_target: Query<&LinearVelocity, (With<hud::IsTargeted>, Without<actor::PlayerCamera>)>,
|
2024-03-29 18:41:46 +00:00
|
|
|
mut q_playercam: Query<(
|
2024-03-31 01:09:14 +00:00
|
|
|
&Transform,
|
2024-03-29 15:33:12 +00:00
|
|
|
&mut actor::Engine,
|
|
|
|
&mut AngularVelocity,
|
|
|
|
&mut LinearVelocity,
|
2024-03-31 01:09:14 +00:00
|
|
|
&mut ExternalTorque,
|
2024-03-30 16:05:32 +00:00
|
|
|
), (With<actor::PlayerCamera>, Without<Camera>)>,
|
2024-03-16 20:00:40 +00:00
|
|
|
) {
|
|
|
|
let dt = time.delta_seconds();
|
2024-03-16 23:41:06 +00:00
|
|
|
let mut play_thruster_sound = false;
|
2024-04-01 14:29:14 +00:00
|
|
|
let mut axis_input: DVec3 = DVec3::ZERO;
|
2024-03-16 20:00:40 +00:00
|
|
|
|
2024-03-16 23:24:47 +00:00
|
|
|
let window_result = windows.get_single_mut();
|
|
|
|
let mut focused = true;
|
|
|
|
if window_result.is_ok() {
|
|
|
|
focused = window_result.unwrap().focused;
|
|
|
|
}
|
|
|
|
|
2024-04-05 18:57:21 +00:00
|
|
|
let target_v: DVec3 = if let Ok(target) = q_target.get_single() {
|
|
|
|
target.0
|
|
|
|
} else {
|
|
|
|
DVec3::splat(0.0)
|
|
|
|
};
|
|
|
|
|
2024-03-31 01:09:14 +00:00
|
|
|
if let Ok((player_transform, mut engine, mut angularvelocity, mut v, mut torque)) = q_playercam.get_single_mut() {
|
2024-03-16 20:00:40 +00:00
|
|
|
// Handle key input
|
2024-03-16 23:24:47 +00:00
|
|
|
if focused {
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_forward) {
|
2024-03-29 18:41:46 +00:00
|
|
|
axis_input.z += 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_back) {
|
2024-03-29 18:41:46 +00:00
|
|
|
axis_input.z -= 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_right) {
|
2024-03-29 18:41:46 +00:00
|
|
|
axis_input.x -= 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_left) {
|
2024-03-29 18:41:46 +00:00
|
|
|
axis_input.x += 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_up) {
|
2024-03-29 01:49:16 +00:00
|
|
|
axis_input.y += 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
if key_input.pressed(settings.key_down) {
|
2024-03-29 01:49:16 +00:00
|
|
|
axis_input.y -= 1.2;
|
2024-03-16 23:24:47 +00:00
|
|
|
}
|
2024-03-29 01:40:55 +00:00
|
|
|
if key_input.pressed(settings.key_stop) {
|
2024-04-05 18:57:21 +00:00
|
|
|
let stop_direction = (target_v - v.0).normalize();
|
2024-03-29 01:40:55 +00:00
|
|
|
if stop_direction.length_squared() > 0.3 {
|
2024-04-01 14:29:14 +00:00
|
|
|
axis_input += 1.0 * DVec3::from(player_transform.rotation.inverse() * stop_direction.as_vec3());
|
2024-03-29 01:40:55 +00:00
|
|
|
}
|
2024-03-21 17:45:43 +00:00
|
|
|
}
|
|
|
|
}
|
2024-03-29 01:41:05 +00:00
|
|
|
// In typical games we would normalize the input vector so that diagonal movement is as
|
|
|
|
// fast as forward or sideways movement. But here, we merely clamp each direction to an
|
|
|
|
// absolute maximum of 1, since every thruster can be used separately. If the forward
|
|
|
|
// thrusters and the leftward thrusters are active at the same time, then of course the
|
|
|
|
// total diagonal acceleration is faster than the forward acceleration alone.
|
2024-04-01 14:29:14 +00:00
|
|
|
axis_input = axis_input.clamp(DVec3::splat(-1.0), DVec3::splat(1.0));
|
2024-03-17 13:39:42 +00:00
|
|
|
|
2024-03-16 20:00:40 +00:00
|
|
|
// Apply movement update
|
2024-03-30 17:58:45 +00:00
|
|
|
let forward_factor = engine.current_warmup * (if axis_input.z > 0.0 {
|
2024-03-28 12:26:14 +00:00
|
|
|
engine.thrust_forward
|
|
|
|
} else {
|
|
|
|
engine.thrust_back
|
|
|
|
});
|
2024-03-29 00:40:58 +00:00
|
|
|
let right_factor = engine.thrust_sideways * engine.current_warmup;
|
|
|
|
let up_factor = engine.thrust_sideways * engine.current_warmup;
|
2024-04-01 14:29:14 +00:00
|
|
|
let factor = DVec3::new(right_factor as f64, up_factor as f64, forward_factor as f64);
|
2024-03-29 00:40:58 +00:00
|
|
|
|
2024-03-29 01:40:55 +00:00
|
|
|
if axis_input.length_squared() > 0.003 {
|
2024-04-01 14:29:14 +00:00
|
|
|
let acceleration_global: DVec3 = DVec3::from(player_transform.rotation * (axis_input * factor).as_vec3());
|
|
|
|
let mut acceleration_total: DVec3 = (actor::ENGINE_SPEED_FACTOR * dt) as f64 * acceleration_global;
|
2024-03-29 01:56:48 +00:00
|
|
|
let threshold = 1e-5;
|
|
|
|
if key_input.pressed(settings.key_stop) {
|
2024-04-05 21:11:10 +00:00
|
|
|
// Decelerate (or match velocity to target_v)
|
2024-04-05 21:10:40 +00:00
|
|
|
let dv = v.0 - target_v;
|
2024-03-29 01:56:48 +00:00
|
|
|
for i in 0..3 {
|
2024-04-05 18:57:21 +00:00
|
|
|
if dv[i].abs() < threshold {
|
|
|
|
v[i] = target_v[i];
|
2024-03-29 01:56:48 +00:00
|
|
|
}
|
2024-04-05 18:57:21 +00:00
|
|
|
else if dv[i].signum() != (dv[i] + acceleration_total[i]).signum() {
|
2024-03-30 18:36:43 +00:00
|
|
|
// Almost stopped, but we overshot v=0
|
2024-04-05 18:57:21 +00:00
|
|
|
v[i] = target_v[i];
|
2024-03-29 01:56:48 +00:00
|
|
|
acceleration_total[i] = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-29 15:33:12 +00:00
|
|
|
v.0 += acceleration_total;
|
2024-03-29 01:21:28 +00:00
|
|
|
engine.current_warmup = (engine.current_warmup + dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
2024-04-03 12:27:44 +00:00
|
|
|
play_thruster_sound = true;
|
2024-03-29 01:21:28 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
engine.current_warmup = (engine.current_warmup - dt / engine.warmup_seconds).clamp(0.0, 1.0);
|
|
|
|
}
|
2024-03-21 17:45:43 +00:00
|
|
|
|
2024-03-30 18:36:43 +00:00
|
|
|
// Handle mouse input and mouse-like key bindings
|
2024-03-31 00:07:35 +00:00
|
|
|
let mut play_reactionwheel_sound = false;
|
2024-03-16 20:00:40 +00:00
|
|
|
let mut mouse_delta = Vec2::ZERO;
|
2024-03-30 18:36:43 +00:00
|
|
|
let mut pitch_yaw_rot = Vec3::ZERO;
|
2024-04-05 21:38:20 +00:00
|
|
|
let sensitivity_factor = if settings.is_zooming { settings.zoom_sensitivity_factor } else { 1.0 };
|
|
|
|
let mouseless_sensitivity = 40.0 * sensitivity_factor;
|
2024-03-30 18:36:43 +00:00
|
|
|
if key_input.pressed(settings.key_mouseup) {
|
|
|
|
pitch_yaw_rot[0] -= mouseless_sensitivity;
|
|
|
|
}
|
|
|
|
if key_input.pressed(settings.key_mousedown) {
|
|
|
|
pitch_yaw_rot[0] += mouseless_sensitivity;
|
|
|
|
}
|
|
|
|
if key_input.pressed(settings.key_mouseleft) {
|
2024-03-31 01:09:14 +00:00
|
|
|
pitch_yaw_rot[1] += mouseless_sensitivity; }
|
2024-03-30 18:36:43 +00:00
|
|
|
if key_input.pressed(settings.key_mouseright) {
|
|
|
|
pitch_yaw_rot[1] -= mouseless_sensitivity;
|
|
|
|
}
|
|
|
|
if key_input.pressed(settings.key_rotateleft) {
|
|
|
|
pitch_yaw_rot[2] -= mouseless_sensitivity;
|
|
|
|
}
|
|
|
|
if key_input.pressed(settings.key_rotateright) {
|
|
|
|
pitch_yaw_rot[2] += mouseless_sensitivity;
|
|
|
|
}
|
2024-03-18 03:39:26 +00:00
|
|
|
for mouse_event in mouse_events.read() {
|
|
|
|
mouse_delta += mouse_event.delta;
|
2024-03-16 20:00:40 +00:00
|
|
|
}
|
|
|
|
if mouse_delta != Vec2::ZERO {
|
2024-03-30 18:14:59 +00:00
|
|
|
if key_input.pressed(settings.key_rotate) {
|
2024-03-30 18:36:43 +00:00
|
|
|
pitch_yaw_rot[2] += mouse_delta.x;
|
2024-03-30 18:14:59 +00:00
|
|
|
} else {
|
2024-03-30 18:36:43 +00:00
|
|
|
pitch_yaw_rot[0] += mouse_delta.y;
|
|
|
|
pitch_yaw_rot[1] -= mouse_delta.x;
|
2024-03-30 18:14:59 +00:00
|
|
|
}
|
2024-03-30 18:36:43 +00:00
|
|
|
}
|
2024-03-31 01:19:15 +00:00
|
|
|
|
2024-04-01 14:29:14 +00:00
|
|
|
let angular_slowdown: f64 = (2.0 - engine.reaction_wheels.powf(0.01).clamp(1.001, 1.1)) as f64;
|
2024-03-30 18:36:43 +00:00
|
|
|
if pitch_yaw_rot.length_squared() > 0.0001 {
|
2024-03-31 00:07:35 +00:00
|
|
|
play_reactionwheel_sound = true;
|
2024-04-05 21:38:20 +00:00
|
|
|
pitch_yaw_rot *= settings.mouse_sensitivity * sensitivity_factor * engine.reaction_wheels;
|
2024-04-01 14:29:14 +00:00
|
|
|
torque.apply_torque(DVec3::from(
|
|
|
|
player_transform.rotation * Vec3::new(
|
|
|
|
pitch_yaw_rot[0] * 2.0,
|
|
|
|
pitch_yaw_rot[1],
|
|
|
|
pitch_yaw_rot[2])));
|
|
|
|
angularvelocity.0 *= angular_slowdown.clamp(0.97, 1.0) as f64;
|
2024-03-31 01:09:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2024-03-31 03:13:13 +00:00
|
|
|
if angularvelocity.length_squared() > 0.001 {
|
2024-03-31 01:19:15 +00:00
|
|
|
angularvelocity.0 *= angular_slowdown;
|
2024-03-31 01:09:14 +00:00
|
|
|
}
|
|
|
|
else {
|
2024-04-01 14:29:14 +00:00
|
|
|
angularvelocity.0 = DVec3::splat(0.0);
|
2024-03-31 01:09:14 +00:00
|
|
|
}
|
2024-03-16 20:00:40 +00:00
|
|
|
}
|
2024-03-16 23:41:06 +00:00
|
|
|
|
2024-03-30 18:36:43 +00:00
|
|
|
// Play sound effects
|
2024-03-31 00:07:35 +00:00
|
|
|
if let Ok(sink) = electricmotor_sound_controller.get_single() {
|
|
|
|
let volume = sink.volume();
|
2024-03-31 03:13:13 +00:00
|
|
|
let speed = sink.speed();
|
|
|
|
let action = pitch_yaw_rot.length_squared().powf(0.2) * 0.0005;
|
2024-04-03 12:27:44 +00:00
|
|
|
if play_reactionwheel_sound && !settings.mute_sfx {
|
2024-03-31 03:13:13 +00:00
|
|
|
sink.set_volume((volume + action).clamp(0.0, 1.0));
|
|
|
|
sink.set_speed((speed + action * 0.2).clamp(0.2, 0.5));
|
2024-03-31 00:07:35 +00:00
|
|
|
sink.play()
|
|
|
|
} else {
|
|
|
|
if volume <= 0.01 {
|
|
|
|
sink.pause()
|
|
|
|
} else {
|
2024-03-31 03:13:13 +00:00
|
|
|
sink.set_volume((volume - 0.01).clamp(0.0, 1.0));
|
|
|
|
sink.set_speed((speed - 0.03).clamp(0.2, 0.5));
|
2024-03-31 00:07:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-16 23:41:06 +00:00
|
|
|
if let Ok(sink) = thruster_sound_controller.get_single() {
|
2024-04-03 12:27:44 +00:00
|
|
|
if play_thruster_sound && !settings.mute_sfx && engine.engine_type == actor::EngineType::Monopropellant {
|
2024-03-28 13:10:10 +00:00
|
|
|
sink.play()
|
|
|
|
} else {
|
|
|
|
sink.pause()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Ok(sink) = rocket_sound_controller.get_single() {
|
2024-04-03 12:27:44 +00:00
|
|
|
if play_thruster_sound && !settings.mute_sfx && engine.engine_type == actor::EngineType::Rocket {
|
2024-03-16 23:41:06 +00:00
|
|
|
sink.play()
|
|
|
|
} else {
|
|
|
|
sink.pause()
|
|
|
|
}
|
|
|
|
}
|
2024-03-29 03:36:20 +00:00
|
|
|
if let Ok(sink) = ion_sound_controller.get_single() {
|
2024-04-03 12:27:44 +00:00
|
|
|
if play_thruster_sound && !settings.mute_sfx && engine.engine_type == actor::EngineType::Ion {
|
2024-03-29 03:36:20 +00:00
|
|
|
sink.play()
|
|
|
|
} else {
|
|
|
|
sink.pause()
|
|
|
|
}
|
|
|
|
}
|
2024-03-16 20:00:40 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-05 17:11:34 +00:00
|
|
|
|
|
|
|
// Find the closest world object that the player is looking at
|
2024-04-05 20:06:58 +00:00
|
|
|
#[inline]
|
2024-04-05 17:34:01 +00:00
|
|
|
pub fn find_closest_target<TargetSpecifier>(
|
|
|
|
objects: Vec<(TargetSpecifier, &Transform)>,
|
|
|
|
camera_transform: &Transform,
|
|
|
|
) -> (Option<TargetSpecifier>, f32)
|
2024-04-05 17:57:55 +00:00
|
|
|
where TargetSpecifier: Clone
|
2024-04-05 17:11:34 +00:00
|
|
|
{
|
2024-04-05 17:34:01 +00:00
|
|
|
let mut closest_entity: Option<TargetSpecifier> = None;
|
2024-04-05 17:11:34 +00:00
|
|
|
let mut closest_distance: f32 = f32::MAX;
|
2024-04-05 20:06:58 +00:00
|
|
|
let target_vector: Vec3 = (camera_transform.rotation * Vec3::new(0.0, 0.0, -1.0))
|
2024-04-05 17:34:01 +00:00
|
|
|
.normalize_or_zero();
|
|
|
|
for (entity, trans) in objects {
|
|
|
|
// Use Transform instead of Position because we're basing this
|
|
|
|
// not on the player mesh but on the camera, which doesn't have a position.
|
2024-04-05 20:06:58 +00:00
|
|
|
let (angular_diameter, angle, distance) = calc_angular_diameter_known_target_vector(
|
|
|
|
trans, camera_transform, &target_vector);
|
2024-04-05 18:28:28 +00:00
|
|
|
if angle <= angular_diameter.clamp(0.001, PI) {
|
2024-04-05 17:34:01 +00:00
|
|
|
// It's in the field of view!
|
|
|
|
//commands.entity(entity).insert(IsTargeted);
|
|
|
|
if distance < closest_distance {
|
|
|
|
closest_distance = distance;
|
2024-04-05 17:59:53 +00:00
|
|
|
closest_entity = Some(entity);
|
2024-04-05 17:11:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (closest_entity, closest_distance);
|
|
|
|
}
|
2024-04-05 20:06:58 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn calc_angular_diameter_known_target_vector(
|
|
|
|
target: &Transform,
|
|
|
|
camera: &Transform,
|
|
|
|
target_vector: &Vec3,
|
|
|
|
) -> (f32, f32, f32) {
|
|
|
|
let pos_vector: Vec3 = (target.translation - camera.translation)
|
|
|
|
.normalize_or_zero();
|
|
|
|
let cosine_of_angle: f32 = target_vector.dot(pos_vector);
|
|
|
|
let angle: f32 = cosine_of_angle.acos();
|
|
|
|
let distance: f32 = target.translation.distance(camera.translation);
|
|
|
|
let leeway: f32 = 1.3;
|
|
|
|
let angular_diameter: f32 = if distance > 0.0 {
|
|
|
|
// Angular Diameter
|
|
|
|
leeway * (target.scale[0] / distance).asin()
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
0.0
|
|
|
|
};
|
|
|
|
return (angular_diameter, angle, distance);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn calc_angular_diameter(
|
|
|
|
target: &Transform,
|
|
|
|
camera: &Transform,
|
|
|
|
) -> (f32, f32, f32) {
|
|
|
|
let target_vector: Vec3 = (camera.rotation * Vec3::new(0.0, 0.0, -1.0))
|
|
|
|
.normalize_or_zero();
|
|
|
|
return calc_angular_diameter_known_target_vector(target, camera, &target_vector);
|
|
|
|
}
|