@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,539 +0,0 @@
|
|
|
1
|
-
// Rat behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
|
|
2
|
-
//
|
|
3
|
-
// pose(engine, dt) after the engine's solve: sniffing (the snout bone twitches at 6-10 Hz in
|
|
4
|
-
// bursts), whisking (both whisker fans sweep forward and back in step with
|
|
5
|
-
// the sniffs, fanned forward when exploring, swept back when eating,
|
|
6
|
-
// grooming, fighting or asleep, and kept clear of the ground), small
|
|
7
|
-
// independent ear swivels
|
|
8
|
-
// update(P, dt, layer) every frame over the action layer: the scurrying style (body low and
|
|
9
|
-
// flattened, head stretched forward, ears back at speed), sniffing / whisking
|
|
10
|
-
// rates, the scurry-and-freeze steering, rearing to look at a far target,
|
|
11
|
-
// automatic idle variants (sniff scans, rearing, face grooming)
|
|
12
|
-
// actions rear (stands up on the hind feet to look around), groom (the face-washing
|
|
13
|
-
// syntactic chain: nose strokes, face strokes, then body licking), eat (sits
|
|
14
|
-
// up holding food in the forepaws and gnaws it), scurry (a fast burst along
|
|
15
|
-
// the heading, a sudden stop, a freeze and a sniff), box (rears and pushes
|
|
16
|
-
// with the forepaws), attack (lunge bite, or boxing on the hind legs), hit
|
|
17
|
-
// (flinch, then a freeze)
|
|
18
|
-
//
|
|
19
|
-
// All distances are rat metres at the reference size, multiplied by cfg.s; posture offsets that the
|
|
20
|
-
// engine multiplies by cfg.k (fwd, side, headRaise, neckReach) are divided by cfg.unit.
|
|
21
|
-
import * as THREE from 'three';
|
|
22
|
-
import { clamp, lerp, smooth, TAU, tv, tqa } from '../../core/motion/common.js';
|
|
23
|
-
import { mx, mxLeg, ONESHOTS } from '../../core/motion/actions.js';
|
|
24
|
-
import { whiskerSamples, WHISKER_BASE, WHISKER_TIP } from './whiskers.js';
|
|
25
|
-
import { HK } from './rig.js';
|
|
26
|
-
|
|
27
|
-
const W_SAMPLES = whiskerSamples();
|
|
28
|
-
const _q = new THREE.Quaternion(), _q2 = new THREE.Quaternion(), _s = new THREE.Vector3(1, 1, 1);
|
|
29
|
-
const _p = new THREE.Vector3(), _a = new THREE.Vector3(), _d = new THREE.Vector3();
|
|
30
|
-
const _M = new THREE.Matrix4(), _sv = new THREE.Vector3();
|
|
31
|
-
const _w = Array.from({ length: 64 }, () => new THREE.Vector3());
|
|
32
|
-
const PI = Math.PI;
|
|
33
|
-
|
|
34
|
-
// per-engine behaviour state
|
|
35
|
-
function B(e) {
|
|
36
|
-
if (!e._ratb) {
|
|
37
|
-
e._ratb = {
|
|
38
|
-
sniffPh: 0, sniffRate: 7, sniffAmp: 1, sniffT: 0, whiskPh: 0, whiskAmp: 0.6, fanT: 0.1,
|
|
39
|
-
whisk: [{ a: 0, v: 0, back: 0, lift: 0 }, { a: 0, v: 0, back: 0, lift: 0 }],
|
|
40
|
-
ear: [{ sw: 0, target: 0, tNext: 0.7, v: 0 }, { sw: 0, target: 0, tNext: 1.6, v: 0 }],
|
|
41
|
-
idleT: 3 + e.rand() * 4, scanPh: 0, scanW: 0, scurry: null, freezeT: 0, rearFor: null,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
return e._ratb;
|
|
45
|
-
}
|
|
46
|
-
const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
|
|
47
|
-
const oneshotW = (L, name) => L.oneshots.reduce((m, a) => (a.name === name ? Math.max(m, a.w) : m), 0);
|
|
48
|
-
|
|
49
|
-
// whisker samples in the whisker bone's bind-local frame, mapped from the reference head (whiskers.js)
|
|
50
|
-
function whiskerLocal(e, kk) {
|
|
51
|
-
const s = kk ? -1 : 1, S = kk ? 'R' : 'L';
|
|
52
|
-
const i = e.sk.bones.indexOf(e.bone['whisker' + S]);
|
|
53
|
-
const inv = e.sk.bind[i].clone().invert();
|
|
54
|
-
const Jb = e.J['whiskerBase' + S], Jt = e.J['whiskerTip' + S];
|
|
55
|
-
const refLen = HK * Math.hypot(WHISKER_TIP[0] - WHISKER_BASE[0], WHISKER_TIP[1] - WHISKER_BASE[1], WHISKER_TIP[2] - WHISKER_BASE[2]);
|
|
56
|
-
const k = Jb.distanceTo(Jt) / refLen;
|
|
57
|
-
// (the sample points were laid out with head-local offsets scaled by HK; the strand lengths are not)
|
|
58
|
-
return W_SAMPLES.map((p) => new THREE.Vector3(Jb.x + (p[0] - WHISKER_BASE[0]) * s * k * HK, Jb.y + (p[1] - WHISKER_BASE[1]) * k * HK, Jb.z + (p[2] - WHISKER_BASE[2]) * k * HK).applyMatrix4(inv));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ------------------------------------------------------------------------------------------------
|
|
62
|
-
// pose: sniffing snout, whisking fans, ear swivels (after the engine wrote the head and its children)
|
|
63
|
-
function pose(e, dt) {
|
|
64
|
-
const b = B(e), P = e.P, cfg = e.cfg;
|
|
65
|
-
if (e.lod >= 2) return;
|
|
66
|
-
const awake = 1 - clamp(P.eyelid, 0, 1);
|
|
67
|
-
// ---- sniffing: the nose and whisker pads twitch up and back at the sniff rate
|
|
68
|
-
// (rates and amplitudes change in bursts: eased so the snout and the fans never jump)
|
|
69
|
-
const ka = 1 - Math.exp(-dt * 8);
|
|
70
|
-
b.sRate = (b.sRate ?? b.sniffRate) + (b.sniffRate - (b.sRate ?? b.sniffRate)) * ka;
|
|
71
|
-
b.sAmp = (b.sAmp ?? 0) + (b.sniffAmp - (b.sAmp ?? 0)) * ka;
|
|
72
|
-
b.wAmp = (b.wAmp ?? 0) + (b.whiskAmp - (b.wAmp ?? 0)) * ka;
|
|
73
|
-
b.sniffPh += dt * TAU * b.sRate;
|
|
74
|
-
const sn = e.bone.snout;
|
|
75
|
-
const tw = (0.5 - 0.5 * Math.cos(b.sniffPh)) * b.sAmp * awake;
|
|
76
|
-
if (sn && tw > 1e-4) {
|
|
77
|
-
const base = _p.setFromMatrixPosition(sn.matrixWorld);
|
|
78
|
-
_q.setFromRotationMatrix(sn.matrixWorld);
|
|
79
|
-
const lat = _a.set(1, 0, 0).applyQuaternion(_q);
|
|
80
|
-
const back = _d.set(0, 1, 0).applyQuaternion(_q).multiplyScalar(-0.0005 * tw * cfg.s);
|
|
81
|
-
sn.matrixWorld.compose(base.add(back), tqa(lat, -0.1 * tw).multiply(_q), _s);
|
|
82
|
-
}
|
|
83
|
-
// ---- whiskers: whisked in step with the sniffs (protraction as the nose rises), fanned forward
|
|
84
|
-
// (exploring) or swept back (b.fanT), lifted clear of the ground
|
|
85
|
-
b.whiskPh = b.sniffPh;
|
|
86
|
-
for (let k = 0; k < 2; k++) {
|
|
87
|
-
const S = k ? 'R' : 'L', s = k ? -1 : 1;
|
|
88
|
-
const bone = e.bone['whisker' + S];
|
|
89
|
-
if (!bone) break;
|
|
90
|
-
const W = b.whisk[k];
|
|
91
|
-
const om = 22;
|
|
92
|
-
W.v += (om * om * (b.fanT - W.a) - 2 * om * W.v) * dt;
|
|
93
|
-
W.a += W.v * dt;
|
|
94
|
-
const whisk = b.wAmp * awake * (0.5 - 0.5 * Math.cos(b.whiskPh)) * (1 - 0.2 * k * Math.sin(b.whiskPh * 0.13));
|
|
95
|
-
const ang = W.a + whisk - 0.35 * b.wAmp * awake;
|
|
96
|
-
if (!b.wLocal) b.wLocal = [0, 1].map((kk) => whiskerLocal(e, kk));
|
|
97
|
-
const base = _p.setFromMatrixPosition(bone.matrixWorld);
|
|
98
|
-
_q.setFromRotationMatrix(bone.matrixWorld);
|
|
99
|
-
_a.set(0, 1, 0).applyQuaternion(e.headPose.quaternion);
|
|
100
|
-
_q.premultiply(tqa(_a, -s * ang));
|
|
101
|
-
// ground: sweep the fan back along the cheek just enough, then lift it about its root
|
|
102
|
-
const vDown = W.y0 === undefined || dt <= 0 ? 0 : Math.max(0, (W.y0 - base.y) / dt);
|
|
103
|
-
W.y0 = base.y;
|
|
104
|
-
const margin = 0.003 * cfg.s + Math.min(0.02 * cfg.s, vDown * 0.12);
|
|
105
|
-
const near = base.y - e.terrainH(base.x, base.z) < 0.07 * cfg.s + margin * 2;
|
|
106
|
-
let needB = 0, needL = 0;
|
|
107
|
-
if (near) {
|
|
108
|
-
const pts = b.wLocal[k];
|
|
109
|
-
const M = _w[0];
|
|
110
|
-
const mw = bone.matrixWorld.clone().compose(base, _q, _s);
|
|
111
|
-
for (let i = 0; i < pts.length; i++) _w[i + 1].copy(pts[i]).applyMatrix4(mw).sub(base);
|
|
112
|
-
const nP = pts.length;
|
|
113
|
-
const clearAt = (phi) => {
|
|
114
|
-
let worst = 0;
|
|
115
|
-
_q2.setFromAxisAngle(_a, s * phi);
|
|
116
|
-
for (let i = 1; i <= nP; i++) {
|
|
117
|
-
const p = _d.copy(_w[i]).applyQuaternion(_q2).add(base);
|
|
118
|
-
worst = Math.min(worst, p.y - (e.terrainH(p.x, p.z) + margin));
|
|
119
|
-
}
|
|
120
|
-
return worst;
|
|
121
|
-
};
|
|
122
|
-
let pw = clearAt(0);
|
|
123
|
-
if (pw < 0) {
|
|
124
|
-
needB = 1.4;
|
|
125
|
-
for (let i = 1; i <= 7; i++) {
|
|
126
|
-
const w = clearAt(i * 0.2);
|
|
127
|
-
if (w >= 0) { needB = (i - 1 + -pw / Math.max(1e-6, w - pw)) * 0.2; break; }
|
|
128
|
-
pw = w;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
const rest = clearAt(needB);
|
|
132
|
-
if (rest < 0) needL = Math.asin(clamp(-rest / (0.035 * cfg.s), 0, 1)) * 1.3;
|
|
133
|
-
void M;
|
|
134
|
-
}
|
|
135
|
-
const omb = 40;
|
|
136
|
-
W.vb = (W.vb || 0) + (omb * omb * (needB - W.back) - 2 * omb * (W.vb || 0)) * dt; W.back = Math.max(0, W.back + W.vb * dt);
|
|
137
|
-
W.vl = (W.vl || 0) + (omb * omb * (needL - W.lift) - 2 * omb * (W.vl || 0)) * dt; W.lift = Math.max(0, W.lift + W.vl * dt);
|
|
138
|
-
if (W.back > 1e-4) _q.premultiply(tqa(_a, s * W.back));
|
|
139
|
-
if (W.lift > 1e-4) {
|
|
140
|
-
const tip = _d.copy(b.wLocal[k][0]).applyMatrix4(bone.matrixWorld).sub(base).applyQuaternion(tqa(_a, s * W.back)).normalize();
|
|
141
|
-
const ax = _w[0].crossVectors(tip, THREE.Object3D.DEFAULT_UP);
|
|
142
|
-
if (ax.lengthSq() > 1e-8) _q.premultiply(tqa(ax.normalize(), W.lift));
|
|
143
|
-
}
|
|
144
|
-
// what still reaches the ground (a head lying on its side): the strands bend against it, the fan
|
|
145
|
-
// shortens about its root just enough
|
|
146
|
-
let need = 1;
|
|
147
|
-
if (near) {
|
|
148
|
-
_M.compose(base, _q, _s);
|
|
149
|
-
for (const pt of b.wLocal[k]) {
|
|
150
|
-
const p = _d.copy(pt).applyMatrix4(_M);
|
|
151
|
-
const dy = p.y - base.y, floor = e.terrainH(p.x, p.z) + 0.6 * margin;
|
|
152
|
-
if (p.y < floor && dy < -1e-5) need = Math.min(need, Math.max(0, base.y - floor) / -dy);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
need = clamp(need, 0.15, 1);
|
|
156
|
-
const oms = 40;
|
|
157
|
-
W.vs = (W.vs || 0) + (oms * oms * (need - (W.sc ?? 1)) - 2 * oms * (W.vs || 0)) * dt;
|
|
158
|
-
W.sc = clamp((W.sc ?? 1) + W.vs * dt, 0.15, 1);
|
|
159
|
-
if (W.sc > need) { W.sc = need; W.vs = Math.min(0, W.vs); }
|
|
160
|
-
bone.matrixWorld.compose(base, _q, _sv.set(W.sc, W.sc, W.sc));
|
|
161
|
-
}
|
|
162
|
-
// ---- ears: small independent swivels toward "sounds"
|
|
163
|
-
if (!cfg.hasEars) return;
|
|
164
|
-
const still = 1 - smooth(0.2, 1, e.speed / cfg.sq);
|
|
165
|
-
for (let k = 0; k < 2; k++) {
|
|
166
|
-
const E = b.ear[k], s = k === 0 ? 1 : -1;
|
|
167
|
-
const bone = k === 0 ? e.bone.earL : e.bone.earR;
|
|
168
|
-
if (!bone) continue;
|
|
169
|
-
E.tNext -= dt;
|
|
170
|
-
if (E.tNext <= 0) { E.tNext = 1 + e.rand() * 3; E.target = e.rand() < 0.4 ? 0 : (e.rand() - 0.3) * 0.6 * s; }
|
|
171
|
-
const want = E.target * still * awake * (1 - clamp(P.earFlat, 0, 1)) * (1 - smooth(0.25, 0.7, Math.abs(P.roll)));
|
|
172
|
-
const om = 14;
|
|
173
|
-
E.v += (om * om * (want - E.sw) - 2 * om * E.v) * dt;
|
|
174
|
-
E.sw += E.v * dt;
|
|
175
|
-
if (Math.abs(E.sw) < 1e-4) continue;
|
|
176
|
-
const base = _p.setFromMatrixPosition(bone.matrixWorld);
|
|
177
|
-
_q.setFromRotationMatrix(bone.matrixWorld);
|
|
178
|
-
_a.set(0, 1, 0).applyQuaternion(_q);
|
|
179
|
-
_q.premultiply(tqa(_a, E.sw));
|
|
180
|
-
bone.matrixWorld.compose(base, _q, _s);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// ------------------------------------------------------------------------------------------------
|
|
185
|
-
// update: locomotion style and idle life over the action layer's parameters
|
|
186
|
-
function update(P, dt, L) {
|
|
187
|
-
const e = L.engine, b = B(e), cfg = e.cfg;
|
|
188
|
-
const vn = e.speed / cfg.sq;
|
|
189
|
-
const run = smooth(0.4, 1.3, vn) * P.gaitW;
|
|
190
|
-
// a head lying on its side (dead, a flop) rests on the cheek and the whisker pad, not in the ground
|
|
191
|
-
const dying = L.postures.reduce((m, p) => (p.name === 'death' ? Math.max(m, p.w) : m), 0);
|
|
192
|
-
const sleeping = L.postures.reduce((m, p) => (p.name === 'sleep' ? Math.max(m, p.w) : m), 0);
|
|
193
|
-
if (P.headW > 0) P.headPos.y += (0.01 * dying + 0.008 * sleeping) * cfg.s;
|
|
194
|
-
P.headPitch -= 0.25 * sleeping;
|
|
195
|
-
// (the small body lying on its side rests on the fur of the flank and the curled paws, not in the ground)
|
|
196
|
-
P.lift += (0.0015 * dying) / cfg.unit;
|
|
197
|
-
// scurrying: the body flattens and stretches, the head reaches forward and low, the ears go back,
|
|
198
|
-
// the tail trails straight out behind
|
|
199
|
-
P.dropF += 0.14 * run; P.dropH += 0.1 * run;
|
|
200
|
-
P.neckReach += (0.004 * run) / cfg.unit;
|
|
201
|
-
P.headRaise -= (0.003 * run) / cfg.unit;
|
|
202
|
-
P.headPitch -= 0.1 * run;
|
|
203
|
-
// sniff / whisk rates: exploratory 6-9 Hz in bursts with pauses; fast whisking while moving slowly
|
|
204
|
-
// and exploring, little when running (whiskers held forward), none asleep
|
|
205
|
-
b.sniffT -= dt;
|
|
206
|
-
if (b.sniffT <= 0) {
|
|
207
|
-
b.sniffT = 0.6 + e.rand() * 1.8;
|
|
208
|
-
const pause = e.rand() < 0.25;
|
|
209
|
-
b.sniffBase = pause ? 0.15 : 0.7 + 0.4 * e.rand();
|
|
210
|
-
b.sniffRate = 6 + e.rand() * 3;
|
|
211
|
-
b.whiskBase = pause ? 0.08 : 0.35 + 0.25 * e.rand();
|
|
212
|
-
}
|
|
213
|
-
const eatW = Math.max(oneshotW(L, 'eat'), oneshotW(L, 'drink'));
|
|
214
|
-
const groomW = oneshotW(L, 'groom');
|
|
215
|
-
const fight = Math.max(oneshotW(L, 'attack'), oneshotW(L, 'box'));
|
|
216
|
-
const lying = L.postures.reduce((m, p) => (p.name === 'sleep' || p.name === 'death' || p.name === 'lie' ? Math.max(m, p.w) : m), 0);
|
|
217
|
-
const quiet = Math.max(eatW, groomW, fight, lying * 0.8, clamp(P.eyelid, 0, 1));
|
|
218
|
-
// (the pose hook reads these)
|
|
219
|
-
b.freezeK = b.freezeT > 0 ? smooth(0, 0.15, b.freezeT) : 0;
|
|
220
|
-
b.whiskAmp = (b.whiskBase ?? 0.5) * (1 - quiet) * (1 - 0.6 * run) * (1 - b.freezeK);
|
|
221
|
-
b.fanT = 0.12 + 0.18 * run - 0.45 * eatW - 0.45 * groomW - 0.5 * fight - 0.3 * lying;
|
|
222
|
-
b.sniffAmp = (b.sniffBase ?? 1) * (1 - quiet * 0.85) * (1 - 0.8 * b.freezeK);
|
|
223
|
-
// head scanning while sniffing at rest (sweeps side to side with the sniffs)
|
|
224
|
-
const idle = L.idle.w * (busy(L) ? 0 : 1);
|
|
225
|
-
b.scanW += ((b.scanOn ? 1 : 0) * idle - b.scanW) * (1 - Math.exp(-dt * 3));
|
|
226
|
-
if (b.scanW > 0.01) {
|
|
227
|
-
b.scanPh += dt * 0.6;
|
|
228
|
-
mx(P, 'headYaw', 0.45 * Math.sin(b.scanPh * TAU) + 0.1 * Math.sin(b.scanPh * TAU * 3.1), b.scanW);
|
|
229
|
-
mx(P, 'headPitch', 0.2 + 0.12 * Math.sin(b.scanPh * TAU * 1.7), b.scanW);
|
|
230
|
-
P.headRaise += (0.0015 * Math.sin(b.sniffPh) * b.sniffAmp * b.scanW) / cfg.unit;
|
|
231
|
-
}
|
|
232
|
-
// scurry steering: a burst along the heading, then a sudden stop and a freeze
|
|
233
|
-
const f = b.scurry;
|
|
234
|
-
if (f) {
|
|
235
|
-
f.t += dt;
|
|
236
|
-
if (f.phase === 'run') {
|
|
237
|
-
e.wantSpeed = f.speed * cfg.sq;
|
|
238
|
-
e.wantHeading = f.h;
|
|
239
|
-
f.dist += e.speed * dt;
|
|
240
|
-
if (f.dist >= f.len || f.t > 4) { f.phase = 'stop'; f.tStop = f.t; e.wantSpeed = 0; }
|
|
241
|
-
} else if (f.phase === 'stop') {
|
|
242
|
-
e.wantSpeed = 0;
|
|
243
|
-
if (e.speed < 0.05 * cfg.sq) { f.phase = 'done'; b.freezeT = 0.5 + e.rand() * 0.8; b.sniffT = b.freezeT; }
|
|
244
|
-
if (f.t - f.tStop > 2) f.phase = 'done';
|
|
245
|
-
}
|
|
246
|
-
if (f.phase === 'done') b.scurry = null;
|
|
247
|
-
}
|
|
248
|
-
if (b.freezeT > 0) {
|
|
249
|
-
b.freezeT -= dt;
|
|
250
|
-
const fz = smooth(0, 0.15, b.freezeT) * P.gaitW;
|
|
251
|
-
mx(P, 'breathRate', 2.5, fz); mx(P, 'earFlat', -0.1, fz); mx(P, 'lookW', 0, fz);
|
|
252
|
-
if (b.freezeT <= 0) { b.sniffBase = 1.2; b.sniffRate = 9; b.whiskBase = 0.6; b.sniffT = 1.2; }
|
|
253
|
-
}
|
|
254
|
-
// rearing to look at a far, raised target (input.look)
|
|
255
|
-
const look = e.input.look;
|
|
256
|
-
if (look && !busy(L) && L.idle.w > 0.9) {
|
|
257
|
-
const dx = look.x - e.pos.x, dz = look.z - e.pos.z, dy = look.y - e.headPose.position.y;
|
|
258
|
-
const far = Math.hypot(dx, dz) > 1.2 * cfg.s && dy > 0.06 * cfg.s;
|
|
259
|
-
if (far) L.play('rear', { loop: true, follow: true });
|
|
260
|
-
}
|
|
261
|
-
// automatic idle variants (standing still, nothing else running, nobody steering the gaze)
|
|
262
|
-
if (e.idleVariants !== false && L.idle.w > 0.98 && !busy(L) && !e.input.look && !e.input.follow) {
|
|
263
|
-
b.idleT -= dt;
|
|
264
|
-
if (b.idleT <= 0) {
|
|
265
|
-
b.idleT = 4 + e.rand() * 6;
|
|
266
|
-
const r = e.rand();
|
|
267
|
-
b.scanOn = false;
|
|
268
|
-
if (r < 0.28) L.play('groom');
|
|
269
|
-
else if (r < 0.5) L.play('rear');
|
|
270
|
-
else if (r < 0.85) { b.scanOn = true; b.sniffBase = 1.2; b.sniffRate = 8.5; b.whiskBase = 0.6; b.sniffT = 2.5; }
|
|
271
|
-
}
|
|
272
|
-
} else if (busy(L)) { b.idleT = Math.max(b.idleT, 2.5); b.scanOn = false; }
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ------------------------------------------------------------------------------------------------
|
|
276
|
-
// posture helpers: sitting up on the haunches (hind feet flat, the body upright, forepaws free)
|
|
277
|
-
// upright: 0 (on all fours) .. 1 (reared, ~70 degrees); the hips sit down onto the heels, the chest
|
|
278
|
-
// rises and the body pitches back over the hind feet
|
|
279
|
-
function sitUp(P, w, e, upright) {
|
|
280
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
281
|
-
mx(P, 'dropF', -2.3 * upright, w);
|
|
282
|
-
mx(P, 'dropH', 0.12, w);
|
|
283
|
-
mx(P, 'flex', -0.35 * upright, w);
|
|
284
|
-
mx(P, 'pitch', 0.12 * upright, w);
|
|
285
|
-
mx(P, 'fwd', (-0.01 * upright * e.cfg.s) / e.cfg.unit, w);
|
|
286
|
-
mx(P, 'groundW', 1, w);
|
|
287
|
-
mx(P, 'tailGround', 0.5, w);
|
|
288
|
-
}
|
|
289
|
-
// envelopes of a sit-up action: up (rising), body (up, but coming down over the last 0.9 s of a timed
|
|
290
|
-
// action) and hands (the forepaws stay off the ground until the chest is down again, so they are never
|
|
291
|
-
// handed back to their plants out of reach)
|
|
292
|
-
function env(inst, w, e, rise = 0.45, t0 = 0, dur = inst.dur) {
|
|
293
|
-
const t = inst.t - t0, cfg = e.cfg;
|
|
294
|
-
const up = w * smooth(0, rise, t);
|
|
295
|
-
const end = Number.isFinite(dur) ? 1 - smooth(dur - 0.95, dur - 0.4, t) : 1;
|
|
296
|
-
const body = up * end;
|
|
297
|
-
const raise = e.fr.shC.y - e.terrainH(e.fr.shC.x, e.fr.shC.z) - cfg.yS;
|
|
298
|
-
const want = Math.min(up, Math.max(body, w * smooth(0.002 * cfg.s, 0.016 * cfg.s, raise)));
|
|
299
|
-
// (eased: a forepaw let go of all at once flicked down to its plant)
|
|
300
|
-
const dt = clamp(inst.t - (inst._ht ?? inst.t), 0, 0.1);
|
|
301
|
-
inst._ht = inst.t;
|
|
302
|
-
if (inst._h === undefined) { inst._h = want; inst._hv = 0; }
|
|
303
|
-
const om = 12;
|
|
304
|
-
inst._hv += (om * om * (want - inst._h) - 2 * om * inst._hv) * dt;
|
|
305
|
-
inst._h = clamp(inst._h + inst._hv * dt, 0, 1);
|
|
306
|
-
const hands = Math.min(up, inst._h);
|
|
307
|
-
return { up, body, hands };
|
|
308
|
-
}
|
|
309
|
-
// forepaw i toward a world point (weight k)
|
|
310
|
-
function paw(P, i, tp, k) {
|
|
311
|
-
const pl = P.legs[i];
|
|
312
|
-
pl.target.lerp(tp, k / Math.max(1e-3, pl.reach + k));
|
|
313
|
-
mxLeg(P, i, 'reach', 1, k);
|
|
314
|
-
}
|
|
315
|
-
// a point in front of the mouth (head frame): fwd / down / side in species metres
|
|
316
|
-
function nearMouth(e, fwd, down, side, out) {
|
|
317
|
-
const s = e.cfg.s;
|
|
318
|
-
const hq = e.headPose.quaternion;
|
|
319
|
-
out.copy(e.cfg.mouthLocal).applyQuaternion(hq).add(e.headPose.position);
|
|
320
|
-
const f = e.forward(e.heading, _d), lat = e.left(e.heading, _a);
|
|
321
|
-
out.addScaledVector(f, fwd * s).addScaledVector(lat, side * s);
|
|
322
|
-
out.y -= down * s;
|
|
323
|
-
return out;
|
|
324
|
-
}
|
|
325
|
-
const stopWhenMoving = (inst, L) => {
|
|
326
|
-
// asked to move: end now (the one-shot fades out; the forepaws come down with the body)
|
|
327
|
-
if (L.engine.wantSpeed > 0.05 && !inst.opts.hold) return true;
|
|
328
|
-
return false;
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
// rear: stands up on the hind feet (soles flat), forepaws tucked against the chest, head high,
|
|
332
|
-
// nose up and sniffing, tail on the ground as a prop; looks around or at the look target
|
|
333
|
-
const rear = {
|
|
334
|
-
kind: 'oneshot', fade: 0.3,
|
|
335
|
-
start(inst, L) {
|
|
336
|
-
const e = L.engine;
|
|
337
|
-
inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 2.8 + e.rand() * 2.5);
|
|
338
|
-
const a = e.heading + (e.rand() - 0.5) * 1.8, d = 3 * e.cfg.s;
|
|
339
|
-
inst.look = inst.opts.target ? inst.opts.target.clone() : new THREE.Vector3(e.pos.x + Math.sin(a) * d, e.pos.y + 0.4 * e.cfg.s, e.pos.z + Math.cos(a) * d);
|
|
340
|
-
},
|
|
341
|
-
update(inst, dt, L) {
|
|
342
|
-
const e = L.engine;
|
|
343
|
-
// following the look input: comes down when the target goes away or comes down / near
|
|
344
|
-
if (inst.opts.follow && !Number.isFinite(inst.dur) && inst.t > 0.8) {
|
|
345
|
-
const lk = e.input.look;
|
|
346
|
-
const ok = lk && Math.hypot(lk.x - e.pos.x, lk.z - e.pos.z) > 1.0 * e.cfg.s && lk.y - e.headPose.position.y > 0.03 * e.cfg.s;
|
|
347
|
-
if (!ok) inst.dur = inst.t + 1.0;
|
|
348
|
-
}
|
|
349
|
-
return inst.t >= inst.dur || stopWhenMoving(inst, L);
|
|
350
|
-
},
|
|
351
|
-
apply(P, w, inst, L) {
|
|
352
|
-
const e = L.engine;
|
|
353
|
-
const { up, body, hands } = env(inst, w, e);
|
|
354
|
-
sitUp(P, body, e, 1);
|
|
355
|
-
for (let i = 0; i < 2; i++) { mxLeg(P, i, 'tuck', 1, hands); P.legs[i].extend = lerp(P.legs[i].extend, 0.2, hands); }
|
|
356
|
-
mx(P, 'headRaise', 0.006 / e.cfg.unit, body); mx(P, 'headPitch', -0.12, body);
|
|
357
|
-
if (!e.input.look) { P.look = inst.look; mx(P, 'lookW', 1, body); }
|
|
358
|
-
mx(P, 'earFlat', -0.15, up);
|
|
359
|
-
mx(P, 'breathRate', 1.6, up);
|
|
360
|
-
},
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// groom: the face-washing chain (Berridge et al. 1987): sits up, small fast elliptical strokes of
|
|
364
|
-
// both forepaws over the nose, larger strokes over the face and behind the ears, then the head turns
|
|
365
|
-
// down to lick the flank
|
|
366
|
-
const GROOM = 4.6;
|
|
367
|
-
const groom = {
|
|
368
|
-
kind: 'oneshot', fade: 0.35,
|
|
369
|
-
start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? GROOM); inst.side = L.engine.rand() < 0.5 ? -1 : 1; },
|
|
370
|
-
update(inst, dt, L) { return inst.t >= inst.dur || stopWhenMoving(inst, L); },
|
|
371
|
-
apply(P, w, inst, L) {
|
|
372
|
-
const e = L.engine, t = inst.opts.loop ? inst.t % GROOM : inst.t;
|
|
373
|
-
const { body, hands } = env(inst, w, e);
|
|
374
|
-
// (looped: the chain restarts; the licking phase eases back into the face strokes)
|
|
375
|
-
const lick = smooth(2.9, 3.3, t) * (1 - smooth(4.0, 4.5, t));
|
|
376
|
-
sitUp(P, body, e, lerp(0.72, 0.35, lick));
|
|
377
|
-
// head: bobs down into the paws with every stroke; turns to the flank for licking
|
|
378
|
-
const big = smooth(1.2, 1.6, t) * (1 - smooth(4.2, 4.5, t));
|
|
379
|
-
const ph = TAU * (5.5 * t - 2.9 * Math.max(0, Math.min(t, 1.6) - 1.2) - 2.9 * Math.max(0, t - 1.6)); // 5.5 -> 2.6 strokes/s, continuous phase
|
|
380
|
-
const face = body * (1 - lick);
|
|
381
|
-
mx(P, 'headPitch', 0.55 + 0.16 * Math.sin(ph), face); mx(P, 'headRaise', -0.004 / e.cfg.unit, face);
|
|
382
|
-
mx(P, 'lookW', 0, body); mx(P, 'eyelid', 0.55, face); mx(P, 'earFlat', 0.3 * big, face);
|
|
383
|
-
const sd = inst.side;
|
|
384
|
-
mx(P, 'headYaw', 1.0 * sd, lick * body); mx(P, 'headPitch', 0.8, lick * body); mx(P, 'bend', 0.3 * sd, lick * body);
|
|
385
|
-
mx(P, 'jaw', 0.12 * Math.max(0, Math.sin(ph * 0.5)), lick * body); mx(P, 'jawOmega', 25, body);
|
|
386
|
-
// forepaws: ellipses over the snout (small, both together), then bigger alternating face strokes;
|
|
387
|
-
// during the licking they rest under the chest
|
|
388
|
-
const tp = tv();
|
|
389
|
-
for (let i = 0; i < 2; i++) {
|
|
390
|
-
const sdI = i === 0 ? 1 : -1;
|
|
391
|
-
const phi = ph + i * PI * big;
|
|
392
|
-
const amp = lerp(0.003, 0.008, big);
|
|
393
|
-
nearMouth(e, 0.004 + 0.3 * amp * Math.cos(phi) - 0.01 * big, 0.004 - amp * Math.sin(phi) - 0.008 * big, sdI * lerp(0.004, 0.008, big), tp);
|
|
394
|
-
const onFace = smooth(0.15, 0.55, inst.t) * (1 - 0.7 * lick);
|
|
395
|
-
paw(P, i, tp, hands * onFace);
|
|
396
|
-
mxLeg(P, i, 'tuck', 1, hands * (1 - onFace));
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
// eat: sits up on the haunches holding the food in both forepaws in front of the mouth, gnawing it
|
|
402
|
-
// with fast incisor strokes (~6 Hz) and turning it; now and then lowers it and looks around
|
|
403
|
-
const eat = {
|
|
404
|
-
kind: 'oneshot', fade: 0.4,
|
|
405
|
-
start(inst) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 6); },
|
|
406
|
-
update(inst, dt, L) { return inst.t >= inst.dur || stopWhenMoving(inst, L); },
|
|
407
|
-
apply(P, w, inst, L) {
|
|
408
|
-
const e = L.engine, t = inst.t;
|
|
409
|
-
const { up, body, hands } = env(inst, w, e, 0.5);
|
|
410
|
-
// pauses: every ~2.7 s the paws drop a little and the head comes up to look around
|
|
411
|
-
const cyc = t % 2.7, pause = smooth(1.9, 2.1, cyc) * (1 - smooth(2.45, 2.65, cyc));
|
|
412
|
-
sitUp(P, body, e, 0.72);
|
|
413
|
-
mx(P, 'headPitch', lerp(0.45, 0.05, pause), body); mx(P, 'lookW', 0, body * (1 - pause));
|
|
414
|
-
const g = Math.max(0, Math.sin(t * TAU * 6)) * (1 - pause);
|
|
415
|
-
mx(P, 'jaw', 0.16 * g, body); mx(P, 'jawOmega', 40, up);
|
|
416
|
-
mx(P, 'earFlat', 0.1, up);
|
|
417
|
-
const tp = tv();
|
|
418
|
-
const turn = Math.sin(t * TAU * 0.4);
|
|
419
|
-
for (let i = 0; i < 2; i++) {
|
|
420
|
-
const sdI = i === 0 ? 1 : -1;
|
|
421
|
-
nearMouth(e, 0.006, 0.006 + 0.008 * pause + 0.0015 * turn * sdI, sdI * 0.0045, tp);
|
|
422
|
-
const k = smooth(0.25, 0.7, t);
|
|
423
|
-
paw(P, i, tp, hands * k);
|
|
424
|
-
mxLeg(P, i, 'tuck', 1, hands * (1 - k));
|
|
425
|
-
}
|
|
426
|
-
},
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
// scurry: a fast burst (0.3-1.5 m) along the heading or away from a point, a sudden stop, a freeze
|
|
430
|
-
const scurry = {
|
|
431
|
-
kind: 'oneshot', fade: 0.15,
|
|
432
|
-
start(inst, L) {
|
|
433
|
-
const e = L.engine, b = B(e);
|
|
434
|
-
let h = e.heading + (e.rand() - 0.5) * 0.6;
|
|
435
|
-
if (inst.opts.from) h = Math.atan2(e.pos.x - inst.opts.from.x, e.pos.z - inst.opts.from.z);
|
|
436
|
-
if (inst.opts.heading !== undefined) h = inst.opts.heading;
|
|
437
|
-
const len = (inst.opts.distance ?? 0.4 + e.rand() * 1.0) * e.cfg.s;
|
|
438
|
-
b.scurry = { t: 0, phase: 'run', h, len, dist: 0, speed: inst.opts.speed ?? 1.5 };
|
|
439
|
-
inst.dur = Infinity;
|
|
440
|
-
},
|
|
441
|
-
update(inst, dt, L) { return !B(L.engine).scurry; },
|
|
442
|
-
apply(P, w) { mx(P, 'earFlat', 0.5, w); },
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
// box: rears up on the hind legs facing the opponent and pushes / strikes with the forepaws
|
|
446
|
-
const BOX_HITS = [0.5, 0.68, 0.98];
|
|
447
|
-
function boxApply(P, w, inst, L, t0, dur) {
|
|
448
|
-
const e = L.engine, t = inst.t - t0;
|
|
449
|
-
const { body, hands } = env(inst, w, e, 0.35, t0, dur);
|
|
450
|
-
sitUp(P, body, e, 0.95);
|
|
451
|
-
mx(P, 'bodyOmega', 1.8, body);
|
|
452
|
-
mx(P, 'headPitch', -0.15, body); mx(P, 'earFlat', 1, body);
|
|
453
|
-
const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
|
|
454
|
-
for (let i = 0; i < 2; i++) {
|
|
455
|
-
const tt = t - (i === 0 ? 0.38 : 0.56);
|
|
456
|
-
const strike = Math.pow(Math.sin(PI * clamp(tt / 0.3, 0, 1)), 2) + 0.5 * Math.pow(Math.sin(PI * clamp((tt - 0.42) / 0.3, 0, 1)), 2);
|
|
457
|
-
const tp = tv().copy(e.fr.shC).addScaledVector(fwd, (0.024 + 0.02 * strike) * e.cfg.s).addScaledVector(lat, (i === 0 ? 1 : -1) * 0.008 * e.cfg.s);
|
|
458
|
-
tp.y += (-0.012 + 0.012 * strike) * e.cfg.s;
|
|
459
|
-
if (inst.target) tp.lerp(tv().copy(inst.target).setY(Math.max(inst.target.y, tp.y)), 0.15 * strike);
|
|
460
|
-
const k = smooth(0.1, 0.3, t);
|
|
461
|
-
paw(P, i, tp, hands * k);
|
|
462
|
-
mxLeg(P, i, 'tuck', 1, hands * (1 - k));
|
|
463
|
-
}
|
|
464
|
-
mx(P, 'jaw', 0.25, body * smooth(0.1, 0.25, t)); mx(P, 'jawOmega', 30, body);
|
|
465
|
-
}
|
|
466
|
-
function boxHits(inst, L, t0) {
|
|
467
|
-
const e = L.engine;
|
|
468
|
-
while (inst.hits < BOX_HITS.length && inst.t - t0 >= BOX_HITS[inst.hits]) {
|
|
469
|
-
inst.hits++;
|
|
470
|
-
const p = new THREE.Vector3().copy(e.fr.shC).addScaledVector(e.forward(e.heading, new THREE.Vector3()), 0.04 * e.cfg.s);
|
|
471
|
-
e.emit('attackHit', { position: p, direction: e.forward(e.heading, new THREE.Vector3()), style: 'box' });
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
const box = {
|
|
475
|
-
kind: 'oneshot', fade: 0.2, needsStand: true,
|
|
476
|
-
start(inst) { inst.dur = inst.opts.duration ?? 2.2; inst.hits = 0; inst.target = inst.opts.target ? inst.opts.target.clone() : null; },
|
|
477
|
-
update(inst, dt, L) { boxHits(inst, L, 0); return inst.t >= inst.dur; },
|
|
478
|
-
apply(P, w, inst, L) {
|
|
479
|
-
if (inst.target) { P.look = inst.target; mx(P, 'lookW', 1, w); }
|
|
480
|
-
boxApply(P, w, inst, L, 0, inst.dur);
|
|
481
|
-
},
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
// attack: a lunge bite (the engine's bite-lunge, jaw wide on the incisors), or, standing still and
|
|
485
|
-
// about half the time (opts.style 'box' / 'bite'), boxing on the hind legs followed by the bite
|
|
486
|
-
const BOX_T = 1.9; // boxing phase (rear, strikes, down on all fours) before the bite
|
|
487
|
-
const attack = {
|
|
488
|
-
kind: 'oneshot', fade: 0.15, needsStand: true,
|
|
489
|
-
start(inst, L) {
|
|
490
|
-
const e = L.engine;
|
|
491
|
-
const still = e.speed < 0.3 * e.cfg.sq;
|
|
492
|
-
inst.boxing = inst.opts.style === 'box' || (inst.opts.style !== 'bite' && still && e.rand() < 0.5);
|
|
493
|
-
inst.t0 = inst.boxing ? BOX_T : 0;
|
|
494
|
-
inst.target = inst.opts.target ? inst.opts.target.clone() : null;
|
|
495
|
-
inst.hits = inst.boxing ? 0 : BOX_HITS.length;
|
|
496
|
-
ONESHOTS.attack.start(inst, L);
|
|
497
|
-
inst.style = 'bite-lunge';
|
|
498
|
-
inst.biteDur = inst.dur;
|
|
499
|
-
inst.dur += inst.t0;
|
|
500
|
-
},
|
|
501
|
-
update(inst, dt, L) {
|
|
502
|
-
boxHits(inst, L, 0);
|
|
503
|
-
const t = inst.t;
|
|
504
|
-
if (t < inst.t0) return false;
|
|
505
|
-
inst.t = t - inst.t0;
|
|
506
|
-
const r = ONESHOTS.attack.update.call(ONESHOTS.attack, inst, dt, L);
|
|
507
|
-
inst.t = t;
|
|
508
|
-
return r;
|
|
509
|
-
},
|
|
510
|
-
apply(P, w, inst, L) {
|
|
511
|
-
const t = inst.t;
|
|
512
|
-
if (inst.boxing && t < inst.t0 + 0.2) boxApply(P, w, inst, L, 0, inst.t0);
|
|
513
|
-
if (t > inst.t0 - 0.15) {
|
|
514
|
-
inst.t = Math.max(0, t - inst.t0);
|
|
515
|
-
const r0 = [P.legs[0].reach, P.legs[1].reach];
|
|
516
|
-
ONESHOTS.attack.apply(P, w * smooth(inst.t0 - 0.15, inst.t0 + 0.05, t), inst, L);
|
|
517
|
-
// (the short forelegs only half follow the lunge's forepaw strike: a full reach flicked the paws)
|
|
518
|
-
for (let i = 0; i < 2; i++) P.legs[i].reach = r0[i] + (P.legs[i].reach - r0[i]) * 0.55;
|
|
519
|
-
inst.t = t;
|
|
520
|
-
}
|
|
521
|
-
},
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
// hit: the engine's flinch (a light animal: shoved less), then a freeze
|
|
525
|
-
const hit = {
|
|
526
|
-
...ONESHOTS.hit,
|
|
527
|
-
kind: 'oneshot',
|
|
528
|
-
start(inst, L) {
|
|
529
|
-
inst.opts = { ...inst.opts, strength: (inst.opts.strength ?? 1) * 0.6 };
|
|
530
|
-
ONESHOTS.hit.start(inst, L);
|
|
531
|
-
B(L.engine).freezeT = 1.2;
|
|
532
|
-
},
|
|
533
|
-
};
|
|
534
|
-
|
|
535
|
-
export const hooks = {
|
|
536
|
-
pose,
|
|
537
|
-
update,
|
|
538
|
-
actions: { rear, groom, eat, scurry, box, attack, hit },
|
|
539
|
-
};
|