Skip to content

Commit fb2bcc4

Browse files
committed
WIP: Fix glTF model forward direction
1 parent 33bed5d commit fb2bcc4

File tree

18 files changed

+261
-11
lines changed

18 files changed

+261
-11
lines changed

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3278,6 +3278,17 @@ description = "A demonstration of Transform's axis-alignment feature"
32783278
category = "Transforms"
32793279
wasm = true
32803280

3281+
[[example]]
3282+
name = "model_forward"
3283+
path = "examples/transforms/model_forward.rs"
3284+
doc-scrape-examples = true
3285+
3286+
[package.metadata.example.model_forward]
3287+
name = "model_forward"
3288+
description = "Illustrates when to use camera_forward vs model_forward, and different cases for model_forward"
3289+
category = "Transforms"
3290+
wasm = true
3291+
32813292
[[example]]
32823293
name = "scale"
32833294
path = "examples/transforms/scale.rs"

crates/bevy_animation/src/animatable.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,20 @@ impl Animatable for Transform {
136136
translation: Vec3::interpolate(&a.translation, &b.translation, t),
137137
rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
138138
scale: Vec3::interpolate(&a.scale, &b.scale, t),
139+
flip_model_forward: a.flip_model_forward,
139140
}
140141
}
141142

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

147149
for input in inputs {
150+
if flip_model_forward.is_none() {
151+
flip_model_forward = Some(input.value.flip_model_forward);
152+
}
148153
if input.additive {
149154
translation += input.weight * Vec3A::from(input.value.translation);
150155
scale += input.weight * Vec3A::from(input.value.scale);
@@ -165,6 +170,7 @@ impl Animatable for Transform {
165170
translation: Vec3::from(translation),
166171
rotation,
167172
scale: Vec3::from(scale),
173+
flip_model_forward: flip_model_forward.unwrap_or_default(),
168174
}
169175
}
170176
}

crates/bevy_gltf/src/loader/gltf_ext/scene.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub(crate) fn node_transform(node: &Node, convert_coordinates: bool) -> Transfor
4242
translation: Vec3::from(translation),
4343
rotation: bevy_math::Quat::from_array(rotation),
4444
scale: Vec3::from(scale),
45+
flip_model_forward: true,
4546
},
4647
};
4748
if convert_coordinates {

crates/bevy_transform/src/components/global_transform.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl GlobalTransform {
136136
translation,
137137
rotation,
138138
scale,
139+
..Default::default()
139140
}
140141
}
141142

@@ -193,6 +194,7 @@ impl GlobalTransform {
193194
translation,
194195
rotation,
195196
scale,
197+
..Default::default()
196198
}
197199
}
198200

@@ -367,11 +369,13 @@ mod test {
367369
translation: Vec3::new(1034.0, 34.0, -1324.34),
368370
rotation: Quat::from_euler(XYZ, 1.0, 0.9, 2.1),
369371
scale: Vec3::new(1.0, 1.0, 1.0),
372+
..Default::default()
370373
});
371374
let t2 = GlobalTransform::from(Transform {
372375
translation: Vec3::new(0.0, -54.493, 324.34),
373376
rotation: Quat::from_euler(XYZ, 1.9, 0.3, 3.0),
374377
scale: Vec3::new(1.345, 1.345, 1.345),
378+
..Default::default()
375379
});
376380
let retransformed = reparent_to_same(t1, t2);
377381
assert!(
@@ -387,11 +391,13 @@ mod test {
387391
translation: Vec3::new(1034.0, 34.0, -1324.34),
388392
rotation: Quat::from_euler(XYZ, 0.8, 1.9, 2.1),
389393
scale: Vec3::new(10.9, 10.9, 10.9),
394+
..Default::default()
390395
});
391396
let t2 = GlobalTransform::from(Transform {
392397
translation: Vec3::new(28.0, -54.493, 324.34),
393398
rotation: Quat::from_euler(XYZ, 0.0, 3.1, 0.1),
394399
scale: Vec3::new(0.9, 0.9, 0.9),
400+
..Default::default()
395401
});
396402
// goal: find `X` such as `t2 * X = t1`
397403
let reparented = t1.reparented_to(&t2);

crates/bevy_transform/src/components/transform.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub struct Transform {
9898
///
9999
/// [`scale`]: https://github.com/bevyengine/bevy/blob/latest/examples/transforms/scale.rs
100100
pub scale: Vec3,
101+
pub flip_model_forward: bool,
101102
}
102103

103104
impl Transform {
@@ -106,6 +107,7 @@ impl Transform {
106107
translation: Vec3::ZERO,
107108
rotation: Quat::IDENTITY,
108109
scale: Vec3::ONE,
110+
flip_model_forward: false,
109111
};
110112

111113
/// Creates a new [`Transform`] at the position `(x, y, z)`. In 2d, the `z` component
@@ -126,6 +128,7 @@ impl Transform {
126128
translation,
127129
rotation,
128130
scale,
131+
flip_model_forward: false,
129132
}
130133
}
131134

@@ -317,16 +320,44 @@ impl Transform {
317320

318321
/// Equivalent to [`-local_z()`][Transform::local_z]
319322
#[inline]
320-
pub fn forward(&self) -> Dir3 {
323+
pub fn camera_forward(&self) -> Dir3 {
321324
-self.local_z()
322325
}
323326

324327
/// Equivalent to [`local_z()`][Transform::local_z]
325328
#[inline]
326-
pub fn back(&self) -> Dir3 {
329+
pub fn camera_back(&self) -> Dir3 {
327330
self.local_z()
328331
}
329332

333+
/// Equivalent to [`-local_z()`][Transform::local_z] if `flip_model_forward` is false,
334+
/// else [`local_z()`][Transform::local_z]
335+
///
336+
/// glTF has opposing forward directions for cameras and lights, and for models. Model
337+
/// forward is +z, whereas camera and light forward is -z.
338+
#[inline]
339+
pub fn model_forward(&self) -> Dir3 {
340+
if self.flip_model_forward {
341+
self.local_z()
342+
} else {
343+
-self.local_z()
344+
}
345+
}
346+
347+
/// Equivalent to [`local_z()`][Transform::local_z] if `flip_model_forward` is false,
348+
/// else [`-local_z()`][Transform::local_z]
349+
///
350+
/// glTF has opposing forward directions for cameras and lights, and for models. Model
351+
/// forward is +z, whereas camera and light forward is -z. Back is the opposite of this.
352+
#[inline]
353+
pub fn model_back(&self) -> Dir3 {
354+
if self.flip_model_forward {
355+
-self.local_z()
356+
} else {
357+
self.local_z()
358+
}
359+
}
360+
330361
/// Rotates this [`Transform`] by the given rotation.
331362
///
332363
/// If this [`Transform`] has a parent, the `rotation` is relative to the rotation of the parent.
@@ -469,7 +500,11 @@ impl Transform {
469500
/// * if `direction` is parallel with `up`, an orthogonal vector is used as the "right" direction
470501
#[inline]
471502
pub fn look_to(&mut self, direction: impl TryInto<Dir3>, up: impl TryInto<Dir3>) {
472-
let back = -direction.try_into().unwrap_or(Dir3::NEG_Z);
503+
let back = if self.flip_model_forward {
504+
direction.try_into().unwrap_or(Dir3::NEG_Z)
505+
} else {
506+
-direction.try_into().unwrap_or(Dir3::NEG_Z)
507+
};
473508
let up = up.try_into().unwrap_or(Dir3::Y);
474509
let right = up
475510
.cross(back.into())
@@ -572,6 +607,7 @@ impl Transform {
572607
translation,
573608
rotation,
574609
scale,
610+
flip_model_forward: self.flip_model_forward,
575611
}
576612
}
577613

examples/3d/motion_blur.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,8 @@ fn move_camera(
343343
}
344344
CameraMode::Chase => {
345345
transform.translation =
346-
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.back() * 0.6;
347-
transform.look_to(tracked.forward(), Vec3::Y);
346+
tracked.translation + Vec3::new(0.0, 0.15, 0.0) + tracked.camera_back() * 0.6;
347+
transform.look_to(tracked.camera_forward(), Vec3::Y);
348348
if let Projection::Perspective(perspective) = &mut *projection {
349349
perspective.fov = 1.0;
350350
}

examples/3d/parallax_mapping.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,21 +166,25 @@ const CAMERA_POSITIONS: &[Transform] = &[
166166
translation: Vec3::new(1.5, 1.5, 1.5),
167167
rotation: Quat::from_xyzw(-0.279, 0.364, 0.115, 0.880),
168168
scale: Vec3::ONE,
169+
flip_model_forward: false,
169170
},
170171
Transform {
171172
translation: Vec3::new(2.4, 0.0, 0.2),
172173
rotation: Quat::from_xyzw(0.094, 0.676, 0.116, 0.721),
173174
scale: Vec3::ONE,
175+
flip_model_forward: false,
174176
},
175177
Transform {
176178
translation: Vec3::new(2.4, 2.6, -4.3),
177179
rotation: Quat::from_xyzw(0.170, 0.908, 0.308, 0.225),
178180
scale: Vec3::ONE,
181+
flip_model_forward: false,
179182
},
180183
Transform {
181184
translation: Vec3::new(-1.0, 0.8, -1.2),
182185
rotation: Quat::from_xyzw(-0.004, 0.909, 0.247, -0.335),
183186
scale: Vec3::ONE,
187+
flip_model_forward: false,
184188
},
185189
];
186190

examples/3d/tonemapping.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ fn setup_color_gradient_scene(
139139
camera_transform: Res<CameraTransform>,
140140
) {
141141
let mut transform = camera_transform.0;
142-
transform.translation += *transform.forward();
142+
transform.translation += *transform.camera_forward();
143143

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

162162
// exr/hdr viewer (exr requires enabling bevy feature)
163163
commands.spawn((

examples/camera/camera_orbit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,5 @@ fn orbit(
137137
// Adjust the translation to maintain the correct orientation toward the orbit target.
138138
// In our example it's a static target, but this could easily be customized.
139139
let target = Vec3::ZERO;
140-
camera.translation = target - camera.forward() * camera_settings.orbit_distance;
140+
camera.translation = target - camera.camera_forward() * camera_settings.orbit_distance;
141141
}

examples/gizmos/axes.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ fn random_transform(rng: &mut impl Rng) -> Transform {
143143
translation: random_translation(rng),
144144
rotation: random_rotation(rng),
145145
scale: random_scale(rng),
146+
..default()
146147
}
147148
}
148149

@@ -216,5 +217,6 @@ fn interpolate_transforms(t1: Transform, t2: Transform, t: f32) -> Transform {
216217
translation,
217218
rotation,
218219
scale,
220+
flip_model_forward: t1.flip_model_forward,
219221
}
220222
}

0 commit comments

Comments
 (0)