Aevyrie 8721911b49
Explicit BigSpaces (#22)
Changes the design of the plugin to work with multiple, independent
high-precision hierarchies at the root with the `BigSpace` component at
the root of each of these hierarchies.

Closes #17
Closes #19 
Closes #21
2024-06-17 01:41:03 -07:00

328 lines
12 KiB
Rust

use std::collections::VecDeque;
/// Example with spheres at the scale and distance of the earth and moon around the sun, at 1:1
/// scale. The earth is rotating on its axis, and the camera is in this reference frame, to
/// demonstrate how high precision nested reference frames work at large scales.
use bevy::{
core_pipeline::bloom::BloomSettings,
math::DVec3,
pbr::{CascadeShadowConfigBuilder, NotShadowCaster},
prelude::*,
render::camera::Exposure,
transform::TransformSystem,
};
use big_space::{
camera::{CameraController, CameraInput},
commands::BigSpaceCommands,
reference_frame::ReferenceFrame,
FloatingOrigin,
};
use rand::Rng;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.build().disable::<TransformPlugin>(),
// bevy_inspector_egui::quick::WorldInspectorPlugin::new(),
big_space::BigSpacePlugin::<i64>::new(true),
// big_space::debug::FloatingOriginDebugPlugin::<i64>::default(),
big_space::camera::CameraControllerPlugin::<i64>::default(),
))
.insert_resource(ClearColor(Color::BLACK))
.insert_resource(AmbientLight {
color: Color::WHITE,
brightness: 200.0,
})
.add_systems(Startup, spawn_solar_system)
.add_systems(
PostUpdate,
(
rotate,
lighting
.in_set(TransformSystem::TransformPropagate)
.after(bevy::transform::systems::sync_simple_transforms)
.after(bevy::transform::systems::propagate_transforms)
.after(big_space::FloatingOriginSet::PropagateLowPrecision),
cursor_grab_system,
springy_ship
.after(big_space::camera::default_camera_inputs)
.before(big_space::camera::camera_controller::<i64>),
),
)
.register_type::<Sun>()
.register_type::<Rotates>()
.run()
}
const EARTH_ORBIT_RADIUS_M: f64 = 149.60e9;
const EARTH_RADIUS_M: f64 = 6.371e6;
const SUN_RADIUS_M: f64 = 695_508_000_f64;
const MOON_RADIUS_M: f64 = 1.7375e6;
#[derive(Component, Reflect)]
struct Sun;
#[derive(Component, Reflect)]
struct PrimaryLight;
#[derive(Component, Reflect)]
struct Spaceship;
#[derive(Component, Reflect)]
struct Rotates(f32);
fn rotate(mut rotate_query: Query<(&mut Transform, &Rotates)>) {
for (mut transform, rotates) in rotate_query.iter_mut() {
transform.rotate_local_y(rotates.0);
}
}
fn lighting(
mut light: Query<(&mut Transform, &mut GlobalTransform), With<PrimaryLight>>,
sun: Query<&GlobalTransform, (With<Sun>, Without<PrimaryLight>)>,
) {
let sun_pos = sun.single().translation();
let (mut light_tr, mut light_gt) = light.single_mut();
light_tr.look_at(-sun_pos, Vec3::Y);
*light_gt = (*light_tr).into();
}
fn springy_ship(
cam_input: Res<CameraInput>,
mut ship: Query<&mut Transform, With<Spaceship>>,
mut desired_dir: Local<(Vec3, Quat)>,
mut smoothed_rot: Local<VecDeque<Vec3>>,
) {
desired_dir.0 = DVec3::new(cam_input.right, cam_input.up, -cam_input.forward).as_vec3()
* (1.0 + cam_input.boost as u8 as f32);
smoothed_rot.truncate(15);
smoothed_rot.push_front(DVec3::new(cam_input.pitch, cam_input.yaw, cam_input.roll).as_vec3());
let avg_rot = smoothed_rot.iter().sum::<Vec3>() / smoothed_rot.len() as f32;
use std::f32::consts::*;
desired_dir.1 = Quat::IDENTITY.slerp(
Quat::from_euler(
EulerRot::XYZ,
avg_rot.x.clamp(-FRAC_PI_4, FRAC_PI_4),
avg_rot.y.clamp(-FRAC_PI_4, FRAC_PI_4),
avg_rot.z.clamp(-FRAC_PI_4, FRAC_PI_4),
),
0.2,
) * Quat::from_rotation_y(PI);
ship.single_mut().translation = ship
.single_mut()
.translation
.lerp(desired_dir.0 * Vec3::new(0.5, 0.5, -2.0), 0.02);
ship.single_mut().rotation = ship.single_mut().rotation.slerp(desired_dir.1, 0.02);
}
fn spawn_solar_system(
asset_server: Res<AssetServer>,
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
let sun_mesh_handle = meshes.add(Sphere::new(SUN_RADIUS_M as f32).mesh().ico(6).unwrap());
let earth_mesh_handle = meshes.add(Sphere::new(1.0).mesh().ico(35).unwrap());
let moon_mesh_handle = meshes.add(Sphere::new(MOON_RADIUS_M as f32).mesh().ico(15).unwrap());
let ball_mesh_handle = meshes.add(Sphere::new(5.0).mesh().ico(5).unwrap());
let plane_mesh_handle = meshes.add(Plane3d::new(Vec3::X));
commands.spawn((
PrimaryLight,
DirectionalLightBundle {
directional_light: DirectionalLight {
color: Color::WHITE,
illuminance: 120_000.,
shadows_enabled: true,
..default()
},
cascade_shadow_config: CascadeShadowConfigBuilder {
num_cascades: 4,
minimum_distance: 0.1,
maximum_distance: 10_000.0,
first_cascade_far_bound: 100.0,
overlap_proportion: 0.2,
}
.build(),
..default()
},
));
commands.spawn_big_space(ReferenceFrame::<i64>::default(), |root_frame| {
root_frame.with_frame_default(|sun| {
sun.insert((Sun, Name::new("Sun")));
sun.spawn_spatial((
PbrBundle {
mesh: sun_mesh_handle,
material: materials.add(StandardMaterial {
base_color: Color::WHITE,
emissive: Color::rgb_linear(100000000., 100000000., 100000000.),
..default()
}),
..default()
},
NotShadowCaster,
));
let earth_pos = DVec3::Z * EARTH_ORBIT_RADIUS_M;
let (earth_cell, earth_pos) = sun.frame().translation_to_grid(earth_pos);
sun.with_frame_default(|earth| {
earth.insert((
Name::new("Earth"),
earth_cell,
PbrBundle {
mesh: earth_mesh_handle,
material: materials.add(StandardMaterial {
base_color: Color::BLUE,
perceptual_roughness: 0.8,
reflectance: 1.0,
..default()
}),
transform: Transform::from_translation(earth_pos)
.with_scale(Vec3::splat(EARTH_RADIUS_M as f32)),
..default()
},
Rotates(0.000001),
));
let moon_orbit_radius_m = 385e6;
let moon_pos = DVec3::NEG_Z * moon_orbit_radius_m;
let (moon_cell, moon_pos) = earth.frame().translation_to_grid(moon_pos);
earth.spawn_spatial((
Name::new("Moon"),
PbrBundle {
mesh: moon_mesh_handle,
material: materials.add(StandardMaterial {
base_color: Color::GRAY,
perceptual_roughness: 1.0,
reflectance: 0.0,
..default()
}),
transform: Transform::from_translation(moon_pos),
..default()
},
moon_cell,
));
let ball_pos =
DVec3::X * (EARTH_RADIUS_M + 1.0) + DVec3::NEG_Z * 30.0 + DVec3::Y * 10.0;
let (ball_cell, ball_pos) = earth.frame().translation_to_grid(ball_pos);
earth
.spawn_spatial((ball_cell, Transform::from_translation(ball_pos)))
.with_children(|children| {
children.spawn((PbrBundle {
mesh: ball_mesh_handle,
material: materials.add(StandardMaterial {
base_color: Color::WHITE,
..default()
}),
..default()
},));
children.spawn((PbrBundle {
mesh: plane_mesh_handle,
material: materials.add(StandardMaterial {
base_color: Color::DARK_GREEN,
perceptual_roughness: 1.0,
reflectance: 0.0,
..default()
}),
transform: Transform::from_scale(Vec3::splat(100.0))
.with_translation(Vec3::X * -5.0),
..default()
},));
});
let cam_pos = DVec3::X * (EARTH_RADIUS_M + 1.0);
let (cam_cell, cam_pos) = earth.frame().translation_to_grid(cam_pos);
earth.with_frame_default(|camera| {
camera.insert((
Transform::from_translation(cam_pos).looking_to(Vec3::NEG_Z, Vec3::X),
CameraController::default() // Built-in camera controller
.with_speed_bounds([0.1, 10e35])
.with_smoothness(0.98, 0.98)
.with_speed(1.0),
cam_cell,
));
camera.spawn_spatial((
FloatingOrigin,
Camera3dBundle {
transform: Transform::from_xyz(0.0, 4.0, 22.0),
camera: Camera {
hdr: true,
..default()
},
exposure: Exposure::SUNLIGHT,
..default()
},
BloomSettings::default(),
));
camera.with_children(|camera| {
camera.spawn((
Spaceship,
SceneBundle {
scene: asset_server
.load("models/low_poly_spaceship/scene.gltf#Scene0"),
transform: Transform::from_rotation(Quat::from_rotation_y(
std::f32::consts::PI,
)),
..default()
},
));
});
});
});
});
let star_mat = materials.add(StandardMaterial {
base_color: Color::WHITE,
emissive: Color::rgb_linear(100000., 100000., 100000.),
..default()
});
let star_mesh_handle = meshes.add(Sphere::new(1e10).mesh().ico(5).unwrap());
let mut rng = rand::thread_rng();
(0..1000).for_each(|_| {
root_frame.spawn_spatial((
star_mesh_handle.clone(),
star_mat.clone(),
Transform::from_xyz(
(rng.gen::<f32>() - 0.5) * 1e14,
(rng.gen::<f32>() - 0.5) * 1e14,
(rng.gen::<f32>() - 0.5) * 1e14,
),
));
});
});
}
fn cursor_grab_system(
mut windows: Query<&mut Window, With<bevy::window::PrimaryWindow>>,
mut cam: ResMut<big_space::camera::CameraInput>,
btn: Res<ButtonInput<MouseButton>>,
key: Res<ButtonInput<KeyCode>>,
) {
let Some(mut window) = windows.get_single_mut().ok() else {
return;
};
if btn.just_pressed(MouseButton::Right) {
window.cursor.grab_mode = bevy::window::CursorGrabMode::Locked;
window.cursor.visible = false;
// window.mode = WindowMode::BorderlessFullscreen;
cam.defaults_disabled = false;
}
if key.just_pressed(KeyCode::Escape) {
window.cursor.grab_mode = bevy::window::CursorGrabMode::None;
window.cursor.visible = true;
// window.mode = WindowMode::Windowed;
cam.defaults_disabled = true;
}
}