Advanced camera inputs and added moon (moon not working yet)

This commit is contained in:
Verox001 2025-05-07 21:54:37 +02:00
parent 8ce9493d00
commit 55faf5c4d3
6 changed files with 241 additions and 104 deletions

View File

@ -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();

View File

@ -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);
}
}
}
}
}
}
}

View File

@ -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) {

View File

@ -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),
}
}

View File

@ -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;

View File

@ -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 {