@bitefoundry/retrieviewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +75 -0
  6. package/INSTALLING.md +8 -5
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +110 -0
  11. package/README.md +119 -58
  12. package/RELEASING.md +27 -14
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/desktop/platform.mjs +27 -0
  17. package/integrations/linux-overlay/README.md +83 -0
  18. package/integrations/linux-overlay/overlay.py +1094 -0
  19. package/integrations/macos-overlay/README.md +83 -0
  20. package/integrations/macos-overlay/overlay.jxa +639 -0
  21. package/integrations/opencode/runtime.mjs +4 -4
  22. package/integrations/opencode/tui.tsx +1 -1
  23. package/integrations/windows-overlay/Overlay.cs +4 -4
  24. package/integrations/windows-overlay/desktop.mjs +40 -8
  25. package/integrations/windows-overlay/protocol.mjs +11 -7
  26. package/integrations/windows-overlay/worker.mjs +13 -7
  27. package/package.json +41 -14
  28. package/puppy-comfort-sprites.mjs +25 -0
  29. package/puppy-companion-comfort-q5.bin.gz +0 -0
  30. package/puppy-companion-comfort.json +77 -0
  31. package/puppy-companion-life.mjs +49 -16
  32. package/puppy-companion-objects.mjs +12 -0
  33. package/puppy-frame-predictive.mjs +15 -9
  34. package/puppy-frame-store.mjs +2 -2
  35. package/puppy-paper-world.mjs +14 -0
  36. package/puppy-paper.mjs +65 -14
  37. package/retrieviewer-cli.mjs +10 -3
  38. package/retrieviewer-plugin.d.mts +1 -1
  39. package/retrieviewer-plugin.mjs +1 -1
  40. package/retrieviewer.d.mts +10 -4
  41. package/retrieviewer.mjs +20 -14
  42. package/locomotion-core.mjs +0 -21
  43. package/locomotion-puppy.animal +0 -0
  44. package/vendor/procedural-animals/package.json +0 -40
  45. package/vendor/procedural-animals/src/animal.js +0 -146
  46. package/vendor/procedural-animals/src/bake.js +0 -51
  47. package/vendor/procedural-animals/src/build.js +0 -81
  48. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  49. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  50. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  51. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  52. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  53. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  54. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  55. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  56. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  57. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  58. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  59. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  60. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  61. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  62. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  63. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  64. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  65. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  66. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  67. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  68. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  69. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  70. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  71. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  72. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  73. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  74. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  75. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  76. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  77. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  79. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  80. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  81. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  82. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  83. package/vendor/procedural-animals/src/index.d.ts +0 -98
  84. package/vendor/procedural-animals/src/index.js +0 -29
  85. package/vendor/procedural-animals/src/registry.js +0 -48
  86. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  87. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  88. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  89. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  90. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  91. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  92. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  94. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  95. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  96. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  97. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  98. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  99. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  100. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  101. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  102. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  103. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  104. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  105. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  106. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  107. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  108. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  109. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  110. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  111. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  112. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  113. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  114. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  115. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  116. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  117. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  118. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  119. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  120. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  121. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  122. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  123. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  124. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  125. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  126. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  127. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  128. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  129. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  130. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  131. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  132. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  133. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  134. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  135. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  136. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  137. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  138. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  139. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  140. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  141. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  142. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  143. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  144. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  145. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  146. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  147. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  148. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  149. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  150. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  151. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  152. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  153. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  154. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  155. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  156. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  157. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  158. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  159. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  160. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  161. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  162. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  163. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  164. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  165. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  166. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  167. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  168. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  169. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  170. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  171. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  172. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  173. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  174. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  175. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  176. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  177. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  178. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  179. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  180. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  181. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  182. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  183. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  184. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  185. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  186. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  187. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  188. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  189. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  190. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  191. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  192. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  193. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  194. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  195. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  196. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  197. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  198. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  199. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  200. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  201. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  202. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  203. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  204. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  205. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  206. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  207. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  208. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  209. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  210. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  211. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  212. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  213. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  214. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  215. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  216. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  217. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  218. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  219. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  220. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  221. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  222. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  223. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  224. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  225. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  226. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  227. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  228. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  229. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  230. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  231. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  232. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  233. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  234. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  235. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  236. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  237. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  238. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  239. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  240. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  241. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  242. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  243. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  244. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  245. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  246. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  247. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  248. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  249. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  250. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  251. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  252. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  253. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  254. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  255. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  256. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  257. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  258. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  259. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  260. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  261. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,509 +0,0 @@
1
- // Goat behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
2
- //
3
- // pose(engine, dt) after the engine's solve: the ruminant's sideways grinding of the lower
4
- // jaw while chewing the cud, quick ear flicks (fly flicks, one ear at a time)
5
- // update(P, dt, layer) every frame over the action layer: cud chewing bouts (standing or lying),
6
- // the short tail flicking up, automatic idle variants (browsing, rearing to
7
- // browse, bleating, head shake, pawing, ear flicks), the buck's raised mane
8
- // in a threat
9
- // actions browse (reach up, or rear onto the hind legs: opts.rear), bleat, headshake,
10
- // paw (scrape the ground with a foreleg), attack (rear up, twist the head,
11
- // drop and clash head-down), sleep (sternal, head turned back to the flank)
12
- //
13
- // Posture lengths (headRaise, neckReach, fwd) are engine units (x cfg.k): species metres / cfg.unit.
14
- import * as THREE from 'three';
15
- import { clamp, lerp, smooth, TAU, tv, tqa } from '../../core/motion/common.js';
16
- import { mx, mxLeg, POSTURES, ONESHOTS } from '../../core/motion/actions.js';
17
-
18
- const _q = new THREE.Quaternion(), _q2 = new THREE.Quaternion(), _qh = new THREE.Quaternion(), _qI = new THREE.Quaternion(), _s = new THREE.Vector3(1, 1, 1), _m = new THREE.Matrix4();
19
- const PI = Math.PI;
20
- // lop ears hang with gravity: the share of the way to the standing hang and the largest turn about the root (25 deg:
21
- // a lop ear's root lies in the head / neck skin blend, and turned further the skin round the root stretched 3x)
22
- export const HANG = { f: 0.8, max: 0.44 };
23
- const sm = (w) => w * w * (3 - 2 * w);
24
-
25
- function B(e) {
26
- if (!e._goat) {
27
- e._goat = {
28
- cud: 0, cudOn: e.rand() < 0.6, cudT: 6 + e.rand() * 10, chewPh: 0, grind: 0,
29
- ear: [{ a: 0, age: 9, next: 2 + e.rand() * 4 }, { a: 0, age: 9, next: 3 + e.rand() * 5 }],
30
- tailT: 1 + e.rand() * 2, tailAge: 9,
31
- idleT: 5 + e.rand() * 6,
32
- };
33
- }
34
- return e._goat;
35
- }
36
- const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
37
-
38
- // ------------------------------------------------------------------------------------------------
39
- // pose: jaw grinding (lateral swing of the mandible) and ear flicks
40
- function pose(e, dt) {
41
- const b = B(e);
42
- // udder / scrotum: drawn up out of the ground when the body rests on it (lying, sleeping, dead)
43
- const ud = e.bone.udder;
44
- if (ud) {
45
- // (a buck's scrotum hangs 12.5 cm below the udder bone's top, its bottom 0.325 m in the bind pose; held up until the
46
- // hindquarters have risen)
47
- const male = e.params?.sex === 'male';
48
- const m = ud.matrixWorld.elements, sY = e.J.udderTop.y / 0.44;
49
- const drop = (male ? 0.125 : 0.168 + 0.065 * (e.params?.udder || 0)) * sY;
50
- const dl = Math.hypot(m[4], m[5], m[6]) || 1;
51
- // (lying on its side the udder's axis is level: a lift toward the sky moved it out of the flank, and the rigid bag
52
- // stood out sideways from the belly like a ball, floating beside the rump. There it hangs toward the ground about
53
- // its attachment and draws up into the belly along its own axis, against the belly and the lower thigh)
54
- const side = smooth(0.2, 0.75, 1 + m[5] / dl);
55
- b.udSide = (b.udSide || 0) + (side - (b.udSide || 0)) * (1 - Math.exp(-dt * 6));
56
- const top = tv().setFromMatrixPosition(ud.matrixWorld);
57
- _q.setFromRotationMatrix(ud.matrixWorld);
58
- if (b.udSide > 1e-3) {
59
- _qh.setFromUnitVectors(tv(0, 1, 0).applyQuaternion(_q), tv(0, -1, 0)).slerp(_qI, 1 - 0.55 * b.udSide);
60
- _q.premultiply(_qh);
61
- top.addScaledVector(tv(0, 1, 0).applyQuaternion(_q), -(0.05 + 0.05 * (e.params?.udder || 0)) * sY * b.udSide);
62
- }
63
- // (the udder is its own rigid surface: all of it must clear the ground, up to 20 cm for a doe in milk; a buck's scrotum,
64
- // 18 cm from the groin, 8 cm: lifted 14.5 cm it vanished into the body when he sat or lay down)
65
- let want = Math.max(e.P.groundW, e.P.dropH > 0 ? 0.6 * Math.min(1, e.P.dropH) : 0) * (male ? 0.08 : 0.1 + 0.14 * (e.params?.udder || 0)) * e.cfg.s * (1 - b.udSide);
66
- // ... and at least as much as its lowest point (the teat tips, the scrotum) needs to clear the ground: rising
67
- // hind end first the posture's weights fell faster than the hindquarters rose, and the udder dipped 7 mm
68
- // into the ground
69
- const low = top.y + tv(0, 1, 0).applyQuaternion(_q).y * drop;
70
- want = Math.max(want, e.terrainH(top.x, top.z) + 0.02 * e.cfg.s - low);
71
- b.udLift = (b.udLift || 0) + (want - (b.udLift || 0)) * (1 - Math.exp(-dt * 12));
72
- if (b.udLift > 1e-5 || b.udSide > 1e-3) {
73
- top.y += Math.max(0, b.udLift);
74
- ud.matrixWorld.compose(top, _q, _s);
75
- }
76
- }
77
- // beard: a long beard that would reach into the ground (grazing, drinking, lying) swings back
78
- // about the chin just enough, smoothly
79
- const bd = e.bone.beard;
80
- if (bd && (e.params?.beard || 0) > 0.01) {
81
- const base = tv().setFromMatrixPosition(bd.matrixWorld);
82
- _q.setFromRotationMatrix(bd.matrixWorld);
83
- // the beard hangs from the chin skin: it follows the skull more than the opening jaw (the long
84
- // lock amplified every jaw movement into a whip at its tip)
85
- if (e.bone.head) { _q2.setFromRotationMatrix(e.bone.head.matrixWorld).multiply(b.beardRel || (b.beardRel = beardRelQ(e))); _q.slerp(_q2, 0.75); }
86
- const len = b.beardLen ?? (b.beardLen = e.J.beardTop.distanceTo(e.J.beardTip));
87
- liftOut(e, b.beardSt || (b.beardSt = {}), base, _q, len, dt, 0.04 * e.cfg.s);
88
- bd.matrixWorld.compose(base, _q, _s);
89
- }
90
- // long lop ears (Nubian, Boer) hang below the muzzle: swung forward and out of the ground when
91
- // grazing or lying
92
- if (e.params?.ear === 'lop' && e.lod < 2) {
93
- // ... and they hang with gravity: turned about the root toward the ear's standing hang (turned with the head's yaw),
94
- // most of the way, while the head is upright (a rigid ear kept its standing angle to the skull, so with the head
95
- // down to graze or drink it stood out behind the jaw like a plate; the Nubian standard: pendulous ears fall along
96
- // the face). A head rolled onto its side (dead, lying flat) leaves the ears to the posture.
97
- const qRel = _q2.setFromRotationMatrix(e.bone.head.matrixWorld).multiply(b.headInv || (b.headInv = e.bindQ.head.clone().invert()));
98
- const lat = tv(1, 0, 0).applyQuaternion(qRel);
99
- const psi = Math.atan2(-lat.z, lat.x);
100
- const hang = HANG.f * (1 - smooth(0.35, 0.7, Math.abs(lat.y)));
101
- for (let k = 0; k < 2 && hang > 1e-3; k++) {
102
- const S = k === 0 ? 'L' : 'R', bone = e.bone['ear' + S];
103
- if (!bone) continue;
104
- const d0 = b['hang' + S] || (b['hang' + S] = tv(0, 1, 0).applyQuaternion(e.bindQ['ear' + S]).clone());
105
- const want = tv().copy(d0).applyAxisAngle(tv(0, 1, 0), psi);
106
- _q.setFromRotationMatrix(bone.matrixWorld);
107
- const now = tv(0, 1, 0).applyQuaternion(_q);
108
- _qh.setFromUnitVectors(now, want);
109
- const ang = 2 * Math.acos(Math.min(1, Math.abs(_qh.w)));
110
- _qh.slerp(_qI, 1 - Math.min(hang, ang > 1e-4 ? HANG.max / ang : 1));
111
- bone.matrixWorld.compose(tv().setFromMatrixPosition(bone.matrixWorld), _q.premultiply(_qh), _s);
112
- }
113
- for (let k = 0; k < 2; k++) {
114
- const S = k === 0 ? 'L' : 'R', bone = e.bone['ear' + S];
115
- if (!bone) continue;
116
- const st = b['lop' + S] || (b['lop' + S] = {});
117
- const base = tv().setFromMatrixPosition(bone.matrixWorld);
118
- _q.setFromRotationMatrix(bone.matrixWorld);
119
- const len = st.len ?? (st.len = e.J['earBase' + S].distanceTo(e.J['earTip' + S]));
120
- if (liftOut(e, st, base, _q, len, dt, 0.02 * e.cfg.s)) bone.matrixWorld.compose(base, _q, _s);
121
- }
122
- }
123
- if (e.lod >= 2) return;
124
- const jaw = e.bone.jaw;
125
- if (jaw && b.grind > 1e-3) {
126
- const base = tv().setFromMatrixPosition(jaw.matrixWorld);
127
- _q.setFromRotationMatrix(jaw.matrixWorld);
128
- const ax = tv(0, 0, 1).applyQuaternion(_q); // perpendicular to the jaw and to the lateral axis
129
- const a = 0.07 * b.grind * Math.sin(b.chewPh);
130
- jaw.matrixWorld.compose(base, tqa(ax, a).multiply(_q), _s);
131
- }
132
- for (let k = 0; k < 2; k++) {
133
- const E = b.ear[k];
134
- if (E.age > 0.5) continue;
135
- const bone = k === 0 ? e.bone.earL : e.bone.earR;
136
- if (!bone) continue;
137
- // a flick: fast twist about the ear's long axis and a flap, decaying in ~0.4 s
138
- const f = Math.sin(Math.min(1, E.age / 0.45) * PI * 2) * Math.exp(-E.age * 5) * (k === 0 ? 1 : -1);
139
- const base = tv().setFromMatrixPosition(bone.matrixWorld);
140
- _q.setFromRotationMatrix(bone.matrixWorld);
141
- const along = tv(0, 1, 0).applyQuaternion(_q), flap = tv(0, 0, 1).applyQuaternion(_q);
142
- _q.premultiply(tqa(along, 0.5 * f * E.a)).premultiply(tqa(flap, 0.35 * f * E.a));
143
- bone.matrixWorld.compose(base, _q, _s);
144
- }
145
- }
146
-
147
- // Keeps a hanging part (beard, lop ear) out of the ground: the smallest swing up about its root that
148
- // lifts sample points along it (fractions of its length) above the terrain, followed by a critically
149
- // damped spring (st: { lift, v }). Returns the rotated quaternion in _q (world) for the bone at base.
150
- function liftOut(e, st, base, q, len, dt, margin) {
151
- const dir = tv(0, 1, 0).applyQuaternion(q);
152
- let need = 0;
153
- if (base.y - e.terrainH(base.x, base.z) < len + margin + 0.02 * e.cfg.s) {
154
- for (const f of [1.12, 0.8, 0.5]) {
155
- const pt = tv().copy(base).addScaledVector(dir, len * f);
156
- const floor = e.terrainH(pt.x, pt.z) + margin;
157
- if (pt.y < floor) need = Math.max(need, Math.asin(clamp((floor - pt.y) / (len * f), 0, 1)) * 1.35);
158
- }
159
- }
160
- const om = 28, h = Math.min(dt, 0.05);
161
- st.v = (st.v || 0) + (om * om * (need - (st.lift || 0)) - 2 * om * (st.v || 0)) * h;
162
- st.lift = Math.max(0, (st.lift || 0) + st.v * h);
163
- if (st.lift > 1e-4) {
164
- const axis = tv().crossVectors(dir, tv(0, 1, 0));
165
- if (axis.lengthSq() > 1e-8) q.premultiply(tqa(axis.normalize(), st.lift));
166
- }
167
- return st.lift > 1e-4;
168
- }
169
-
170
- // the beard bone's bind rotation relative to the head bone's (so the head-following beard keeps its
171
- // bind hang)
172
- function beardRelQ(e) {
173
- return e.bindQ.head.clone().invert().multiply(e.bindQ.beard);
174
- }
175
-
176
- // ------------------------------------------------------------------------------------------------
177
- // update: cud chewing, tail flicks, ear flicks, idle variants
178
- function update(P, dt, L) {
179
- const e = L.engine, b = B(e), cfg = e.cfg;
180
- // a kid runs slower than dynamic similarity would give it (a 20 kg kid: ~5.2 m/s, not 6.5): requests
181
- // above 0.8 of its scaled top speed are capped there (a cap on every speed also slowed the trot it jumps
182
- // from, and the jump's crouch caught a foreleg mid-stride)
183
- if (e.params?.age === 'juvenile' && e.wantSpeed > 0.8 * cfg.maxSpeed) P.speedScale *= 0.8 * cfg.maxSpeed / e.wantSpeed;
184
- // a kid carries its head lower and further forward, on a less upright neck (kid_side, front2: with the
185
- // adult's high, alert carriage its long legs and upright neck read as a llama); not in actions
186
- if (e.params?.age === 'juvenile') {
187
- let act = 0;
188
- for (const a of L.oneshots) act = Math.max(act, a.w || 0);
189
- for (const p of L.postures) act = Math.max(act, p.w || 0);
190
- const kw = 1 - act;
191
- P.headRaise -= (0.045 / cfg.unit) * kw;
192
- P.neckReach += (0.025 / cfg.unit) * kw;
193
- P.headPitch += 0.1 * kw;
194
- }
195
- const vn = e.speed / cfg.sq;
196
- const still = 1 - smooth(0.1, 0.6, vn);
197
- // --- rumination: chewing bouts of 20-40 s with pauses, standing or lying (not while asleep,
198
- // eating, bleating or fighting)
199
- b.cudT -= dt;
200
- if (b.cudT <= 0) { b.cudOn = !b.cudOn; b.cudT = b.cudOn ? 18 + e.rand() * 22 : 8 + e.rand() * 14; }
201
- const lying = L.postures.some((p) => p.name === 'lie') ? 1 : 0;
202
- const quiet = L.oneshots.some((a) => !a.stopping && a.name !== 'hit' && a.name !== 'headshake') ? 0 : 1;
203
- const asleep = L.postures.some((p) => p.name === 'sleep' || p.name === 'death') ? 1 : 0;
204
- const want = b.cudOn && quiet && !asleep ? Math.max(still * L.idle.w, lying) : 0;
205
- b.cud += (want - b.cud) * (1 - Math.exp(-dt * 3));
206
- // chew rate ~1.3 Hz; every ~40 chews a bolus is swallowed (a short pause)
207
- const rate = 1.3 * (1 - 0.8 * smooth(0.85, 1, (b.chewPh / TAU) % 40 / 40));
208
- b.chewPh += dt * TAU * rate;
209
- const chew = b.cud * (0.5 + 0.5 * Math.sin(b.chewPh));
210
- b.grind = b.cud;
211
- // (ruminating goats chew with the lips closed: a small drop of the jaw under the sideways grind, less
212
- // than the lips' overlap; 0.1 rad opened the mouth 2 cm at the lips for most of an idle goat's time)
213
- if (b.cud > 1e-3) { P.jaw = Math.max(P.jaw, 0.015 * chew); P.jawOmega = Math.max(P.jawOmega, 18); mx(P, 'eyelid', 0.25, b.cud * 0.6); }
214
- // --- tail: held up, flicks in quick bursts (a few rapid side flicks)
215
- b.tailT -= dt;
216
- if (b.tailT <= 0) { b.tailT = 2 + e.rand() * 5; b.tailAge = 0; }
217
- b.tailAge += dt;
218
- const tw = b.tailAge < 0.8 ? Math.sin(PI * b.tailAge / 0.8) : 0;
219
- if (tw > 0) { P.tailWag = Math.max(P.tailWag, 0.9 * tw * (1 - P.groundW)); mx(P, 'tailLift', 0.25, tw * 0.5); }
220
- // --- ear flicks (one ear at a time, more often standing)
221
- for (const E of b.ear) {
222
- E.age += dt; E.next -= dt * (0.5 + 0.5 * still);
223
- if (E.next <= 0) { E.next = 2.5 + e.rand() * 7; E.age = 0; E.a = (0.7 + 0.5 * e.rand()) * (1 - P.eyelid * 0.8) * (e.params?.ear === 'lop' ? 0.3 : 1); }
224
- }
225
- // --- lying on the side (dead): the ears fold back along the neck, out of the ground
226
- const dead = L.postures.reduce((m, p) => (p.name === 'death' ? Math.max(m, p.w) : m), 0);
227
- const flat = Math.max(smooth(0.3, 1.0, Math.abs(P.roll)), dead * smooth(0.3, 1, dead));
228
- if (flat > 0) { mx(P, 'earFlat', 1.1, flat); mx(P, 'earTwitch', 0, flat); }
229
- // ... and the head rests on the lower horn and ear, a little higher than a bare skull would
230
- // (a long lop ear lies flat under the cheek: without the lift it was folded sharply about its root, out of
231
- // the ground, and its root tore)
232
- const lopLift = e.params?.ear === 'lop' ? 0.03 : 0;
233
- if (dead > 0 && P.headW > 0) P.headPos.y += (0.035 + lopLift + 0.12 * (e.params?.horns?.len || 0)) * cfg.s * flat;
234
- // (the head rolls over with the body rather than twisting on the atlas: the long throat stretched)
235
- if (dead > 0) P.headRoll *= 1 - 0.45 * dead;
236
- // --- lop ears hang: they hardly swivel, prick or flatten
237
- if (e.params?.ear === 'lop') { P.earTwitch *= 0.25; P.earSwivel *= 0.3; P.earFlat *= 0.3; }
238
- // --- lying / sleeping: the short tail lies flat, not curled round on the ground like a long tail
239
- P.tailSide *= 0.2;
240
- // --- grazing / drinking: the head pitches down ahead of the neck as it goes down (with both blended by
241
- // the action's fade the neck was down before the nose had turned, and the poll opened 25 deg further than
242
- // in the pose itself for a moment: the throat stretched)
243
- for (const a of L.oneshots) {
244
- if ((a.name !== 'eat' && a.name !== 'drink') || a.stopping || !(a.w > 0 && a.w < 1)) continue;
245
- const pitch = a.tune?.pitch ?? 1.3;
246
- const lead = Math.min(1, 3 * a.w) - a.w;
247
- P.headPitch += lead * (pitch - P.headPitch);
248
- }
249
- // --- any other action interrupts grazing
250
- if (L.oneshots.some((a) => !a.stopping && ['attack', 'jump', 'bleat', 'browse', 'paw'].includes(a.name))) for (const a of L.oneshots) if (a.name === 'eat' || a.name === 'drink') a.stopping = true;
251
- // --- idle variants
252
- if (e.idleVariants !== false && L.idle.w > 0.98 && !busy(L) && !e.input.look && !e.input.follow) {
253
- b.idleT -= dt;
254
- if (b.idleT <= 0) {
255
- b.idleT = 7 + e.rand() * 10;
256
- const r = e.rand();
257
- if (r < 0.22) L.play('browse', { rear: e.rand() < 0.55 });
258
- else if (r < 0.4) L.play('bleat');
259
- else if (r < 0.58) L.play('headshake');
260
- else if (r < 0.7) L.play('paw');
261
- else if (r < 0.85) { b.ear[0].next = 0; b.ear[1].next = 0.25; }
262
- }
263
- } else if (busy(L)) b.idleT = Math.max(b.idleT, 3);
264
- }
265
-
266
- // ------------------------------------------------------------------------------------------------
267
- // browse: stretches the neck up to strip leaves (opts.rear: rises onto the hind legs, forelegs
268
- // raised as if resting on a trunk). Lips work, the head pulls back after each bite.
269
- const browse = {
270
- kind: 'oneshot', fade: 0.5, needsStand: true,
271
- start(inst, L) {
272
- const e = L.engine;
273
- inst.rear = inst.opts.rear ?? true;
274
- inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? (inst.rear ? 5 : 4.5));
275
- inst.yaw = (e.rand() - 0.5) * 0.5;
276
- },
277
- update(inst) { return inst.t >= inst.dur; },
278
- apply(P, w, inst, L) {
279
- const e = L.engine, cfg = e.cfg, t = inst.t, u = 1 / cfg.unit;
280
- const up = w * smooth(0, 0.9, t);
281
- // reach and bite cycle (~0.9 s): stretch, bite, pull back and chew
282
- const c = (t * 1.1) % 1;
283
- const bite = smooth(0.2, 0.35, c) * (1 - smooth(0.4, 0.7, c));
284
- mx(P, 'lookW', 0, up);
285
- mx(P, 'headPitch', -0.75 - 0.12 * bite, up); mx(P, 'headRaise', (0.1 + 0.015 * bite) * u, up); mx(P, 'neckReach', 0.03 * u, up);
286
- mx(P, 'headYaw', inst.yaw + 0.1 * Math.sin(t * 0.8), up);
287
- mx(P, 'jaw', 0.05 + 0.18 * bite, up); mx(P, 'jawOmega', 16, up);
288
- mx(P, 'earFlat', -0.1, up); mx(P, 'tailLift', 0.15, up);
289
- if (inst.rear) {
290
- // up on the hind legs: the body comes down first, then the forefeet reach the ground again
291
- const body = w * smooth(0.2, 1.3, t) * (1 - smooth(inst.dur - 1.2, inst.dur - 0.2, t));
292
- const legs = Math.min(w * smooth(0.2, 0.7, t), w * smooth(0.004 * cfg.k, 0.04 * cfg.k, e.shY.x) + body);
293
- mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
294
- // (body ~55 deg up: the neck still reaches up and forward, the forelegs half folded as if
295
- // resting on a trunk)
296
- mx(P, 'dropF', -1.2, body); mx(P, 'dropH', 0.15, body);
297
- for (let i = 0; i < 2; i++) { mxLeg(P, i, 'tuck', 1, legs); P.legs[i].extend = lerp(P.legs[i].extend, 0.3, legs); }
298
- mx(P, 'headPitch', 0.1 - 0.1 * bite, body); mx(P, 'headRaise', 0.0, body); mx(P, 'neckReach', 0.045 * u, body);
299
- }
300
- },
301
- };
302
-
303
- // bleat: head up, mouth open, jaw quiver, ears back a little
304
- const bleat = {
305
- kind: 'oneshot', fade: 0.25,
306
- start(inst) { inst.dur = inst.opts.duration ?? 1.7; inst.said = false; },
307
- update(inst, dt, L) {
308
- if (!inst.said && inst.t > 0.4) { inst.said = true; L.engine.emit('vocalize', { kind: 'bleat' }); }
309
- return inst.t >= inst.dur;
310
- },
311
- apply(P, w, inst, L) {
312
- const e = L.engine, t = inst.t, u = 1 / e.cfg.unit;
313
- const up = w * smooth(0, 0.35, t);
314
- const call = smooth(0.35, 0.5, t) * (1 - smooth(inst.dur - 0.45, inst.dur - 0.15, t));
315
- mx(P, 'lookW', 0.3, up);
316
- mx(P, 'headPitch', -0.55, up); mx(P, 'headRaise', 0.04 * u, up); mx(P, 'neckReach', 0.015 * u, up);
317
- mx(P, 'jaw', 0.42 + 0.07 * Math.sin(t * TAU * 11), call * w); mx(P, 'jawOmega', 30, w);
318
- mx(P, 'earFlat', 0.3, up); mx(P, 'breathAmp', 2.2, call * w); mx(P, 'tailLift', 0.2, up);
319
- },
320
- };
321
-
322
- // head shake: fast rotations about the nose axis (flies, water), ears flapping
323
- const headshake = {
324
- kind: 'oneshot', fade: 0.12,
325
- start(inst) { inst.dur = 1.0; },
326
- update(inst) { return inst.t >= inst.dur; },
327
- apply(P, w, inst) {
328
- const t = inst.t;
329
- const env = smooth(0, 0.12, t) * (1 - smooth(0.55, 0.95, t)) * w;
330
- const s = Math.sin(t * TAU * 4.2);
331
- mx(P, 'headOmega', 5, env); // (the head springs follow the fast shake)
332
- mx(P, 'headRoll', 0.42 * s, env); mx(P, 'headYaw', 0.16 * s, env); mx(P, 'headPitch', -0.1, env);
333
- mx(P, 'earTwitch', 2.5, env); mx(P, 'eyelid', 0.5, env); mx(P, 'lookW', 0, env);
334
- },
335
- };
336
-
337
- // paw: scrapes the ground two or three times with one foreleg (before lying down, or in agitation)
338
- // Each scrape (0.9 s): the hoof comes up and forward and is held there (~0.4 s, so the lifted foreleg reads:
339
- // a continuous 0.55 s loop was never caught lifted in the action strip), strikes down and drags back along
340
- // the ground.
341
- const PAW_T0 = 0.35, PAW_CYC = 0.9, PAW_N = 2;
342
- // hoof path per cycle phase f (0..1): [forward (x 0.07 m), height (x 0.1 m)]
343
- function pawPath(f) {
344
- const up = smooth(0.0, 0.16, f) * (1 - smooth(0.6, 0.7, f));
345
- const fwd = -0.15 + 1.15 * smooth(0.0, 0.2, f) - 1.15 * smooth(0.62, 0.98, f); // (periodic: -0.15 at both ends)
346
- return [fwd, up];
347
- }
348
- const paw = {
349
- kind: 'oneshot', fade: 0.3, needsStand: true,
350
- start(inst, L) { const e = L.engine; inst.dur = PAW_T0 + PAW_N * PAW_CYC + 0.45; inst.leg = inst.opts.side === -1 ? 1 : inst.opts.side === 1 ? 0 : (e.rand() < 0.5 ? 0 : 1); inst.hits = 0; },
351
- update(inst, dt, L) {
352
- const e = L.engine;
353
- // the strike lands at 0.68 of each cycle
354
- const n = Math.floor(clamp((inst.t - PAW_T0 - 0.68 * PAW_CYC) / PAW_CYC + 1, 0, PAW_N));
355
- if (n > inst.hits && inst.hits < PAW_N) {
356
- inst.hits = n;
357
- e.emit('footstep', { foot: e.legs[inst.leg].def.key, position: e.legs[inst.leg].contact.clone(), speed: 0, strength: 0.6, paw: true });
358
- }
359
- return inst.t >= inst.dur;
360
- },
361
- apply(P, w, inst, L) {
362
- const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t, i = inst.leg;
363
- const tEnd = PAW_T0 + PAW_N * PAW_CYC;
364
- const k = w * smooth(0.15, 0.35, t) * (1 - smooth(tEnd + 0.05, tEnd + 0.35, t));
365
- mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
366
- mx(P, 'headPitch', 0.35, k); mx(P, 'headRaise', -0.05 / cfg.unit, k); mx(P, 'lookW', 0, k);
367
- mx(P, 'side', -0.012 * (i === 0 ? 1 : -1) / cfg.unit, k); // weight onto the other three legs
368
- const u = clamp((t - PAW_T0) / PAW_CYC, 0, PAW_N);
369
- const f = u >= PAW_N ? 1 : u - Math.floor(u);
370
- const [pf, pu] = u >= PAW_N ? [-0.15, 0] : pawPath(f);
371
- const leg = e.legs[i];
372
- const fwd = e.forward(e.heading, tv());
373
- // (a clear lift, the hoof raised ~10 cm and brought forward, then struck down and dragged back: a 3.5 cm
374
- // scrape did not read)
375
- const tp = tv().copy(leg.plant).addScaledVector(fwd, 0.07 * s * pf);
376
- tp.y += 0.1 * s * pu + 0.004 * s;
377
- P.legs[i].target.lerp(tp, k / Math.max(1e-3, P.legs[i].reach + k));
378
- mxLeg(P, i, 'reach', 1, k);
379
- },
380
- };
381
-
382
- // attack: the goat's clash. Rises on the hind legs with the head twisted to one side, then drops
383
- // forward and brings the horns / forehead down onto the opponent (the impact at the forefeet's
384
- // landing). Buck: mane raised.
385
- const attack = {
386
- kind: 'oneshot', fade: 0.15, needsStand: true,
387
- start(inst, L) {
388
- const e = L.engine;
389
- inst.dur = 2.1; inst.hit = false;
390
- inst.side = inst.opts.side ?? (e.rand() < 0.5 ? -1 : 1);
391
- inst.run = smooth(0.8, 2.5, e.speed / e.cfg.sq);
392
- inst.target = inst.opts.target ? inst.opts.target.clone() : null;
393
- },
394
- update(inst, dt, L) {
395
- const e = L.engine;
396
- if (!inst.hit && inst.t >= 1.18) {
397
- inst.hit = true;
398
- const p = new THREE.Vector3().copy(e.headPose.position);
399
- const dir = inst.target ? inst.target.clone().sub(e.pos).setY(0).normalize() : e.forward(e.heading, new THREE.Vector3());
400
- e.emit('attackHit', { position: p, direction: dir, style: 'headbutt' });
401
- }
402
- return inst.t >= inst.dur;
403
- },
404
- apply(P, w, inst, L) {
405
- const e = L.engine, cfg = e.cfg, t = inst.t, u = 1 / cfg.unit;
406
- if (inst.target) { P.look = inst.target; mx(P, 'lookW', 0.6, w); }
407
- const rise = smooth(0.1, 0.8, t);
408
- const drop = smooth(0.92, 1.2, t);
409
- // (running: no rearing, a lowered-head charge into the clash)
410
- const still = 1 - inst.run;
411
- const up = rise * (1 - drop) * w * still;
412
- const clash = smooth(0.98, 1.2, t) * (1 - smooth(1.45, 2.0, t)) * w;
413
- const legsW = Math.max(up, w * still * smooth(0.1, 0.4, t) * (1 - smooth(1.02, 1.16, t)));
414
- mx(P, 'gaitW', 0, w * still * (1 - smooth(1.6, 2.05, t))); mx(P, 'legGait', 0, w * still * (1 - smooth(1.5, 2.0, t)));
415
- if (inst.run > 0) { const low = smooth(0.3, 0.9, t) * (1 - smooth(1.3, 1.9, t)) * w * inst.run; mx(P, 'headPitch', 0.8, low); mx(P, 'headRaise', -0.06 * u, low); }
416
- // rear
417
- mx(P, 'dropF', -1.35, up); mx(P, 'dropH', 0.2, up); mx(P, 'pitch', 0.05, up);
418
- for (let i = 0; i < 2; i++) { mxLeg(P, i, 'tuck', 1, legsW); P.legs[i].extend = lerp(P.legs[i].extend, lerp(0.25, 0.9, smooth(0.85, 1.1, t)), legsW); }
419
- mx(P, 'headRoll', 0.45 * inst.side, up); mx(P, 'headYaw', -0.2 * inst.side, up); mx(P, 'headPitch', 0.15, up);
420
- mx(P, 'headRaise', 0.03 * u, up);
421
- // drop and clash: head down, forehead first, body lunging forward, forequarters low
422
- mx(P, 'dropF', 0.28 * still, clash); mx(P, 'fwd', 0.07 * u, clash); mx(P, 'headPitch', 0.95, clash);
423
- mx(P, 'headRaise', -0.07 * u, clash); mx(P, 'neckReach', 0.03 * u, clash);
424
- mx(P, 'earFlat', 0.8, w * smooth(0, 0.2, t)); mx(P, 'earTwitch', 0, w);
425
- mx(P, 'tailLift', 0.4, w);
426
- // (the buck's mane rises in the threat)
427
- if (e.params?.sex === 'male' && e.params?.age !== 'juvenile') P.furRaise = Math.max(P.furRaise || 0, 0.6 * w);
428
- },
429
- };
430
-
431
- // lie: the engine's kneeling lie (actions.lie: down front, up hind), with the rise finished a little
432
- // before the posture is released: its end-first springs (kF / kH) must settle at zero before the
433
- // posture instance is removed, or the legs jump in the last frame of standing up
434
- function lieApply(P, w, inst, L) {
435
- const we = inst.target > 0 ? w : smooth(0.3, 1, w);
436
- POSTURES.lie.apply(P, we, inst, L);
437
- }
438
- const lie = { kind: 'posture', fadeIn: 1.4, fadeOut: 1.3, apply: lieApply };
439
-
440
- // sleep: sternal (goats rarely sleep flat out), head turned back and resting against the flank,
441
- // eyes closed
442
- const sleep = {
443
- kind: 'posture', fadeIn: 1.3, fadeOut: 1.0,
444
- apply(P, w, inst, L) {
445
- lieApply(P, w, inst, L);
446
- const e = L.engine;
447
- const c = sm(clamp(w, 0, 1)) * smooth(1.2, 3.2, inst.t);
448
- mx(P, 'headYaw', inst.side * 0.8, c); mx(P, 'headPitch', 0.45, c); mx(P, 'headRaise', -0.07 / e.cfg.unit, c);
449
- mx(P, 'bend', inst.side * 0.35, c);
450
- mx(P, 'earFlat', 0.45, c); mx(P, 'earTwitch', 0.2, c);
451
- mx(P, 'eyelid', 1, smooth(1.8, 3.2, inst.t) * w);
452
- mx(P, 'breathRate', 0.6, c); mx(P, 'lookW', 0, c); mx(P, 'tailIdle', 0.2, c);
453
- },
454
- };
455
-
456
- // death: the engine's collapse (legs buckle, topple onto the side, settle), with the body sinking level and
457
- // leaning to its side while the legs buckle (falls onto its side): the forelegs, the
458
- // longer pair, buckled further than the hind ones and the goat pitched onto its nose with its hindquarters
459
- // high before it rolled over
460
- const death = {
461
- kind: 'posture', fadeIn: POSTURES.death.fadeIn, fadeOut: POSTURES.death.fadeOut,
462
- apply(P, w, inst, L) {
463
- POSTURES.death.apply(P, w, inst, L);
464
- const e = L.engine, cfg = e.cfg, t = inst.t, side = inst.side;
465
- const buckle = smooth(0, 0.35, t), fall = clamp((t - 0.3) / 0.55, 0, 1), fallE = fall * fall;
466
- const settle = fall >= 1 ? Math.exp(-(t - 0.85) * 6) * Math.sin((t - 0.85) * 18) * 0.06 : 0;
467
- const mv = e.air.active ? 0 : smooth(0.08, 0.45, e.speed / cfg.sk) * (1 - smooth(0.3, 0.7, t));
468
- const kb = 1 - mv;
469
- mx(P, 'dropF', lerp(0.42 * buckle * kb, 1, fallE), w);
470
- mx(P, 'roll', side * (1.42 * (fallE + 0.22 * buckle * kb * (1 - fallE)) - settle), w);
471
- // (the head goes down with the body as it goes over, not ahead of it: the engine drops it to the ground
472
- // from 0.35 s, and the goat dived onto its nose before it rolled)
473
- P.headW *= smooth(0.5, 1.05, t);
474
- },
475
- };
476
-
477
- // hit: the engine's flinch (a shove away from the blow and a restep), held long enough to read on a goat: the
478
- // body leans and bends away for ~0.4 s with the head thrown up and away, then the goat shakes it off (the
479
- // engine's 0.12 s recoil barely showed in any frame)
480
- const hit = {
481
- ...ONESHOTS.hit,
482
- kind: 'oneshot',
483
- start(inst, L) {
484
- ONESHOTS.hit.start(inst, L);
485
- inst.dur = 1.4;
486
- inst.dead = L.postures.some((p) => p.name === 'death');
487
- },
488
- apply(P, w, inst) {
489
- const t = inst.t;
490
- const k = smooth(0, 0.1, t) * (1 - 0.55 * smooth(0.3, 0.75, t)) * (1 - smooth(0.75, 1.25, t)) * w * (inst.dead ? 0.4 : 1);
491
- mx(P, 'roll', inst.lx * 0.22, k); mx(P, 'bend', -inst.lx * 0.36, k);
492
- mx(P, 'pitch', inst.lz * 0.1, k); mx(P, 'dropF', 0.2, k); mx(P, 'dropH', 0.13, k);
493
- P.headLocal.set(inst.lx * 0.08, 0.03, inst.lz * 0.07 - 0.03); mx(P, 'headLocalW', 1, k);
494
- mx(P, 'headYaw', -inst.lx * 0.5, k); mx(P, 'headPitch', -0.3, k);
495
- mx(P, 'earFlat', 1, w * (1 - smooth(0.55, 0.95, t))); mx(P, 'eyelid', 0.7, k);
496
- mx(P, 'jaw', 0.16, k); mx(P, 'tailLift', -0.15, k);
497
- if (inst.dead) return;
498
- // shaking it off
499
- const sh = smooth(0.7, 0.85, t) * (1 - smooth(1.12, 1.32, t)) * w;
500
- mx(P, 'headOmega', 5, sh); mx(P, 'headRoll', 0.3 * Math.sin((t - 0.7) * TAU * 4.2), sh);
501
- mx(P, 'earTwitch', 2.5, sh);
502
- },
503
- };
504
-
505
- export const hooks = {
506
- pose,
507
- update,
508
- actions: { browse, bleat, headshake, paw, attack, hit, lie, sleep, death },
509
- };