demo and camera improvements

This commit is contained in:
Aevyrie Roessler 2023-01-30 02:05:54 -08:00
parent fb71b2baa4
commit 95d4483afb
No known key found for this signature in database
GPG Key ID: F975B68AD0BCCF40
7 changed files with 206 additions and 60 deletions

View File

@ -17,6 +17,7 @@ bevy = { version = "0.9", default_features = false, features = [
"bevy_winit",
"x11",
] }
bevy_framepace = "0.11"
[features]
default = ["debug"]

Binary file not shown.

Binary file not shown.

View File

@ -3,17 +3,23 @@ use bevy::{
prelude::*,
render::primitives::{Aabb, Sphere},
};
use big_space::{camera::CameraController, FloatingOrigin, GridCell};
use big_space::{
camera::{CameraController, CameraInput, CameraVelocity},
FloatingOrigin, GridCell,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins.build().disable::<TransformPlugin>())
.add_plugin(big_space::FloatingOriginPlugin::<i128>::default())
.add_plugin(big_space::debug::FloatingOriginDebugPlugin::<i128>::default())
.add_plugin(big_space::camera::CameraControllerPlugin)
.add_plugin(big_space::camera::CameraControllerPlugin::<i128>::default())
.add_plugin(bevy_framepace::FramepacePlugin)
.insert_resource(ClearColor(Color::BLACK))
.add_startup_system(setup)
.add_system(cursor_grab_system)
.add_system(ui_text_system)
.add_startup_system(ui_setup)
.run()
}
@ -32,8 +38,8 @@ fn setup(
GridCell::<i128>::default(), // All spatial entities need this component
FloatingOrigin, // Important: marks this as the entity to use as the floating origin
CameraController {
max_speed: 10e12,
smoothness: 0.9,
max_speed: 10e35,
smoothness: 0.8,
..default()
}, // Built-in camera controller
));
@ -41,20 +47,21 @@ fn setup(
let mesh_handle = meshes.add(
shape::Icosphere {
radius: 0.5,
subdivisions: 16,
subdivisions: 32,
}
.try_into()
.unwrap(),
);
let matl_handle = materials.add(StandardMaterial {
base_color: Color::YELLOW,
unlit: false,
base_color: Color::MIDNIGHT_BLUE,
perceptual_roughness: 0.8,
reflectance: 1.0,
..default()
});
let mut translation = Vec3::ZERO;
for i in 1..100i128 {
let j = i.pow(10) as f32 + 1.0;
for i in 1..=100i128 {
let j = i.pow(14) as f32;
translation.x += j;
commands.spawn((
PbrBundle {
@ -81,21 +88,79 @@ fn setup(
},));
}
#[derive(Component, Reflect)]
pub struct BigSpaceDebugText;
fn ui_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
TextBundle::from_section(
"",
TextStyle {
font: asset_server.load("fonts/FiraMono-Regular.ttf"),
font_size: 18.0,
color: Color::WHITE,
},
)
.with_text_alignment(TextAlignment::TOP_LEFT)
.with_style(Style {
position_type: PositionType::Absolute,
position: UiRect {
top: Val::Px(10.0),
left: Val::Px(10.0),
..default()
},
..default()
}),
BigSpaceDebugText,
));
}
fn ui_text_system(
mut text: Query<&mut Text, With<BigSpaceDebugText>>,
time: Res<Time>,
origin: Query<(&GridCell<i128>, &Transform), With<FloatingOrigin>>,
velocity: Res<CameraVelocity>,
) {
let (cell, transform) = origin.single();
let translation = transform.translation;
let grid_text = format!("Origin GridCell: {}x, {}y, {}z", cell.x, cell.y, cell.z);
let translation_text = format!(
"Origin Transform: {:>8.2}x, {:>8.2}y, {:>8.2}z",
translation.x, translation.y, translation.z
);
let speed = velocity.translation().length() / time.delta_seconds_f64();
let camera_text = if speed > 3.0e8 {
format!("Camera Speed: {:.0e} x speed of light", speed / 3.0e8)
} else {
format!("Camera Speed: {:.2e}", speed)
};
text.single_mut().sections[0].value = format!("{grid_text}\n{translation_text}\n{camera_text}");
}
fn cursor_grab_system(
mut windows: ResMut<Windows>,
mut cam: ResMut<CameraInput>,
btn: Res<Input<MouseButton>>,
key: Res<Input<KeyCode>>,
) {
use bevy::window::CursorGrabMode;
let window = windows.get_primary_mut().unwrap();
use bevy::window::CursorGrabMode;
if btn.just_pressed(MouseButton::Left) {
window.set_cursor_grab_mode(CursorGrabMode::Locked);
window.set_cursor_visibility(false);
window.set_mode(WindowMode::BorderlessFullscreen);
cam.defaults_disabled = false;
}
if key.just_pressed(KeyCode::Escape) {
window.set_cursor_grab_mode(CursorGrabMode::None);
window.set_cursor_visibility(true);
window.set_mode(WindowMode::Windowed);
cam.defaults_disabled = true;
}
}

View File

@ -81,7 +81,7 @@ fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator
}
fn setup_ui(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraMono-Medium.ttf");
let font = asset_server.load("fonts/FiraMono-Regular.ttf");
commands.spawn(TextBundle {
style: Style {
align_self: AlignSelf::FlexStart,

View File

@ -1,20 +1,43 @@
//! Provides a camera controller compatible with the floating origin plugin.
use std::marker::PhantomData;
use bevy::{
input::mouse::MouseMotion, prelude::*, render::primitives::Aabb, transform::TransformSystem,
utils::HashMap,
ecs::schedule::ShouldRun,
input::mouse::MouseMotion,
math::{DQuat, DVec3},
prelude::*,
render::primitives::Aabb,
transform::TransformSystem,
};
use crate::{precision::GridPrecision, FloatingOriginSettings, GridCell};
/// Adds the `big_space` camera controller
pub struct CameraControllerPlugin;
impl Plugin for CameraControllerPlugin {
#[derive(Default)]
pub struct CameraControllerPlugin<P: GridPrecision>(PhantomData<P>);
impl<P: GridPrecision> Plugin for CameraControllerPlugin<P> {
fn build(&self, app: &mut App) {
app.init_resource::<CameraInput>().add_system_set_to_stage(
CoreStage::PostUpdate,
SystemSet::new()
.with_system(default_camera_inputs.before(camera_controller))
.with_system(camera_controller.before(TransformSystem::TransformPropagate)),
);
app.init_resource::<CameraInput>()
.init_resource::<CameraVelocity>()
.add_system_set_to_stage(
CoreStage::PostUpdate,
SystemSet::new()
.with_system(
default_camera_inputs
.before(camera_controller::<P>)
.with_run_criteria(|input: Res<CameraInput>| {
if input.defaults_disabled {
ShouldRun::No
} else {
ShouldRun::Yes
}
}),
)
.with_system(
camera_controller::<P>.before(TransformSystem::TransformPropagate),
),
);
}
}
@ -22,9 +45,9 @@ impl Plugin for CameraControllerPlugin {
#[derive(Clone, Debug, Reflect, Component)]
pub struct CameraController {
/// Smoothness of motion, from `0.0` to `1.0`.
pub smoothness: f32,
pub smoothness: f64,
/// Maximum possible speed.
pub max_speed: f32,
pub max_speed: f64,
/// Whether the camera should slow down when approaching an entity's [`Aabb`].
pub slow_near_objects: bool,
}
@ -42,39 +65,46 @@ impl Default for CameraController {
/// camera. Allows you to map any input to camera motions. Uses aircraft principle axes conventions.
#[derive(Clone, Debug, Default, Reflect, Resource)]
pub struct CameraInput {
/// When disabled, the camera input system is not run.
pub defaults_disabled: bool,
/// Z-negative
pub forward: f32,
pub forward: f64,
/// Y-positive
pub up: f32,
pub up: f64,
/// X-positive
pub right: f32,
pub right: f64,
/// Positive = right wing down
pub roll: f32,
pub roll: f64,
/// Positive = nose up
pub pitch: f32,
pub pitch: f64,
/// Positive = nose right
pub yaw: f32,
pub yaw: f64,
/// Modifier to increase speed, e.g. "sprint"
pub boost: bool,
}
impl CameraInput {
/// Reset the controller back to zero to ready fro the next frame.
pub fn reset(&mut self) {
*self = CameraInput::default();
*self = CameraInput {
defaults_disabled: self.defaults_disabled,
..default()
};
}
/// Returns the desired velocity transform.
pub fn target_velocity(&self, speed: f32, dt: f32) -> Transform {
let mut new_transform = Transform::from_rotation(Quat::from_euler(
pub fn target_velocity(&self, speed: f64, dt: f64) -> (DVec3, DQuat) {
let rotation = DQuat::from_euler(
EulerRot::XYZ,
self.pitch * dt,
self.yaw * dt,
self.roll * dt,
));
);
let delta = Vec3::new(self.right, self.up, self.forward) * speed * dt;
let translation =
DVec3::new(self.right as f64, self.up as f64, self.forward as f64) * speed * dt as f64;
new_transform.translation = delta;
new_transform
(translation, rotation)
}
}
@ -92,55 +122,98 @@ pub fn default_camera_inputs(
keyboard.pressed(KeyCode::LControl).then(|| cam.up -= 1.0);
keyboard.pressed(KeyCode::Q).then(|| cam.roll += 1.0);
keyboard.pressed(KeyCode::E).then(|| cam.roll -= 1.0);
keyboard.pressed(KeyCode::LShift).then(|| cam.boost = true);
if let Some(total_mouse_motion) = mouse_move.iter().map(|e| e.delta).reduce(|sum, i| sum + i) {
cam.pitch += total_mouse_motion.y * -0.1;
cam.yaw += total_mouse_motion.x * -0.1;
cam.pitch += total_mouse_motion.y as f64 * -0.1;
cam.yaw += total_mouse_motion.x as f64 * -0.1;
}
}
/// Tracks the camera's velocity
#[derive(Debug, Default, Resource, Reflect)]
pub struct CameraVelocity {
entity: Option<Entity>,
translation: DVec3,
rotation: DQuat,
}
impl CameraVelocity {
/// Get the translation component of the camera velocity.
pub fn translation(&self) -> DVec3 {
self.translation
}
/// Get the rotation component of the camera velocity.
pub fn rotation(&self) -> DQuat {
self.rotation
}
}
/// Uses [`CameraInput`] state to update the camera position.
pub fn camera_controller(
pub fn camera_controller<P: GridPrecision>(
time: Res<Time>,
settings: Res<FloatingOriginSettings>,
mut input: ResMut<CameraInput>,
mut camera: Query<(
Entity,
&mut Transform,
&GlobalTransform,
&mut CameraController,
&CameraController,
&mut GridCell<P>,
)>,
objects: Query<(&GlobalTransform, &Aabb)>,
mut velocities: Local<HashMap<Entity, Transform>>,
mut velocity: ResMut<CameraVelocity>,
) {
let (entity, mut cam_transform, cam_global_transform, controller) = camera.single_mut();
let (entity, mut cam_transform, cam_global_transform, controller, mut cell) =
camera.single_mut();
let speed = if controller.slow_near_objects {
let mut nearest_object = f32::MAX;
let mut nearest_object = f64::MAX;
for (transform, aabb) in &objects {
let distance = (transform.translation() + Vec3::from(aabb.center)
- cam_global_transform.translation())
let distance = (transform.translation().as_dvec3() + aabb.center.as_dvec3()
- cam_global_transform.translation().as_dvec3())
.length()
- aabb.half_extents.max_element();
- aabb.half_extents.as_dvec3().max_element();
nearest_object = nearest_object.min(distance);
}
nearest_object.abs().clamp(1.0, controller.max_speed)
} else {
controller.max_speed
} * (1.0 + input.boost as usize as f64);
let lerp_val = 1.0 - controller.smoothness.clamp(0.0, 0.999); // The lerp factor
if velocity.entity != Some(entity) {
velocity.entity = Some(entity);
velocity.translation = DVec3::ZERO;
velocity.rotation = DQuat::IDENTITY;
}
let (vel_t_current, vel_r_current) = (velocity.translation, velocity.rotation);
let (vel_t_target, vel_r_target) = input.target_velocity(speed, time.delta_seconds_f64());
let cam_rot = cam_transform.rotation.as_f64();
let vel_t_next = cam_rot * vel_t_target; // Orients the translation to match the camera
let vel_t_next = vel_t_current.lerp(vel_t_next, lerp_val);
// Convert the high precision translation to a grid cell and low precision translation
let (cell_offset, new_translation) = settings.translation_to_grid(vel_t_next);
*cell += cell_offset;
cam_transform.translation += new_translation;
let new_rotation = vel_r_current.slerp(vel_r_target, lerp_val);
cam_transform.rotation *= new_rotation.as_f32();
// Store the new velocity to be used in the next frame
velocity.translation = if vel_t_next.length().abs() < 0.001 {
DVec3::ZERO
} else {
vel_t_next
};
let lerp_val = 1.0 - controller.smoothness.clamp(0.0, 0.99999999); // The lerp factor
let v_current = velocities.entry(entity).or_default();
let v_target = input.target_velocity(speed, time.delta_seconds());
let v_next = Transform {
translation: v_current.translation.lerp(v_target.translation, lerp_val),
rotation: v_current.rotation.slerp(v_target.rotation, lerp_val),
..default()
velocity.rotation = if new_rotation.to_axis_angle().1.abs() < 0.001 {
DQuat::IDENTITY
} else {
new_rotation
};
let cam_rot = cam_transform.rotation;
cam_transform.translation += cam_rot * v_next.translation;
cam_transform.rotation *= v_next.rotation;
*v_current = v_next;
input.reset();
}

View File

@ -362,6 +362,13 @@ impl<P: GridPrecision> std::ops::Sub for &GridCell<P> {
}
}
impl<P: GridPrecision> std::ops::AddAssign for GridCell<P> {
fn add_assign(&mut self, rhs: Self) {
use std::ops::Add;
*self = self.add(rhs);
}
}
/// Marks the entity to use as the floating origin. All other entities will be positioned relative
/// to this entity's [`GridCell`].
#[derive(Component, Reflect)]