@bitefoundry/retrieviewer 0.0.3 → 0.0.5
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 +11 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +95 -0
- package/DESKTOP-PLATFORMS.md +6 -1
- package/INSTALLING.md +4 -0
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +116 -0
- package/README.md +121 -59
- package/RELEASING.md +33 -13
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/linux-overlay/overlay.py +8 -2
- package/integrations/macos-overlay/overlay.jxa +9 -7
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +7 -6
- package/integrations/windows-overlay/desktop.mjs +2 -1
- package/integrations/windows-overlay/protocol.mjs +13 -7
- package/integrations/windows-overlay/worker.mjs +1 -1
- package/package.json +34 -13
- package/puppy-comfort-sprites.mjs +33 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +140 -0
- package/puppy-companion-life.mjs +58 -18
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-companion-sprites.mjs +4 -2
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-actions.mjs +4 -2
- package/puppy-paper-behaviors.mjs +1 -1
- package/puppy-paper-contact.mjs +4 -2
- package/puppy-paper-seat.mjs +4 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +79 -21
- package/retrieviewer-cli.mjs +8 -6
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +12 -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,521 +0,0 @@
|
|
|
1
|
-
// Dog behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
|
|
2
|
-
//
|
|
3
|
-
// update(P, dt, layer) every frame over the action layer: mood (happy / alert / angry / afraid) from
|
|
4
|
-
// what the dog is doing, which sets the tail wag (rate and amplitude), the tail
|
|
5
|
-
// carriage (raised stiff in an attack, tucked when hit), the ears, hackles and
|
|
6
|
-
// the snarl; panting after running (jaw, tongue); the head tilt; the curled
|
|
7
|
-
// sleep (tail over the nose); automatic idle variants (head tilt, sniffing,
|
|
8
|
-
// pawing the ground, play bow, a happy wag burst)
|
|
9
|
-
// pose(engine, dt) after the engine's solve: the wag itself (any rate: 2-3 Hz relaxed, 4-6 Hz
|
|
10
|
-
// excited, whip-like from the root, biased to the dog's right when happy,
|
|
11
|
-
// Quaranta et al. 2007), the tongue sliding out to pant and lapping when
|
|
12
|
-
// drinking, and the soft drop ears of retrievers (and pups / button ears)
|
|
13
|
-
// swinging as pendulums driven by the head's acceleration and gravity
|
|
14
|
-
// actions greet (wiggling, fast-wagging welcome), playbow, sniff, dig (pawing the
|
|
15
|
-
// ground), hit (yelp and cower, wraps the built-in flinch)
|
|
16
|
-
//
|
|
17
|
-
// Distances are dog metres at the reference size x cfg.s; posture offsets that the engine multiplies by
|
|
18
|
-
// cfg.k (fwd, headRaise, neckReach) are divided by cfg.unit.
|
|
19
|
-
import * as THREE from 'three';
|
|
20
|
-
import { clamp, lerp, smooth, TAU, tv, tq, tqa } from '../../core/motion/common.js';
|
|
21
|
-
import { mx, mxLeg, ONESHOTS, POSTURES, mouthGroundTarget } from '../../core/motion/actions.js';
|
|
22
|
-
|
|
23
|
-
const sm = (w) => w * w * (3 - 2 * w);
|
|
24
|
-
const _q = new THREE.Quaternion(), _q2 = new THREE.Quaternion(), _s = new THREE.Vector3(1, 1, 1);
|
|
25
|
-
const _p = new THREE.Vector3(), _d = new THREE.Vector3(), _a = new THREE.Vector3(), _lat = new THREE.Vector3();
|
|
26
|
-
const _hx = new THREE.Vector3(), _hz = new THREE.Vector3(), _g = new THREE.Vector3(), _qi = new THREE.Quaternion();
|
|
27
|
-
const TAIL_W = [0.5, 0.45, 0.4, 0.36, 0.32, 0.29, 0.27, 0.25]; // share of the wag per tail segment, root -> tip
|
|
28
|
-
const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
|
|
29
|
-
|
|
30
|
-
// tail carriage offsets per variant (rad): raised stiff in an attack, tucked when afraid
|
|
31
|
-
const TAIL_MOOD = { shepherd: { up: 1.25, down: -1.0 }, retriever: { up: 1.0, down: -1.3 }, terrier: { up: 0.3, down: -2.2 } };
|
|
32
|
-
// roll of the curled sleep onto the outer hip (rad; the core's is 0.35). The terrier lies further over:
|
|
33
|
-
// its chest rests on the ground with the shoulders so low that the elbow on the inside of the curl
|
|
34
|
-
// sank 6 mm below its skin's clearance at 0.35 and had to be swung out of the ground (measured:
|
|
35
|
-
// at 0.45 both elbows stay clear unswung on seeds 1-4, the lowest 1.4 mm above it)
|
|
36
|
-
const CURL_ROLL = { terrier: 0.45 };
|
|
37
|
-
|
|
38
|
-
function B(e) {
|
|
39
|
-
if (!e._dog) {
|
|
40
|
-
const v = e.params.variant || 'shepherd';
|
|
41
|
-
const earType = e.params.earType || 'prick';
|
|
42
|
-
e._dog = {
|
|
43
|
-
variant: v, mood: TAIL_MOOD[v] || TAIL_MOOD.shepherd,
|
|
44
|
-
happy: 0.4 + 0.3 * e.rand(), wagPh: 0, wagAmp: 0, wagAmpV: 0, wagFreq: 2.4, wagBias: 0, wagOn: 0,
|
|
45
|
-
tongue: 0, tongueV: 0, lap: 0, lapW: 0, pant: 0,
|
|
46
|
-
tilt: null, idleT: 4 + e.rand() * 6,
|
|
47
|
-
swing: earType === 'drop' ? 1 : earType === 'button' ? 0.45 : 0,
|
|
48
|
-
ear: [{ f: 0, vf: 0, s: 0, vs: 0 }, { f: 0, vf: 0, s: 0, vs: 0 }],
|
|
49
|
-
hp: new THREE.Vector3(), hv: new THREE.Vector3(), ha: new THREE.Vector3(), hInit: 0,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return e._dog;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// ------------------------------------------------------------------------------------------------
|
|
56
|
-
// update: mood, tail, ears, panting, head tilt, idle variants
|
|
57
|
-
function update(P, dt, L) {
|
|
58
|
-
const e = L.engine, b = B(e), cfg = e.cfg;
|
|
59
|
-
const vn = e.speed / cfg.sq;
|
|
60
|
-
const moving = smooth(0.05, 0.8, vn) * P.gaitW;
|
|
61
|
-
let attack = 0, hit = 0, greet = 0, bow = 0, eat = 0, drink = 0, sniff = 0;
|
|
62
|
-
for (const a of L.oneshots) {
|
|
63
|
-
const w = sm(a.w);
|
|
64
|
-
if (w <= 0) continue;
|
|
65
|
-
if (a.name === 'attack') {
|
|
66
|
-
attack = Math.max(attack, w);
|
|
67
|
-
const t = a.t;
|
|
68
|
-
// threat -> lunge: hackles up, tail raised stiff, lips drawn back (the jaw opens a little on the
|
|
69
|
-
// crouch so the canines show), a growl
|
|
70
|
-
const snarl = smooth(0.0, 0.12, t) * (1 - smooth(0.24, 0.32, t));
|
|
71
|
-
P.jaw = Math.max(P.jaw, 0.16 * snarl * w);
|
|
72
|
-
mx(P, 'headRaise', -0.03 / cfg.unit, snarl * w);
|
|
73
|
-
if (!a._voc) { a._voc = true; e.emit('vocalize', { kind: 'snarl' }); }
|
|
74
|
-
} else if (a.name === 'hit') hit = Math.max(hit, w * (1 - smooth(0.9, 1.5, a.t)));
|
|
75
|
-
else if (a.name === 'greet') greet = Math.max(greet, w);
|
|
76
|
-
else if (a.name === 'playbow') bow = Math.max(bow, w);
|
|
77
|
-
else if (a.name === 'eat') eat = Math.max(eat, w);
|
|
78
|
-
else if (a.name === 'drink') { drink = Math.max(drink, w); b.lapT = a.t; }
|
|
79
|
-
else if (a.name === 'sniff' || a.name === 'dig') sniff = Math.max(sniff, w);
|
|
80
|
-
}
|
|
81
|
-
let asleep = 0, dead = 0, lying = 0, sitting = 0;
|
|
82
|
-
for (const p of L.postures) {
|
|
83
|
-
const w = sm(clamp(p.w, 0, 1));
|
|
84
|
-
if (p.name === 'sleep') asleep = Math.max(asleep, w * smooth(1.0, 3.0, p.t));
|
|
85
|
-
if (p.name === 'death') dead = Math.max(dead, w);
|
|
86
|
-
if (p.name === 'lie' || p.name === 'sleep') lying = Math.max(lying, w);
|
|
87
|
-
if (p.name === 'sit') sitting = Math.max(sitting, w);
|
|
88
|
-
// curled sleep: the tail is drawn round over the nose and forepaws
|
|
89
|
-
if (p.name === 'sleep' && p.tune.style !== 'lateral') {
|
|
90
|
-
const c = w * smooth(1.2, 3.2, p.t);
|
|
91
|
-
mx(P, 'tailSide', p.side * 3.0, c); mx(P, 'tailStiff', 0.7, c); mx(P, 'bend', p.side * 1.35, c); mx(P, 'headYaw', p.side * 0.9, c); mx(P, 'headRoll', p.side * 0.3, c);
|
|
92
|
-
if (CURL_ROLL[b.variant]) mx(P, 'roll', p.side * CURL_ROLL[b.variant], c);
|
|
93
|
-
}
|
|
94
|
-
// (the head lies on its side on the cheek and the lower ear: the occiput rests higher than the core's)
|
|
95
|
-
if (p.name === 'death') { const c = w * smooth(0.3, 1.0, p.t); mx(P, 'earFlat', 1.1, c); mx(P, 'earTwitch', 0, c); P.headPos.y += (0.035 + 0.1 * Math.max(0, (e.params.headScale || 1) * (e.params.headW || 1) - 1)) * cfg.s * c; }
|
|
96
|
-
if (p.name === 'sleep') { const c = w * smooth(1.0, 3.0, p.t); P.headPos.y += (0.03 + 0.22 * Math.max(0, (e.params.headScale || 1) * (e.params.headW || 1) - 1)) * cfg.s * c; mx(P, 'headPitch', 0.55, c); }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// eating: the mouth works just above the food (the jaw opens below the closed-mouth target)
|
|
100
|
-
if (eat > 0) { P.headPos.y += 0.02 * cfg.s * eat; P.jaw = Math.min(P.jaw, 0.22); }
|
|
101
|
-
|
|
102
|
-
// ---- mood: happiness rises when greeting / playing, fades slowly; fear and anger override it
|
|
103
|
-
b.happy = clamp(b.happy + dt * (0.5 * Math.max(greet, bow) - 0.012) - dt * 0.6 * (attack + hit), 0.15, 1);
|
|
104
|
-
|
|
105
|
-
// ---- tail carriage by mood
|
|
106
|
-
mx(P, 'tailLift', b.mood.up, attack); mx(P, 'tailStiff', 2.0, attack); mx(P, 'tailIdle', 0.2, attack);
|
|
107
|
-
mx(P, 'tailLift', b.mood.down, hit); mx(P, 'tailStiff', 1.6, hit); mx(P, 'tailIdle', 0, hit);
|
|
108
|
-
// happy: carried a little higher (never above the back in a shepherd)
|
|
109
|
-
const up = Math.max(greet, bow) * (b.variant === 'terrier' ? 0.1 : 0.55);
|
|
110
|
-
if (up > 0) P.tailLift += up;
|
|
111
|
-
P.furRaise = Math.max(P.furRaise || 0, attack * 0.8);
|
|
112
|
-
|
|
113
|
-
// ---- wag: target amplitude (rad at the root segment) and rate
|
|
114
|
-
let amp = 0, freq = 2.4;
|
|
115
|
-
const idleW = L.idle.w;
|
|
116
|
-
// relaxed: a loose wag while standing about, faster and wider the happier the dog is
|
|
117
|
-
amp = idleW * (0.1 + 0.35 * b.happy) * (b.wagOn > 0 ? 1 : 0.35);
|
|
118
|
-
freq = lerp(2.0, 3.0, b.happy);
|
|
119
|
-
// on the move: a small wag at walk and trot, none at the gallop
|
|
120
|
-
amp = Math.max(amp, moving * (1 - smooth(1.5, 4, vn)) * 0.12 * b.happy);
|
|
121
|
-
// sitting: the tail sweeps the ground
|
|
122
|
-
amp = Math.max(amp, sitting * 0.25 * b.happy);
|
|
123
|
-
// greeting / play bow: fast, wide, whole-rear wag
|
|
124
|
-
if (greet + bow > 0) { amp = lerp(amp, 0.62, Math.max(greet, bow)); freq = lerp(freq, 5.2, Math.max(greet, bow)); }
|
|
125
|
-
if (sniff > 0) { amp = lerp(amp, 0.18, sniff); freq = lerp(freq, 3.2, sniff); }
|
|
126
|
-
amp *= (1 - attack) * (1 - hit) * (1 - asleep) * (1 - dead) * (1 - 0.8 * lying) * (1 - 0.6 * eat);
|
|
127
|
-
// attack: the raised tail quivers ("flagging")
|
|
128
|
-
amp += attack * 0.05; if (attack > 0.3) freq = lerp(freq, 7, attack);
|
|
129
|
-
b.wagAmpT = amp; b.wagFreqT = freq;
|
|
130
|
-
// occasional bursts of wagging while idle
|
|
131
|
-
b.wagOn -= dt;
|
|
132
|
-
if (b.wagOn < -3 - 6 * (1 - b.happy) && idleW > 0.5) b.wagOn = 1.5 + 3 * e.rand();
|
|
133
|
-
|
|
134
|
-
// ---- panting after running (the engine's heat), jaw wider, tongue out
|
|
135
|
-
const hot = smooth(0.12, 0.45, e.heat) * (1 - smooth(5, 8, vn));
|
|
136
|
-
b.pant = hot * (1 - attack) * (1 - asleep) * (1 - dead) * (1 - eat) * (1 - drink) * (1 - sniff);
|
|
137
|
-
if (b.pant > 0.01) { P.jaw += 0.13 * b.pant; mx(P, 'breathAmp', 1.5, b.pant); }
|
|
138
|
-
// tongue: panting, a happy greeting, lapping
|
|
139
|
-
b.tongueT = Math.max(b.pant, 0.35 * greet * b.happy, 0.25 * bow);
|
|
140
|
-
b.lapW = drink;
|
|
141
|
-
|
|
142
|
-
// ---- ears
|
|
143
|
-
// alert (a look target or sniffing): pricked forward; greeting: laid softly back; attack: core flattens
|
|
144
|
-
if (e.input.look && attack < 0.01) mx(P, 'earFlat', -0.2, clamp(P.lookW, 0, 1));
|
|
145
|
-
mx(P, 'earFlat', 0.45, greet); mx(P, 'earFlat', -0.15, bow + sniff);
|
|
146
|
-
mx(P, 'earFlat', 1.1, hit);
|
|
147
|
-
|
|
148
|
-
// ---- head tilt (idle variant): the head rolls 20-25 deg to one side, ears pricked, sometimes twice
|
|
149
|
-
const tl = b.tilt;
|
|
150
|
-
if (tl) {
|
|
151
|
-
tl.t += dt;
|
|
152
|
-
const k = smooth(0, 0.35, tl.t) * (1 - smooth(tl.dur, tl.dur + 0.4, tl.t)) * idleW;
|
|
153
|
-
mx(P, 'headRoll', tl.side * 0.42, k); mx(P, 'headYaw', -tl.side * 0.12, k); mx(P, 'headPitch', -0.08, k);
|
|
154
|
-
mx(P, 'earFlat', -0.25, k);
|
|
155
|
-
if (tl.t > tl.dur + 0.4) b.tilt = tl.again ? { t: -0.2, dur: 0.8 + 0.6 * e.rand(), side: -tl.side, again: false } : null;
|
|
156
|
-
if (idleW < 0.3) b.tilt = null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ---- automatic idle variants (standing still, nothing else running, nobody steering the gaze)
|
|
160
|
-
if (e.idleVariants !== false && idleW > 0.98 && !busy(L) && !e.input.look && !e.input.follow && !b.tilt) {
|
|
161
|
-
b.idleT -= dt;
|
|
162
|
-
if (b.idleT <= 0) {
|
|
163
|
-
b.idleT = 5 + e.rand() * 9;
|
|
164
|
-
const r = e.rand();
|
|
165
|
-
if (r < 0.34) b.tilt = { t: 0, dur: 1.0 + 1.2 * e.rand(), side: e.rand() < 0.5 ? -1 : 1, again: e.rand() < 0.4 };
|
|
166
|
-
else if (r < 0.54) L.play('sniff', { duration: 2.5 + 2 * e.rand() });
|
|
167
|
-
else if (r < 0.66) L.play('playbow');
|
|
168
|
-
else if (r < 0.76) L.play('dig', { duration: 1.8 + e.rand() });
|
|
169
|
-
else b.wagOn = 2 + 2 * e.rand(), b.happy = Math.min(1, b.happy + 0.2);
|
|
170
|
-
}
|
|
171
|
-
} else if (busy(L)) b.idleT = Math.max(b.idleT, 3);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ------------------------------------------------------------------------------------------------
|
|
175
|
-
// pose: wag, tongue, swinging ears (after the engine wrote every bone)
|
|
176
|
-
function pose(e, dt) {
|
|
177
|
-
const b = B(e), cfg = e.cfg;
|
|
178
|
-
// wag state (critically damped amplitude, soft rate changes)
|
|
179
|
-
const om = 7;
|
|
180
|
-
b.wagAmpV += (om * om * ((b.wagAmpT || 0) - b.wagAmp) - 2 * om * b.wagAmpV) * dt;
|
|
181
|
-
b.wagAmp += b.wagAmpV * dt;
|
|
182
|
-
b.wagFreq += ((b.wagFreqT || 2.4) - b.wagFreq) * (1 - Math.exp(-dt * 3));
|
|
183
|
-
b.wagPh = (b.wagPh + dt * TAU * b.wagFreq) % (TAU * 1000);
|
|
184
|
-
// right-biased when positive (the dog's right = -x: negative yaw), smoothed (no steps)
|
|
185
|
-
const biasT = -0.18 * b.happy * Math.max(0, b.wagAmp);
|
|
186
|
-
b.biasV = (b.biasV || 0) + (om * om * (biasT - b.wagBias) - 2 * om * (b.biasV || 0)) * dt;
|
|
187
|
-
b.wagBias += b.biasV * dt;
|
|
188
|
-
const A = b.wagAmp;
|
|
189
|
-
const lod = e.lod;
|
|
190
|
-
// (crowds: only a real wag, not the relaxed idle sway)
|
|
191
|
-
const minA = lod >= 2 ? 0.12 : 1e-4;
|
|
192
|
-
if ((Math.abs(A) > minA || Math.abs(b.wagBias) > minA) && e.tail && e.tail.n) wagTail(e, b, A);
|
|
193
|
-
// tongue (not in crowds)
|
|
194
|
-
if (lod < 2) { clearJaw(e, b, dt); tongue(e, b, dt); }
|
|
195
|
-
if (b.swing > 0 && lod < 2) swingEars(e, b, dt);
|
|
196
|
-
void cfg;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// rotate the tail chain from the root: each segment turns about its own dorsal axis (lateral bending)
|
|
200
|
-
function wagTail(e, b, A) {
|
|
201
|
-
const n = e.tail.n, T = e.cfg.tail;
|
|
202
|
-
const rad0 = T.radius[0] * e.cfg.s, rad1 = T.radius[1] * e.cfg.s;
|
|
203
|
-
_lat.set(1, 0, 0).applyQuaternion(e.fr.qPel);
|
|
204
|
-
_q2.identity(); // cumulative rotation
|
|
205
|
-
let px = 0, py = 0, pz = 0; // accumulated new head position
|
|
206
|
-
for (let i = 0; i < n; i++) {
|
|
207
|
-
const bone = e.bone['tail' + i];
|
|
208
|
-
if (!bone) return;
|
|
209
|
-
const m = bone.matrixWorld;
|
|
210
|
-
_p.setFromMatrixPosition(m);
|
|
211
|
-
_q.setFromRotationMatrix(m);
|
|
212
|
-
if (i === 0) { px = _p.x; py = _p.y; pz = _p.z; }
|
|
213
|
-
_d.set(0, 1, 0).applyQuaternion(_q); // original direction
|
|
214
|
-
// the segment's dorsal axis: lat x dir, plus the pelvis' up axis orthogonal to the segment (never
|
|
215
|
-
// zero together, and both point the same way: continuous for a hanging, level, erect or curled tail)
|
|
216
|
-
const ax = tv().crossVectors(_lat, _d);
|
|
217
|
-
const upx = tv(0, 1, 0).applyQuaternion(e.fr.qPel);
|
|
218
|
-
ax.add(upx.addScaledVector(_d, -upx.dot(_d))).normalize();
|
|
219
|
-
const a = (A * Math.sin(b.wagPh - 0.38 * i) + b.wagBias) * TAIL_W[Math.min(i, TAIL_W.length - 1)];
|
|
220
|
-
_q2.multiply(tqa(ax, a));
|
|
221
|
-
const len = e.tail.len[i];
|
|
222
|
-
const newQ = tqa(ax, 0).copy(_q2).multiply(_q);
|
|
223
|
-
const dn = tv(0, 1, 0).applyQuaternion(newQ);
|
|
224
|
-
// the wag never swings a segment into the ground (a tail lying on it when sitting or lying):
|
|
225
|
-
// raise the segment's slope just enough, keeping its heading; the rest of the tail follows
|
|
226
|
-
const floor = e.terrainH(px + dn.x * len, pz + dn.z * len) + lerp(rad0, rad1, (i + 1) / n);
|
|
227
|
-
if (py + dn.y * len < floor) {
|
|
228
|
-
const dy = clamp((floor - py) / len, -1, 1), hl = Math.hypot(dn.x, dn.z) || 1, c = Math.sqrt(1 - dy * dy);
|
|
229
|
-
const d2 = tv(dn.x / hl * c, dy, dn.z / hl * c);
|
|
230
|
-
const fix = tq().setFromUnitVectors(dn, d2);
|
|
231
|
-
newQ.premultiply(fix); _q2.premultiply(fix);
|
|
232
|
-
dn.copy(d2);
|
|
233
|
-
}
|
|
234
|
-
m.compose(tv(px, py, pz), newQ, _s);
|
|
235
|
-
px += dn.x * len; py += dn.y * len; pz += dn.z * len;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// the lower jaw never enters the ground (the core clears the closed mouth; an open jaw reaches lower
|
|
240
|
-
// when tearing food or panting with the head down): close it about the hinge just enough, springy
|
|
241
|
-
function clearJaw(e, b, dt) {
|
|
242
|
-
const jaw = e.bone.jaw;
|
|
243
|
-
if (!jaw) return;
|
|
244
|
-
const s = e.cfg.s, m = jaw.matrixWorld;
|
|
245
|
-
_p.setFromMatrixPosition(m);
|
|
246
|
-
_q.setFromRotationMatrix(m);
|
|
247
|
-
_d.set(0, 1, 0).applyQuaternion(_q);
|
|
248
|
-
const len = b.jawLen || (b.jawLen = e.J.jawHinge.distanceTo(e.J.jawTip));
|
|
249
|
-
let need = 0;
|
|
250
|
-
// chin (under the jaw tip) and the middle of the mandible's lower edge
|
|
251
|
-
for (const [t, r] of [[1, 0.012], [0.55, 0.02]]) {
|
|
252
|
-
const pt = tv().copy(_p).addScaledVector(_d, len * t);
|
|
253
|
-
const lack = e.terrainH(pt.x, pt.z) + r * s - pt.y;
|
|
254
|
-
if (lack > 0) need = Math.max(need, Math.asin(clamp(lack / (len * t), 0, 1)));
|
|
255
|
-
}
|
|
256
|
-
// rate-limited release (no snap back when the head lifts)
|
|
257
|
-
b.jawClr = Math.max(need, (b.jawClr || 0) - dt * 2);
|
|
258
|
-
if (b.jawClr < 1e-4) return;
|
|
259
|
-
_hx.set(1, 0, 0).applyQuaternion(_q);
|
|
260
|
-
// (closing = rotating the jaw's +Y up about its lateral axis: toward the skull)
|
|
261
|
-
_q.premultiply(tqa(_hx, -b.jawClr));
|
|
262
|
-
m.compose(_p, _q, _s);
|
|
263
|
-
// the tongue rides on the jaw: re-derive it from the corrected jaw
|
|
264
|
-
const tb = e.bone.tongue;
|
|
265
|
-
if (tb) for (const x of e.extraBones) if (x.bone === tb) tb.matrixWorld.multiplyMatrices(m, x.rel);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// tongue: slides out over the lower incisors and hangs (panting), or ladles in and out (lapping)
|
|
269
|
-
function tongue(e, b, dt) {
|
|
270
|
-
const bone = e.bone.tongue;
|
|
271
|
-
if (!bone) return;
|
|
272
|
-
// lapping: ~3 laps / s in phase with the built-in drink's jaw (tongue curled back into a ladle)
|
|
273
|
-
let tgt = b.tongueT || 0, droop = 0.55;
|
|
274
|
-
if (b.lapW > 0.01) {
|
|
275
|
-
const lap = Math.max(0, Math.sin((b.lapT || 0) * 3 * TAU));
|
|
276
|
-
tgt = lerp(tgt, 0.3 + 0.45 * lap, b.lapW);
|
|
277
|
-
droop = lerp(droop, 0.15 + 0.35 * lap, b.lapW);
|
|
278
|
-
}
|
|
279
|
-
const om = b.lapW > 0.01 ? 30 : 9;
|
|
280
|
-
b.tongueV += (om * om * (tgt - b.tongue) - 2 * om * b.tongueV) * dt;
|
|
281
|
-
b.tongue += b.tongueV * dt;
|
|
282
|
-
const out = clamp(b.tongue, 0, 1.2);
|
|
283
|
-
if (out < 1e-3) return;
|
|
284
|
-
const s = e.cfg.s;
|
|
285
|
-
const m = bone.matrixWorld;
|
|
286
|
-
_p.setFromMatrixPosition(m);
|
|
287
|
-
_q.setFromRotationMatrix(m);
|
|
288
|
-
_d.set(0, 1, 0).applyQuaternion(_q);
|
|
289
|
-
_hx.set(1, 0, 0).applyQuaternion(_q);
|
|
290
|
-
// slide forward, then droop about the lower incisors; the hanging part bobs with the breath
|
|
291
|
-
const slide = 0.058 * s * out;
|
|
292
|
-
const pivotAlong = 0.11 * s;
|
|
293
|
-
const ang = droop * smooth(0, 0.6, out) * (1 + 0.12 * Math.sin(e.breath));
|
|
294
|
-
const base0 = tv().copy(_p), pivot = tv().copy(_p).addScaledVector(_d, pivotAlong);
|
|
295
|
-
// (the tip never enters the ground: the tongue first slides back in just enough (the tip's height is
|
|
296
|
-
// linear in the slide, well conditioned while the tongue points down, as when lapping with the head
|
|
297
|
-
// low), then the droop is reduced, down to curling the tongue up; both by bisection, continuous in
|
|
298
|
-
// the pose. Reducing only the droop near the bottom of its arc swung the tongue ~0.6 rad in 10 ms.)
|
|
299
|
-
const len = e.J.tongueBase.distanceTo(e.J.tongueTip), p0 = tv();
|
|
300
|
-
const tipY = (a, sl) => {
|
|
301
|
-
const R = tqa(_hx, a);
|
|
302
|
-
p0.copy(base0).addScaledVector(_d, sl);
|
|
303
|
-
const base = tv().subVectors(p0, pivot).applyQuaternion(R).add(pivot);
|
|
304
|
-
const tip = tv().copy(_d).applyQuaternion(R).multiplyScalar(len).add(base);
|
|
305
|
-
return tip.y - (e.terrainH(tip.x, tip.z) + 0.004 * s); // (it may dip into water: lapping)
|
|
306
|
-
};
|
|
307
|
-
let A = ang, sl = slide;
|
|
308
|
-
if (tipY(A, sl) < 0) {
|
|
309
|
-
if (tipY(A, 0) >= 0) {
|
|
310
|
-
// the longest feasible slide
|
|
311
|
-
let lo = 0, hi = sl;
|
|
312
|
-
for (let i = 0; i < 14; i++) { const mid = (lo + hi) / 2; if (tipY(A, mid) >= 0) lo = mid; else hi = mid; }
|
|
313
|
-
sl = lo;
|
|
314
|
-
} else {
|
|
315
|
-
// fully in, and the largest feasible droop below A: scan down, then refine between the last two samples
|
|
316
|
-
sl = 0;
|
|
317
|
-
let hi = A, lo = A;
|
|
318
|
-
const step = (A + 0.9) / 24;
|
|
319
|
-
for (let i = 1; i <= 24; i++) { lo = A - step * i; if (tipY(lo, 0) >= 0) break; hi = lo; }
|
|
320
|
-
for (let i = 0; i < 12; i++) { const mid = (lo + hi) / 2; if (tipY(mid, 0) >= 0) lo = mid; else hi = mid; }
|
|
321
|
-
A = lo;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
p0.copy(base0).addScaledVector(_d, sl);
|
|
325
|
-
A *= smooth(0, 0.25, out); // (retracted: rigid on the jaw, whatever the clamp says)
|
|
326
|
-
const R = tqa(_hx, A);
|
|
327
|
-
const rel = tv().subVectors(p0, pivot).applyQuaternion(R);
|
|
328
|
-
_p.copy(pivot).add(rel);
|
|
329
|
-
_q.premultiply(R);
|
|
330
|
-
m.compose(_p, _q, _s);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// soft drop ears: two damped pendulum angles per ear in the head frame (fore-aft about the head's
|
|
334
|
-
// lateral axis, outward about its long axis), driven by the head's acceleration and by gravity
|
|
335
|
-
function swingEars(e, b, dt) {
|
|
336
|
-
if (!(dt > 1e-5)) return;
|
|
337
|
-
const hp = e.headPose.position, hq = e.headPose.quaternion;
|
|
338
|
-
if (b.hInit < 2) {
|
|
339
|
-
if (b.hInit === 1) b.hv.subVectors(hp, b.hp).multiplyScalar(1 / dt);
|
|
340
|
-
b.hp.copy(hp); b.hInit++;
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const v = tv().subVectors(hp, b.hp).multiplyScalar(1 / dt);
|
|
344
|
-
const acc = tv().subVectors(v, b.hv).multiplyScalar(1 / dt);
|
|
345
|
-
b.hp.copy(hp); b.hv.copy(v);
|
|
346
|
-
// low-pass the acceleration (finite differences of a stepped body are noisy)
|
|
347
|
-
b.ha.lerp(acc.clampLength(0, 60), 1 - Math.exp(-dt * 30));
|
|
348
|
-
_qi.copy(hq).invert();
|
|
349
|
-
_a.copy(b.ha).applyQuaternion(_qi); // head-local acceleration
|
|
350
|
-
_g.set(0, -1, 0).applyQuaternion(_qi); // gravity direction in the head frame
|
|
351
|
-
const s = e.cfg.s;
|
|
352
|
-
const lenE = 0.09 * s;
|
|
353
|
-
const om = 13 / Math.sqrt(s), z = 0.28;
|
|
354
|
-
const K = b.swing;
|
|
355
|
-
_hx.set(1, 0, 0).applyQuaternion(hq); _hz.set(0, 0, 1).applyQuaternion(hq);
|
|
356
|
-
const sub = Math.max(1, Math.ceil(dt / (1 / 120))), h = dt / sub;
|
|
357
|
-
for (let k = 0; k < 2; k++) {
|
|
358
|
-
const E = b.ear[k], side = k === 0 ? 1 : -1;
|
|
359
|
-
// equilibrium: hang along gravity (a lowered or tilted head swings the flaps), outward flop when the
|
|
360
|
-
// head drops fast (trot bounce)
|
|
361
|
-
const tf = -0.8 * Math.asin(clamp(_g.z, -1, 1));
|
|
362
|
-
let ts = 0.8 * Math.asin(clamp(_g.x, -1, 1));
|
|
363
|
-
for (let i = 0; i < sub; i++) {
|
|
364
|
-
const af = om * om * (tf - E.f) - 2 * z * om * E.vf + (_a.z / lenE) * 0.9;
|
|
365
|
-
const as = om * om * (ts - E.s) - 2 * z * om * E.vs - (_a.x / lenE) * 0.9 - side * Math.min(0, _a.y) / lenE * 0.35;
|
|
366
|
-
E.vf += af * h; E.f += E.vf * h;
|
|
367
|
-
E.vs += as * h; E.s += E.vs * h;
|
|
368
|
-
// the flap cannot swing into the cheek; limits
|
|
369
|
-
if (side * E.s < -0.04) { E.s = -0.04 * side; if (side * E.vs < 0) E.vs = 0; }
|
|
370
|
-
E.f = clamp(E.f, -0.55, 0.55); E.s = clamp(E.s, -0.65, 0.65);
|
|
371
|
-
}
|
|
372
|
-
const bone = e.bone[k === 0 ? 'earL' : 'earR'];
|
|
373
|
-
if (!bone) continue;
|
|
374
|
-
const m = bone.matrixWorld;
|
|
375
|
-
_p.setFromMatrixPosition(m);
|
|
376
|
-
_q.setFromRotationMatrix(m);
|
|
377
|
-
// the flap never swings into the ground (a head lowered or lying on its side): keep the largest
|
|
378
|
-
// share of the swing that leaves its tip and its broad middle clear (the core cleared the unswung ear)
|
|
379
|
-
const L = b.earLen || (b.earLen = e.J.earBaseL.distanceTo(e.J.earTipL));
|
|
380
|
-
const clear = (f) => {
|
|
381
|
-
const q = tqa(_hz, E.s * K * f).multiply(tqa(_hx, E.f * K * f)).multiply(_q);
|
|
382
|
-
const dy = tv(0, 1, 0).applyQuaternion(q);
|
|
383
|
-
let worst = 1;
|
|
384
|
-
for (const t of [1, 0.6]) {
|
|
385
|
-
const pt = tv().copy(_p).addScaledVector(dy, L * t);
|
|
386
|
-
worst = Math.min(worst, pt.y - (e.terrainH(pt.x, pt.z) + 0.012 * s));
|
|
387
|
-
}
|
|
388
|
-
return worst;
|
|
389
|
-
};
|
|
390
|
-
let f = 1;
|
|
391
|
-
if (clear(1) < 0) {
|
|
392
|
-
let lo = 0, hi = 1;
|
|
393
|
-
for (let i = 0; i < 10; i++) { const mid = (lo + hi) / 2; if (clear(mid) >= 0) lo = mid; else hi = mid; }
|
|
394
|
-
f = lo;
|
|
395
|
-
E.vf *= 0.5; E.vs *= 0.5;
|
|
396
|
-
}
|
|
397
|
-
_q.premultiply(tqa(_hx, E.f * K * f)).premultiply(tqa(_hz, E.s * K * f));
|
|
398
|
-
m.compose(_p, _q, _s);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ------------------------------------------------------------------------------------------------
|
|
403
|
-
// actions
|
|
404
|
-
|
|
405
|
-
// greeting: fast wide wag that swings the whole rear, body wiggles, ears back, head a little low
|
|
406
|
-
const greet = {
|
|
407
|
-
kind: 'oneshot', fade: 0.3,
|
|
408
|
-
start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 3); L.engine.emit('vocalize', { kind: 'whine' }); },
|
|
409
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
410
|
-
apply(P, w, inst, L) {
|
|
411
|
-
const e = L.engine, t = inst.t, b = B(e);
|
|
412
|
-
const k = w * smooth(0, 0.4, t);
|
|
413
|
-
// the rear swings with the wag ("wiggle"): a lateral spine bend in phase with the tail
|
|
414
|
-
mx(P, 'bend', 0.14 * Math.sin(b.wagPh + 0.6), k);
|
|
415
|
-
mx(P, 'roll', 0.03 * Math.sin(b.wagPh), k);
|
|
416
|
-
mx(P, 'headRaise', -0.015 / e.cfg.unit, k); mx(P, 'headPitch', -0.1, k);
|
|
417
|
-
// small bounces on the forelegs
|
|
418
|
-
mx(P, 'dropF', 0.06 + 0.05 * Math.sin(t * TAU * 2.2), k);
|
|
419
|
-
mx(P, 'eyelid', 0.2, k);
|
|
420
|
-
},
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
// play bow: forequarters down on the elbows, forelegs stretched forward, rear up, tail up and wagging
|
|
424
|
-
const playbow = {
|
|
425
|
-
kind: 'oneshot', fade: 0.3, needsStand: true,
|
|
426
|
-
start(inst, L) {
|
|
427
|
-
inst.dur = inst.opts.duration ?? 2.4;
|
|
428
|
-
const e = L.engine;
|
|
429
|
-
if (e.rand() < 0.5) e.emit('vocalize', { kind: 'bark' });
|
|
430
|
-
},
|
|
431
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
432
|
-
apply(P, w, inst, L) {
|
|
433
|
-
const e = L.engine, t = inst.t;
|
|
434
|
-
const down = w * smooth(0.0, 0.5, t) * (1 - smooth(inst.dur - 0.5, inst.dur, t));
|
|
435
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
436
|
-
// the forelegs fold forward along the ground as in the sphinx (the core's lying fold), the chest
|
|
437
|
-
// drops to the elbows while the hind legs stay straight, rear up
|
|
438
|
-
mx(P, 'dropF', 0.88, down); mx(P, 'dropH', -0.06, down);
|
|
439
|
-
for (let i = 0; i < 2; i++) mxLeg(P, i, 'fold', 1, smooth(0.1, 0.8, down));
|
|
440
|
-
mx(P, 'headRaise', 0.03 / e.cfg.unit, down); mx(P, 'headPitch', -0.3, down); mx(P, 'neckReach', 0.02 / e.cfg.unit, down);
|
|
441
|
-
mx(P, 'lookW', 0, down);
|
|
442
|
-
},
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
// sniffing: nose to the ground ahead, sweeping side to side
|
|
446
|
-
const sniff = {
|
|
447
|
-
kind: 'oneshot', fade: 0.5,
|
|
448
|
-
start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 4); inst.ph = L.engine.rand() * 6; },
|
|
449
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
450
|
-
apply(P, w, inst, L) {
|
|
451
|
-
const e = L.engine, t = inst.t, cfg = e.cfg;
|
|
452
|
-
const pitchA = 0.82;
|
|
453
|
-
const hp = mouthGroundTarget(e, tv(), pitchA, (x, z) => e.terrainH(x, z), 0.95);
|
|
454
|
-
// side to side sweeps, the nose just above the ground; quick sniff bobs
|
|
455
|
-
const sweep = Math.sin(t * 1.3 + inst.ph) * 0.8 + Math.sin(t * 0.47 + 2 * inst.ph) * 0.2;
|
|
456
|
-
hp.addScaledVector(e.left(e.heading, tv()), 0.07 * cfg.s * sweep);
|
|
457
|
-
hp.y += (0.012 + 0.006 * Math.sin(t * TAU * 5)) * cfg.s;
|
|
458
|
-
P.headPos.copy(hp);
|
|
459
|
-
mx(P, 'headW', 1, w); mx(P, 'headReach', 1, w); mx(P, 'headPitch', pitchA, w); mx(P, 'headYaw', 0.3 * sweep, w);
|
|
460
|
-
mx(P, 'lookW', 0, w); mx(P, 'headOmega', 1.6, w);
|
|
461
|
-
mx(P, 'gaitW', 0, w); mx(P, 'dropF', 0.18, w);
|
|
462
|
-
mx(P, 'speedScale', 0, w);
|
|
463
|
-
},
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
// pawing the ground: head down over the spot, the forepaws scrape it alternately, back under the chest
|
|
467
|
-
const dig = {
|
|
468
|
-
kind: 'oneshot', fade: 0.3, needsStand: true,
|
|
469
|
-
start(inst, L) {
|
|
470
|
-
inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 2.5);
|
|
471
|
-
const e = L.engine;
|
|
472
|
-
inst.base = [0, 1].map((i) => new THREE.Vector3().copy(e.legs[i].contact));
|
|
473
|
-
inst.fwd = e.forward(e.heading, new THREE.Vector3());
|
|
474
|
-
},
|
|
475
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
476
|
-
apply(P, w, inst, L) {
|
|
477
|
-
const e = L.engine, t = inst.t, cfg = e.cfg;
|
|
478
|
-
const k = w * smooth(0, 0.3, t);
|
|
479
|
-
mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
|
|
480
|
-
mx(P, 'dropF', 0.3, k); mx(P, 'dropH', 0.05, k);
|
|
481
|
-
mx(P, 'headPitch', 0.75, k); mx(P, 'headRaise', -0.06 / cfg.unit, k); mx(P, 'lookW', 0, k);
|
|
482
|
-
mx(P, 'earFlat', -0.1, k);
|
|
483
|
-
const env = smooth(0.15, 0.4, t) * (1 - smooth(inst.dur - 0.35, inst.dur, t));
|
|
484
|
-
for (let i = 0; i < 2; i++) {
|
|
485
|
-
const leg = e.legs[i], len = leg.def.len;
|
|
486
|
-
// one stroke: reach forward high, press down, drag back under the chest, lift (3 Hz, legs alternate)
|
|
487
|
-
const ph = (t * 3.0 + i * 0.5) % 1;
|
|
488
|
-
const u = ph < 0.55 ? 0.28 - 0.4 * smooth(0, 0.55, ph) : -0.12 + 0.4 * smooth(0.55, 1, ph);
|
|
489
|
-
const hgt = ph < 0.55 ? 0.03 : 0.03 + 0.08 * Math.sin(Math.PI * (ph - 0.55) / 0.45);
|
|
490
|
-
const tp = tv().copy(inst.base[i]).addScaledVector(inst.fwd, u * len);
|
|
491
|
-
tp.y = e.terrainH(tp.x, tp.z) + hgt * len;
|
|
492
|
-
// the planted foot stays where it stood outside the strokes
|
|
493
|
-
tp.lerp(inst.base[i], 1 - env);
|
|
494
|
-
const pl = P.legs[i];
|
|
495
|
-
pl.target.copy(tp);
|
|
496
|
-
mxLeg(P, i, 'reach', 1, w);
|
|
497
|
-
}
|
|
498
|
-
if (Math.floor(t * 3) !== inst.lastStroke) { inst.lastStroke = Math.floor(t * 3); }
|
|
499
|
-
},
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// hit: the built-in flinch, then a yelp and a cower (body low, tail tucked, ears flat, head down)
|
|
503
|
-
const hit = {
|
|
504
|
-
kind: 'oneshot', fade: 0.12,
|
|
505
|
-
start(inst, L) { ONESHOTS.hit.start(inst, L); inst.dur = 1.6; L.engine.emit('vocalize', { kind: 'yelp' }); },
|
|
506
|
-
update(inst) { return inst.t >= inst.dur; },
|
|
507
|
-
apply(P, w, inst, L) {
|
|
508
|
-
ONESHOTS.hit.apply(P, w, inst, L);
|
|
509
|
-
const t = inst.t, e = L.engine;
|
|
510
|
-
const c = w * smooth(0.12, 0.4, t) * (1 - smooth(1.0, 1.6, t));
|
|
511
|
-
mx(P, 'dropF', 0.3, c); mx(P, 'dropH', 0.42, c);
|
|
512
|
-
mx(P, 'headRaise', -0.035 / e.cfg.unit, c); mx(P, 'headPitch', 0.25, c);
|
|
513
|
-
mx(P, 'eyelid', 0.35, c); mx(P, 'lookW', 0.3, c);
|
|
514
|
-
},
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
// the curled sleep: the core's, unrolling a little more slowly when the dog gets up (the forelegs
|
|
518
|
-
// come out from under the curled body)
|
|
519
|
-
const sleep = { ...POSTURES.sleep, kind: 'posture', fadeOut: 1.6 };
|
|
520
|
-
|
|
521
|
-
export const dogHooks = { update, pose, actions: { greet, playbow, sniff, dig, hit, sleep } };
|