@bitefoundry/retrieviewer 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ASSET-PROVENANCE.md +6 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +65 -0
- package/DESKTOP-PLATFORMS.md +75 -0
- package/INSTALLING.md +8 -5
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +110 -0
- package/README.md +119 -58
- package/RELEASING.md +27 -14
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/desktop/platform.mjs +27 -0
- package/integrations/linux-overlay/README.md +83 -0
- package/integrations/linux-overlay/overlay.py +1094 -0
- package/integrations/macos-overlay/README.md +83 -0
- package/integrations/macos-overlay/overlay.jxa +639 -0
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +4 -4
- package/integrations/windows-overlay/desktop.mjs +40 -8
- package/integrations/windows-overlay/protocol.mjs +11 -7
- package/integrations/windows-overlay/worker.mjs +13 -7
- package/package.json +41 -14
- package/puppy-comfort-sprites.mjs +25 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +77 -0
- package/puppy-companion-life.mjs +49 -16
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +65 -14
- package/retrieviewer-cli.mjs +10 -3
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +10 -4
- package/retrieviewer.mjs +20 -14
- package/locomotion-core.mjs +0 -21
- package/locomotion-puppy.animal +0 -0
- package/vendor/procedural-animals/package.json +0 -40
- package/vendor/procedural-animals/src/animal.js +0 -146
- package/vendor/procedural-animals/src/bake.js +0 -51
- package/vendor/procedural-animals/src/build.js +0 -81
- package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
- package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
- package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
- package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
- package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
- package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
- package/vendor/procedural-animals/src/core/build/seams.js +0 -183
- package/vendor/procedural-animals/src/core/build/warp.js +0 -112
- package/vendor/procedural-animals/src/core/build/weights.js +0 -393
- package/vendor/procedural-animals/src/core/math/vec.js +0 -81
- package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
- package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
- package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
- package/vendor/procedural-animals/src/core/motion/common.js +0 -158
- package/vendor/procedural-animals/src/core/motion/index.js +0 -22
- package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
- package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
- package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
- package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
- package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
- package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
- package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
- package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
- package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
- package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
- package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
- package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
- package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
- package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
- package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
- package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
- package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
- package/vendor/procedural-animals/src/index.d.ts +0 -98
- package/vendor/procedural-animals/src/index.js +0 -29
- package/vendor/procedural-animals/src/registry.js +0 -48
- package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
- package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
- package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
- package/vendor/procedural-animals/src/species/bear/index.js +0 -139
- package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
- package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
- package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
- package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
- package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
- package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
- package/vendor/procedural-animals/src/species/boar/index.js +0 -110
- package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
- package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
- package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
- package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
- package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
- package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
- package/vendor/procedural-animals/src/species/cat/index.js +0 -211
- package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
- package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
- package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
- package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
- package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
- package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
- package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
- package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
- package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
- package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
- package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
- package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
- package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
- package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
- package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
- package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
- package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
- package/vendor/procedural-animals/src/species/cow/index.js +0 -118
- package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
- package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
- package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
- package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
- package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
- package/vendor/procedural-animals/src/species/crow/index.js +0 -70
- package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
- package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
- package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
- package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
- package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
- package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
- package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
- package/vendor/procedural-animals/src/species/deer/index.js +0 -106
- package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
- package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
- package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
- package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
- package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
- package/vendor/procedural-animals/src/species/dog/index.js +0 -144
- package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
- package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
- package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
- package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
- package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
- package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
- package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
- package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
- package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
- package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
- package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
- package/vendor/procedural-animals/src/species/fish/index.js +0 -88
- package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
- package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
- package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
- package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
- package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
- package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
- package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
- package/vendor/procedural-animals/src/species/fox/index.js +0 -120
- package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
- package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
- package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
- package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
- package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
- package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
- package/vendor/procedural-animals/src/species/frog/index.js +0 -97
- package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
- package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
- package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
- package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
- package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
- package/vendor/procedural-animals/src/species/goat/index.js +0 -149
- package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
- package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
- package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
- package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
- package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
- package/vendor/procedural-animals/src/species/horse/index.js +0 -80
- package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
- package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
- package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
- package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
- package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
- package/vendor/procedural-animals/src/species/lion/index.js +0 -111
- package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
- package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
- package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
- package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
- package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
- package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
- package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
- package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
- package/vendor/procedural-animals/src/species/pig/index.js +0 -161
- package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
- package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
- package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
- package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
- package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
- package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
- package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
- package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
- package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
- package/vendor/procedural-animals/src/species/rat/index.js +0 -111
- package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
- package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
- package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
- package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
- package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
- package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
- package/vendor/procedural-animals/src/species/shark/index.js +0 -104
- package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
- package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
- package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
- package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
- package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
- package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
- package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
- package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
- package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
- package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
- package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
- package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
- package/vendor/procedural-animals/src/species/snake/index.js +0 -73
- package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
- package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
- package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
- package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
- package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
- package/vendor/procedural-animals/src/species/spider/index.js +0 -78
- package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
- package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
- package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
- package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
- package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
- package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
- package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
- package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
- package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
- package/vendor/procedural-animals/src/worker.js +0 -29
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
// Rabbit behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
|
|
2
|
-
//
|
|
3
|
-
// pose(engine, dt) after the engine's solve: nose twitch (the snout bone: nose leather and
|
|
4
|
-
// split upper lip), independent ear swivels (each pinna turns on its own
|
|
5
|
-
// toward "sounds"), ears kept out of the ground (lying, dead)
|
|
6
|
-
// update(P, dt, layer) every frame over the action layer: the hopping style (the crouched rest
|
|
7
|
-
// stance opens up: hips rise, heels lift, head reaches forward), nose
|
|
8
|
-
// twitch rate, the zig-zag escape steering, automatic idle variants
|
|
9
|
-
// (grooming, periscope, thumping, ear scans)
|
|
10
|
-
// actions hop (single hop), jump, binky, groom, periscope, thump, flee (zig-zag
|
|
11
|
-
// escape), attack (boxing: rears up, forepaw strikes, lunge-bite), hit
|
|
12
|
-
// (startle, then often an alarm thump)
|
|
13
|
-
//
|
|
14
|
-
// All distances are rabbit metres at the reference size, multiplied by cfg.s; posture offsets that
|
|
15
|
-
// the engine multiplies by cfg.k (fwd, side, headRaise, neckReach) are divided by cfg.unit.
|
|
16
|
-
import * as THREE from 'three';
|
|
17
|
-
import { clamp, lerp, smooth, smin, TAU, tv, tqa, angDiff } from '../../core/motion/common.js';
|
|
18
|
-
import { mx, mxLeg, ONESHOTS, POSTURES } from '../../core/motion/actions.js';
|
|
19
|
-
|
|
20
|
-
// The loaf, made from the half-extended bind pose (rig.js):
|
|
21
|
-
// LOAF_DROP hip drop below the bind (m at size 1; a little less than the sculpt's rear lift: the rump
|
|
22
|
-
// rounds under, LOAF_FLEX, and rests just clear of the ground)
|
|
23
|
-
// LOAF_FWD forward shift of the body (m at size 1), cancelling the pitch-back of the girdles
|
|
24
|
-
// LOAF_FLEX spine flexion: the back rounded over the loins
|
|
25
|
-
const LOAF_DROP = 0.031;
|
|
26
|
-
const LOAF_FWD = 0.034;
|
|
27
|
-
const LOAF_FLEX = 0.15;
|
|
28
|
-
|
|
29
|
-
const _q = new THREE.Quaternion(), _s = new THREE.Vector3(1, 1, 1);
|
|
30
|
-
const PI = Math.PI;
|
|
31
|
-
|
|
32
|
-
// per-engine behaviour state
|
|
33
|
-
function B(e) {
|
|
34
|
-
if (!e._rb) {
|
|
35
|
-
e._rb = {
|
|
36
|
-
twitchPh: 0, twitchRate: 2.2, twitchAmp: 1, twitchT: 0,
|
|
37
|
-
ear: [{ sw: 0, target: 0, tNext: 0.5, v: 0 }, { sw: 0, target: 0, tNext: 1.3, v: 0 }],
|
|
38
|
-
earLift: [0, 0], earLen: [undefined, undefined], earNear: [true, true],
|
|
39
|
-
idleT: 7 + e.rand() * 6, thumpAfter: 0, flee: null, scan: 1,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
return e._rb;
|
|
43
|
-
}
|
|
44
|
-
const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
|
|
45
|
-
|
|
46
|
-
// ------------------------------------------------------------------------------------------------
|
|
47
|
-
// pose: snout twitch and ear swivels (runs after the engine wrote the head, ears and extra bones)
|
|
48
|
-
function pose(e, dt) {
|
|
49
|
-
const b = B(e), P = e.P, cfg = e.cfg;
|
|
50
|
-
const sn = e.bone.snout;
|
|
51
|
-
const awake = 1 - P.eyelid;
|
|
52
|
-
// nose twitch: small fast up-down wiggles in bursts; faster when moving / alert, none asleep
|
|
53
|
-
b.twitchPh += dt * TAU * b.twitchRate;
|
|
54
|
-
const tw = Math.pow(Math.max(0, Math.sin(b.twitchPh)), 2) * b.twitchAmp * awake * (e.lod >= 2 ? 0 : 1);
|
|
55
|
-
if (sn && tw > 1e-4) {
|
|
56
|
-
const base = tv().setFromMatrixPosition(sn.matrixWorld);
|
|
57
|
-
_q.setFromRotationMatrix(sn.matrixWorld);
|
|
58
|
-
const lat = tv(1, 0, 0).applyQuaternion(_q);
|
|
59
|
-
const back = tv(0, 1, 0).applyQuaternion(_q).multiplyScalar(-0.0012 * tw * cfg.s);
|
|
60
|
-
sn.matrixWorld.compose(base.add(back), tqa(lat, -0.2 * tw).multiply(_q), _s);
|
|
61
|
-
}
|
|
62
|
-
if (!cfg.hasEars) return;
|
|
63
|
-
const lop = !!e.params.lop;
|
|
64
|
-
const still = 1 - smooth(0.5, 3, e.speed / cfg.sq);
|
|
65
|
-
for (let k = 0; k < 2; k++) {
|
|
66
|
-
const E = b.ear[k], S = k === 0 ? 'L' : 'R', s = k === 0 ? 1 : -1;
|
|
67
|
-
const bone = k === 0 ? e.bone.earL : e.bone.earR;
|
|
68
|
-
if (!bone) continue;
|
|
69
|
-
// a new "sound" every 1-4 s: mostly outward / backward, sometimes straight ahead
|
|
70
|
-
E.tNext -= dt;
|
|
71
|
-
if (E.tNext <= 0) {
|
|
72
|
-
E.tNext = (1 + e.rand() * 3) / (0.6 + 0.4 * b.scan);
|
|
73
|
-
E.target = e.rand() < 0.3 ? 0 : (e.rand() - 0.3) * 1.0 * s;
|
|
74
|
-
}
|
|
75
|
-
const want = E.target * still * (1 - P.earFlat) * awake * (lop ? 0.12 : 1) * b.scan;
|
|
76
|
-
const om = 13; // critically damped swivel
|
|
77
|
-
E.v += (om * om * (want - E.sw) - 2 * om * E.v) * dt;
|
|
78
|
-
E.sw += E.v * dt;
|
|
79
|
-
// (cheap exit: nothing to add to the engine's ear this frame)
|
|
80
|
-
if (Math.abs(E.sw) < 1e-4 && b.earLift[k] < 1e-4 && !b.earNear[k]) continue;
|
|
81
|
-
const base = tv().setFromMatrixPosition(bone.matrixWorld);
|
|
82
|
-
_q.setFromRotationMatrix(bone.matrixWorld);
|
|
83
|
-
const ax = tv(0, 1, 0).applyQuaternion(_q);
|
|
84
|
-
_q.premultiply(tqa(ax, E.sw));
|
|
85
|
-
// keep the long pinna out of the ground (lying on the side, dead, grazing): swing it up about
|
|
86
|
-
// its base just enough, smoothly (a spring on the correction angle)
|
|
87
|
-
const len = b.earLen[k] ?? (b.earLen[k] = e.J['earBase' + S].distanceTo(e.J['earTip' + S]));
|
|
88
|
-
const dir = tv(0, 1, 0).applyQuaternion(_q);
|
|
89
|
-
// sample the pinna at its tip and at both edges of its broad middle (it lies flat when the head
|
|
90
|
-
// rests on its side); only when the ear base is within reach of the ground
|
|
91
|
-
const latE = tv(1, 0, 0).applyQuaternion(_q);
|
|
92
|
-
let need = 0;
|
|
93
|
-
b.earNear[k] = base.y - e.terrainH(base.x, base.z) < len + 0.03 * cfg.s;
|
|
94
|
-
for (let j = 0; j < 3 && b.earNear[k]; j++) {
|
|
95
|
-
const pt = tv().copy(base).addScaledVector(dir, len * (j === 0 ? 1 : 0.6));
|
|
96
|
-
if (j) pt.addScaledVector(latE, (j === 1 ? 1 : -1) * 0.02 * cfg.s);
|
|
97
|
-
const floor = e.terrainH(pt.x, pt.z) + 0.014 * cfg.s;
|
|
98
|
-
if (pt.y < floor) need = Math.max(need, Math.asin(clamp((floor - pt.y) / pt.distanceTo(base), 0, 1)) * 1.15);
|
|
99
|
-
}
|
|
100
|
-
b.earLift[k] += (need - b.earLift[k]) * (1 - Math.exp(-dt * 25));
|
|
101
|
-
if (b.earLift[k] > 1e-4) {
|
|
102
|
-
const axis = tv().crossVectors(dir, tv(0, 1, 0));
|
|
103
|
-
if (axis.lengthSq() > 1e-8) _q.premultiply(tqa(axis.normalize(), b.earLift[k]));
|
|
104
|
-
}
|
|
105
|
-
bone.matrixWorld.compose(base, _q, _s);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ------------------------------------------------------------------------------------------------
|
|
110
|
-
// update: locomotion style and idle life, over the action layer's parameters
|
|
111
|
-
function update(P, dt, L) {
|
|
112
|
-
const e = L.engine, b = B(e), cfg = e.cfg;
|
|
113
|
-
const vn = e.speed / cfg.sq;
|
|
114
|
-
// the scut stays pressed to the rump in every posture (never laid on the ground like a long tail)
|
|
115
|
-
P.tailGround = 0; P.tailSide = 0;
|
|
116
|
-
// the loaf: at rest the hips sit down onto the folded hind legs (the bind pose stands with them
|
|
117
|
-
// half extended). Offsets the actions add are relative to the loaf; postures that rest the body on
|
|
118
|
-
// the ground (lie, sleep, death: groundW) place it themselves.
|
|
119
|
-
const hipRest = cfg.pelvis.hh - (cfg.pelvis.y - cfg.yH);
|
|
120
|
-
// (an action that lowers the hips itself, dropH 0..1 toward the resting height, takes the loaf's share
|
|
121
|
-
// with it: dropH 1 is the rump on the ground either way. The rabbit's own actions are tuned relative
|
|
122
|
-
// to the loaf: 0.1 of dropH ~ 6 mm)
|
|
123
|
-
const loaf = (1 - clamp(P.dropH, 0, 1)) * (1 - P.groundW);
|
|
124
|
-
P.dropH += (LOAF_DROP * cfg.s) / Math.max(1e-4, cfg.yH - hipRest) * loaf;
|
|
125
|
-
// ... the body pitches back about its ground-level origin as the rump sits down, which carried the
|
|
126
|
-
// shoulders ~35 mm back over the planted forefeet (forelegs slanting forward): shift it forward again
|
|
127
|
-
// (only standing still: in motion the gait places the feet under the girdles itself; eased in and
|
|
128
|
-
// out so starting and stopping do not jerk the body over its feet)
|
|
129
|
-
b.sitting = (b.sitting ?? 1) + (loaf * (1 - smooth(0.0, 0.08, vn) * P.gaitW) - (b.sitting ?? 1)) * (1 - Math.exp(-dt * 4));
|
|
130
|
-
const sitting = b.sitting;
|
|
131
|
-
P.fwd += (LOAF_FWD / cfg.unit) * sitting;
|
|
132
|
-
// ... and the back rounds over the loins, the rump tucked under (the egg-shaped loaf)
|
|
133
|
-
P.flex += LOAF_FLEX * Math.min(sitting, loaf);
|
|
134
|
-
if (!b.seated) {
|
|
135
|
-
// a new rabbit starts in its loaf (the engine's girdle springs start at the bind heights)
|
|
136
|
-
b.seated = true;
|
|
137
|
-
const dy = LOAF_DROP * cfg.s;
|
|
138
|
-
e.hipY.reset(e.hipY.x - dy);
|
|
139
|
-
e.height.reset(e.height.x - dy * cfg.zS / (cfg.zS - cfg.zH));
|
|
140
|
-
e.pitch.reset(e.pitch.x + Math.atan2(dy, cfg.zS - cfg.zH));
|
|
141
|
-
}
|
|
142
|
-
// lying on the side (dead, a flop): the long ears fold back along the neck
|
|
143
|
-
const lying = smooth(0.3, 1.1, Math.abs(P.roll));
|
|
144
|
-
P.earFlat = Math.max(P.earFlat, lying);
|
|
145
|
-
// (the short neck hardly twists: the head rolls over with the body as it goes down, carried by the
|
|
146
|
-
// chest rather than held level, and rests on its side; the skull turning on the atlas stretched
|
|
147
|
-
// the skin of the short neck)
|
|
148
|
-
const dying = L.postures.reduce((m, p) => (p.name === 'death' ? Math.max(m, p.w) : m), 0);
|
|
149
|
-
const tip = Math.max(smooth(0.4, 1.0, Math.abs(P.roll)), dying);
|
|
150
|
-
P.headLimp = lerp(P.headLimp, 1, tip);
|
|
151
|
-
P.headRoll *= 1 - tip;
|
|
152
|
-
P.headYaw *= 1 - 0.7 * tip;
|
|
153
|
-
// ... and the head rests on the lower ear's root, a little higher than a short-eared head would
|
|
154
|
-
if (P.headW > 0) P.headPos.y += 0.014 * cfg.s * lying;
|
|
155
|
-
P.headW *= 1 - 0.5 * tip;
|
|
156
|
-
// hopping: the crouched rest stance opens up. The hips rise (the hind limb unfolds, the heels leave
|
|
157
|
-
// the ground: plantigrade at rest, on the toes in motion), the back lengthens and the head reaches
|
|
158
|
-
// forward and down; at escape speed the ears go back along the neck (ears table).
|
|
159
|
-
const run = smooth(0.08, 1.6, vn) * P.gaitW;
|
|
160
|
-
const fast = smooth(1.5, 6, vn) * P.gaitW;
|
|
161
|
-
P.dropH -= 0.25 * run + 0.08 * fast + 0.05 * smooth(0, 0.15, vn) * P.gaitW; // (any step lifts the rump off the ground a little: uneven ground)
|
|
162
|
-
// the chest drops so the short forelegs can reach forward and back (they are nearly straight at rest)
|
|
163
|
-
P.dropF += 0.45 * run;
|
|
164
|
-
P.neckReach += (0.004 * run + 0.003 * fast) / cfg.unit;
|
|
165
|
-
// the head leads a turn only a little (the short neck: the engine's lead of the gaze into a turn
|
|
166
|
-
// twisted the skull on the atlas in the zig-zag escape)
|
|
167
|
-
P.headYaw -= 0.75 * clamp(e.yawRate * 0.22, -0.4, 0.4);
|
|
168
|
-
P.headRaise -= (0.004 * run + 0.002 * fast) / cfg.unit;
|
|
169
|
-
// nose twitch rate: calm 1.5-3 Hz in bursts with pauses, faster when moving
|
|
170
|
-
b.twitchT -= dt;
|
|
171
|
-
if (b.twitchT <= 0) { b.twitchT = 0.8 + e.rand() * 2.5; b.twitchAmp = e.rand() < 0.2 ? 0 : 0.6 + 0.5 * e.rand(); b.twitchRate = 1.6 + e.rand() * 1.4; }
|
|
172
|
-
b.twitchRate += (lerp(b.twitchRate, 4.5, run) - b.twitchRate) * 0.1;
|
|
173
|
-
b.scan = lerp(1, 1.6, P.lookW > 0 && P.look ? 0.5 : 0);
|
|
174
|
-
// zig-zag escape steering (the flee action)
|
|
175
|
-
const f = b.flee;
|
|
176
|
-
if (f) {
|
|
177
|
-
f.t += dt;
|
|
178
|
-
if (f.t >= f.dur) b.flee = null;
|
|
179
|
-
else {
|
|
180
|
-
f.next -= dt;
|
|
181
|
-
if (f.next <= 0) { f.side = -f.side; f.next = 0.3 + e.rand() * 0.35; }
|
|
182
|
-
e.wantSpeed = Math.max(e.wantSpeed, f.speed * cfg.sq * smooth(0, 0.3, f.t));
|
|
183
|
-
e.wantHeading = f.h0 + f.side * f.amp * smooth(0.2, 0.6, f.t);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
// any other action interrupts grazing / drinking
|
|
187
|
-
if (L.oneshots.some((a) => !a.stopping && a.name !== 'eat' && a.name !== 'drink' && a.name !== 'hit')) for (const a of L.oneshots) if (a.name === 'eat' || a.name === 'drink') a.stopping = true;
|
|
188
|
-
// alarm thump after being startled
|
|
189
|
-
if (b.thumpAfter > 0) {
|
|
190
|
-
b.thumpAfter -= dt;
|
|
191
|
-
if (b.thumpAfter <= 0) {
|
|
192
|
-
if (!busy(L) && e.speed < 0.05 && e.legs.every((l) => l.state === 'stance')) L.play('thump');
|
|
193
|
-
else if (!busy(L)) b.thumpAfter = 0.1; // wait until it stands still on all four
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// automatic idle variants (standing still, nothing else running, nobody steering the gaze)
|
|
197
|
-
if (e.idleVariants !== false && L.idle.w > 0.98 && !busy(L) && !e.input.look && !e.input.follow) {
|
|
198
|
-
b.idleT -= dt;
|
|
199
|
-
if (b.idleT <= 0) {
|
|
200
|
-
b.idleT = 6 + e.rand() * 9;
|
|
201
|
-
const r = e.rand();
|
|
202
|
-
if (r < 0.35) L.play('groom');
|
|
203
|
-
else if (r < 0.6) L.play('periscope');
|
|
204
|
-
else if (r < 0.7) L.play('thump');
|
|
205
|
-
else b.twitchAmp = 1.2; // a sniffing burst
|
|
206
|
-
}
|
|
207
|
-
} else if (busy(L)) b.idleT = Math.max(b.idleT, 3);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ------------------------------------------------------------------------------------------------
|
|
211
|
-
// hops and jumps: a real ballistic flight (engine.takeOff) with scripted foot paths in world space.
|
|
212
|
-
// Each foot leaves from where it stood (A) and lands where the stance after the hop will put it (B),
|
|
213
|
-
// so handing the feet back to the gait at the end is continuous. Hind feet push off together, trail
|
|
214
|
-
// extended behind, swing forward outside the forelegs and land together just after the forefeet.
|
|
215
|
-
function hopDef(o) {
|
|
216
|
-
return {
|
|
217
|
-
kind: 'oneshot', airborne: true, fade: 0.1,
|
|
218
|
-
start(inst, L) {
|
|
219
|
-
const e = L.engine, cfg = e.cfg, s = cfg.s;
|
|
220
|
-
inst.run = smooth(0.8, 2.5, e.speed / cfg.sq);
|
|
221
|
-
inst.tc = lerp(o.crouch, 0.16, inst.run) * cfg.sq;
|
|
222
|
-
const h = (inst.opts.height ?? o.height) * s * lerp(1, 0.6, inst.run);
|
|
223
|
-
inst.vy = Math.sqrt(2 * 9.81 * h);
|
|
224
|
-
inst.T = (2 * inst.vy) / 9.81;
|
|
225
|
-
let dist = (inst.opts.distance ?? o.distance) * s;
|
|
226
|
-
if (inst.opts.target) { const t = inst.opts.target; dist = clamp(Math.hypot(t.x - e.pos.x, t.z - e.pos.z), 0.05 * s, dist * 2.5); }
|
|
227
|
-
inst.vh = Math.max(e.speed, lerp(dist / inst.T, e.speed, inst.run));
|
|
228
|
-
inst.tp = Math.min(0.08 * cfg.sq, inst.tc * 0.6);
|
|
229
|
-
inst.LD = lerp(0.42, 0.2, inst.run) * cfg.sq; // landing hand-back time
|
|
230
|
-
inst.L0 = (inst.vy * inst.tp) / 2;
|
|
231
|
-
inst.phase = 'crouch';
|
|
232
|
-
inst.dur = Infinity;
|
|
233
|
-
inst.side = inst.opts.side ?? (e.rand() < 0.5 ? -1 : 1);
|
|
234
|
-
inst.A = [0, 1, 2, 3].map(() => new THREE.Vector3());
|
|
235
|
-
inst.Bp = [0, 1, 2, 3].map(() => new THREE.Vector3());
|
|
236
|
-
inst.end = [0, 1, 2, 3].map(() => new THREE.Vector3());
|
|
237
|
-
inst.last = [0, 1, 2, 3].map(() => new THREE.Vector3());
|
|
238
|
-
},
|
|
239
|
-
update(inst, dt, L) {
|
|
240
|
-
inst.dt = dt;
|
|
241
|
-
const e = L.engine, cfg = e.cfg;
|
|
242
|
-
if (inst.phase === 'crouch' && inst.t >= inst.tc) {
|
|
243
|
-
inst.phase = 'air'; inst.tAir = inst.t;
|
|
244
|
-
// the push's height so far (last frame): the engine's flight adds this frame's rise
|
|
245
|
-
{ const u = Math.max(0, inst.t - dt - (inst.tc - inst.tp)); inst.L0 = u <= inst.tp ? (inst.vy * u * u) / (2 * inst.tp) : inst.vy * (inst.tp / 2 + u - inst.tp); }
|
|
246
|
-
e.takeOff(inst.vy, inst.vh);
|
|
247
|
-
if (inst.run > 0.5) e.phase = ((-e.gait.f * inst.T) % 1 + 1) % 1;
|
|
248
|
-
// landing: body ground point after the flight (heading is held in the air)
|
|
249
|
-
const land = tv().copy(e.pos).addScaledVector(e.forward(e.heading, tv()), inst.vh * inst.T);
|
|
250
|
-
for (let i = 0; i < 4; i++) {
|
|
251
|
-
const Ld = e.legs[i].def;
|
|
252
|
-
inst.A[i].copy(e.legs[i].contact);
|
|
253
|
-
if (i === 0) { inst.P0 = e.pos.clone(); inst.Pl = land.clone(); inst.F = e.forward(e.heading, new THREE.Vector3()); }
|
|
254
|
-
e.bodyToWorld(Ld.cx * Ld.s, 0, Ld.cz, e.heading, land, inst.Bp[i]);
|
|
255
|
-
if (inst.run > 0.5) {
|
|
256
|
-
// running: land in step with the stride (hind feet plant together as it lands), so the
|
|
257
|
-
// gait carries on without hurried catch-up swings
|
|
258
|
-
const g = e.gait, half = Math.min(e.speed * g.D / g.f * 0.5, Ld.leadMax);
|
|
259
|
-
if (!Ld.front) inst.Bp[i].addScaledVector(e.forward(e.heading, tv()), half);
|
|
260
|
-
}
|
|
261
|
-
inst.Bp[i].y = e.terrainH(inst.Bp[i].x, inst.Bp[i].z);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
if (inst.phase === 'land' && inst.t - inst.tLand > inst.LD) return true;
|
|
265
|
-
if (inst.phase === 'air' && inst.t - inst.tAir > 3) e.touchDown();
|
|
266
|
-
return false;
|
|
267
|
-
},
|
|
268
|
-
onLand(inst, L) {
|
|
269
|
-
inst.phase = 'land'; inst.tLand = inst.t;
|
|
270
|
-
const e = L.engine;
|
|
271
|
-
if (inst.run < 0.5) e.speed *= 0.3; // a single hop stops; a running hop runs on
|
|
272
|
-
// the legs take the landing (the crouching rump rides millimetres above the ground)
|
|
273
|
-
e.hipY.v *= 0.25; e.shY.v *= 0.5;
|
|
274
|
-
for (let i = 0; i < 4; i++) inst.end[i].copy(inst.last[i]);
|
|
275
|
-
},
|
|
276
|
-
apply(P, w, inst, L) {
|
|
277
|
-
const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t;
|
|
278
|
-
if (inst.phase === 'crouch') {
|
|
279
|
-
const c = smooth(0, inst.tc, t) * (1 - inst.run * 0.7) * w;
|
|
280
|
-
mx(P, 'dropF', 0.2, c); mx(P, 'dropH', 0.03 + 0.1 * o.crouchH, c); mx(P, 'earFlat', 0.25, c);
|
|
281
|
-
// the feet are taken over by the scripted paths already while they still stand (target =
|
|
282
|
-
// where they are), so nothing jumps at take-off
|
|
283
|
-
const r = smooth(lerp(0.2, 0, inst.run) * inst.tc, 0.9 * inst.tc, t) * w;
|
|
284
|
-
const u0 = clamp(t - (inst.tc - inst.tp), 0, inst.tp), push = (inst.vy * u0 * u0) / (2 * inst.tp);
|
|
285
|
-
for (let i = 0; i < 4; i++) { P.legs[i].target.copy(e.legs[i].contact); if (i < 2) P.legs[i].target.y += push * o.follow[0]; mxLeg(P, i, 'reach', 1, r); }
|
|
286
|
-
// push-off: the forequarters rise first (P.pitch is applied unfiltered: ramp it here)
|
|
287
|
-
mx(P, 'pitch', 0.22 * o.pitch * (1 - 0.5 * inst.run), r);
|
|
288
|
-
mx(P, 'gaitW', 0, r); mx(P, 'legGait', 0, r);
|
|
289
|
-
// push: the body accelerates up (and forward) over the last few frames, so the ballistic
|
|
290
|
-
// take-off does not start with a velocity step
|
|
291
|
-
const u = clamp(t - (inst.tc - inst.tp), 0, inst.tp);
|
|
292
|
-
P.lift += (inst.vy * u * u) / (2 * inst.tp) * w;
|
|
293
|
-
if (inst.run < 0.5 && u > 0) e.speed = Math.max(e.speed, inst.vh * (u / inst.tp));
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
// the push's height is carried through the flight and given back while landing
|
|
297
|
-
P.lift += inst.L0 * w * (inst.phase === 'land' ? 1 - smooth(0, 0.7 * inst.LD, t - inst.tLand) : 1);
|
|
298
|
-
const x = (t - inst.tAir + (inst.dt || 1 / 60)) / inst.T; // after this frame's step
|
|
299
|
-
const k0 = w;
|
|
300
|
-
let hand = 0; // 0 = scripted foot paths, 1 = back to the gait's own contacts
|
|
301
|
-
if (inst.phase === 'land') hand = smooth(0.0, 0.7 * inst.LD, t - inst.tLand);
|
|
302
|
-
const lg = inst.phase === 'land' ? 1 - smooth(0.3 * inst.LD, 0.95 * inst.LD, t - inst.tLand) : 1;
|
|
303
|
-
mx(P, 'gaitW', 0, k0 * lg); mx(P, 'legGait', 0, k0 * lg);
|
|
304
|
-
for (let i = 0; i < 4; i++) {
|
|
305
|
-
const pl = P.legs[i];
|
|
306
|
-
const p = inst.phase === 'land' ? landTarget(inst, e, i, hand) : pathAt(inst, e, i, x, flightLift(inst, e) + inst.L0 * (1 - smooth(0.55, 0.95, x)));
|
|
307
|
-
pl.target.copy(p);
|
|
308
|
-
if (inst.phase === 'air') inst.last[i].copy(p);
|
|
309
|
-
mxLeg(P, i, 'reach', 1, k0 * lg);
|
|
310
|
-
}
|
|
311
|
-
// body: nose up at take-off, level in flight, nose down to land on the forefeet
|
|
312
|
-
const xp = clamp(x, 0, 1);
|
|
313
|
-
if (inst.phase === 'air') {
|
|
314
|
-
mx(P, 'pitch', lerp(0.22, -0.2, smooth(0.05, 0.95, xp)) * o.pitch * (1 - 0.5 * inst.run), k0);
|
|
315
|
-
mx(P, 'earFlat', 0.45, k0); mx(P, 'tailLift', 0.5, k0);
|
|
316
|
-
if (o.twist) {
|
|
317
|
-
// binky: the body twists in mid-air and the hind end flicks to the side
|
|
318
|
-
const tw = Math.sin(PI * xp) * inst.side;
|
|
319
|
-
mx(P, 'bend', 0.45 * tw, k0); mx(P, 'roll', 0.3 * Math.sin(TAU * xp) * inst.side, k0);
|
|
320
|
-
mx(P, 'headYaw', -0.4 * tw, k0);
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
// lands on extended legs, then settles back into the crouch (the rump sits millimetres above
|
|
324
|
-
// the ground at rest, so the landing is absorbed by the legs, not by dropping the body)
|
|
325
|
-
const xl = (t - inst.tLand) / inst.LD;
|
|
326
|
-
const a = Math.sin(PI * Math.min(1, xl * 1.5)) * (1 - xl) * w;
|
|
327
|
-
mx(P, 'dropF', 0.2, a);
|
|
328
|
-
mx(P, 'dropH', -0.09 * (1 - smooth(0, 1, xl)), w);
|
|
329
|
-
}
|
|
330
|
-
void s;
|
|
331
|
-
},
|
|
332
|
-
};
|
|
333
|
-
// after landing: the feet ride with the body (a running landing carries on) and blend into the
|
|
334
|
-
// gait's own contacts
|
|
335
|
-
function landTarget(inst, e, i, hand) {
|
|
336
|
-
const p = tv().copy(inst.end[i]).sub(inst.Pl).add(e.pos).addScaledVector(inst.F, e.speed * (inst.dt || 1 / 60));
|
|
337
|
-
p.y = inst.end[i].y;
|
|
338
|
-
return p.lerp(e.legs[i].contact, hand);
|
|
339
|
-
}
|
|
340
|
-
// body height in flight as the engine will have it after this frame's simulation step (the action
|
|
341
|
-
// layer runs before the step, so e.lift is one frame old)
|
|
342
|
-
function flightLift(inst, e) {
|
|
343
|
-
if (!e.air.active) return 0;
|
|
344
|
-
const ta = e.air.t + (inst.dt || 1 / 60);
|
|
345
|
-
return Math.max(0, inst.vy * ta - 4.905 * ta * ta);
|
|
346
|
-
}
|
|
347
|
-
// foot path in world space at flight fraction x (body lift `lift` above the ground)
|
|
348
|
-
function pathAt(inst, e, i, x, lift) {
|
|
349
|
-
const front = i < 2, s = e.cfg.s;
|
|
350
|
-
const A = inst.A[i], Bq = inst.Bp[i];
|
|
351
|
-
if (inst.run > 0.5) {
|
|
352
|
-
// running: the feet keep cycling as in the stride, carried up with the body
|
|
353
|
-
const c = tv().copy(e.legs[i].contact);
|
|
354
|
-
c.y += lift * (front ? o.follow[0] : o.follow[1] * smooth(0, 0.3, x));
|
|
355
|
-
// ... but never folded up into the girdle (the leg chain would flip through its minimum length)
|
|
356
|
-
const Ld = e.legs[i].def, gy = (front ? e.fr.shC.y : e.fr.hipC.y) - (front ? 0.62 : 0.55) * Ld.len;
|
|
357
|
-
c.y = smin(c.y, gy, 0.05 * e.cfg.k);
|
|
358
|
-
return c;
|
|
359
|
-
}
|
|
360
|
-
// feet are carried with the body (ground point Pb, known in advance: constant horizontal speed)
|
|
361
|
-
// and move from their take-off to their landing offset; the hind feet first stay where they
|
|
362
|
-
// pushed off (the extended phase) and are then swung forward under the body
|
|
363
|
-
const xb = clamp(x, 0, 1);
|
|
364
|
-
const Pb = tv().copy(inst.P0).lerp(inst.Pl, xb);
|
|
365
|
-
const u = front ? smooth(0.0, 0.9, x) : smooth(0.3, 1.0, x);
|
|
366
|
-
const rel = tv().subVectors(A, inst.P0).lerp(tv().subVectors(Bq, inst.Pl), u);
|
|
367
|
-
const p = tv().copy(Pb).add(rel);
|
|
368
|
-
if (front) p.addScaledVector(inst.F, o.reachF * s * Math.sin(PI * u));
|
|
369
|
-
else p.lerp(tv().copy(A), 1 - smooth(0.08, 0.5, x));
|
|
370
|
-
const arc = Math.sin(PI * clamp(u, 0, 1)) * (front ? o.arcF : o.arcH) * s;
|
|
371
|
-
// forefeet leave with the body; the hind feet push until the body has left, then follow
|
|
372
|
-
p.y = lerp(A.y, Bq.y, u) + lift * (front ? o.follow[0] : o.follow[1] * smooth(0, 0.3, x)) + arc;
|
|
373
|
-
if (o.twist && !front) {
|
|
374
|
-
const Ld = e.legs[i].def;
|
|
375
|
-
const lat = e.left(e.heading, tv());
|
|
376
|
-
p.addScaledVector(lat, inst.side * 0.05 * s * Math.sin(PI * clamp(x, 0, 1)) * (Ld.s > 0 ? 1 : 0.8));
|
|
377
|
-
}
|
|
378
|
-
return p;
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ------------------------------------------------------------------------------------------------
|
|
383
|
-
// posture helpers
|
|
384
|
-
const raiseFront = (P, w, amount, pitch = 0) => { mx(P, 'dropF', -amount, w); if (pitch) mx(P, 'pitch', pitch, w); };
|
|
385
|
-
|
|
386
|
-
// grooming: sits up and washes the face with both forepaws, licking them between strokes
|
|
387
|
-
const groom = {
|
|
388
|
-
kind: 'oneshot', fade: 0.35,
|
|
389
|
-
start(inst) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 4.2); },
|
|
390
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
391
|
-
apply(P, w, inst, L) {
|
|
392
|
-
const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t;
|
|
393
|
-
const up = w * smooth(0, 0.5, t);
|
|
394
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
395
|
-
raiseFront(P, up, 0.35, 0.02); mx(P, 'dropH', -0.02, up);
|
|
396
|
-
// the forepaws are held up under the chin and the face is rubbed down over them (the head does
|
|
397
|
-
// the wiping: nose down to lick the paws, then the cheeks drawn over them)
|
|
398
|
-
const wipe = 0.5 - 0.5 * Math.cos(t * TAU * 1.1);
|
|
399
|
-
mx(P, 'headPitch', 0.9 + 0.3 * wipe, up); mx(P, 'headRaise', -0.03 / cfg.unit, up); mx(P, 'lookW', 0, up);
|
|
400
|
-
mx(P, 'earFlat', 0.15, up);
|
|
401
|
-
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
402
|
-
const pawIn = up * smooth(0.25, 0.7, t);
|
|
403
|
-
for (let i = 0; i < 2; i++) {
|
|
404
|
-
const sd = i === 0 ? 1 : -1;
|
|
405
|
-
const tp = tv().copy(e.fr.shC).addScaledVector(fwd, (0.05 - 0.008 * wipe) * s).addScaledVector(lat, sd * 0.012 * s);
|
|
406
|
-
tp.y += (-0.012 + 0.008 * wipe) * s;
|
|
407
|
-
P.legs[i].target.lerp(tp, pawIn / Math.max(1e-3, P.legs[i].reach + pawIn));
|
|
408
|
-
mxLeg(P, i, 'reach', 1, pawIn);
|
|
409
|
-
}
|
|
410
|
-
mx(P, 'jaw', 0.14 * Math.max(0, Math.sin(t * TAU * 2.2)) * (1 - wipe), up); mx(P, 'jawOmega', 25, up);
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
// periscope: rises on the hind feet (metatarsi flat), forelegs dangling, head high, looking far
|
|
415
|
-
const periscope = {
|
|
416
|
-
kind: 'oneshot', fade: 0.45,
|
|
417
|
-
start(inst, L) {
|
|
418
|
-
inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 3.2);
|
|
419
|
-
const e = L.engine;
|
|
420
|
-
const a = e.heading + (e.rand() - 0.5) * 1.6, d = 8 * e.cfg.s;
|
|
421
|
-
inst.look = inst.opts.target ? inst.opts.target.clone() : new THREE.Vector3(e.pos.x + Math.sin(a) * d, e.pos.y + 0.35 * e.cfg.s, e.pos.z + Math.cos(a) * d);
|
|
422
|
-
},
|
|
423
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
424
|
-
apply(P, w, inst, L) {
|
|
425
|
-
const e = L.engine, t = inst.t;
|
|
426
|
-
const up = w * smooth(0, 0.6, t);
|
|
427
|
-
// the body comes down first, then the forepaws reach for the ground (handed back to their plants
|
|
428
|
-
// while the chest was still up, they hung in the air out of reach)
|
|
429
|
-
const body = up * (1 - smooth(inst.dur - 0.8, inst.dur - 0.15, t));
|
|
430
|
-
const legs = Math.min(up, w * smooth(0.004 * e.cfg.k, 0.03 * e.cfg.k, e.shY.x) + body);
|
|
431
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
432
|
-
raiseFront(P, body, 2.3, 0.1);
|
|
433
|
-
mx(P, 'dropH', -0.8, body);
|
|
434
|
-
for (let i = 0; i < 2; i++) { mxLeg(P, i, 'tuck', 1, legs); P.legs[i].extend = 0.35; }
|
|
435
|
-
mx(P, 'headRaise', 0.012 / e.cfg.unit, up); mx(P, 'headPitch', -0.15, up);
|
|
436
|
-
if (!e.input.look) { P.look = inst.look; mx(P, 'lookW', 1, up); }
|
|
437
|
-
mx(P, 'earFlat', 0, up);
|
|
438
|
-
mx(P, 'breathRate', 1.5, up);
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
// thump: alert, then slams both hind feet on the ground (twice), scut up
|
|
443
|
-
const THUMPS = [0.45, 0.95];
|
|
444
|
-
const thump = {
|
|
445
|
-
kind: 'oneshot', fade: 0.2,
|
|
446
|
-
start(inst) { inst.dur = 1.45; inst.hit = [false, false]; },
|
|
447
|
-
update(inst, dt, L) {
|
|
448
|
-
const e = L.engine;
|
|
449
|
-
THUMPS.forEach((tt, j) => {
|
|
450
|
-
if (!inst.hit[j] && inst.t >= tt + 0.12) {
|
|
451
|
-
inst.hit[j] = true;
|
|
452
|
-
for (const i of [2, 3]) e.emit('footstep', { foot: e.legs[i].def.key, position: e.legs[i].plant.clone(), speed: 0, strength: 1, thump: true });
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
return inst.t >= inst.dur;
|
|
456
|
-
},
|
|
457
|
-
apply(P, w, inst, L) {
|
|
458
|
-
const e = L.engine, s = e.cfg.s, t = inst.t;
|
|
459
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
460
|
-
mx(P, 'dropF', -0.2, w); mx(P, 'tailLift', 0.9, w); mx(P, 'headRaise', 0.008 / e.cfg.unit, w);
|
|
461
|
-
let lift = 0;
|
|
462
|
-
for (const tt of THUMPS) lift = Math.max(lift, smooth(tt - 0.14, tt, t) * (1 - smooth(tt + 0.04, tt + 0.12, t)));
|
|
463
|
-
const k = w * smooth(0.15, 0.35, t) * (1 - smooth(1.1, 1.35, t));
|
|
464
|
-
for (const i of [2, 3]) {
|
|
465
|
-
const leg = e.legs[i];
|
|
466
|
-
const tp = tv().copy(leg.plant);
|
|
467
|
-
tp.y += 0.022 * s * lift;
|
|
468
|
-
tp.addScaledVector(e.forward(e.heading, tv()), -0.01 * s * lift);
|
|
469
|
-
P.legs[i].target.copy(tp);
|
|
470
|
-
mxLeg(P, i, 'reach', 1, k);
|
|
471
|
-
}
|
|
472
|
-
mx(P, 'pitch', 0.05 * lift, w);
|
|
473
|
-
},
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
// sit (posture): the rabbit's resting stance is already a crouch, so 'sit' is the alert upright
|
|
477
|
-
// sit on the haunches: forequarters raised on straight forelegs, head high, ears up
|
|
478
|
-
const sit = {
|
|
479
|
-
kind: 'posture', fadeIn: 0.7, fadeOut: 0.6,
|
|
480
|
-
apply(P, w, inst, L) {
|
|
481
|
-
const e = L.engine;
|
|
482
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
483
|
-
raiseFront(P, w, 0.9, 0.04);
|
|
484
|
-
mx(P, 'dropH', 0.015, w);
|
|
485
|
-
mxLeg(P, 0, 'sit', 1, w); mxLeg(P, 1, 'sit', 1, w);
|
|
486
|
-
mx(P, 'headRaise', 0.01 / e.cfg.unit, w); mx(P, 'headPitch', -0.08, w);
|
|
487
|
-
mx(P, 'earFlat', 0, w);
|
|
488
|
-
},
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
// sleep (posture): rabbits doze in the loaf (not curled like a cat): lying sternal, head sunk
|
|
492
|
-
// between the shoulders, ears laid back along the back, eyes closed, nose twitching stops
|
|
493
|
-
const sleep = {
|
|
494
|
-
kind: 'posture', fadeIn: 1.3, fadeOut: 1.0,
|
|
495
|
-
apply(P, w, inst, L) {
|
|
496
|
-
POSTURES.lie.apply(P, w, inst, L);
|
|
497
|
-
const c = smooth(0.8, 2.5, inst.t) * w;
|
|
498
|
-
mx(P, 'headRaise', -0.012 / L.engine.cfg.unit, c); mx(P, 'headPitch', 0.25, c);
|
|
499
|
-
mx(P, 'earFlat', 0.85, c); mx(P, 'earTwitch', 0.3, c);
|
|
500
|
-
mx(P, 'eyelid', 1, smooth(1.5, 3, inst.t) * w);
|
|
501
|
-
mx(P, 'breathRate', 0.5, c); mx(P, 'breathAmp', 1.3, c);
|
|
502
|
-
mx(P, 'lookW', 0, c); mx(P, 'tailIdle', 0.1, c);
|
|
503
|
-
},
|
|
504
|
-
};
|
|
505
|
-
|
|
506
|
-
// flee: bolts with sharp zig-zag turns (45-90 degrees), ears back, white scut raised
|
|
507
|
-
const flee = {
|
|
508
|
-
kind: 'oneshot', fade: 0.2,
|
|
509
|
-
start(inst, L) {
|
|
510
|
-
const e = L.engine, b = B(e);
|
|
511
|
-
inst.dur = inst.opts.duration ?? 3;
|
|
512
|
-
let h0 = e.heading;
|
|
513
|
-
if (inst.opts.from) h0 = Math.atan2(e.pos.x - inst.opts.from.x, e.pos.z - inst.opts.from.z);
|
|
514
|
-
if (inst.opts.heading !== undefined) h0 = inst.opts.heading;
|
|
515
|
-
b.flee = { t: 0, dur: inst.dur, h0, side: e.rand() < 0.5 ? -1 : 1, next: 0.35, amp: 0.7, speed: inst.opts.speed ?? (e.cfg.gears.bound || 3 * e.cfg.sq) / e.cfg.sq };
|
|
516
|
-
},
|
|
517
|
-
update(inst, dt, L) { if (inst.t >= inst.dur) { B(L.engine).flee = null; return true; } return false; },
|
|
518
|
-
apply(P, w) { mx(P, 'earFlat', 0.8, w); mx(P, 'tailLift', 0.6, w); },
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
// attack: territorial boxing. Rears up, strikes with alternating forepaws, then lunges and bites.
|
|
522
|
-
const attack = {
|
|
523
|
-
kind: 'oneshot', fade: 0.15, needsStand: true,
|
|
524
|
-
start(inst, L) {
|
|
525
|
-
const e = L.engine;
|
|
526
|
-
inst.dur = 1.55; inst.hit = false;
|
|
527
|
-
inst.run = smooth(0.8, 2.5, e.speed / e.cfg.sq);
|
|
528
|
-
inst.target = inst.opts.target ? inst.opts.target.clone() : null;
|
|
529
|
-
},
|
|
530
|
-
update(inst, dt, L) {
|
|
531
|
-
const e = L.engine;
|
|
532
|
-
if (!inst.hit && inst.t >= 0.5) {
|
|
533
|
-
inst.hit = true;
|
|
534
|
-
const p = e.legs[0].contact.clone().lerp(e.legs[1].contact, 0.5);
|
|
535
|
-
const dir = inst.target ? inst.target.clone().sub(e.pos).setY(0).normalize() : e.forward(e.heading, new THREE.Vector3());
|
|
536
|
-
e.emit('attackHit', { position: p, direction: dir, style: 'box' });
|
|
537
|
-
}
|
|
538
|
-
if (inst.hit && !inst.bit && inst.t >= 1.12) {
|
|
539
|
-
inst.bit = true;
|
|
540
|
-
const p = new THREE.Vector3().copy(e.cfg.mouthLocal).applyQuaternion(e.headPose.quaternion).add(e.headPose.position);
|
|
541
|
-
e.emit('attackHit', { position: p, direction: e.forward(e.heading, new THREE.Vector3()), style: 'bite' });
|
|
542
|
-
}
|
|
543
|
-
return inst.t >= inst.dur;
|
|
544
|
-
},
|
|
545
|
-
apply(P, w, inst, L) {
|
|
546
|
-
const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t, still = 1 - inst.run;
|
|
547
|
-
if (inst.target) { P.look = inst.target; mx(P, 'lookW', 1, w); }
|
|
548
|
-
mx(P, 'earFlat', 1, w * smooth(0, 0.12, t)); mx(P, 'earTwitch', 0, w);
|
|
549
|
-
const rear = smooth(0.0, 0.28, t) * (1 - smooth(0.8, 1.15, t)) * w * still;
|
|
550
|
-
const lunge = smooth(0.95, 1.12, t) * (1 - smooth(1.2, 1.5, t)) * w;
|
|
551
|
-
mx(P, 'gaitW', 0, rear); mx(P, 'legGait', 0, rear);
|
|
552
|
-
raiseFront(P, rear, 1.5, 0.05); mx(P, 'dropH', -0.25, rear);
|
|
553
|
-
for (let i = 0; i < 2; i++) { mxLeg(P, i, 'tuck', 1, rear); P.legs[i].extend = 0.25; }
|
|
554
|
-
// forepaw strikes, alternating, toward the opponent's face
|
|
555
|
-
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
556
|
-
const sh = tv().copy(e.fr.shC);
|
|
557
|
-
for (let i = 0; i < 2; i++) {
|
|
558
|
-
const t0 = i === 0 ? 0.35 : 0.55;
|
|
559
|
-
const strike = Math.pow(Math.sin(PI * clamp((t - t0) / 0.24, 0, 1)), 2) * rear;
|
|
560
|
-
const tp = tv().copy(sh).addScaledVector(fwd, 0.1 * s).addScaledVector(lat, (i === 0 ? 1 : -1) * 0.012 * s);
|
|
561
|
-
tp.y += 0.01 * s;
|
|
562
|
-
if (inst.target) tp.lerp(tv().copy(inst.target).setY(Math.max(inst.target.y, tp.y)), 0.2);
|
|
563
|
-
P.legs[i].target.lerp(tp, strike / Math.max(1e-3, P.legs[i].reach + strike));
|
|
564
|
-
mxLeg(P, i, 'reach', 1, strike);
|
|
565
|
-
}
|
|
566
|
-
// lunge and bite
|
|
567
|
-
mx(P, 'fwd', 0.03 * s / cfg.unit, lunge); mx(P, 'neckReach', 0.012 * s / cfg.unit, lunge);
|
|
568
|
-
mx(P, 'jaw', 0.5, w * smooth(0.98, 1.08, t) * (1 - smooth(1.12, 1.2, t))); mx(P, 'jawOmega', 30, w);
|
|
569
|
-
},
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
// hit: the engine's stagger, then (often) an alarm thump
|
|
573
|
-
const hit = {
|
|
574
|
-
...ONESHOTS.hit,
|
|
575
|
-
kind: 'oneshot',
|
|
576
|
-
start(inst, L) {
|
|
577
|
-
// a light animal: the blow shoves it less (fewer recovery steps); often an alarm thump after
|
|
578
|
-
inst.opts = { ...inst.opts, strength: (inst.opts.strength ?? 1) * 0.5 };
|
|
579
|
-
ONESHOTS.hit.start(inst, L);
|
|
580
|
-
if (L.engine.rand() < 0.6) B(L.engine).thumpAfter = 1.0;
|
|
581
|
-
},
|
|
582
|
-
// the engine's flinch with a continuous envelope (sharp rise, exponential settle), ears flat
|
|
583
|
-
apply(P, w, inst) {
|
|
584
|
-
const t = inst.t;
|
|
585
|
-
const k = smooth(0, 0.09, t) * Math.exp(-Math.max(0, t - 0.09) * 5.5) * w;
|
|
586
|
-
mx(P, 'roll', inst.lx * 0.16, k); mx(P, 'bend', -inst.lx * 0.25, k);
|
|
587
|
-
mx(P, 'pitch', inst.lz * 0.08, k); mx(P, 'dropF', 0.18, k); mx(P, 'dropH', 0.012, k);
|
|
588
|
-
P.headLocal.set(inst.lx * 0.06, 0.02, inst.lz * 0.06 - 0.03); mx(P, 'headLocalW', 1, k);
|
|
589
|
-
mx(P, 'headYaw', -inst.lx * 0.35, k); mx(P, 'headPitch', -0.15, k);
|
|
590
|
-
mx(P, 'earFlat', 1, w * (1 - smooth(0.4, 0.75, t))); mx(P, 'eyelid', 0.7, k);
|
|
591
|
-
mx(P, 'tailLift', 0.6, k);
|
|
592
|
-
},
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
export const hooks = {
|
|
596
|
-
pose,
|
|
597
|
-
update,
|
|
598
|
-
actions: {
|
|
599
|
-
hop: hopDef({ height: 0.08, distance: 0.35, crouch: 0.12, crouchH: 0, arcF: 0.02, arcH: 0.02, reachF: 0.03, follow: [0.8, 0.55], pitch: 1, twist: false }),
|
|
600
|
-
jump: hopDef({ height: 0.4, distance: 0.7, crouch: 0.2, crouchH: 0.2, arcF: 0.03, arcH: 0.03, reachF: 0.03, follow: [0.93, 0.8], pitch: 1.3, twist: false }),
|
|
601
|
-
binky: hopDef({ height: 0.22, distance: 0.12, crouch: 0.14, crouchH: 0.1, arcF: 0.03, arcH: 0.03, reachF: 0.01, follow: [0.9, 0.75], pitch: 0.4, twist: true }),
|
|
602
|
-
groom, periscope, thump, flee, attack, hit, sit, sleep,
|
|
603
|
-
death: { ...POSTURES.death, kind: 'posture', fadeOut: 2.0 },
|
|
604
|
-
},
|
|
605
|
-
};
|