WIP: Fix glTF model forward direction
This commit is contained in:
parent
33bed5dd70
commit
aaa8f70833
11
Cargo.toml
11
Cargo.toml
@ -3278,6 +3278,17 @@ description = "A demonstration of Transform's axis-alignment feature"
|
|||||||
category = "Transforms"
|
category = "Transforms"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "model_forward"
|
||||||
|
path = "examples/transforms/model_forward.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.model_forward]
|
||||||
|
name = "model_forward"
|
||||||
|
description = "Illustrates when to use camera_forward vs model_forward, and different cases for model_forward"
|
||||||
|
category = "Transforms"
|
||||||
|
wasm = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "scale"
|
name = "scale"
|
||||||
path = "examples/transforms/scale.rs"
|
path = "examples/transforms/scale.rs"
|
||||||
|
@ -136,6 +136,7 @@ impl Animatable for Transform {
|
|||||||
translation: Vec3::interpolate(&a.translation, &b.translation, t),
|
translation: Vec3::interpolate(&a.translation, &b.translation, t),
|
||||||
rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
|
rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
|
||||||
scale: Vec3::interpolate(&a.scale, &b.scale, t),
|
scale: Vec3::interpolate(&a.scale, &b.scale, t),
|
||||||
|
flip_model_forward: a.flip_model_forward,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +144,12 @@ impl Animatable for Transform {
|
|||||||
let mut translation = Vec3A::ZERO;
|
let mut translation = Vec3A::ZERO;
|
||||||
let mut scale = Vec3A::ZERO;
|
let mut scale = Vec3A::ZERO;
|
||||||
let mut rotation = Quat::IDENTITY;
|
let mut rotation = Quat::IDENTITY;
|
||||||
|
let mut flip_model_forward = None;
|
||||||
|
|
||||||
for input in inputs {
|
for input in inputs {
|
||||||
|
if flip_model_forward.is_none() {
|
||||||
|
flip_model_forward = Some(input.value.flip_model_forward);
|
||||||
|
}
|
||||||
if input.additive {
|
if input.additive {
|
||||||
translation += input.weight * Vec3A::from(input.value.translation);
|
translation += input.weight * Vec3A::from(input.value.translation);
|
||||||
scale += input.weight * Vec3A::from(input.value.scale);
|
scale += input.weight * Vec3A::from(input.value.scale);
|
||||||
@ -165,6 +170,7 @@ impl Animatable for Transform {
|
|||||||
translation: Vec3::from(translation),
|
translation: Vec3::from(translation),
|
||||||
rotation,
|
rotation,
|
||||||
scale: Vec3::from(scale),
|
scale: Vec3::from(scale),
|
||||||
|
flip_model_forward: flip_model_forward.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transfor
|
|||||||
translation: Vec3::from(translation),
|
translation: Vec3::from(translation),
|
||||||
rotation: bevy_math::Quat::from_array(rotation),
|
rotation: bevy_math::Quat::from_array(rotation),
|
||||||
scale: Vec3::from(scale),
|
scale: Vec3::from(scale),
|
||||||
|
flip_model_forward: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if convert_coordinates {
|
if convert_coordinates {
|
||||||
|
@ -136,6 +136,7 @@ impl GlobalTransform {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +194,7 @@ impl GlobalTransform {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,11 +369,13 @@ mod test {
|
|||||||
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
||||||
rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
|
rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
|
||||||
scale: Vec3::new(1.0, 1.0, 1.0),
|
scale: Vec3::new(1.0, 1.0, 1.0),
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
let t2 = GlobalTransform::from(Transform {
|
let t2 = GlobalTransform::from(Transform {
|
||||||
translation: Vec3::new(0.0, -54.493, 324.34),
|
translation: Vec3::new(0.0, -54.493, 324.34),
|
||||||
rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
|
rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
|
||||||
scale: Vec3::new(1.345, 1.345, 1.345),
|
scale: Vec3::new(1.345, 1.345, 1.345),
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
let retransformed = reparent_to_same(t1, t2);
|
let retransformed = reparent_to_same(t1, t2);
|
||||||
assert!(
|
assert!(
|
||||||
@ -387,11 +391,13 @@ mod test {
|
|||||||
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
translation: Vec3::new(1034.0, 34.0, -1324.34),
|
||||||
rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
|
rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
|
||||||
scale: Vec3::new(10.9, 10.9, 10.9),
|
scale: Vec3::new(10.9, 10.9, 10.9),
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
let t2 = GlobalTransform::from(Transform {
|
let t2 = GlobalTransform::from(Transform {
|
||||||
translation: Vec3::new(28.0, -54.493, 324.34),
|
translation: Vec3::new(28.0, -54.493, 324.34),
|
||||||
rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
|
rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
|
||||||
scale: Vec3::new(0.9, 0.9, 0.9),
|
scale: Vec3::new(0.9, 0.9, 0.9),
|
||||||
|
..Default::default()
|
||||||
});
|
});
|
||||||
// goal: find `X` such as `t2 * X = t1`
|
// goal: find `X` such as `t2 * X = t1`
|
||||||
let reparented = t1.reparented_to(&t2);
|
let reparented = t1.reparented_to(&t2);
|
||||||
|
@ -98,6 +98,11 @@ pub struct Transform {
|
|||||||
///
|
///
|
||||||
/// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
|
/// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
|
||||||
pub scale: Vec3,
|
pub scale: Vec3,
|
||||||
|
/// Whether the model forward direction is flipped from -z to +z.
|
||||||
|
///
|
||||||
|
/// glTF specifies that models have a forward direction of +z whereas cameras and lights have -z.
|
||||||
|
/// This option allows the glTF importer and other usages to make appropriate adjustments.
|
||||||
|
pub flip_model_forward: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform {
|
impl Transform {
|
||||||
@ -106,6 +111,7 @@ impl Transform {
|
|||||||
translation: Vec3::ZERO,
|
translation: Vec3::ZERO,
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
|
/// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
|
||||||
@ -126,6 +132,7 @@ impl Transform {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
flip_model_forward: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,16 +324,44 @@ impl Transform {
|
|||||||
|
|
||||||
/// Equivalent to [`-local_z()`][Transform::local_z]
|
/// Equivalent to [`-local_z()`][Transform::local_z]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn forward(&self) -> Dir3 {
|
pub fn camera_forward(&self) -> Dir3 {
|
||||||
-self.local_z()
|
-self.local_z()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equivalent to [`local_z()`][Transform::local_z]
|
/// Equivalent to [`local_z()`][Transform::local_z]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn back(&self) -> Dir3 {
|
pub fn camera_back(&self) -> Dir3 {
|
||||||
self.local_z()
|
self.local_z()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Equivalent to [`-local_z()`][Transform::local_z] if `flip_model_forward` is false,
|
||||||
|
/// else [`local_z()`][Transform::local_z]
|
||||||
|
///
|
||||||
|
/// glTF has opposing forward directions for cameras and lights, and for models. Model
|
||||||
|
/// forward is +z, whereas camera and light forward is -z.
|
||||||
|
#[inline]
|
||||||
|
pub fn model_forward(&self) -> Dir3 {
|
||||||
|
if self.flip_model_forward {
|
||||||
|
self.local_z()
|
||||||
|
} else {
|
||||||
|
-self.local_z()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equivalent to [`local_z()`][Transform::local_z] if `flip_model_forward` is false,
|
||||||
|
/// else [`-local_z()`][Transform::local_z]
|
||||||
|
///
|
||||||
|
/// glTF has opposing forward directions for cameras and lights, and for models. Model
|
||||||
|
/// forward is +z, whereas camera and light forward is -z. Back is the opposite of this.
|
||||||
|
#[inline]
|
||||||
|
pub fn model_back(&self) -> Dir3 {
|
||||||
|
if self.flip_model_forward {
|
||||||
|
-self.local_z()
|
||||||
|
} else {
|
||||||
|
self.local_z()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Rotates this [`Transform`] by the given rotation.
|
/// Rotates this [`Transform`] by the given rotation.
|
||||||
///
|
///
|
||||||
/// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
|
/// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
|
||||||
@ -469,7 +504,11 @@ impl Transform {
|
|||||||
/// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
|
/// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
|
pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
|
||||||
let back = -direction.try_into().unwrap_or(Dir3::NEG_Z);
|
let back = if self.flip_model_forward {
|
||||||
|
direction.try_into().unwrap_or(Dir3::NEG_Z)
|
||||||
|
} else {
|
||||||
|
-direction.try_into().unwrap_or(Dir3::NEG_Z)
|
||||||
|
};
|
||||||
let up = up.try_into().unwrap_or(Dir3::Y);
|
let up = up.try_into().unwrap_or(Dir3::Y);
|
||||||
let right = up
|
let right = up
|
||||||
.cross(back.into())
|
.cross(back.into())
|
||||||
@ -572,6 +611,7 @@ impl Transform {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
flip_model_forward: self.flip_model_forward,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,8 +343,8 @@ fn move_camera(
|
|||||||
}
|
}
|
||||||
CameraMode::Chase => {
|
CameraMode::Chase => {
|
||||||
transform.translation =
|
transform.translation =
|
||||||
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.back() * 0.6;
|
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.camera_back() * 0.6;
|
||||||
transform.look_to(tracked.forward(), Vec3::Y);
|
transform.look_to(tracked.camera_forward(), Vec3::Y);
|
||||||
if let Projection::Perspective(perspective) = &mut *projection {
|
if let Projection::Perspective(perspective) = &mut *projection {
|
||||||
perspective.fov = 1.0;
|
perspective.fov = 1.0;
|
||||||
}
|
}
|
||||||
|
@ -166,21 +166,25 @@ const CAMERA_POSITIONS: &[Transform] = &[
|
|||||||
translation: Vec3::new(1.5, 1.5, 1.5),
|
translation: Vec3::new(1.5, 1.5, 1.5),
|
||||||
rotation: Quat::from_xyzw(-0.279, 0.364, 0.115, 0.880),
|
rotation: Quat::from_xyzw(-0.279, 0.364, 0.115, 0.880),
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
},
|
},
|
||||||
Transform {
|
Transform {
|
||||||
translation: Vec3::new(2.4, 0.0, 0.2),
|
translation: Vec3::new(2.4, 0.0, 0.2),
|
||||||
rotation: Quat::from_xyzw(0.094, 0.676, 0.116, 0.721),
|
rotation: Quat::from_xyzw(0.094, 0.676, 0.116, 0.721),
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
},
|
},
|
||||||
Transform {
|
Transform {
|
||||||
translation: Vec3::new(2.4, 2.6, -4.3),
|
translation: Vec3::new(2.4, 2.6, -4.3),
|
||||||
rotation: Quat::from_xyzw(0.170, 0.908, 0.308, 0.225),
|
rotation: Quat::from_xyzw(0.170, 0.908, 0.308, 0.225),
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
},
|
},
|
||||||
Transform {
|
Transform {
|
||||||
translation: Vec3::new(-1.0, 0.8, -1.2),
|
translation: Vec3::new(-1.0, 0.8, -1.2),
|
||||||
rotation: Quat::from_xyzw(-0.004, 0.909, 0.247, -0.335),
|
rotation: Quat::from_xyzw(-0.004, 0.909, 0.247, -0.335),
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ fn setup_color_gradient_scene(
|
|||||||
camera_transform: Res<CameraTransform>,
|
camera_transform: Res<CameraTransform>,
|
||||||
) {
|
) {
|
||||||
let mut transform = camera_transform.0;
|
let mut transform = camera_transform.0;
|
||||||
transform.translation += *transform.forward();
|
transform.translation += *transform.camera_forward();
|
||||||
|
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh3d(meshes.add(Rectangle::new(0.7, 0.7))),
|
Mesh3d(meshes.add(Rectangle::new(0.7, 0.7))),
|
||||||
@ -157,7 +157,7 @@ fn setup_image_viewer_scene(
|
|||||||
camera_transform: Res<CameraTransform>,
|
camera_transform: Res<CameraTransform>,
|
||||||
) {
|
) {
|
||||||
let mut transform = camera_transform.0;
|
let mut transform = camera_transform.0;
|
||||||
transform.translation += *transform.forward();
|
transform.translation += *transform.camera_forward();
|
||||||
|
|
||||||
// exr/hdr viewer (exr requires enabling bevy feature)
|
// exr/hdr viewer (exr requires enabling bevy feature)
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
|
@ -137,5 +137,5 @@ fn orbit(
|
|||||||
// Adjust the translation to maintain the correct orientation toward the orbit target.
|
// Adjust the translation to maintain the correct orientation toward the orbit target.
|
||||||
// In our example it's a static target, but this could easily be customized.
|
// In our example it's a static target, but this could easily be customized.
|
||||||
let target = Vec3::ZERO;
|
let target = Vec3::ZERO;
|
||||||
camera.translation = target - camera.forward() * camera_settings.orbit_distance;
|
camera.translation = target - camera.camera_forward() * camera_settings.orbit_distance;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,7 @@ fn random_transform(rng: &mut impl Rng) -> Transform {
|
|||||||
translation: random_translation(rng),
|
translation: random_translation(rng),
|
||||||
rotation: random_rotation(rng),
|
rotation: random_rotation(rng),
|
||||||
scale: random_scale(rng),
|
scale: random_scale(rng),
|
||||||
|
..default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,5 +217,6 @@ fn interpolate_transforms(t1: Transform, t2: Transform, t: f32) -> Transform {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
flip_model_forward: t1.flip_model_forward,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ fn run_camera_controller(
|
|||||||
|
|
||||||
// Apply movement update
|
// Apply movement update
|
||||||
if controller.velocity != Vec3::ZERO {
|
if controller.velocity != Vec3::ZERO {
|
||||||
let forward = *transform.forward();
|
let forward = *transform.camera_forward();
|
||||||
let right = *transform.right();
|
let right = *transform.right();
|
||||||
transform.translation += controller.velocity.x * dt * right
|
transform.translation += controller.velocity.x * dt * right
|
||||||
+ controller.velocity.y * dt * Vec3::Y
|
+ controller.velocity.y * dt * Vec3::Y
|
||||||
|
@ -31,6 +31,7 @@ const TRANSFORM_2D: Transform = Transform {
|
|||||||
translation: Vec3::ZERO,
|
translation: Vec3::ZERO,
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
};
|
};
|
||||||
// The projection used for the camera in 2D
|
// The projection used for the camera in 2D
|
||||||
const PROJECTION_2D: Projection = Projection::Orthographic(OrthographicProjection {
|
const PROJECTION_2D: Projection = Projection::Orthographic(OrthographicProjection {
|
||||||
@ -54,6 +55,7 @@ const TRANSFORM_3D: Transform = Transform {
|
|||||||
// The camera is pointing at the 3D shape
|
// The camera is pointing at the 3D shape
|
||||||
rotation: Quat::from_xyzw(-0.14521316, -0.0, -0.0, 0.98940045),
|
rotation: Quat::from_xyzw(-0.14521316, -0.0, -0.0, 0.98940045),
|
||||||
scale: Vec3::ONE,
|
scale: Vec3::ONE,
|
||||||
|
flip_model_forward: false,
|
||||||
};
|
};
|
||||||
// The projection used for the camera in 3D
|
// The projection used for the camera in 3D
|
||||||
const PROJECTION_3D: Projection = Projection::Perspective(PerspectiveProjection {
|
const PROJECTION_3D: Projection = Projection::Perspective(PerspectiveProjection {
|
||||||
|
@ -87,7 +87,7 @@ fn touch_camera(
|
|||||||
}
|
}
|
||||||
// Rotation gestures only work on iOS
|
// Rotation gestures only work on iOS
|
||||||
for rotation in rotations.read() {
|
for rotation in rotations.read() {
|
||||||
let forward = camera_transform.forward();
|
let forward = camera_transform.camera_forward();
|
||||||
camera_transform.rotate_axis(forward, rotation.0 / 10.0);
|
camera_transform.rotate_axis(forward, rotation.0 / 10.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ fn setup(
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
..default()
|
||||||
},
|
},
|
||||||
AnimationTimer(timer),
|
AnimationTimer(timer),
|
||||||
));
|
));
|
||||||
|
@ -96,6 +96,7 @@ fn setup(mut commands: Commands, assets: Res<AssetServer>, color_tint: Res<Color
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -140,6 +140,7 @@ fn setup(mut commands: Commands, font: Res<FontHandle>, args: Res<Args>) {
|
|||||||
translation,
|
translation,
|
||||||
rotation,
|
rotation,
|
||||||
scale,
|
scale,
|
||||||
|
..default()
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
180
examples/transforms/model_forward.rs
Normal file
180
examples/transforms/model_forward.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
//! 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);
|
||||||
|
}
|
||||||
|
}
|
@ -90,7 +90,7 @@ fn setup(
|
|||||||
fn move_cube(mut cubes: Query<(&mut Transform, &mut CubeState)>, timer: Res<Time>) {
|
fn move_cube(mut cubes: Query<(&mut Transform, &mut CubeState)>, timer: Res<Time>) {
|
||||||
for (mut transform, cube) in &mut cubes {
|
for (mut transform, cube) in &mut cubes {
|
||||||
// Move the cube forward smoothly at a given move_speed.
|
// Move the cube forward smoothly at a given move_speed.
|
||||||
let forward = transform.forward();
|
let forward = transform.model_forward();
|
||||||
transform.translation += forward * cube.move_speed * timer.delta_secs();
|
transform.translation += forward * cube.move_speed * timer.delta_secs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user