@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,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
- };