Skip to content

WIP: Fix glTF model forward direction #20135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3278,6 +3278,17 @@ description = "A demonstration of Transform's axis-alignment feature"
category = "Transforms"
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]]
name = "scale"
path = "examples/transforms/scale.rs"
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_animation/src/animatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,20 @@ impl Animatable for Transform {
translation: Vec3::interpolate(&a.translation, &b.translation, t),
rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
scale: Vec3::interpolate(&a.scale, &b.scale, t),
flip_model_forward: a.flip_model_forward,
}
}

fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut translation = Vec3A::ZERO;
let mut scale = Vec3A::ZERO;
let mut rotation = Quat::IDENTITY;
let mut flip_model_forward = None;

for input in inputs {
if flip_model_forward.is_none() {
flip_model_forward = Some(input.value.flip_model_forward);
}
if input.additive {
translation += input.weight * Vec3A::from(input.value.translation);
scale += input.weight * Vec3A::from(input.value.scale);
Expand All @@ -165,6 +170,7 @@ impl Animatable for Transform {
translation: Vec3::from(translation),
rotation,
scale: Vec3::from(scale),
flip_model_forward: flip_model_forward.unwrap_or_default(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_gltf/src/loader/gltf_ext/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transfor
translation: Vec3::from(translation),
rotation: bevy_math::Quat::from_array(rotation),
scale: Vec3::from(scale),
flip_model_forward: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per @atlv24: let's make this a u8 enum :)

},
};
if convert_coordinates {
Expand Down
6 changes: 6 additions & 0 deletions crates/bevy_transform/src/components/global_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ impl GlobalTransform {
translation,
rotation,
scale,
..Default::default()
}
}

Expand Down Expand Up @@ -193,6 +194,7 @@ impl GlobalTransform {
translation,
rotation,
scale,
..Default::default()
}
}

Expand Down Expand Up @@ -367,11 +369,13 @@ mod test {
translation: Vec3::new(1034.0, 34.0, -1324.34),
rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
scale: Vec3::new(1.0, 1.0, 1.0),
..Default::default()
});
let t2 = GlobalTransform::from(Transform {
translation: Vec3::new(0.0, -54.493, 324.34),
rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
scale: Vec3::new(1.345, 1.345, 1.345),
..Default::default()
});
let retransformed = reparent_to_same(t1, t2);
assert!(
Expand All @@ -387,11 +391,13 @@ mod test {
translation: Vec3::new(1034.0, 34.0, -1324.34),
rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
scale: Vec3::new(10.9, 10.9, 10.9),
..Default::default()
});
let t2 = GlobalTransform::from(Transform {
translation: Vec3::new(28.0, -54.493, 324.34),
rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
scale: Vec3::new(0.9, 0.9, 0.9),
..Default::default()
});
// goal: find `X` such as `t2 * X = t1`
let reparented = t1.reparented_to(&t2);
Expand Down
46 changes: 43 additions & 3 deletions crates/bevy_transform/src/components/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ pub struct Transform {
///
/// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
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 {
Expand All @@ -106,6 +111,7 @@ impl Transform {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
flip_model_forward: false,
};

/// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
Expand All @@ -126,6 +132,7 @@ impl Transform {
translation,
rotation,
scale,
flip_model_forward: false,
}
}

Expand Down Expand Up @@ -317,16 +324,44 @@ impl Transform {

/// Equivalent to [`-local_z()`][Transform::local_z]
#[inline]
pub fn forward(&self) -> Dir3 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably deprecate these methods instead of removing them

pub fn camera_forward(&self) -> Dir3 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to cover lights too. Maybe just duplicate methods?

-self.local_z()
}

/// Equivalent to [`local_z()`][Transform::local_z]
#[inline]
pub fn back(&self) -> Dir3 {
pub fn camera_back(&self) -> Dir3 {
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be asset agnostic, this should not even mention glTF imo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to do this, we should only mention glTF in an explanatory note.

/// 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.
///
/// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
Expand Down Expand Up @@ -469,7 +504,11 @@ impl Transform {
/// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
#[inline]
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 right = up
.cross(back.into())
Expand Down Expand Up @@ -572,6 +611,7 @@ impl Transform {
translation,
rotation,
scale,
flip_model_forward: self.flip_model_forward,
}
}

Expand Down
4 changes: 2 additions & 2 deletions examples/3d/motion_blur.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ fn move_camera(
}
CameraMode::Chase => {
transform.translation =
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.back() * 0.6;
transform.look_to(tracked.forward(), Vec3::Y);
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.camera_back() * 0.6;
transform.look_to(tracked.camera_forward(), Vec3::Y);
if let Projection::Perspective(perspective) = &mut *projection {
perspective.fov = 1.0;
}
Expand Down
4 changes: 4 additions & 0 deletions examples/3d/parallax_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,21 +166,25 @@ const CAMERA_POSITIONS: &[Transform] = &[
translation: Vec3::new(1.5, 1.5, 1.5),
rotation: Quat::from_xyzw(-0.279, 0.364, 0.115, 0.880),
scale: Vec3::ONE,
flip_model_forward: false,
},
Transform {
translation: Vec3::new(2.4, 0.0, 0.2),
rotation: Quat::from_xyzw(0.094, 0.676, 0.116, 0.721),
scale: Vec3::ONE,
flip_model_forward: false,
},
Transform {
translation: Vec3::new(2.4, 2.6, -4.3),
rotation: Quat::from_xyzw(0.170, 0.908, 0.308, 0.225),
scale: Vec3::ONE,
flip_model_forward: false,
},
Transform {
translation: Vec3::new(-1.0, 0.8, -1.2),
rotation: Quat::from_xyzw(-0.004, 0.909, 0.247, -0.335),
scale: Vec3::ONE,
flip_model_forward: false,
},
];

Expand Down
4 changes: 2 additions & 2 deletions examples/3d/tonemapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn setup_color_gradient_scene(
camera_transform: Res<CameraTransform>,
) {
let mut transform = camera_transform.0;
transform.translation += *transform.forward();
transform.translation += *transform.camera_forward();

commands.spawn((
Mesh3d(meshes.add(Rectangle::new(0.7, 0.7))),
Expand All @@ -157,7 +157,7 @@ fn setup_image_viewer_scene(
camera_transform: Res<CameraTransform>,
) {
let mut transform = camera_transform.0;
transform.translation += *transform.forward();
transform.translation += *transform.camera_forward();

// exr/hdr viewer (exr requires enabling bevy feature)
commands.spawn((
Expand Down
2 changes: 1 addition & 1 deletion examples/camera/camera_orbit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,5 @@ fn orbit(
// 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.
let target = Vec3::ZERO;
camera.translation = target - camera.forward() * camera_settings.orbit_distance;
camera.translation = target - camera.camera_forward() * camera_settings.orbit_distance;
}
2 changes: 2 additions & 0 deletions examples/gizmos/axes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ fn random_transform(rng: &mut impl Rng) -> Transform {
translation: random_translation(rng),
rotation: random_rotation(rng),
scale: random_scale(rng),
..default()
}
}

Expand Down Expand Up @@ -216,5 +217,6 @@ fn interpolate_transforms(t1: Transform, t2: Transform, t: f32) -> Transform {
translation,
rotation,
scale,
flip_model_forward: t1.flip_model_forward,
}
}
2 changes: 1 addition & 1 deletion examples/helpers/camera_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ fn run_camera_controller(

// Apply movement update
if controller.velocity != Vec3::ZERO {
let forward = *transform.forward();
let forward = *transform.camera_forward();
let right = *transform.right();
transform.translation += controller.velocity.x * dt * right
+ controller.velocity.y * dt * Vec3::Y
Expand Down
2 changes: 2 additions & 0 deletions examples/math/custom_primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const TRANSFORM_2D: Transform = Transform {
translation: Vec3::ZERO,
rotation: Quat::IDENTITY,
scale: Vec3::ONE,
flip_model_forward: false,
};
// The projection used for the camera in 2D
const PROJECTION_2D: Projection = Projection::Orthographic(OrthographicProjection {
Expand All @@ -54,6 +55,7 @@ const TRANSFORM_3D: Transform = Transform {
// The camera is pointing at the 3D shape
rotation: Quat::from_xyzw(-0.14521316, -0.0, -0.0, 0.98940045),
scale: Vec3::ONE,
flip_model_forward: false,
};
// The projection used for the camera in 3D
const PROJECTION_3D: Projection = Projection::Perspective(PerspectiveProjection {
Expand Down
2 changes: 1 addition & 1 deletion examples/mobile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ fn touch_camera(
}
// Rotation gestures only work on iOS
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);
}
}
Expand Down
1 change: 1 addition & 0 deletions examples/stress_tests/many_animated_sprites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ fn setup(
translation,
rotation,
scale,
..default()
},
AnimationTimer(timer),
));
Expand Down
1 change: 1 addition & 0 deletions examples/stress_tests/many_sprites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fn setup(mut commands: Commands, assets: Res<AssetServer>, color_tint: Res<Color
translation,
rotation,
scale,
..default()
},
));
}
Expand Down
1 change: 1 addition & 0 deletions examples/stress_tests/many_text2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ fn setup(mut commands: Commands, font: Res<FontHandle>, args: Res<Args>) {
translation,
rotation,
scale,
..default()
},
));
}
Expand Down
Loading
Loading