mirror of
https://github.com/eliasstepanik/big_space_with_trim.git
synced 2026-01-11 22:58:30 +00:00
Implements spatial hashing, to performantly run distance checks, find all entities located in the same cell, or neighboring cells.
295 lines
11 KiB
Rust
295 lines
11 KiB
Rust
use std::collections::VecDeque;
|
|
|
|
use bevy::{
|
|
color::palettes,
|
|
core_pipeline::bloom::Bloom,
|
|
math::DVec3,
|
|
pbr::{CascadeShadowConfigBuilder, NotShadowCaster},
|
|
prelude::*,
|
|
render::camera::Exposure,
|
|
transform::TransformSystem,
|
|
};
|
|
use big_space::prelude::*;
|
|
use turborand::{rng::Rng, TurboRand};
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins((
|
|
DefaultPlugins,
|
|
BigSpacePlugin::<i64>::new(true),
|
|
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(FloatingOriginSystem::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<big_space::camera::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, Vec2::splat(0.5)));
|
|
|
|
commands.spawn((
|
|
PrimaryLight,
|
|
DirectionalLight {
|
|
color: Color::WHITE,
|
|
illuminance: 120_000.,
|
|
shadows_enabled: true,
|
|
..default()
|
|
},
|
|
CascadeShadowConfigBuilder {
|
|
num_cascades: 4,
|
|
minimum_distance: 0.1,
|
|
maximum_distance: 10_000.0,
|
|
first_cascade_far_bound: 100.0,
|
|
overlap_proportion: 0.2,
|
|
}
|
|
.build(),
|
|
));
|
|
|
|
commands.spawn_big_space_default::<i64>(|root_grid| {
|
|
root_grid.with_grid_default(|sun| {
|
|
sun.insert((Sun, Name::new("Sun")));
|
|
sun.spawn_spatial((
|
|
Mesh3d(sun_mesh_handle),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::WHITE,
|
|
emissive: LinearRgba::rgb(100000., 100000., 100000.),
|
|
..default()
|
|
})),
|
|
NotShadowCaster,
|
|
));
|
|
|
|
let earth_pos = DVec3::Z * EARTH_ORBIT_RADIUS_M;
|
|
let (earth_cell, earth_pos) = sun.grid().translation_to_grid(earth_pos);
|
|
sun.with_grid_default(|earth| {
|
|
earth.insert((
|
|
Name::new("Earth"),
|
|
earth_cell,
|
|
Mesh3d(earth_mesh_handle),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::Srgba(palettes::css::BLUE),
|
|
perceptual_roughness: 0.8,
|
|
reflectance: 1.0,
|
|
..default()
|
|
})),
|
|
Transform::from_translation(earth_pos)
|
|
.with_scale(Vec3::splat(EARTH_RADIUS_M as f32)),
|
|
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.grid().translation_to_grid(moon_pos);
|
|
earth.spawn_spatial((
|
|
Name::new("Moon"),
|
|
Mesh3d(moon_mesh_handle),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::Srgba(palettes::css::GRAY),
|
|
perceptual_roughness: 1.0,
|
|
reflectance: 0.0,
|
|
..default()
|
|
})),
|
|
Transform::from_translation(moon_pos),
|
|
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.grid().translation_to_grid(ball_pos);
|
|
earth
|
|
.spawn_spatial((ball_cell, Transform::from_translation(ball_pos)))
|
|
.with_children(|children| {
|
|
children.spawn((
|
|
Mesh3d(ball_mesh_handle),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::WHITE,
|
|
..default()
|
|
})),
|
|
));
|
|
|
|
children.spawn((
|
|
Mesh3d(plane_mesh_handle),
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
|
base_color: Color::Srgba(palettes::css::DARK_GREEN),
|
|
perceptual_roughness: 1.0,
|
|
reflectance: 0.0,
|
|
..default()
|
|
})),
|
|
Transform::from_scale(Vec3::splat(100.0))
|
|
.with_translation(Vec3::X * -5.0),
|
|
));
|
|
});
|
|
|
|
let cam_pos = DVec3::X * (EARTH_RADIUS_M + 1.0);
|
|
let (cam_cell, cam_pos) = earth.grid().translation_to_grid(cam_pos);
|
|
earth.with_grid_default(|camera| {
|
|
camera.insert((
|
|
Transform::from_translation(cam_pos).looking_to(Vec3::NEG_Z, Vec3::X),
|
|
big_space::camera::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,
|
|
Camera3d::default(),
|
|
Transform::from_xyz(0.0, 4.0, 22.0),
|
|
Camera {
|
|
hdr: true,
|
|
..default()
|
|
},
|
|
Exposure::SUNLIGHT,
|
|
Bloom::default(),
|
|
));
|
|
|
|
camera.with_child((
|
|
Spaceship,
|
|
SceneRoot(asset_server.load("models/low_poly_spaceship/scene.gltf#Scene0")),
|
|
Transform::from_rotation(Quat::from_rotation_y(std::f32::consts::PI)),
|
|
));
|
|
});
|
|
});
|
|
});
|
|
|
|
let star_mat = materials.add(StandardMaterial {
|
|
base_color: Color::WHITE,
|
|
emissive: LinearRgba::rgb(2., 2., 2.),
|
|
..default()
|
|
});
|
|
let star_mesh_handle = meshes.add(Sphere::new(1e10).mesh().ico(5).unwrap());
|
|
let rng = Rng::new();
|
|
(0..1000).for_each(|_| {
|
|
root_grid.spawn_spatial((
|
|
Mesh3d(star_mesh_handle.clone()),
|
|
MeshMaterial3d(star_mat.clone()),
|
|
Transform::from_xyz(
|
|
(rng.f32() - 0.5) * 1e14,
|
|
(rng.f32() - 0.5) * 1e14,
|
|
(rng.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_options.grab_mode = bevy::window::CursorGrabMode::Locked;
|
|
window.cursor_options.visible = false;
|
|
// window.mode = WindowMode::BorderlessFullscreen;
|
|
cam.defaults_disabled = false;
|
|
}
|
|
|
|
if key.just_pressed(KeyCode::Escape) {
|
|
window.cursor_options.grab_mode = bevy::window::CursorGrabMode::None;
|
|
window.cursor_options.visible = true;
|
|
// window.mode = WindowMode::Windowed;
|
|
cam.defaults_disabled = true;
|
|
}
|
|
}
|