181 lines
7.0 KiB
Rust
181 lines
7.0 KiB
Rust
//! Shows the difference between Transform camera forward and model forward.
|
|
|
|
use std::f32::consts::PI;
|
|
|
|
use bevy::{color::palettes::basic::YELLOW, prelude::*};
|
|
|
|
// A struct for additional data of for a moving cube.
|
|
#[derive(Component)]
|
|
struct OrbitState {
|
|
start_pos: Vec3,
|
|
move_speed: f32,
|
|
turn_speed: f32,
|
|
}
|
|
|
|
// A struct adding information to a scalable entity,
|
|
// that will be stationary at the center of the scene.
|
|
#[derive(Component)]
|
|
struct Center {
|
|
max_size: f32,
|
|
min_size: f32,
|
|
scale_factor: f32,
|
|
}
|
|
|
|
fn main() {
|
|
App::new()
|
|
.add_plugins(DefaultPlugins)
|
|
.add_systems(Startup, setup)
|
|
.add_systems(
|
|
Update,
|
|
(
|
|
move_orbiters,
|
|
rotate_orbiters,
|
|
scale_down_sphere_proportional_to_cube_travel_distance,
|
|
)
|
|
.chain(),
|
|
)
|
|
.run();
|
|
}
|
|
|
|
#[derive(Component)]
|
|
struct Helmet;
|
|
|
|
// Startup system to setup the scene and spawn all relevant entities.
|
|
fn setup(
|
|
mut commands: Commands,
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
asset_server: Res<AssetServer>,
|
|
) {
|
|
// Add an object (sphere) for visualizing scaling.
|
|
commands.spawn((
|
|
Mesh3d(meshes.add(Sphere::new(3.0).mesh().ico(32).unwrap())),
|
|
MeshMaterial3d(materials.add(Color::from(YELLOW))),
|
|
Transform::from_translation(Vec3::ZERO),
|
|
Center {
|
|
max_size: 1.0,
|
|
min_size: 0.1,
|
|
scale_factor: 0.05,
|
|
},
|
|
));
|
|
|
|
// Add the cube to visualize rotation and translation.
|
|
// This cube will circle around the center_sphere
|
|
// by changing its rotation each frame and moving forward.
|
|
// Define a start transform for an orbiting cube, that's away from our central object (sphere)
|
|
// and rotate it so it will be able to move around the sphere and not towards it.
|
|
let cube_spawn =
|
|
Transform::from_translation(Vec3::Z * -10.0).with_rotation(Quat::from_rotation_y(PI / 2.));
|
|
commands.spawn((
|
|
Mesh3d(meshes.add(Cuboid::default())),
|
|
MeshMaterial3d(materials.add(Color::WHITE)),
|
|
cube_spawn,
|
|
OrbitState {
|
|
start_pos: cube_spawn.translation,
|
|
move_speed: 2.0,
|
|
turn_speed: 0.2,
|
|
},
|
|
));
|
|
|
|
// Spawn a camera looking at the entities to show what's happening in this example.
|
|
commands.spawn((
|
|
Camera3d::default(),
|
|
Transform::from_xyz(0.0, 10.0, 20.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
));
|
|
|
|
// Add a light source for better 3d visibility.
|
|
commands.spawn((
|
|
DirectionalLight::default(),
|
|
Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
|
|
));
|
|
|
|
// Add the helmet to visualize rotation and translation using the glTF model forward convention of +z.
|
|
// This helmet will circle around the center_sphere
|
|
// by changing its rotation each frame and moving forward along the model_forward direction.
|
|
// Define a start transform for an orbiting helmet, that's away from our central object (sphere)
|
|
// and rotate it so it will be able to move around the sphere and not towards it.
|
|
// Note that it orbits in the opposite direction to the cube due to the model_forward directions being
|
|
// flipped.
|
|
let mut helmet_spawn = Transform::from_translation(Vec3::Z * -10.0)
|
|
.with_rotation(Quat::from_rotation_y(PI / 2.))
|
|
.with_scale(Vec3::splat(2.0));
|
|
helmet_spawn.flip_model_forward = true;
|
|
commands.spawn((
|
|
helmet_spawn,
|
|
Visibility::default(),
|
|
OrbitState {
|
|
start_pos: helmet_spawn.translation,
|
|
move_speed: 2.0,
|
|
turn_speed: 0.2,
|
|
},
|
|
SceneRoot(
|
|
asset_server
|
|
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
|
|
),
|
|
));
|
|
}
|
|
|
|
// This system will move the orbiter forward.
|
|
fn move_orbiters(mut orbiters: Query<(&mut Transform, &mut OrbitState)>, timer: Res<Time>) {
|
|
for (mut transform, orbiter) in &mut orbiters {
|
|
// Move the orbiter forward smoothly at a given move_speed.
|
|
let forward = transform.model_forward();
|
|
transform.translation += forward * orbiter.move_speed * timer.delta_secs();
|
|
}
|
|
}
|
|
|
|
// This system will rotate the orbiter slightly towards the center_sphere.
|
|
// Due to the forward movement the resulting movement
|
|
// will be a circular motion around the center_sphere.
|
|
fn rotate_orbiters(
|
|
mut orbiters: Query<(&mut Transform, &mut OrbitState), Without<Center>>,
|
|
center_spheres: Query<&Transform, With<Center>>,
|
|
timer: Res<Time>,
|
|
) {
|
|
// Calculate the point to circle around. (The position of the center_sphere)
|
|
let mut center: Vec3 = Vec3::ZERO;
|
|
for sphere in ¢er_spheres {
|
|
center += sphere.translation;
|
|
}
|
|
// Update the rotation of the orbiter(s).
|
|
for (mut transform, orbiter) in &mut orbiters {
|
|
// Calculate the rotation of the orbiter if it would be looking at the sphere in the center.
|
|
let look_at_sphere = transform.looking_at(center, *transform.local_y());
|
|
// Interpolate between the current rotation and the fully turned rotation
|
|
// when looking at the sphere, with a given turn speed to get a smooth motion.
|
|
// With higher speed the curvature of the orbit would be smaller.
|
|
let incremental_turn_weight = orbiter.turn_speed * timer.delta_secs();
|
|
let old_rotation = transform.rotation;
|
|
transform.rotation = old_rotation.lerp(look_at_sphere.rotation, incremental_turn_weight);
|
|
}
|
|
}
|
|
|
|
// This system will scale down the sphere in the center of the scene
|
|
// according to the traveling distance of the orbiting cube(s) from their start position(s).
|
|
fn scale_down_sphere_proportional_to_cube_travel_distance(
|
|
cubes: Query<(&Transform, &OrbitState), Without<Center>>,
|
|
mut centers: Query<(&mut Transform, &Center)>,
|
|
) {
|
|
// First we need to calculate the length of between
|
|
// the current position of the orbiting cube and the spawn position.
|
|
let mut distances = 0.0;
|
|
for (cube_transform, cube_state) in &cubes {
|
|
distances += (cube_state.start_pos - cube_transform.translation).length();
|
|
}
|
|
// Now we use the calculated value to scale the sphere in the center accordingly.
|
|
for (mut transform, center) in &mut centers {
|
|
// Calculate the new size from the calculated distances and the centers scale_factor.
|
|
// Since we want to have the sphere at its max_size at the cubes spawn location we start by
|
|
// using the max_size as start value and subtract the distances scaled by a scaling factor.
|
|
let mut new_size: f32 = center.max_size - center.scale_factor * distances;
|
|
|
|
// The new size should also not be smaller than the centers min_size.
|
|
// Therefore the max value out of (new_size, center.min_size) is used.
|
|
new_size = new_size.max(center.min_size);
|
|
|
|
// Now scale the sphere uniformly in all directions using new_size.
|
|
// Here Vec3:splat is used to create a vector with new_size in x, y and z direction.
|
|
transform.scale = Vec3::splat(new_size);
|
|
}
|
|
}
|