mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #1090 from vangel76/fix-1081-orient-along-path-flip
This commit is contained in:
commit
5249ff8bad
2 files changed
+45
-5
No files matched your search
@@ -212,18 +212,35 @@ pub fn rig_rotation(ctx: &EvalCtx, layer: &Layer) -> Mat4 {
|
||||
// ---------------------------------------------------------------- auto-orient
|
||||
|
||||
/// Direction of travel of a layer's position (parent space) at the context time.
|
||||
///
|
||||
/// Where the position is not moving (at and after the last keyframe, before the first, or while
|
||||
/// holding) the last valid direction of travel is kept, so the layer does not snap back to its
|
||||
/// unrotated pose at the end of a path.
|
||||
fn path_tangent(ctx: &EvalCtx, layer: &Layer) -> Option<Vec3> {
|
||||
let tr = layer.transform()?;
|
||||
// Position (or its separated X/Y/Z dimensions) must be animated.
|
||||
let moving = ["position", "positionX", "positionY", "positionZ"].iter().any(|m| tr.get(m).is_some_and(|p| p.keys.len() > 1 || p.has_expression()));
|
||||
let dims = ["position", "positionX", "positionY", "positionZ"];
|
||||
let moving = dims.iter().any(|m| tr.get(m).is_some_and(|p| p.keys.len() > 1 || p.has_expression()));
|
||||
if !moving {
|
||||
return None;
|
||||
}
|
||||
let dt = effectcraft_time::Tick::from_seconds_f64(0.005);
|
||||
let a = Vec3::from(ctx.at(ctx.time - dt).v3(layer, tr, "position", [0.0; 3]));
|
||||
let b = Vec3::from(ctx.at(ctx.time + dt).v3(layer, tr, "position", [0.0; 3]));
|
||||
let d = b - a;
|
||||
(d.length() > 1e-9).then(|| d.normalize())
|
||||
let pos = |t| Vec3::from(ctx.at(t).v3(layer, tr, "position", [0.0; 3]));
|
||||
let dir = |d: Vec3| (d.length() > 1e-9 && d.length().is_finite()).then(|| d.normalize());
|
||||
if let Some(d) = dir(pos(ctx.time + dt) - pos(ctx.time - dt)) {
|
||||
return Some(d);
|
||||
}
|
||||
// No motion here: fall back on the nearest motion before this time (a backward difference at
|
||||
// each earlier key, latest first), then on the first motion after it.
|
||||
let mut keys: Vec<effectcraft_time::Tick> = dims.iter().filter_map(|m| tr.get(m)).flat_map(|p| p.keys.iter().map(|k| layer.comp_time(k.time))).collect();
|
||||
|
||||
keys.sort();
|
||||
keys.dedup();
|
||||
keys.iter()
|
||||
.rev()
|
||||
.filter(|k| **k <= ctx.time)
|
||||
.find_map(|&k| dir(pos(k) - pos(k - dt)))
|
||||
.or_else(|| keys.iter().filter(|k| **k > ctx.time).find_map(|&k| dir(pos(k + dt) - pos(k))))
|
||||
}
|
||||
|
||||
/// Auto-Orient ▸ Orient Along Path for 2D layers: extra Z rotation in degrees.
|
||||
|
||||
@@ -55,6 +55,29 @@ fn orient_along_path_2d_follows_motion() {
|
||||
assert!(x_axis.x.abs() < 1e-6 && (x_axis.y - 1.0).abs() < 1e-6, "{x_axis:?}");
|
||||
}
|
||||
|
||||
/// #1081: a layer travelling left kept its heading until the last keyframe, then (no motion left
|
||||
/// to measure) snapped back to 0 degrees and looked flipped. It must keep its last direction of
|
||||
/// travel at the final key and after it, in 2D and 3D.
|
||||
#[test]
|
||||
fn orient_along_path_keeps_the_last_heading_at_and_after_the_end() {
|
||||
for three_d in [false, true] {
|
||||
let (mut p, cid, comp) = setup();
|
||||
let mut l = solid(&mut p, &comp, three_d, [150.0, 50.0, 0.0]);
|
||||
l.auto_orient = AutoOrient::AlongPath;
|
||||
let end = Tick::from_seconds_f64(1.0);
|
||||
l.props.prop_mut("transform/position").unwrap().keys =
|
||||
vec![Keyframe::new(Tick::ZERO, Value::Vec3([150.0, 50.0, 0.0])), Keyframe::new(end, Value::Vec3([50.0, 50.0, 0.0]))];
|
||||
p.comp_mut(cid).unwrap().layers.push(l);
|
||||
let c = p.comp(cid).unwrap();
|
||||
for secs in [0.5, 0.999, 1.0, 1.2, 1.9] {
|
||||
let ctx = EvalCtx::new(&p, cid, c, Tick::from_seconds_f64(secs));
|
||||
let x_axis = ctx.world_matrix(&c.layers[0]).apply_vec(vec3(1.0, 0.0, 0.0));
|
||||
// Moving left (-x): the layer's x axis keeps pointing left.
|
||||
assert!((x_axis.x + 1.0).abs() < 1e-6 && x_axis.y.abs() < 1e-6 && x_axis.z.abs() < 1e-6, "3d={three_d} t={secs}: {x_axis:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn layers_parented_to_a_camera_follow_its_rotation() {
|
||||
let (mut p, cid, comp) = setup();
|
||||
|
||||
Reference in new issue
Block a user