← Paracosm·Docs/Handbook
Contents · Motion

Motion

Things move in this world by four mechanisms: keyframed motion (server-side, smooth in the viewer), llTargetOmega (client-side spin), texture animation (client-side scrolling), and scripted position/colour/size changes on a timer. Particles are covered in weather-and-light.md.

Keyframed motion

1. Keyframed rotation frames apply in the object's LOCAL frame

Tested: a yaw of 90 degrees, then a frame about x, turned the object about its own x. So a frame's rotation delta is rock_i / rock_(i-1), and a loop of such deltas closes exactly (no drift over hours); deltas in the other convention leave a residue every loop.

2. Build loops that close on themselves and never jolt

Put every phase at zero at the loop's start (use terms like sin w, 1 - cos w, and a gust as one sine over the whole loop), and rebuild the loop only at a boundary (on a timer of whole loops) so a new amplitude (a storm) never jolts it.

3. Rock about a pivot with absolute poses

For frame i with a world-frame tilt T_i: q_i = HROT * T_i, p_i = PIV + (HOME - PIV) * T_i; the keyframes are [p_i - p_(i-1), q_i / q_(i-1), dt].

4. Two objects sway as one if they run identical frames from the same wall-clock mark

A tree's bark and its leaves are separate objects. Both carry the same loop about the same pivot and START on the same wall-clock mark (seconds into the minute from llGetTimestamp; a loop length that divides the minute keeps the marks on loop boundaries). Rebuild once a minute: stop the motion, set the recorded REST pose, start a fresh loop.

5. Looping keyframed motion costs every tick; gate it

OpenSim's keyframe timer moves each looping object about 20 times a second: 266 swaying objects were 16% of a core with nobody on the region. Move only what a visitor could see: each tree's halves decide at their shared mark, from the tree's ROOT, whether a visitor is within 80 m (so the halves agree), and something invisible (summer snow on branches) does not move at all. Idle cost fell to 3%, 12% with a visitor present.

6. A weathervane turns by a keyframed rotation delta

llWind(ZERO_VECTOR) would give the region's wind, but it is a weak random field; read the region's one shared wind instead (scripts.md). The head points where the wind comes FROM (atan2 + PI); turn by a keyframed rotation delta over a couple of seconds (delta = target yaw - current yaw, since keyframed rotation is local).

7. Flight on keyframed motion

Heading from the path tangent, bank from the turn rate (clamped), pitch from the climb, a flare at the landing; the path is a Catmull-Rom spline through a loop. Birds come in to land from downwind and settle facing the wind.

WorldBridge wander and behavior

8. Use many waypoints on a wander loop

With few waypoints keyframed motion interpolates the whole corner turn across the straight segment and the creature visibly spins; about 24 steps on the circle makes the turn gentle and continuous. (The module's default is 14.) The module now also modulates the loop's radius with low-frequency lobes so the path is smooth everywhere; a jagged random-radius loop made creatures "spin on a dime".

9. Tune facing by measurement

faceoffset (degrees) corrects a model whose forward axis is not +X. Tune it by polling the object's x, y and yaw over time and comparing the movement heading with the object's yaw (the elephant needed -14 degrees, the zebra -5).

10. Stop the old motion before starting a new one

Re-applying a wander without stopping the previous keyframed motion left both timers running and the object yo-yoed between two loops. ApplyWander stops the old one first.

11. Pause with Suspend(), not Pause()

KeyframeMotion.Pause() clears the running flag, and Resume() only restarts a running motion, so Pause then Resume froze the creature for good. Suspend() halts the timer but keeps the flag, so Resume() works.

12. Followers ride the leader's exact path

wander with leader (a uuid) and trail (waypoints behind) seeds the loop from the leader, so a follower walks single file in its footsteps (same cx, cy, radius and steps required). behavior jitters every creature's phase so a herd desynchronises.

WorldBridge roam: creatures that wander a real landscape

roam (worldbridge/Roam.cs) supersedes wander + behavior for creatures. Body: {uuid, walk_anim, pauses:[{anim,kind}], speed, faceoffset, clear, max_slope, height, body_len, cx, cy, radius, leader, leg_min, leg_max, pause_min, pause_max}; {uuid, off:true}; {status:true}; {regrid:true}. tools/spawn_herd.py is a complete example.

17. Build the walk grid from physics, not from names or boxes

One downward physics ray per 1 m cell, from the body's height to the ground: anything solid met above a step's height blocks the cell; phantom ground cover (ferns, tussocks, shrubs) never blocks. A chamfer distance field gives each creature its own clearance. Measure slope as the ground's gradient across about 4 m: a steepest-1-m-step measure called a third of a gentle, bumpy range too steep and stranded the elephants.

18. Physics fills in after the prims

A grid built the moment the region loads sees nothing solid ("0 solid" in the log). Wait about 75 s after load, and treat a grid that found nothing solid as stale after two minutes.

19. Journeys, ground and gait

Dijkstra over the cells this creature can use; a destination 10-28 m off, mostly ahead and away from recent stops, turning back before the range's edge; string-pulled, Chaikin-smoothed, resampled every metre. Each keyframe sits the body on the ground's tangent plane under its four feet: pitched with the slope along its length AND rolled with it across its width (body_len, body_wid), both eased along the path. Pitch alone left a herd standing level across a hillside. Land exactly on the journey's end, and if a busy server has run the keyframes slow, wait for it rather than snapping.

20. Never stuck

A blocked start cell steps to the nearest usable one. If no normal destination can be reached (a creature spawned into a pocket between a boulder and a steep bank stood there half an hour, and its followers with it), plan again with relaxed limits and accept any cell over 3 m away. A follower whose leader has stood still for long goes its own way. Log repeated planning failures instead of retrying silently.

20b. Predators, prey and the chase

A roamer names the kinds it is wary_of (a zebra: the lions) and the kinds it hunts (a lioness: zebras, with a hunt_chance per journey). The wary move off at flee_speed with flee_anim from a predator walking their way (within 1.4 x flee_dist) or one that comes very close; a hunter now and then STALKS, walking to a point short of the nearest of its prey. Give it charge_anim, charge_speed and charge_dist and the stalk ends in a CHARGE: stopped within charge_dist of its prey, it runs at where the prey stands, and the prey, seeing it come, runs too; the charger stops where the prey stood (a pursuit with no kill). Who notices whom: a predator walking is noticed at 1.4 x the prey's flee_dist, one lying up or STALKING only at half of it (a stalk creeps), one CHARGING at 1.6 x. Before the stalk crept, every stalk ended with the prey long gone and no charge: they saw it walking at 17 m. The gallops are solved by the same IK as the walks (tools/ik_gait.py with a short stance and high folds): a ROTARY gallop for cats (the hind feet land one after the other, then the fore in the opposite order), a TRANSVERSE one for horses and zebras (the fore in the same order).

21. A creature carries its own roam

Roam registers a creature by uuid, and a copy rezzed out of a gift box has a new uuid, so it stood still. An object that enters the scene holding a notecard named roam in its root prim now registers itself: the notecard is the roam body as JSON (no uuid), its animations may be NAMES of animation items in the root prim, and its range is centred where it was put down (radius 16 unless the notecard says). roam_notecard {uuid} writes that notecard from a live roamer, with this world's keys (cx, cy, water, wary_of, hunts, leader) removed and its animations named. Details and an example: worldbridge.md, "A creature that carries its own roam".

Spin and scroll

13. One mesh prim spins as a whole with llTargetOmega

A waterwheel needs no linkset: it is one mesh turned by llTargetOmega (client-side). Bake the yaw into the vertices so the spin axis means the same in local and region terms.

14. A positive texture-animation rate scrolls toward -u

llSetTextureAnim(ANIM_ON | SMOOTH | LOOP, ALL_SIDES, 1, 1, 1.0, 1.0, rate) scrolls TOWARD -u for a positive rate. Pass a NEGATIVE rate to run water streaks downstream along +u.

15. Texture animation of a single frame is not held

See textures.md: to show one cell of an atlas, use repeats and offset; animate only where motion is wanted.

Rigid bodies from unlinked prims

16. Apply one shared transform to every part

When a thing is several unlinked prims (an early rowboat), bake each prim's offset from the thing's centre and its base rotation into its script, and apply one shared rock (position AND rotation) to all of them every tick. Check rigidity by reading back the distance between two parts over time (it held at 3.60 m while the bow-to-stern height difference swung 0.34 m).

Checking motion