Advanced camera inputs and added moon (moon not working yet)
This commit is contained in:
parent
8ce9493d00
commit
55faf5c4d3
@ -1,5 +1,5 @@
|
||||
use cgmath::{Rotation3, Vector3};
|
||||
use solar_engine::{Application, Body, Key, KeyState, Light, LightType, Simulator};
|
||||
use solar_engine::{Application, Body, InputEvent, Key, Light, LightType, MouseButton, Simulator};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread;
|
||||
|
||||
@ -21,6 +21,17 @@ pub async fn run() {
|
||||
mass: 5.972e24,
|
||||
radius: 6.371e6,
|
||||
});
|
||||
|
||||
let earth_position = sim.bodies[1].position;
|
||||
let earth_velocity = sim.bodies[1].velocity;
|
||||
|
||||
sim.add_body(Body {
|
||||
name: "Moon".into(),
|
||||
position: earth_position + Vector3::new(384.4e6, 0.0, 0.0),
|
||||
velocity: earth_velocity + Vector3::new(0.0, 1022.0, 0.0),
|
||||
mass: 7.342e22,
|
||||
radius: 1.737e6,
|
||||
});
|
||||
}
|
||||
|
||||
let sim_clone = simulator.clone();
|
||||
@ -81,9 +92,30 @@ pub async fn run() {
|
||||
|
||||
state.set_instances(instances);
|
||||
})
|
||||
.on_input(move |state, event| {
|
||||
if event.state == KeyState::Pressed {
|
||||
return match event.key {
|
||||
.on_input({
|
||||
let simulator = simulator.clone();
|
||||
move |state, event| {
|
||||
match event {
|
||||
InputEvent::MouseDragged { delta, button: MouseButton::Left } => {
|
||||
state.camera_mut().rotate_yaw_pitch(-delta.x as f32 * 0.1, delta.y as f32 * 0.1);
|
||||
}
|
||||
InputEvent::MouseWheel { delta } => {
|
||||
state.camera_mut().zoom(delta * 0.05);
|
||||
}
|
||||
InputEvent::KeyPressed { key, .. } => {
|
||||
match key {
|
||||
Key::ArrowLeft => {
|
||||
state.camera_mut().translate(Vector3::new(-1.0, 0.0, 0.0));
|
||||
}
|
||||
Key::ArrowRight => {
|
||||
state.camera_mut().translate(Vector3::new(1.0, 0.0, 0.0));
|
||||
}
|
||||
Key::ArrowUp => {
|
||||
state.camera_mut().translate(Vector3::new(0.0, 1.0, 0.0));
|
||||
}
|
||||
Key::ArrowDown => {
|
||||
state.camera_mut().translate(Vector3::new(0.0, -1.0, 0.0));
|
||||
}
|
||||
Key::Period => {
|
||||
let mut sim = simulator.write().unwrap();
|
||||
sim.increase_timewarp();
|
||||
@ -99,12 +131,11 @@ pub async fn run() {
|
||||
sim.reset_timewarp();
|
||||
println!("Timewarp: {}", sim.get_timewarp());
|
||||
}
|
||||
Key::ArrowLeft => state.camera_mut().rotate_yaw_pitch(-5.0, 0.0),
|
||||
Key::ArrowRight => state.camera_mut().rotate_yaw_pitch(5.0, 0.0),
|
||||
Key::ArrowUp => state.camera_mut().zoom(-0.2),
|
||||
Key::ArrowDown => state.camera_mut().zoom(0.2),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
})
|
||||
.run();
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::{Modifiers, WindowEvent};
|
||||
use winit::event::{ElementState, Modifiers, MouseScrollDelta, WindowEvent};
|
||||
use winit::event_loop::ActiveEventLoop;
|
||||
use winit::window::{Window, WindowId};
|
||||
use crate::input::{from_winit_input, InputEvent};
|
||||
use crate::input::{InputEvent, InputTracker};
|
||||
|
||||
pub struct StateApplication<'a> {
|
||||
state: Option<crate::state::State<'a>>,
|
||||
modifiers: Modifiers,
|
||||
update_fn: Option<Box<dyn FnMut(&mut crate::state::State<'a>) + 'a>>,
|
||||
input_fn: Option<Box<dyn FnMut(&mut crate::state::State<'a>, &InputEvent) + 'a>>,
|
||||
input_tracker: InputTracker
|
||||
}
|
||||
|
||||
impl<'a> StateApplication<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { state: None, update_fn: None, input_fn: None, modifiers: Modifiers::default() }
|
||||
Self { state: None, update_fn: None, input_fn: None, modifiers: Modifiers::default(), input_tracker: InputTracker::default() }
|
||||
}
|
||||
|
||||
pub fn on_update<F: FnMut(&mut crate::state::State<'a>) + 'a>(mut self, func: F) -> Self {
|
||||
@ -63,18 +64,18 @@ impl<'a> ApplicationHandler for StateApplication<'a> {
|
||||
|
||||
self.state.as_mut().unwrap().render().unwrap();
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
if let Some(input_fn) = self.input_fn.as_mut() {
|
||||
let key_event = from_winit_input(&event, self.modifiers);
|
||||
input_fn(state, &key_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
self.modifiers = modifiers;
|
||||
}
|
||||
_ => {}
|
||||
_ => {
|
||||
if let Some(state) = self.state.as_mut() {
|
||||
if let Some(event) = self.input_tracker.handle_window_event(&event, self.modifiers) {
|
||||
if let Some(input_fn) = self.input_fn.as_mut() {
|
||||
input_fn(state, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,19 +35,27 @@ impl Camera {
|
||||
}
|
||||
|
||||
pub fn rotate_yaw_pitch(&mut self, yaw: f32, pitch: f32) {
|
||||
let dir = (self.target - self.eye).normalize();
|
||||
let offset = self.eye - self.target;
|
||||
let distance = offset.magnitude();
|
||||
|
||||
let yaw_q = Quaternion::from_axis_angle(Vector3::unit_y(), Rad(yaw.to_radians()));
|
||||
let right = offset.cross(self.up).normalize();
|
||||
let pitch_q = Quaternion::from_axis_angle(right, Rad(pitch.to_radians()));
|
||||
|
||||
let rotation = yaw_q * pitch_q;
|
||||
let new_offset = rotation.rotate_vector(offset);
|
||||
|
||||
self.eye = self.target + new_offset;
|
||||
self.up = rotation.rotate_vector(self.up);
|
||||
}
|
||||
|
||||
pub fn translate(&mut self, translation: Vector3<f32>) {
|
||||
let dir = (self.target - self.eye).normalize();
|
||||
let horizontal = Vector3::unit_y().cross(dir).normalize();
|
||||
let vertical = horizontal.cross(dir).normalize();
|
||||
|
||||
let pitch_q = Quaternion::from_axis_angle(vertical, Rad(pitch.to_radians()));
|
||||
let yaw_q = Quaternion::from_axis_angle(Vector3::unit_y(), Rad(yaw.to_radians()));
|
||||
|
||||
let rotation = yaw_q * pitch_q;
|
||||
let rotated = rotation.rotate_vector(dir).normalize();
|
||||
|
||||
let distance = (self.target - self.eye).magnitude();
|
||||
self.eye = self.target - rotated * distance;
|
||||
self.eye += horizontal * translation.x + vertical * translation.y;
|
||||
self.target += horizontal * translation.x + vertical * translation.y;
|
||||
}
|
||||
|
||||
pub fn zoom(&mut self, amount: f32) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use log::info;
|
||||
use winit::event::{ElementState, KeyEvent, Modifiers};
|
||||
use cgmath::Vector2;
|
||||
use winit::event::{ElementState, KeyEvent, MouseScrollDelta, Modifiers, WindowEvent};
|
||||
use winit::keyboard::{Key as WinitKey, ModifiersKeyState};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@ -33,16 +33,9 @@ pub enum Key {
|
||||
Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
|
||||
NumpadAdd, NumpadSubtract, NumpadMultiply, NumpadDivide, NumpadEnter,
|
||||
|
||||
// Unknown
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum KeyState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct KeyModifiers {
|
||||
pub lshift: bool,
|
||||
@ -55,23 +48,88 @@ pub struct KeyModifiers {
|
||||
pub lsuper_key: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InputEvent {
|
||||
pub key: Key,
|
||||
pub state: KeyState,
|
||||
pub text: String,
|
||||
pub modifiers: KeyModifiers,
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Other(u16),
|
||||
}
|
||||
|
||||
pub fn from_winit_input(event: &KeyEvent, modifiers: Modifiers) -> InputEvent {
|
||||
InputEvent {
|
||||
key: map_winit_key(&event.logical_key),
|
||||
state: match event.state {
|
||||
ElementState::Pressed => KeyState::Pressed,
|
||||
ElementState::Released => KeyState::Released,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InputEvent {
|
||||
KeyPressed {
|
||||
key: Key,
|
||||
modifiers: KeyModifiers,
|
||||
text: String,
|
||||
},
|
||||
text: event.text.clone().unwrap_or_default().into(),
|
||||
modifiers: KeyModifiers {
|
||||
KeyReleased {
|
||||
key: Key,
|
||||
modifiers: KeyModifiers,
|
||||
},
|
||||
MouseMoved {
|
||||
position: (f64, f64),
|
||||
},
|
||||
MouseDragged {
|
||||
delta: Vector2<f64>,
|
||||
button: MouseButton,
|
||||
},
|
||||
MouseWheel {
|
||||
delta: f32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InputTracker {
|
||||
pub last_cursor_pos: Option<(f64, f64)>,
|
||||
pub dragging_button: Option<MouseButton>,
|
||||
}
|
||||
|
||||
impl InputTracker {
|
||||
pub fn handle_window_event(&mut self, event: &WindowEvent, modifiers: Modifiers) -> Option<InputEvent> {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
Some(handle_keyboard_input(event, modifiers))
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
if let (Some(last), Some(button)) = (self.last_cursor_pos, self.dragging_button) {
|
||||
let delta = Vector2::new(position.x - last.0, position.y - last.1);
|
||||
self.last_cursor_pos = Some((position.x, position.y));
|
||||
Some(InputEvent::MouseDragged { delta, button })
|
||||
} else {
|
||||
self.last_cursor_pos = Some((position.x, position.y));
|
||||
Some(InputEvent::MouseMoved {
|
||||
position: (position.x, position.y),
|
||||
})
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let mapped = map_button(*button);
|
||||
match state {
|
||||
ElementState::Pressed => {
|
||||
self.dragging_button = Some(mapped);
|
||||
}
|
||||
ElementState::Released => {
|
||||
self.dragging_button = None;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
let scroll = match delta {
|
||||
MouseScrollDelta::LineDelta(_, y) => *y,
|
||||
MouseScrollDelta::PixelDelta(p) => p.y as f32,
|
||||
};
|
||||
Some(InputEvent::MouseWheel { delta: scroll })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_keyboard_input(event: &KeyEvent, modifiers: Modifiers) -> InputEvent {
|
||||
let key = map_winit_key(&event.logical_key);
|
||||
let mods = KeyModifiers {
|
||||
lshift: modifiers.lshift_state() == ModifiersKeyState::Pressed,
|
||||
rshift: modifiers.rshift_state() == ModifiersKeyState::Pressed,
|
||||
lcontrol: modifiers.lcontrol_state() == ModifiersKeyState::Pressed,
|
||||
@ -80,7 +138,28 @@ pub fn from_winit_input(event: &KeyEvent, modifiers: Modifiers) -> InputEvent {
|
||||
ralt: modifiers.ralt_state() == ModifiersKeyState::Pressed,
|
||||
rsuper_key: modifiers.rsuper_state() == ModifiersKeyState::Pressed,
|
||||
lsuper_key: modifiers.lsuper_state() == ModifiersKeyState::Pressed,
|
||||
};
|
||||
|
||||
match event.state {
|
||||
ElementState::Pressed => InputEvent::KeyPressed {
|
||||
key,
|
||||
modifiers: mods,
|
||||
text: event.text.clone().unwrap_or_default().into(),
|
||||
},
|
||||
ElementState::Released => InputEvent::KeyReleased {
|
||||
key,
|
||||
modifiers: mods,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_button(button: winit::event::MouseButton) -> MouseButton {
|
||||
match button {
|
||||
winit::event::MouseButton::Left => MouseButton::Left,
|
||||
winit::event::MouseButton::Right => MouseButton::Right,
|
||||
winit::event::MouseButton::Middle => MouseButton::Middle,
|
||||
winit::event::MouseButton::Other(n) => MouseButton::Other(n),
|
||||
_ => MouseButton::Other(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ pub use state::State;
|
||||
pub use input::Key;
|
||||
pub use input::map_winit_key;
|
||||
pub use input::InputEvent;
|
||||
pub use input::KeyState;
|
||||
pub use input::MouseButton;
|
||||
|
||||
pub use light::Light;
|
||||
pub use light::LightType;
|
||||
@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use cgmath::{InnerSpace, Vector3};
|
||||
use crate::body::Body;
|
||||
@ -51,12 +52,10 @@ impl Simulator {
|
||||
|
||||
let masses: Vec<f64> = self.bodies.iter().map(|b| b.mass).collect();
|
||||
|
||||
fn compute_accelerations(states: &[State], masses: &[f64]) -> Vec<Vector3<f64>> {
|
||||
let n = states.len();
|
||||
let accels = (0..n).map(|_| Mutex::new(Vector3::new(0.0, 0.0, 0.0))).collect::<Vec<_>>();
|
||||
fn compute_accelerations(states: &[State], masses: &[f64], ownership: &HashMap<usize, usize>) -> Vec<Vector3<f64>> {
|
||||
let mut accels = vec![Vector3::new(0.0, 0.0, 0.0); states.len()];
|
||||
|
||||
(0..n).into_par_iter().for_each(|i| {
|
||||
for j in (i + 1)..n {
|
||||
for (&i, &j) in ownership {
|
||||
let r = states[j].position - states[i].position;
|
||||
let dist_sq = r.magnitude2();
|
||||
let dist = dist_sq.sqrt();
|
||||
@ -67,24 +66,16 @@ impl Simulator {
|
||||
|
||||
let force = G * masses[i] * masses[j] / dist_sq;
|
||||
let accel = force * r / (dist * masses[i]);
|
||||
let accel_j = -force * r / (dist * masses[j]);
|
||||
|
||||
{
|
||||
let mut a_i_lock = accels[i].lock().unwrap();
|
||||
*a_i_lock += accel;
|
||||
}
|
||||
{
|
||||
let mut a_j_lock = accels[j].lock().unwrap();
|
||||
*a_j_lock += accel_j;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
accels.into_iter().map(|mutex| mutex.into_inner().unwrap()).collect()
|
||||
accels[i] += accel;
|
||||
}
|
||||
|
||||
accels
|
||||
}
|
||||
|
||||
let ownership = self.compute_soi_owners();
|
||||
let k1_pos = original_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||
let k1_vel = compute_accelerations(&original_states, &masses);
|
||||
let k1_vel = compute_accelerations(&original_states, &masses, &ownership);
|
||||
|
||||
let mut temp_states: Vec<State> = original_states.iter().enumerate().map(|(i, s)| {
|
||||
State {
|
||||
@ -94,7 +85,7 @@ impl Simulator {
|
||||
}).collect();
|
||||
|
||||
let k2_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||
let k2_vel = compute_accelerations(&temp_states, &masses);
|
||||
let k2_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||
|
||||
for i in 0..n {
|
||||
temp_states[i].position = original_states[i].position + k2_pos[i] * (dt / 2.0);
|
||||
@ -102,7 +93,7 @@ impl Simulator {
|
||||
}
|
||||
|
||||
let k3_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||
let k3_vel = compute_accelerations(&temp_states, &masses);
|
||||
let k3_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||
|
||||
for i in 0..n {
|
||||
temp_states[i].position = original_states[i].position + k3_pos[i] * dt;
|
||||
@ -110,7 +101,7 @@ impl Simulator {
|
||||
}
|
||||
|
||||
let k4_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||
let k4_vel = compute_accelerations(&temp_states, &masses);
|
||||
let k4_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||
|
||||
for i in 0..n {
|
||||
let body = &mut self.bodies[i];
|
||||
@ -122,6 +113,33 @@ impl Simulator {
|
||||
self.time += dt;
|
||||
}
|
||||
|
||||
fn compute_soi_owners(&self) -> HashMap<usize, usize> {
|
||||
let mut ownership = HashMap::new();
|
||||
for (i, body) in self.bodies.iter().enumerate() {
|
||||
let mut min_distance = f64::MAX;
|
||||
let mut dominant_index = None;
|
||||
|
||||
for (j, other) in self.bodies.iter().enumerate() {
|
||||
if i == j {
|
||||
continue;
|
||||
}
|
||||
|
||||
let r = (body.position - other.position).magnitude();
|
||||
let soi_radius = r * (body.mass / other.mass).powf(2.0 / 5.0);
|
||||
|
||||
if r < soi_radius && r < min_distance {
|
||||
min_distance = r;
|
||||
dominant_index = Some(j);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(j) = dominant_index {
|
||||
ownership.insert(i, j);
|
||||
}
|
||||
}
|
||||
ownership
|
||||
}
|
||||
|
||||
pub fn increase_timewarp(&mut self) {
|
||||
if let Some(new) = self.timewarp.checked_mul(2) {
|
||||
if new <= MAX_TIMEWARP {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user