@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.
Files changed (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. 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 } };