update
This commit is contained in:
parent
1e6d352e00
commit
432e5a737a
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -154,6 +154,12 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_log-sys"
|
name = "android_log-sys"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -1704,6 +1710,18 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"num-traits",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@ -2663,6 +2681,30 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.25.6"
|
version = "0.25.6"
|
||||||
@ -2756,6 +2798,17 @@ version = "1.0.15"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iyes_perf_ui"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e4468c51a47d2422a3a3e01b45cb47ed0fbce520495dd7de495bcfe8ce0f856"
|
||||||
|
dependencies = [
|
||||||
|
"bevy",
|
||||||
|
"chrono",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@ -4204,6 +4257,7 @@ dependencies = [
|
|||||||
"bevy",
|
"bevy",
|
||||||
"bevy-inspector-egui",
|
"bevy-inspector-egui",
|
||||||
"bevy_panorbit_camera",
|
"bevy_panorbit_camera",
|
||||||
|
"iyes_perf_ui",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -7,6 +7,7 @@ edition = "2024"
|
|||||||
bevy = "0.16"
|
bevy = "0.16"
|
||||||
bevy-inspector-egui = "0.33.1"
|
bevy-inspector-egui = "0.33.1"
|
||||||
bevy_panorbit_camera = "0.26"
|
bevy_panorbit_camera = "0.26"
|
||||||
|
iyes_perf_ui = "0.5.0"
|
||||||
|
|
||||||
# Enable a small amount of optimization in the dev profile.
|
# Enable a small amount of optimization in the dev profile.
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
87
examples/cam_follow.rs
Normal file
87
examples/cam_follow.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
//! Demonstrates how to have the camera follow a target object
|
||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_plugins(PanOrbitCameraPlugin)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, (animate_cube, cam_follow).chain())
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Cube;
|
||||||
|
|
||||||
|
fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
// Ground
|
||||||
|
commands.spawn((
|
||||||
|
Mesh3d(meshes.add(Plane3d::default().mesh().size(0.5, 0.5))),
|
||||||
|
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
|
||||||
|
));
|
||||||
|
// Cube
|
||||||
|
commands
|
||||||
|
.spawn((
|
||||||
|
Mesh3d(meshes.add(Cuboid::new(0.1, 0.1, 0.1))),
|
||||||
|
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
|
||||||
|
Transform::from_xyz(0.0, 0.5, 0.0),
|
||||||
|
))
|
||||||
|
.insert(Cube);
|
||||||
|
// Light
|
||||||
|
commands.spawn((
|
||||||
|
PointLight {
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
Transform::from_xyz(4.0, 8.0, 4.0),
|
||||||
|
));
|
||||||
|
// Camera
|
||||||
|
commands.spawn((
|
||||||
|
Transform::from_translation(Vec3::new(0.0, 0.15, 0.5)),
|
||||||
|
PanOrbitCamera {
|
||||||
|
// Panning the camera changes the focus, and so you most likely want to disable
|
||||||
|
// panning when setting the focus manually
|
||||||
|
pan_sensitivity: 0.0,
|
||||||
|
// If you want to fully control the camera's focus, set smoothness to 0 so it
|
||||||
|
// immediately snaps to that location. If you want the 'follow' to be smoothed,
|
||||||
|
// leave this at default or set it to something between 0 and 1.
|
||||||
|
pan_smoothness: 0.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Move the cube in a circle around the Y axis
|
||||||
|
fn animate_cube(
|
||||||
|
time: Res<Time>,
|
||||||
|
mut cube_q: Query<&mut Transform, With<Cube>>,
|
||||||
|
mut angle: Local<f32>,
|
||||||
|
) {
|
||||||
|
if let Ok(mut cube_tfm) = cube_q.single_mut() {
|
||||||
|
// Rotate 20 degrees a second, wrapping around to 0 after a full rotation
|
||||||
|
*angle += 20f32.to_radians() * time.delta_secs() % TAU;
|
||||||
|
// Convert angle to position
|
||||||
|
let pos = Vec3::new(angle.sin() * 0.15, 0.05, angle.cos() * 0.15);
|
||||||
|
cube_tfm.translation = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the camera's focus to the cube's position
|
||||||
|
fn cam_follow(mut pan_orbit_q: Query<&mut PanOrbitCamera>, cube_q: Query<&Transform, With<Cube>>) {
|
||||||
|
if let Ok(mut pan_orbit) = pan_orbit_q.single_mut() {
|
||||||
|
if let Ok(cube_tfm) = cube_q.single() {
|
||||||
|
pan_orbit.target_focus = cube_tfm.translation;
|
||||||
|
// Whenever changing properties manually like this, it's necessary to force
|
||||||
|
// PanOrbitCamera to update this frame (by default it only updates when there are
|
||||||
|
// input events).
|
||||||
|
pan_orbit.force_update = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
153
src/main.rs
153
src/main.rs
@ -1,11 +1,12 @@
|
|||||||
use bevy::{
|
use bevy::{
|
||||||
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
|
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
|
||||||
|
input::mouse::{MouseScrollUnit, MouseWheel},
|
||||||
math::DVec3,
|
math::DVec3,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
window::WindowMode,
|
window::{WindowMode, WindowResolution},
|
||||||
};
|
};
|
||||||
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};
|
use bevy_inspector_egui::{bevy_egui::EguiPlugin, quick::WorldInspectorPlugin};
|
||||||
use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin};
|
use iyes_perf_ui::{PerfUiPlugin, prelude::PerfUiAllEntries};
|
||||||
|
|
||||||
// Scaling factor to convert AU to game units
|
// Scaling factor to convert AU to game units
|
||||||
// Neptune is approximately 30.1 astronomical units (AU) from the Sun
|
// Neptune is approximately 30.1 astronomical units (AU) from the Sun
|
||||||
@ -55,6 +56,8 @@ struct ObjectBundle {
|
|||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Star;
|
struct Star;
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Earth;
|
||||||
|
|
||||||
// Component for UI name labels
|
// Component for UI name labels
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
@ -66,20 +69,33 @@ struct ObjectLabel {
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
struct Trackable {}
|
struct Trackable {}
|
||||||
|
|
||||||
|
// Resource to track which entity the camera should follow
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct CameraFollow {
|
||||||
|
target: Option<Entity>,
|
||||||
|
distance: f32, // Current zoom distance from target
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SolarRenderingPlugin;
|
pub struct SolarRenderingPlugin;
|
||||||
|
|
||||||
impl Plugin for SolarRenderingPlugin {
|
impl Plugin for SolarRenderingPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, (setup_rendering, setup_ui))
|
app.insert_resource(CameraFollow {
|
||||||
|
target: None,
|
||||||
|
distance: 10.0,
|
||||||
|
})
|
||||||
|
.add_systems(Startup, (setup_rendering, setup_ui))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
FixedPostUpdate,
|
FixedPostUpdate,
|
||||||
(
|
(
|
||||||
sync_radius_to_mesh,
|
sync_radius_to_mesh,
|
||||||
sync_position_to_transform,
|
sync_position_to_transform,
|
||||||
sync_name_labels,
|
sync_name_labels,
|
||||||
|
camera_follow_system.after(sync_position_to_transform),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.add_systems(Update, (update_label_positions, handle_label_clicks));
|
.add_systems(PostUpdate, update_label_positions)
|
||||||
|
.add_systems(Update, (handle_label_clicks, handle_scroll_zoom));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +109,6 @@ fn sync_radius_to_mesh(
|
|||||||
// Convert AU to game units for rendering
|
// Convert AU to game units for rendering
|
||||||
let render_radius = radius.0.0 * AU_TO_GAME_UNITS;
|
let render_radius = radius.0.0 * AU_TO_GAME_UNITS;
|
||||||
|
|
||||||
println!("render_radius: {:?}", render_radius);
|
|
||||||
|
|
||||||
// Create or update sphere mesh
|
// Create or update sphere mesh
|
||||||
let sphere_mesh = meshes.add(Sphere::new(render_radius as f32));
|
let sphere_mesh = meshes.add(Sphere::new(render_radius as f32));
|
||||||
let material = materials.add(if star.is_none() {
|
let material = materials.add(if star.is_none() {
|
||||||
@ -129,7 +143,6 @@ fn sync_position_to_transform(
|
|||||||
for (entity, position, transform) in query.iter_mut() {
|
for (entity, position, transform) in query.iter_mut() {
|
||||||
// Convert AU to game units for rendering
|
// Convert AU to game units for rendering
|
||||||
let scaled_position = position.0.0 * AU_TO_GAME_UNITS;
|
let scaled_position = position.0.0 * AU_TO_GAME_UNITS;
|
||||||
println!("scaled_position: {:?}", scaled_position);
|
|
||||||
match transform {
|
match transform {
|
||||||
Some(mut t) => {
|
Some(mut t) => {
|
||||||
// Update existing transform
|
// Update existing transform
|
||||||
@ -161,13 +174,14 @@ fn setup_rendering(mut commands: Commands) {
|
|||||||
}),
|
}),
|
||||||
Tonemapping::TonyMcMapface,
|
Tonemapping::TonyMcMapface,
|
||||||
Transform::from_translation(Vec3::new(0., 0., 10.0)),
|
Transform::from_translation(Vec3::new(0., 0., 10.0)),
|
||||||
Bloom::NATURAL,
|
// Bloom::NATURAL,
|
||||||
PanOrbitCamera {
|
// PanOrbitCamera {
|
||||||
pan_sensitivity: 0.0, // Disable panning by setting sensitivity to 0
|
// pan_sensitivity: 0.0, // Disable panning by setting sensitivity to 0
|
||||||
focus: Vec3::ZERO,
|
// focus: Vec3::ZERO,
|
||||||
zoom_lower_limit: 1e-8,
|
// zoom_lower_limit: 1e-8,
|
||||||
..default()
|
// allow_upside_down: true,
|
||||||
},
|
// ..default()
|
||||||
|
// },
|
||||||
));
|
));
|
||||||
|
|
||||||
commands.spawn(PointLight {
|
commands.spawn(PointLight {
|
||||||
@ -175,6 +189,7 @@ fn setup_rendering(mut commands: Commands) {
|
|||||||
shadows_enabled: true,
|
shadows_enabled: true,
|
||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
commands.spawn(PerfUiAllEntries::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_ui(mut commands: Commands) {
|
fn setup_ui(mut commands: Commands) {
|
||||||
@ -225,7 +240,7 @@ fn sync_name_labels(
|
|||||||
|
|
||||||
fn update_label_positions(
|
fn update_label_positions(
|
||||||
mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text), With<ObjectLabel>>,
|
mut label_query: Query<(&mut Node, &ObjectLabel, &mut Text), With<ObjectLabel>>,
|
||||||
objects_query: Query<(&Position, &Name, &Radius)>,
|
objects_query: Query<(&GlobalTransform, &Name, &Radius)>,
|
||||||
camera_query: Query<(&Camera, &GlobalTransform)>,
|
camera_query: Query<(&Camera, &GlobalTransform)>,
|
||||||
) {
|
) {
|
||||||
let Ok((camera, camera_transform)) = camera_query.single() else {
|
let Ok((camera, camera_transform)) = camera_query.single() else {
|
||||||
@ -233,14 +248,12 @@ fn update_label_positions(
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (mut node, label, mut text) in label_query.iter_mut() {
|
for (mut node, label, mut text) in label_query.iter_mut() {
|
||||||
if let Ok((position, name, _radius)) = objects_query.get(label.target_entity) {
|
if let Ok((global_transform, name, _radius)) = objects_query.get(label.target_entity) {
|
||||||
let world_pos = position.0.0 * AU_TO_GAME_UNITS;
|
let world_pos = global_transform.translation();
|
||||||
|
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos) {
|
||||||
if let Ok(screen_pos) = camera.world_to_viewport(camera_transform, world_pos.as_vec3())
|
|
||||||
{
|
|
||||||
// Position the label on screen, offset slightly to avoid overlapping the object
|
// Position the label on screen, offset slightly to avoid overlapping the object
|
||||||
node.left = Val::Px(screen_pos.x + 10.0);
|
node.left = Val::Px(screen_pos.x);
|
||||||
node.top = Val::Px(screen_pos.y - 10.0);
|
node.top = Val::Px(screen_pos.y);
|
||||||
|
|
||||||
// Update text in case name changed
|
// Update text in case name changed
|
||||||
text.0 = name.0.0.clone();
|
text.0 = name.0.0.clone();
|
||||||
@ -261,26 +274,87 @@ fn handle_label_clicks(
|
|||||||
(&Interaction, &ObjectLabel),
|
(&Interaction, &ObjectLabel),
|
||||||
(Changed<Interaction>, With<ObjectLabel>),
|
(Changed<Interaction>, With<ObjectLabel>),
|
||||||
>,
|
>,
|
||||||
trackable_objects: Query<(&Position, &Trackable, &Radius)>,
|
trackable_objects: Query<(&Transform, &Trackable, &Radius)>,
|
||||||
mut camera_query: Query<&mut PanOrbitCamera>,
|
mut camera_query: Query<&mut Transform, (With<Camera>, Without<Trackable>)>,
|
||||||
|
mut camera_follow: ResMut<CameraFollow>,
|
||||||
) {
|
) {
|
||||||
for (interaction, label) in interaction_query.iter() {
|
for (interaction, label) in interaction_query.iter() {
|
||||||
if *interaction == Interaction::Pressed {
|
if *interaction == Interaction::Pressed {
|
||||||
if let Ok((position, _trackable, radius)) = trackable_objects.get(label.target_entity) {
|
if let Ok((target_transform, _trackable, radius)) =
|
||||||
if let Ok(mut pan_orbit) = camera_query.single_mut() {
|
trackable_objects.get(label.target_entity)
|
||||||
// Focus camera on the clicked object using target values for smooth transitions
|
{
|
||||||
pan_orbit.target_focus = (position.0.0 * AU_TO_GAME_UNITS).as_vec3();
|
if let Ok(mut camera_transform) = camera_query.single_mut() {
|
||||||
pan_orbit.target_radius = (radius.0.0 * AU_TO_GAME_UNITS) as f32 * 4.;
|
let target_world_pos = target_transform.translation;
|
||||||
|
|
||||||
// Also set the immediate values to ensure it takes effect
|
// Calculate appropriate distance based on entity radius (with some padding)
|
||||||
// pan_orbit.focus = position.0.0.as_vec3();
|
let entity_radius = (radius.0.0 * AU_TO_GAME_UNITS) as f32;
|
||||||
// pan_orbit.radius = Some(trackable.zoom_distance);
|
let desired_distance = entity_radius * 16.0; // 8x radius
|
||||||
|
|
||||||
|
// Position camera at desired distance from target
|
||||||
|
let camera_direction =
|
||||||
|
(camera_transform.translation - target_world_pos).normalize();
|
||||||
|
let new_camera_pos = target_world_pos + camera_direction * desired_distance;
|
||||||
|
|
||||||
|
camera_transform.translation = new_camera_pos;
|
||||||
|
camera_transform.look_at(target_world_pos, Vec3::Y);
|
||||||
|
|
||||||
|
// Set the follow target and initial distance
|
||||||
|
camera_follow.target = Some(label.target_entity);
|
||||||
|
camera_follow.distance = desired_distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn camera_follow_system(
|
||||||
|
camera_follow: Res<CameraFollow>,
|
||||||
|
objects_query: Query<&Transform, With<Trackable>>,
|
||||||
|
mut camera_query: Query<&mut Transform, (With<Camera>, Without<Trackable>)>,
|
||||||
|
) {
|
||||||
|
if let Some(target_entity) = camera_follow.target {
|
||||||
|
if let Ok(target_transform) = objects_query.get(target_entity) {
|
||||||
|
if let Ok(mut camera_transform) = camera_query.single_mut() {
|
||||||
|
// Fixed camera direction relative to tracked object (stationary relative position)
|
||||||
|
let fixed_direction = Vec3::new(0.0, 0.0, 1.0);
|
||||||
|
|
||||||
|
// Position camera at the specified distance from target in fixed direction
|
||||||
|
let new_camera_pos =
|
||||||
|
target_transform.translation + fixed_direction * camera_follow.distance;
|
||||||
|
camera_transform.translation = new_camera_pos;
|
||||||
|
|
||||||
|
// Keep camera looking at the target entity
|
||||||
|
camera_transform.look_at(target_transform.translation, Vec3::Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_scroll_zoom(
|
||||||
|
mut scroll_events: EventReader<MouseWheel>,
|
||||||
|
mut camera_follow: ResMut<CameraFollow>,
|
||||||
|
) {
|
||||||
|
for event in scroll_events.read() {
|
||||||
|
let zoom_delta = match event.unit {
|
||||||
|
MouseScrollUnit::Line => event.y * 0.1,
|
||||||
|
MouseScrollUnit::Pixel => event.y * 0.01,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update zoom distance with limits
|
||||||
|
camera_follow.distance = (camera_follow.distance * (1.0 - zoom_delta)).clamp(1e-9, 100.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_camera_follow(
|
||||||
|
earth_query: Query<Entity, (With<Earth>, With<Trackable>)>,
|
||||||
|
mut camera_follow: ResMut<CameraFollow>,
|
||||||
|
) {
|
||||||
|
if let Ok(earth) = earth_query.single() {
|
||||||
|
camera_follow.target = Some(earth);
|
||||||
|
camera_follow.distance = 1e-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_solar_system(mut commands: Commands) {
|
fn setup_solar_system(mut commands: Commands) {
|
||||||
// Sun - From NASA JPL Horizons data (J2000.0 epoch)
|
// Sun - From NASA JPL Horizons data (J2000.0 epoch)
|
||||||
// Physical properties: Mass = 1988410 x 10^24 kg, Radius = 695700 km = 0.00465 AU
|
// Physical properties: Mass = 1988410 x 10^24 kg, Radius = 695700 km = 0.00465 AU
|
||||||
@ -316,6 +390,7 @@ fn setup_solar_system(mut commands: Commands) {
|
|||||||
radius: Radius(DistanceAu(0.0000426)), // Mean radius in AU (6371.01 km / 149597870.691)
|
radius: Radius(DistanceAu(0.0000426)), // Mean radius in AU (6371.01 km / 149597870.691)
|
||||||
},
|
},
|
||||||
Trackable {},
|
Trackable {},
|
||||||
|
Earth,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Moon - From NASA JPL Horizons data (A.D. 2000-Jan-01 12:00:00.0000 TDB)
|
// Moon - From NASA JPL Horizons data (A.D. 2000-Jan-01 12:00:00.0000 TDB)
|
||||||
@ -415,15 +490,23 @@ fn main() {
|
|||||||
primary_window: Some(Window {
|
primary_window: Some(Window {
|
||||||
title: "Solar Sim".to_string(),
|
title: "Solar Sim".to_string(),
|
||||||
mode: WindowMode::BorderlessFullscreen(MonitorSelection::Primary),
|
mode: WindowMode::BorderlessFullscreen(MonitorSelection::Primary),
|
||||||
|
resolution: WindowResolution::default().with_scale_factor_override(2.0),
|
||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
..default()
|
..default()
|
||||||
}))
|
}))
|
||||||
.add_plugins(PanOrbitCameraPlugin)
|
|
||||||
.add_plugins(EguiPlugin::default())
|
.add_plugins(EguiPlugin::default())
|
||||||
.add_plugins(WorldInspectorPlugin::new())
|
.add_plugins(WorldInspectorPlugin::new())
|
||||||
.add_plugins(SolarRenderingPlugin)
|
.add_plugins(SolarRenderingPlugin) // we want Bevy to measure these values for us:
|
||||||
.add_systems(Startup, setup_solar_system)
|
.add_plugins(bevy::diagnostic::FrameTimeDiagnosticsPlugin::default())
|
||||||
|
.add_plugins(bevy::diagnostic::EntityCountDiagnosticsPlugin)
|
||||||
|
.add_plugins(bevy::diagnostic::SystemInformationDiagnosticsPlugin)
|
||||||
|
.add_plugins(bevy::render::diagnostic::RenderDiagnosticsPlugin)
|
||||||
|
.add_plugins(PerfUiPlugin)
|
||||||
|
.add_systems(
|
||||||
|
Startup,
|
||||||
|
(setup_solar_system, initialize_camera_follow).chain(),
|
||||||
|
)
|
||||||
.add_systems(FixedUpdate, n_body)
|
.add_systems(FixedUpdate, n_body)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user