Skip to content
Merged
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
24 changes: 24 additions & 0 deletions docs/animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,30 @@ it is the lowest, as it lands and lifts, played on the spot as much as
under a body. Pacing cannot take that out; locking the feet (`ae3d.feet`)
does, on a figure with legs to solve.

## Going to a spot

`locomotion.go_to(l, at, facing, running)` (#825) sends a figure to a spot and stops it there, facing a given way: the spot a game sends someone to, to sit, to work, to stand guard. It sets the input each step; `arrived(l)` says it is within 5 cm and 0.1 rad, standing, and `set_input` or `stop_going` ends it.

- **Arriving.** Toward the spot at the speed it can still stop from in the distance left, braking at half its `DECELERATION` (Reynolds' arrive, 1999), so its own momentum brings it to rest there. Once there it stays, until it is moved more than 0.3 m off: a person does not walk a 5 cm correction, and steered again for every drift, the box man turned to face each one and ended 104 degrees off.
- **Coming in along the facing.** Over its last metre it comes along the way it is to face, as a GTA scenario point's or an Unreal Smart Object slot's entry has a character come in: it heads for a point a metre back along the facing, then walks the line. Turned on the spot at the end instead, its body was walked round its planted foot, 0.26 m off the spot. From nearer than a metre and a half when sent, it goes straight there and turns on the spot.
- **Stopping on a footfall (#828).** The walk is paced to the body, so its cycle and the ground go together. At attach the walk's footfalls are read from the clip (the moments its lowest foot changes to the other), and the stop is planned onto the footfall nearest the spot that it can still brake to: Distance Matching's aim (Paragon, GDC 2016), a move ending on its mark with its feet where they land. It stops with both feet down. Cut wherever the speed ran out, the walk ended mid-stride and the lifted foot was carried 8 cm low over the ground to the idle's place. A run brakes into the walk before the stop is planned, since a run's footfall nearest the spot can be 0.38 m off it.
- **Keeping its gait.** It runs once past the middle of the walk's and the run's speeds by `GAIT_BAND` (0.15 m/s), and walks again only below it by as much: switched at the middle itself, a figure braking from a run changed clip every frame, each change a new blend, and a foot was flung up to its hips.
- **Turning on the spot by steps (#827).** `set_turn_steps(l, true)`, for a figure on `ae3d.feet`: turning on the spot it plays its idle, turns at `TURN_SPOT_RATE` (2.5 rad/s, about a second and a quarter for a turn round), and the feet stay planted and step themselves round, about every 45 degrees, as `ae3d.feet` steps a standing figure's feet. That is procedural legs' planting and stepping. Off, as before, the walk is played at a slow pace and held on its planted foot, which carries the body round it.

For the feet's own steps, `ae3d.feet` now asks the body to be still where it stands: the clip's ankle still in the figure's own frame, and the root not going over the ground. Turning where it stands is still. Measured in the world, the turning body moved every ankle and no foot stepped until the turn was over; measured in the clip alone, a body carried over a held foot stepped it by itself. And a step is over its place by 80% of its way before it is set down, as a foot reaches its place before it lands: carried the whole way, a turning figure's foot came down still going 13 mm a frame.

`tests/test_go_to.ae` sends the box man on `ae3d.feet` to a spot 10 m off five ways:

| | arrived in | off the spot | toe slid in a plant, at the most |
|---|---|---|---|
| straight ahead | 10.7 s | 6.0 cm | 4.1 mm |
| ahead, then turned a quarter | 11.7 s | 16.3 cm | 5.0 mm |
| to its side | 11.8 s | 19.8 cm | 7.7 mm |
| behind it | 11.3 s | 10.3 cm | 8.4 mm |
| ahead at a run | 5.6 s | 7.8 cm | 15.6 mm |

Each faces the way asked to the degree, no bone goes faster than 7 m/s in the figure's frame, and its toes move under 5 mm in the 1.5 s after it arrives. #825's bar is 5 mm a plant: setting off and turning while walking (#827) and braking a run (#828) still slide a toe further, and the test holds them to what they slide now until those are fixed.

## Motion matching

`ae3d.matching` (#586, part of #509) moves a figure the way motion matching
Expand Down
41 changes: 38 additions & 3 deletions src/ae3d/feet/module.ae
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,13 @@ const HEEL_RISE = 0.08
// along an arc up to STEP_LIFT and set down over STEP_SECONDS, once it has
// stood STEP_REST and while another foot holds the body, one foot at a time,
// and only where the clip has set it, still (its ankle slower than
// STEP_STILL): a clip moving it is about to step it itself. Held there, a
// STEP_STILL as the clip moves it, in its root's frame), and the body not
// going over the ground (its root slower than STEP_STILL; turning where it
// stands is not going): a clip moving it is about to step it itself, and a
// body carried over a held foot is walked on by its clip. Measured in the
// world alone, a body turned on the spot over its idle moved every ankle,
// and no foot stepped until the turn was over, the legs twisted round under
// it (#827). Held there, a
// body stopping over it drew the leg out straight and dragged the toe up off
// the ground and along it. Stepped while the clip still moved it, at the
// start of a turn on the spot, it was in the air as the walk lifted the
Expand All @@ -104,6 +110,7 @@ const STEP_STILL = 0.15
const STEP_LIFT = 0.06
const STEP_SECONDS = 0.25
const STEP_REST = 0.1
const STEP_PLACE = 0.8 // of a step, by when the foot is over its place
// How far above a foot the ground is looked for, and how far below.
const LOOK_UP = 1.0
const LOOK_DOWN = 2.0
Expand Down Expand Up @@ -136,6 +143,7 @@ struct Leg {
step_lift: float // the arc's height
step_share: float // how far along, eased
last: Vec3 // the animated ankle last frame
last_in: Vec3 // and in its root's frame
known: bool
target: Vec3 // this frame's
ground: Vec3 // the ground under it, and its normal
Expand All @@ -162,6 +170,8 @@ struct Feet {
figure: ptr // *Figure posing it, or null: whether it put the hips and legs back (set_figure)
legs: ptr // list of *Leg
pelvis: float // the pelvis's offset now, metres (negative, down)
root_was: Vec3 // the root last frame, and whether known: how fast the body goes over the ground
root_known: bool
weight: float // 1 on, 0 off
wanted_weight: float
// The ground: a physics world's static bodies, or a function of a place.
Expand Down Expand Up @@ -413,6 +423,16 @@ solve(f: *Feet, delta: float) {
root = f.root as *Model
core.model_sync_transform(root)
floor = root.world_position.y
// The body going over the ground (a locomotion carrying it, its hold's
// offset bleeding out), not turning where it stands.
going = 0.0
if f.root_known && delta > 0.0 {
gx = root.world_position.x - f.root_was.x
gz = root.world_position.z - f.root_was.z
going = core.sqrt_of(gx * gx + gz * gz) / delta
}
f.root_was = root.world_position
f.root_known = true
n = list.size(f.legs)
// Where each foot goes, and how far the pelvis has to come down for the
// lowest of them.
Expand All @@ -438,12 +458,21 @@ solve(f: *Feet, delta: float) {
target = core.vec3(a.x, gy + above, a.z)
// Down and slow: the clip has set this foot down.
speed = 0.0
// And as the clip moves it, in its root's frame: the body turned
// over a still clip (a turn on the spot over the idle) moves the
// ankle in the world, not in the clip.
in_root = core.quat_apply(core.quat_conjugate(root.world_rotation), core.v3_sub(a, root.world_position))
in_clip = 0.0
if l.known && delta > 0.0 {
dx = a.x - l.last.x
dz = a.z - l.last.z
speed = core.sqrt_of(dx * dx + dz * dz) / delta
ix = in_root.x - l.last_in.x
iz = in_root.z - l.last_in.z
in_clip = core.sqrt_of(ix * ix + iz * iz) / delta
}
l.last = a
l.last_in = in_root
l.known = true
low = above < l.standing * CONTACT_LIFT
// Held only once slow, a foot the box man's walk set down at 1.2 m/s
Expand Down Expand Up @@ -495,7 +524,7 @@ solve(f: *Feet, delta: float) {
if l.locked {
l.held_for = l.held_for + delta
off = core.v3_len(core.vec3(target.x - l.lock_at.x, 0.0, target.z - l.lock_at.z))
if off > STEP_OFF && speed < STEP_STILL && !stepping && held >= 2 && l.held_for >= STEP_REST {
if off > STEP_OFF && in_clip < STEP_STILL && going < STEP_STILL && !stepping && held >= 2 && l.held_for >= STEP_REST {
l.locked = false
held = held - 1
stepping = true
Expand All @@ -521,7 +550,13 @@ solve(f: *Feet, delta: float) {
l.step_t = l.step_t + delta
u = l.step_t / STEP_SECONDS
if u > 1.0 { u = 1.0 }
l.step_share = u * u * (3.0 - 2.0 * u)
// Over the ground by STEP_PLACE of the step, then set down: a
// foot comes to its place before it lands. Carried the whole
// step, a turning figure's foot was within 1.5 cm of the ground
// still going 13 mm a frame as it came down.
over = u / STEP_PLACE
if over > 1.0 { over = 1.0 }
l.step_share = over * over * (3.0 - 2.0 * over)
lift = l.step_lift * core.sin_of(3.14159265 * u)
target = core.vec3(l.step_from.x + (target.x - l.step_from.x) * l.step_share, target.y,
l.step_from.z + (target.z - l.step_from.z) * l.step_share)
Expand Down
Loading
Loading