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