use std::f32::consts::SQRT_2;
use narcissus_core::{BitIter, box_assume_init, default, random::Pcg64, zeroed_box};
-use narcissus_maths::{Deg, HalfTurn, Mat4, Point3, Vec3, clamp, perlin_noise3, sin_pi_f32, vec3};
+use narcissus_maths::{
+ Deg, HalfTurn, Mat4, Point3, Vec2, Vec3, clamp, perlin_noise3, sin_cos_pi_f32, sin_pi_f32,
+ vec2, vec3,
+};
use crate::spring::simple_spring_damper_exact;
Right,
Up,
Down,
+ CameraLeft,
+ CameraRight,
+ CameraUp,
+ CameraDown,
Damage,
}
pub struct CameraState {
pub eye_offset: Vec3,
+ pub azimuth: HalfTurn,
+ pub altitude: HalfTurn,
+ pub distance: f32,
+
pub shake: f32,
pub shake_offset: Vec3,
impl CameraState {
pub fn new() -> Self {
- let theta = HalfTurn::from(GAME_VARIABLES.camera_angle).as_f32();
- let hypotenuse = GAME_VARIABLES.camera_distance;
- let height = sin_pi_f32(theta) * hypotenuse;
- let base = (hypotenuse * hypotenuse - height * height).sqrt();
-
- // Rotate camera
- let one_on_sqrt2 = 1.0 / SQRT_2;
- let eye_offset = vec3(-base * one_on_sqrt2, height, -base * one_on_sqrt2);
+ let altitude = HalfTurn::from(GAME_VARIABLES.camera_angle);
+ let distance = GAME_VARIABLES.camera_distance;
+ let azimuth = HalfTurn::new(0.25);
Self {
- eye_offset,
+ eye_offset: Vec3::ZERO,
+ altitude,
+ azimuth,
+ distance,
shake: 0.0,
shake_offset: Vec3::ZERO,
}
}
- pub fn tick(&mut self, target: Point3, time: f32, delta_time: f32) {
+ pub fn tick(&mut self, actions: &Actions, target: Point3, time: f32, delta_time: f32) {
+ let movement_bitmap = actions.is_active(Action::CameraUp) as usize
+ | (actions.is_active(Action::CameraDown) as usize) << 1
+ | (actions.is_active(Action::CameraLeft) as usize) << 2
+ | (actions.is_active(Action::CameraRight) as usize) << 3;
+
+ const UP: Vec2 = vec2(0.0, 1.0);
+ const DOWN: Vec2 = vec2(0.0, -1.0);
+ const LEFT: Vec2 = vec2(-1.0, 0.0);
+ const RIGHT: Vec2 = vec2(1.0, 0.0);
+ //SQRT_2 / 2.0
+ const UP_LEFT: Vec2 = vec2(1.0, 0.0);
+ const UP_RIGHT: Vec2 = vec2(0.0, 1.0);
+ const DOWN_LEFT: Vec2 = vec2(0.0, -1.0);
+ const DOWN_RIGHT: Vec2 = vec2(-1.0, 0.0);
+
+ let movement = [
+ // 0 0 0 0
+ Vec2::ZERO,
+ // 0 0 0 1
+ UP,
+ // 0 0 1 0
+ DOWN,
+ // 0 0 1 1
+ Vec2::ZERO,
+ // 0 1 0 0
+ LEFT,
+ // 0 1 0 1
+ UP_LEFT,
+ // 0 1 1 0
+ DOWN_LEFT,
+ // 0 1 1 1
+ LEFT,
+ // 1 0 0 0
+ RIGHT,
+ // 1 0 0 1
+ UP_RIGHT,
+ // 1 0 1 0
+ DOWN_RIGHT,
+ // 1 0 1 1
+ RIGHT,
+ // 1 1 0 0
+ Vec2::ZERO,
+ // 1 1 0 1
+ UP,
+ // 1 1 1 0
+ DOWN,
+ // 1 1 1 1
+ Vec2::ZERO,
+ ][movement_bitmap];
+
+ let camera_half_turn_per_second = 0.5;
+
+ let altitude = self.altitude.as_f32();
+ let altitude = altitude + movement.y * delta_time * camera_half_turn_per_second;
+ let altitude = clamp(altitude, -0.49, 0.49);
+ self.altitude = HalfTurn::new(altitude);
+
+ let azimuth = self.azimuth.as_f32();
+ let azimuth = azimuth + movement.x * delta_time * camera_half_turn_per_second;
+ let azimuth = (azimuth / 2.0).fract() * 2.0;
+ self.azimuth = HalfTurn::new(azimuth);
+
+ let height = sin_pi_f32(altitude) * self.distance;
+ let base = (self.distance * self.distance - height * height).sqrt();
+ let (s, c) = sin_cos_pi_f32(azimuth);
+ self.eye_offset = vec3(-base * s, height, -base * c);
+
if Point3::distance_sq(self.position, target)
> (GAME_VARIABLES.camera_deadzone * GAME_VARIABLES.camera_deadzone)
{
self.shake_offset.z = shake * perlin_noise3(1.0, t, 0.0);
}
+ pub fn position(&self) -> Point3 {
+ self.position + self.eye_offset
+ }
+
pub fn camera_from_model(&self) -> Mat4 {
let position = self.position + self.shake_offset;
- let eye = position + self.eye_offset;
+ let eye = self.position();
Mat4::look_at(eye, position, Vec3::Y)
}
}
self.player.position += player_velocity * delta_time;
self.camera
- .tick(self.player.position, self.time, delta_time);
+ .tick(&self.actions, self.player.position, self.time, delta_time);
self.player.weapon_cooldown -= delta_time;
}
let action = match key {
- Key::Left | Key::A => Some(Action::Left),
- Key::Right | Key::D => Some(Action::Right),
- Key::Up | Key::W => Some(Action::Up),
- Key::Down | Key::S => Some(Action::Down),
+ Key::A => Some(Action::Left),
+ Key::D => Some(Action::Right),
+ Key::W => Some(Action::Up),
+ Key::S => Some(Action::Down),
+ Key::Left => Some(Action::CameraLeft),
+ Key::Right => Some(Action::CameraRight),
+ Key::Up => Some(Action::CameraUp),
+ Key::Down => Some(Action::CameraDown),
Key::Space => Some(Action::Damage),
Key::Escape => break 'main,
_ => None,