mirror of
https://github.com/eliasstepanik/big_space_with_trim.git
synced 2026-01-11 17:48:26 +00:00
demo features, added controller multi cam support
This commit is contained in:
parent
95d4483afb
commit
19e8e2e9c5
@ -4,7 +4,7 @@ use bevy::{
|
||||
render::primitives::{Aabb, Sphere},
|
||||
};
|
||||
use big_space::{
|
||||
camera::{CameraController, CameraInput, CameraVelocity},
|
||||
camera::{CameraController, CameraInput},
|
||||
FloatingOrigin, GridCell,
|
||||
};
|
||||
|
||||
@ -37,11 +37,7 @@ 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: 10e35,
|
||||
smoothness: 0.8,
|
||||
..default()
|
||||
}, // Built-in camera controller
|
||||
CameraController::default().with_max_speed(10e35), // Built-in camera controller
|
||||
));
|
||||
|
||||
let mesh_handle = meshes.add(
|
||||
@ -53,15 +49,15 @@ fn setup(
|
||||
.unwrap(),
|
||||
);
|
||||
let matl_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::MIDNIGHT_BLUE,
|
||||
base_color: Color::BLUE,
|
||||
perceptual_roughness: 0.8,
|
||||
reflectance: 1.0,
|
||||
..default()
|
||||
});
|
||||
|
||||
let mut translation = Vec3::ZERO;
|
||||
for i in 1..=100i128 {
|
||||
let j = i.pow(14) as f32;
|
||||
for i in 1..=37_i128 {
|
||||
let j = 10_f32.powf(i as f32 - 10.0) as f32;
|
||||
translation.x += j;
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
@ -119,7 +115,8 @@ fn ui_text_system(
|
||||
mut text: Query<&mut Text, With<BigSpaceDebugText>>,
|
||||
time: Res<Time>,
|
||||
origin: Query<(&GridCell<i128>, &Transform), With<FloatingOrigin>>,
|
||||
velocity: Res<CameraVelocity>,
|
||||
camera: Query<&CameraController>,
|
||||
objects: Query<&Transform, With<Handle<Mesh>>>,
|
||||
) {
|
||||
let (cell, transform) = origin.single();
|
||||
let translation = transform.translation;
|
||||
@ -131,14 +128,34 @@ fn ui_text_system(
|
||||
translation.x, translation.y, translation.z
|
||||
);
|
||||
|
||||
let speed = velocity.translation().length() / time.delta_seconds_f64();
|
||||
let velocity = camera.single().velocity();
|
||||
let speed = velocity.0.length() / time.delta_seconds_f64();
|
||||
let camera_text = if speed > 3.0e8 {
|
||||
format!("Camera Speed: {:.0e} x speed of light", speed / 3.0e8)
|
||||
format!("Camera Speed: {:.0e} * speed of light", speed / 3.0e8)
|
||||
} else {
|
||||
format!("Camera Speed: {:.2e}", speed)
|
||||
format!("Camera Speed: {:.2e} m/s", speed)
|
||||
};
|
||||
|
||||
text.single_mut().sections[0].value = format!("{grid_text}\n{translation_text}\n{camera_text}");
|
||||
let nearest_text = if let Some(nearest) = camera.single().nearest_object() {
|
||||
let dia = objects.get(nearest.0).unwrap().scale.max_element();
|
||||
let dia_fact = match dia {
|
||||
d if d > 8.8e26 => "(Greater than the diameter of the observable universe)",
|
||||
d if d > 1e21 => "(Greater than the diameter of the Milky Way galaxy)",
|
||||
d if d > 7e12 => "(Greater than the diameter of Pluto's orbit)",
|
||||
d if d > 1.4e9 => "(Greater than the diameter of the Sun)",
|
||||
d if d > 1.4e8 => "(Greater than the diameter of Earth)",
|
||||
d if d > 12e6 => "(Greater than the diameter of Earth)",
|
||||
d if d > 3e6 => "(Greater than the diameter of the Moon)",
|
||||
_ => "",
|
||||
};
|
||||
let dist = nearest.1;
|
||||
format!("Nearest sphere diameter: {dia:.0e} m {dia_fact}\nNearest sphere distance: {dist:.0e} m",)
|
||||
} else {
|
||||
"".into()
|
||||
};
|
||||
|
||||
text.single_mut().sections[0].value =
|
||||
format!("{grid_text}\n{translation_text}\n{camera_text}\n{nearest_text}");
|
||||
}
|
||||
|
||||
fn cursor_grab_system(
|
||||
|
||||
207
src/camera.rs
207
src/camera.rs
@ -18,45 +18,83 @@ use crate::{precision::GridPrecision, FloatingOriginSettings, GridCell};
|
||||
pub struct CameraControllerPlugin<P: GridPrecision>(PhantomData<P>);
|
||||
impl<P: GridPrecision> Plugin for CameraControllerPlugin<P> {
|
||||
fn build(&self, app: &mut App) {
|
||||
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),
|
||||
),
|
||||
);
|
||||
app.init_resource::<CameraInput>().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(nearest_objects.before(camera_controller::<P>))
|
||||
.with_system(camera_controller::<P>.before(TransformSystem::TransformPropagate)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-camera settings for the `big_space` floating origin camera controller.
|
||||
#[derive(Clone, Debug, Reflect, Component)]
|
||||
pub struct CameraController {
|
||||
/// Smoothness of motion, from `0.0` to `1.0`.
|
||||
/// Smoothness of translation, from `0.0` to `1.0`.
|
||||
pub smoothness: f64,
|
||||
/// Rotational smoothness, from `0.0` to `1.0`.
|
||||
pub rotational_smoothness: f64,
|
||||
/// Maximum possible speed.
|
||||
pub max_speed: f64,
|
||||
/// Whether the camera should slow down when approaching an entity's [`Aabb`].
|
||||
pub slow_near_objects: bool,
|
||||
nearest_object: Option<(Entity, f64)>,
|
||||
vel_translation: DVec3,
|
||||
vel_rotation: DQuat,
|
||||
}
|
||||
|
||||
impl CameraController {
|
||||
/// Sets the `smoothness` parameter of the controller, and returns the modified result.
|
||||
pub fn with_smoothness(mut self, translation: f64, rotation: f64) -> Self {
|
||||
self.smoothness = translation;
|
||||
self.rotational_smoothness = rotation;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `max_speed` parameter of the controller, and returns the modified result.
|
||||
pub fn with_max_speed(mut self, max_speed: f64) -> Self {
|
||||
self.max_speed = max_speed;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the `slow_near_objects` parameter of the controller, and returns the modified result.
|
||||
pub fn with_slowing(mut self, slow_near_objects: bool) -> Self {
|
||||
self.slow_near_objects = slow_near_objects;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the translational and rotational velocity of the camera.
|
||||
pub fn velocity(&self) -> (DVec3, DQuat) {
|
||||
(self.vel_translation, self.vel_rotation)
|
||||
}
|
||||
|
||||
/// Returns the object nearest the camera, and its distance.
|
||||
pub fn nearest_object(&self) -> Option<(Entity, f64)> {
|
||||
self.nearest_object
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CameraController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
smoothness: 0.2,
|
||||
smoothness: 0.8,
|
||||
rotational_smoothness: 0.5,
|
||||
max_speed: 10e8,
|
||||
slow_near_objects: true,
|
||||
nearest_object: None,
|
||||
vel_translation: DVec3::ZERO,
|
||||
vel_rotation: DQuat::IDENTITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -129,23 +167,23 @@ pub fn default_camera_inputs(
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// Find the object nearest the camera
|
||||
pub fn nearest_objects(
|
||||
objects: Query<(Entity, &GlobalTransform, &Aabb)>,
|
||||
mut camera: Query<(&GlobalTransform, &mut CameraController)>,
|
||||
) {
|
||||
for (cam_global_transform, mut controller) in camera.iter_mut() {
|
||||
let nearest_object = objects
|
||||
.iter()
|
||||
.map(|(entity, transform, aabb)| {
|
||||
let dist = (transform.translation().as_dvec3() + aabb.center.as_dvec3()
|
||||
- cam_global_transform.translation().as_dvec3())
|
||||
.length()
|
||||
- aabb.half_extents.as_dvec3().max_element();
|
||||
(entity, dist)
|
||||
})
|
||||
.reduce(|nearest, this| if this.1 < nearest.1 { this } else { nearest });
|
||||
controller.nearest_object = nearest_object;
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,66 +192,43 @@ pub fn camera_controller<P: GridPrecision>(
|
||||
time: Res<Time>,
|
||||
settings: Res<FloatingOriginSettings>,
|
||||
mut input: ResMut<CameraInput>,
|
||||
mut camera: Query<(
|
||||
Entity,
|
||||
&mut Transform,
|
||||
&GlobalTransform,
|
||||
&CameraController,
|
||||
&mut GridCell<P>,
|
||||
)>,
|
||||
objects: Query<(&GlobalTransform, &Aabb)>,
|
||||
mut velocity: ResMut<CameraVelocity>,
|
||||
mut camera: Query<(&mut Transform, &mut CameraController, &mut GridCell<P>)>,
|
||||
) {
|
||||
let (entity, mut cam_transform, cam_global_transform, controller, mut cell) =
|
||||
camera.single_mut();
|
||||
for (mut cam_transform, mut controller, mut cell) in camera.iter_mut() {
|
||||
let speed = match (controller.nearest_object, controller.slow_near_objects) {
|
||||
(Some(nearest), true) => nearest.1,
|
||||
_ => controller.max_speed,
|
||||
} * (1.0 + input.boost as usize as f64);
|
||||
|
||||
let speed = if controller.slow_near_objects {
|
||||
let mut nearest_object = f64::MAX;
|
||||
for (transform, aabb) in &objects {
|
||||
let distance = (transform.translation().as_dvec3() + aabb.center.as_dvec3()
|
||||
- cam_global_transform.translation().as_dvec3())
|
||||
.length()
|
||||
- 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_translation = 1.0 - controller.smoothness.clamp(0.0, 0.999);
|
||||
let lerp_rotation = 1.0 - controller.rotational_smoothness.clamp(0.0, 0.999);
|
||||
|
||||
let lerp_val = 1.0 - controller.smoothness.clamp(0.0, 0.999); // The lerp factor
|
||||
let (vel_t_current, vel_r_current) = (controller.vel_translation, controller.vel_rotation);
|
||||
let (vel_t_target, vel_r_target) = input.target_velocity(speed, time.delta_seconds_f64());
|
||||
|
||||
if velocity.entity != Some(entity) {
|
||||
velocity.entity = Some(entity);
|
||||
velocity.translation = DVec3::ZERO;
|
||||
velocity.rotation = DQuat::IDENTITY;
|
||||
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_translation);
|
||||
// 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_rotation);
|
||||
cam_transform.rotation *= new_rotation.as_f32();
|
||||
|
||||
// Store the new velocity to be used in the next frame
|
||||
controller.vel_translation = if vel_t_next.length().abs() < 0.001 {
|
||||
DVec3::ZERO
|
||||
} else {
|
||||
vel_t_next
|
||||
};
|
||||
controller.vel_rotation = if new_rotation.to_axis_angle().1.abs() < 0.001 {
|
||||
DQuat::IDENTITY
|
||||
} else {
|
||||
new_rotation
|
||||
};
|
||||
|
||||
input.reset();
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
velocity.rotation = if new_rotation.to_axis_angle().1.abs() < 0.001 {
|
||||
DQuat::IDENTITY
|
||||
} else {
|
||||
new_rotation
|
||||
};
|
||||
|
||||
input.reset();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user