@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,575 +0,0 @@
1
- // Pig behaviour layer for the quadruped engine (species.motion.hooks, see core/motion/actions.js):
2
- //
3
- // update(P, dt, layer) every frame over the action layer: the corkscrew tail (curled and wagging
4
- // when active or feeding, uncurled and hanging when resting, dead or afraid),
5
- // the waddle (the heavy trunk rolls from side to side at the walk and trot,
6
- // the head nods), piglets' springy bounce, grunting (jaw pulses, a vocalize
7
- // event), rooting while eating (the disc pushed into the soil in forward-up
8
- // nudges, the jaw working, tail wagging), idle variants (sniffing with the head
9
- // up, grunts, piglets' frolics)
10
- // pose(engine, dt) after the engine's solve: the rostral disc wiggles and twitches (sniffing)
11
- // actions lie (kneels, drops the hindquarters, then flops onto its side), sleep
12
- // (lateral, eyes shut), wallow (rocking on the side), attack (sow: a shoulder
13
- // shove with a sideways bite; boar: head low, then an upward tusk slash),
14
- // hit (a squeal, tail uncurls), grunt, sniff, root (looping eat), frolic
15
- // (piglet: two stiff-legged bounces in place with a head toss)
16
- //
17
- // Distances are pig metres at the reference size (x cfg.s); posture offsets the engine multiplies by
18
- // cfg.k (fwd, side, headRaise, neckReach) are divided by cfg.unit.
19
- import * as THREE from 'three';
20
- import { clamp, lerp, smooth, TAU, tv, tqa } from '../../core/motion/common.js';
21
- import { mx, ONESHOTS, POSTURES } from '../../core/motion/actions.js';
22
- import { earSpine, earLeaf } from './ears.js';
23
- import { HZ, HY, pigJoints } from './rig.js';
24
- import { composeWarps } from '../../core/build/warp.js';
25
-
26
- const _q = new THREE.Quaternion(), _s = new THREE.Vector3(1, 1, 1);
27
- // extra height of the occiput over the ground when the head lies on its side (m at the reference
28
- // size): the engine's value fits a slim skull, a pig's jowls are ~5 cm wider
29
- const HEAD_REST = 0.085;
30
- // lying on the side (lie, sleep, wallow, death): the forelegs only partly laid out on the ground and not
31
- // far forward (full, the upper foreleg was pulled down across the chest and its armpit skin folded over on
32
- // itself: a maroon patch, red in the holes view)
33
- const PIG_FRONT_SPREAD = 0.15, PIG_FRONT_SIDE = 0.3;
34
- // lying on the side: the spine's lateral bend (a little toward the up side; see flop)
35
- const PIG_LIE_BEND = 0.08;
36
-
37
- function B(e) {
38
- if (!e._pig) {
39
- e._pig = {
40
- curl: 1, fear: 0, wagT: 3 + e.rand() * 5, wagAge: 9, wagAmp: 0,
41
- gruntT: 2 + e.rand() * 5, grunt: null,
42
- sniff: 0, sniffPh: 0, twitchPh: 0,
43
- idleT: 6 + e.rand() * 8,
44
- rootPh: 0, wag: 0, wagPh: 0, tail: null,
45
- };
46
- }
47
- return e._pig;
48
- }
49
- const busy = (L) => L.oneshots.some((a) => !a.stopping && a.name !== 'grunt');
50
- const find = (L, n) => L.oneshots.find((a) => a.name === n && a.w > 0);
51
- // turning on the spot (0 .. 1): a pivot keeps the core's idle layer on (it looks at the speed only), and
52
- // its look-around target and the sniff variant turned the head 6-12 deg away from the turn
53
- const turning = (e) => {
54
- const err = Math.abs(Math.atan2(Math.sin((e.wantHeading ?? e.heading) - e.heading), Math.cos((e.wantHeading ?? e.heading) - e.heading)));
55
- return Math.max(smooth(0.1, 0.3, Math.abs(e.yawRate || 0)), smooth(0.15, 0.4, err));
56
- };
57
-
58
- // ------------------------------------------------------------------------------------------------
59
- function update(P, dt, L) {
60
- const e = L.engine, cfg = e.cfg, b = B(e), u = 1 / cfg.unit, s = cfg.s;
61
- const vn = e.speed / cfg.sq, g = e.gait;
62
- const piglet = e.params.age === 'juvenile';
63
- const side = e.params.tailSide ?? 1;
64
- const dead = L.postures.some((p) => p.name === 'death' && p.w > 0);
65
- const resting = L.postures.find((p) => (p.name === 'lie' || p.name === 'sleep' || p.name === 'wallow') && p.target > 0);
66
- const eat = find(L, 'eat') || find(L, 'root') || find(L, 'drink');
67
-
68
- // ---- tail: curled when active, uncurled (hanging) when resting, dead or afraid
69
- b.fear = Math.max(0, b.fear - dt);
70
- let want = 1;
71
- if (resting) want = 0;
72
- if (b.fear > 0) want = 0;
73
- if (dead) want = 0.1;
74
- if (piglet && !dead && b.fear <= 0) want = Math.max(want, 0.7);
75
- b.curl += (want - b.curl) * (1 - Math.exp(-dt * (want > b.curl ? 1.5 : 3)));
76
- const c = b.curl;
77
- void side;
78
- // wagging: bursts when feeding, excited (moving off), and now and then standing
79
- b.wagT -= dt;
80
- if (b.wagT <= 0) {
81
- b.wagT = (eat ? 1.5 : 4) + e.rand() * (eat ? 3 : 8);
82
- if (!resting && !dead) { b.wagAge = 0; b.wagAmp = (eat ? 0.8 : 0.5) + 0.3 * e.rand(); }
83
- }
84
- b.wagAge += dt;
85
- const wa = Math.sin(Math.PI * clamp(b.wagAge / 2.4, 0, 1)) * b.wagAmp;
86
- b.wag = wa * c; // (the curled tail wags in pose(); the hanging one through the engine)
87
- b.wagPh += dt * TAU * 3.2;
88
- if (wa > 0.001) P.tailWag = Math.max(P.tailWag, wa * (1 - c));
89
-
90
- // ---- waddle: the heavy trunk rolls over the stance legs, the head nods at the walk
91
- const walkW = smooth(0.1, 0.5, vn) * (1 - smooth(3.0, 4.0, vn)) * P.gaitW;
92
- if (walkW > 0.001 && g) {
93
- const trotK = smooth(1.3, 1.9, vn);
94
- const ph = TAU * (e.phase - (g.off ? g.off[2] : 0));
95
- const amp = lerp(0.045, 0.03, trotK) * (piglet ? 0.7 : 1);
96
- P.roll += amp * Math.sin(ph) * walkW;
97
- P.side += 0.006 * u * Math.sin(ph) * walkW;
98
- const nod = 0.5 + 0.5 * Math.cos(2 * ph);
99
- P.headRaise -= 0.018 * u * walkW * nod * (1 - trotK);
100
- // piglets: a springy, bouncing trot
101
- if (piglet) P.lift += 0.012 * u * Math.pow(Math.sin(2 * ph), 2) * walkW * trotK;
102
- }
103
- // ---- a piglet carries its head higher than a finisher (side1, threequarter3, front2: the snout 15-20 deg below
104
- // level against 35 deg, the ears standing up over the poll), except while it eats, rests or lies dead
105
- if (piglet) {
106
- let busyW = dead ? 1 : eat ? eat.w : 0;
107
- for (const p of L.postures) busyW = Math.max(busyW, p.w);
108
- const k = 1 - clamp(busyW, 0, 1);
109
- P.headPitch -= 0.26 * k;
110
- P.headRaise += 0.025 * u * k;
111
- }
112
-
113
- // ---- grunting: short bursts of 1-3 grunts (jaw pulses and a flank pulse)
114
- const calm = !dead && !L.postures.some((p) => p.name === 'sleep' && p.w > 0.5);
115
- b.gruntT -= dt * (eat ? 1.6 : 1);
116
- if (b.gruntT <= 0 && calm && !L.oneshots.some((a) => a.name === 'grunt')) { b.gruntT = 3 + e.rand() * 9; L.play('grunt'); }
117
-
118
- // ---- rooting: the disc pushed into the soil in forward-up nudges, the head swinging slowly
119
- const root = find(L, 'eat') || find(L, 'root');
120
- if (root) {
121
- const w = root.w * root.w * (3 - 2 * root.w);
122
- b.rootPh += dt * 1.6;
123
- const c1 = b.rootPh % 1;
124
- const push = smooth(0.0, 0.35, c1) * (1 - smooth(0.45, 0.75, c1));
125
- const flick = smooth(0.4, 0.6, c1) * (1 - smooth(0.7, 0.95, c1));
126
- const fw = e.forward(e.heading, tv());
127
- P.headPos.addScaledVector(fw, 0.04 * s * push * w);
128
- P.headPos.y += (-0.012 * push + 0.035 * flick) * s * w;
129
- P.headPitch += (0.12 * push - 0.18 * flick) * w;
130
- P.headYaw += 0.12 * Math.sin(b.rootPh * 0.37 * TAU) * w;
131
- P.fwd += 0.015 * u * push * w;
132
- mx(P, 'jaw', 0.03 + 0.05 * flick, w);
133
- b.sniff = Math.max(b.sniff, w);
134
- }
135
-
136
- // ---- a pivot: the head leads the turn (the engine's steer lead) without the idle look pulling it back
137
- // to a point picked before the turn; a new point is picked after it
138
- const turnK = e.input.follow ? 0 : turning(e);
139
- if (turnK > 0.001 && !e.input.look && L.idle.w > 0.001 && P.look && P.look === L.idle.look) {
140
- P.lookW *= 1 - turnK;
141
- if (turnK > 0.5) { L.idle.hasLook = false; L.idle.lookT = Math.max(L.idle.lookT, 1.2); }
142
- }
143
-
144
- // ---- idle variants (not while pivoting)
145
- const idle = L.idle.w > 0.9 && !L.postures.length && !busy(L) && turnK < 0.05;
146
- if (idle) {
147
- b.idleT -= dt;
148
- if (b.idleT <= 0) {
149
- b.idleT = 6 + e.rand() * 10;
150
- if (piglet && e.rand() < 0.45) L.play('frolic');
151
- else L.play('sniff');
152
- }
153
- }
154
- // sniffing between: a quick disc twitch now and then, stronger when idle
155
- b.sniff = Math.max(b.sniff * Math.exp(-dt * 1.5), idle ? 0.35 : 0.15);
156
- }
157
-
158
- // ------------------------------------------------------------------------------------------------
159
- // pose: the rostral disc wiggles (sniffing): small fast up-down and sideways twitches
160
- function pose(e, dt) {
161
- const b = B(e), P = e.P, cfg = e.cfg;
162
- poseTail(e, b);
163
- if (e.lod >= 2) return;
164
- poseEars(e, b, dt);
165
- const sn = e.bone.snout;
166
- if (!sn) return;
167
- const awake = 1 - P.eyelid;
168
- b.twitchPh += dt * TAU * (2.5 + 3.5 * b.sniff);
169
- const a = b.sniff * awake;
170
- if (a < 1e-3) return;
171
- const tw = Math.pow(Math.max(0, Math.sin(b.twitchPh)), 2) * a;
172
- const lat = Math.sin(b.twitchPh * 0.43 + 1.3) * a;
173
- const base = tv().setFromMatrixPosition(sn.matrixWorld);
174
- _q.setFromRotationMatrix(sn.matrixWorld);
175
- const ax = tv(1, 0, 0).applyQuaternion(_q), up = tv(0, 0, 1).applyQuaternion(_q);
176
- const along = tv(0, 1, 0).applyQuaternion(_q);
177
- base.addScaledVector(along, -0.003 * tw * cfg.s);
178
- sn.matrixWorld.compose(base, tqa(ax, -0.12 * tw).multiply(tqa(up, 0.06 * lat)).multiply(_q), _s);
179
- }
180
-
181
- // the corkscrew tail: the engine has written a hanging spring chain (E); the curled tail is the rest
182
- // helix carried rigidly by the root bone (C). Per joint, the relative rotation is slerped from the
183
- // engine's toward the rest helix's by the curl amount, and the chain is rebuilt from the root; the
184
- // curled tail wags about the root.
185
- const _qa = new THREE.Quaternion(), _qb = new THREE.Quaternion(), _qc = new THREE.Quaternion(), _qp = new THREE.Quaternion();
186
- const _p = new THREE.Vector3(), _d = new THREE.Vector3(), _up = new THREE.Vector3();
187
- function poseTail(e, b) {
188
- let T = b.tail;
189
- if (!T) {
190
- const bones = [];
191
- for (let i = 0; ; i++) { const bn = e.bone['tail' + i]; if (!bn) break; bones.push(bn); }
192
- const idx = bones.map((bn) => e.sk.bones.indexOf(bn));
193
- const rest = idx.map((i) => new THREE.Quaternion().setFromRotationMatrix(e.sk.bind[i]));
194
- const rel = rest.map((q, i) => (i ? rest[i - 1].clone().invert().multiply(q) : null));
195
- const len = idx.map((i, k) => (k < idx.length - 1 ? new THREE.Vector3().setFromMatrixPosition(e.sk.bind[idx[k + 1]]).distanceTo(new THREE.Vector3().setFromMatrixPosition(e.sk.bind[i])) : 0));
196
- // rigid curl (crowd tier, fully curled): bone_i = root * inv(rest_0) * rest_i
197
- const inv0 = e.sk.bind[idx[0]].clone().invert();
198
- const R = idx.map((i) => inv0.clone().multiply(e.sk.bind[i]));
199
- T = b.tail = { bones, rel, len, R, q: bones.map(() => new THREE.Quaternion()), e: bones.map(() => new THREE.Quaternion()) };
200
- }
201
- const c = b.curl, n = T.bones.length;
202
- if (c < 1e-3 && Math.abs(b.wag) < 1e-4) return;
203
- if (e.lod >= 2 || (c > 0.995 && Math.abs(b.wag) < 1e-4)) {
204
- if (c < 0.5) return;
205
- const m0 = T.bones[0].matrixWorld;
206
- for (let i = 1; i < n; i++) T.bones[i].matrixWorld.multiplyMatrices(m0, T.R[i]);
207
- return;
208
- }
209
- for (let i = 0; i < n; i++) T.e[i].setFromRotationMatrix(T.bones[i].matrixWorld);
210
- // root: the engine's, wagged about the pelvis' up axis
211
- const q0 = T.q[0].copy(T.e[0]);
212
- if (Math.abs(b.wag) > 1e-4) {
213
- _up.set(0, 0, 1).applyQuaternion(T.e[0]); // (the root's dorsal axis)
214
- q0.premultiply(_qp.setFromAxisAngle(_up.normalize(), 0.45 * b.wag * Math.sin(b.wagPh)));
215
- _p.setFromMatrixPosition(T.bones[0].matrixWorld);
216
- T.bones[0].matrixWorld.compose(_p, q0, _s);
217
- }
218
- _p.setFromMatrixPosition(T.bones[0].matrixWorld);
219
- for (let i = 1; i < n; i++) {
220
- _qa.copy(T.e[i - 1]).invert().multiply(T.e[i]); // engine relative rotation
221
- _qb.copy(_qa).slerp(T.rel[i], c);
222
- T.q[i].copy(T.q[i - 1]).multiply(_qb);
223
- _d.set(0, T.len[i - 1], 0).applyQuaternion(T.q[i - 1]);
224
- _p.add(_d);
225
- T.bones[i].matrixWorld.compose(_p, T.q[i], _s);
226
- }
227
- void _qc;
228
- }
229
-
230
- // ears, after the engine's solve:
231
- // - a head lying on its side (lie, sleep, wallow, death) folds the ground-side ear back and up into the jowl
232
- // and the neck, where it is hidden, as a lying pig's lower ear is squashed under its head (Landrace photos;
233
- // swung up out of the ground it stuck out along the ground as a rigid blade; laid level beside the
234
- // neck it still showed as one); the upper ear falls onto the head: an erect or semi-lop leaf back over the
235
- // neck, its hollow down, a lop leaf forward over the face and the eye (lr1-lr3). The target is a whole frame
236
- // (the ear's direction and its leaf's hollow side, from the bind leaf, ears.js), so the leaf lies flat rather
237
- // than edge-on;
238
- // - then any pinna still reaching into the ground (rooting) is swung up about its base, just enough (a spring
239
- // on the correction angle keeps it smooth).
240
- const _eb = new THREE.Vector3(), _ed = new THREE.Vector3(), _el = new THREE.Vector3(), _ep = new THREE.Vector3(), _ea = new THREE.Vector3();
241
- const _hx = new THREE.Vector3(), _hy = new THREE.Vector3() /* back along the neck, level */, _hz = new THREE.Vector3(), _dT = new THREE.Vector3(), _nT = new THREE.Vector3();
242
- const _hv = new THREE.Vector3(), _dC = new THREE.Vector3(), _nC = new THREE.Vector3(), _w3 = new THREE.Vector3(), _mA = new THREE.Matrix4(), _mB = new THREE.Matrix4();
243
- const _qt = new THREE.Quaternion(), _qi = new THREE.Quaternion(), _qr = new THREE.Quaternion();
244
- // rotation taking the frame (d, n) onto (d2, n2) (n, n2 made orthogonal to d, d2)
245
- function frameRot(d, n, d2, n2, out) {
246
- _w3.copy(n).addScaledVector(d, -n.dot(d)).normalize();
247
- _mA.makeBasis(d, _w3, _ea.crossVectors(d, _w3));
248
- _w3.copy(n2).addScaledVector(d2, -n2.dot(d2)).normalize();
249
- _mB.makeBasis(d2, _w3, _ea.crossVectors(d2, _w3));
250
- return out.setFromRotationMatrix(_mB.multiply(_mA.transpose()));
251
- }
252
- function poseEars(e, b, dt) {
253
- const cfg = e.cfg;
254
- if (!b.ear) {
255
- b.ear = [0, 1].map((k) => {
256
- // (the leaf in the bind pose: the ear bone's direction and the hollow side at mid-length)
257
- // (and its first third: a tucked ear is aimed by it, since a drooping leaf's chord runs well below its root)
258
- // (the bind leaf as the mesh has it: the reference leaf through the individual's warps and size, as the pipeline
259
- // warps the mesh and the joints; the head scale fades out along a long leaf and tilts its chord several degrees)
260
- const S = k ? 'R' : 'L', P0 = e.params;
261
- const W = composeWarps([...(P0.warps || []), ...(P0.size && P0.size !== 1 ? [{ type: 'scale', k: P0.size }] : [])]) || ((p) => p);
262
- // (rig.js gives the left side; the rig mirrors it)
263
- const bl = pigJoints(P0).earBaseL, rb = S === 'L' ? bl : [-bl[0], bl[1], bl[2]], wb = W(rb);
264
- const off = (p) => { const q = W(p); return new THREE.Vector3(q[0] - wb[0], q[1] - wb[1], q[2] - wb[2]); };
265
- const dirAt = (p, v) => { const a = W(p), q = W([p[0] + 0.01 * v[0], p[1] + 0.01 * v[1], p[2] + 0.01 * v[2]]); return new THREE.Vector3(q[0] - a[0], q[1] - a[1], q[2] - a[2]).normalize(); };
266
- const sp = earSpine(rb, S, P0), q3 = sp.steps / 3 | 0, mid = sp.steps >> 1;
267
- const leaf = earLeaf(rb, S, P0);
268
- // (points on the leaf: the ground check; a drooping leaf's belly hangs 4-5 cm off the bone's chord)
269
- const pts = [];
270
- for (const u of [0.3, 0.55, 0.8, 1]) for (const a of [-0.85, 0, 0.85]) pts.push(off(leaf.mid(u, a).p));
271
- return {
272
- lift: 0, tuck: 0, relax: 0, len: e.J['earBase' + S].distanceTo(e.J['earTip' + S]), pts,
273
- d: off(sp.tip).normalize(), h: dirAt(sp.pts[mid], sp.fr[mid].h),
274
- d1: off(sp.pts[q3]).normalize(), h1: dirAt(sp.pts[q3 >> 1], sp.fr[q3 >> 1].h),
275
- };
276
- });
277
- }
278
- const qH = e.headPose.quaternion;
279
- // (the head's lateral axis and its long axis toward the disc, HZ, in the world)
280
- _hx.set(1, 0, 0).applyQuaternion(qH); _hz.set(HZ[0], HZ[1], HZ[2]).applyQuaternion(qH);
281
- // back along the neck, level (the neck lies on the ground beside it)
282
- _hy.set(-_hz.x, 0, -_hz.z);
283
- if (_hy.lengthSq() < 1e-6) _hy.set(0, 0, -1);
284
- _hy.normalize();
285
- // toward the throat, level (the head's ventral side: -HY)
286
- _hv.set(HY[0], HY[1], HY[2]).applyQuaternion(qH).multiplyScalar(-1); _hv.y = 0;
287
- if (_hv.lengthSq() < 1e-6) _hv.set(0, 0, 0); else _hv.normalize();
288
- const lop = e.params.ear === 'lop', relaxK = 0.85;
289
- const ke = 1 - Math.exp(-dt * 5);
290
- for (let k = 0; k < 2; k++) {
291
- const name = k === 0 ? 'earL' : 'earR', bone = e.bone[name], E = b.ear[k], s = k === 0 ? 1 : -1;
292
- if (!bone) continue;
293
- // (how far this ear's side of the head faces the ground: the head on its side)
294
- const side = s * _hx.y;
295
- E.tuck += (smooth(0.35, 0.75, -side) - E.tuck) * ke;
296
- E.relax += (relaxK * smooth(0.35, 0.75, side) - E.relax) * ke;
297
- _eb.setFromMatrixPosition(bone.matrixWorld);
298
- const w = Math.max(E.tuck, E.relax);
299
- if (w > 1e-3 && e.bindQ?.[name]) {
300
- _q.setFromRotationMatrix(bone.matrixWorld);
301
- // current leaf: the bind leaf carried by the bone's rotation from its bind orientation
302
- _qr.copy(e.bindQ[name]).invert().premultiply(_q);
303
- const tuck = E.tuck >= E.relax;
304
- _dC.copy(tuck ? E.d1 : E.d).applyQuaternion(_qr); _nC.copy(tuck ? E.h1 : E.h).applyQuaternion(_qr);
305
- if (tuck) {
306
- // folded under: back along the neck, a little up and toward the throat, into the jowl and the neck (which
307
- // lie on the ground over it), the hollow toward the throat: a lying neck is thickest from its back to the
308
- // throat, and the drooping leaf curls that way (its hollow up, it curled out of the neck's upper side)
309
- _dT.copy(_hy).addScaledVector(tv(0, 1, 0), 0.35).addScaledVector(_hv, 0.3).normalize();
310
- _nT.copy(_hv);
311
- } else if (lop) {
312
- // a lop ear keeps lying along the head and falls across it onto the cheek and the eye, its hollow on them
313
- // (gravity across the head's long axis)
314
- _w3.set(0, -1, 0).addScaledVector(_hz, _hz.y);
315
- _dT.copy(_dC).addScaledVector(_w3, 0.45).normalize();
316
- _nT.set(0, -1, 0);
317
- } else {
318
- // fallen back over the neck and draped down its side, the hollow on it
319
- _dT.copy(_hy).addScaledVector(tv(0, 1, 0), -0.45).normalize();
320
- _nT.copy(_hv).multiplyScalar(0.5).addScaledVector(tv(0, 1, 0), -1).normalize();
321
- }
322
- frameRot(_dC, _nC, _dT, _nT, _qt);
323
- // (the part w of that rotation, continuous in time: of q and -q the one nearer last frame's, then its
324
- // axis-angle scaled; a shortest-arc slerp switched sides where the rotation passed 180 deg and the ear
325
- // flipped through the ground in one frame)
326
- if (E.qs ? _qt.dot(E.qs) < 0 : _qt.w < 0) _qt.set(-_qt.x, -_qt.y, -_qt.z, -_qt.w);
327
- (E.qs || (E.qs = new THREE.Quaternion())).copy(_qt);
328
- const th = 2 * Math.acos(clamp(_qt.w, -1, 1)), sn = Math.sqrt(Math.max(0, 1 - _qt.w * _qt.w));
329
- if (sn > 1e-6) { _ea.set(_qt.x / sn, _qt.y / sn, _qt.z / sn); _qi.setFromAxisAngle(_ea, th * w); _q.premultiply(_qi); }
330
- bone.matrixWorld.compose(_eb, _q, _s);
331
- } else if (E.qs) E.qs = null;
332
- // the ground: a pinna reaching into it is swung up about its base, just enough: the swing about the horizontal
333
- // axis across the bone that lifts each point of the leaf above the floor (its height gain per radian is
334
- // (axis x offset).y), the largest of them; fast to rise (the head falls fast in a collapse: a spring lagged and
335
- // the leaf's belly went 29 mm into the ground), slow to let go
336
- const near = _eb.y - e.terrainH(_eb.x, _eb.z) < E.len * 1.2 + 0.03 * cfg.s;
337
- let need = 0;
338
- _q.setFromRotationMatrix(bone.matrixWorld);
339
- _ed.set(0, 1, 0).applyQuaternion(_q);
340
- _ea.crossVectors(_ed, tv(0, 1, 0));
341
- const axOk = _ea.lengthSq() > 1e-8;
342
- if (near && axOk && e.bindQ?.[name]) {
343
- _ea.normalize();
344
- _qr.copy(e.bindQ[name]).invert().premultiply(_q);
345
- for (const pt of E.pts) {
346
- _ep.copy(pt).applyQuaternion(_qr);
347
- const floor = e.terrainH(_eb.x + _ep.x, _eb.z + _ep.z) + 0.012 * cfg.s;
348
- const lack = floor - (_eb.y + _ep.y);
349
- if (lack <= 0) continue;
350
- const gain = _ea.z * _ep.x - _ea.x * _ep.z;
351
- need = Math.max(need, gain > 0.02 * E.len ? lack / gain : 1.2);
352
- }
353
- need = Math.min(1.2, need * 1.1);
354
- }
355
- E.lift = need > E.lift ? Math.min(need, E.lift + 25 * dt) : E.lift + (need - E.lift) * (1 - Math.exp(-dt * 10));
356
- if (E.lift < 1e-4 || !axOk) continue;
357
- _q.premultiply(tqa(_ea, E.lift));
358
- bone.matrixWorld.compose(_eb, _q, _s);
359
- }
360
- }
361
-
362
- // ------------------------------------------------------------------------------------------------
363
- // actions
364
-
365
- // grunt: 1-3 short jaw pulses with a flank pulse; emits vocalize { kind: 'grunt' }
366
- const grunt = {
367
- kind: 'oneshot', fade: 0.08, overlay: true, // a vocalisation: it neither cuts nor is cut by a commanded one-shot (eat, root, attack...)
368
- start(inst, L) { const b = B(L.engine); inst.n = 1 + Math.floor(L.engine.rand() * 3); inst.dur = 0.32 * inst.n + 0.1; b.grunt = inst; inst.said = 0; },
369
- update(inst, dt, L) {
370
- const k = Math.floor(inst.t / 0.32);
371
- if (k >= inst.said && k < inst.n) { inst.said = k + 1; L.engine.emit('vocalize', { kind: 'grunt', position: L.engine.headPose.position.clone() }); }
372
- if (inst.t >= inst.dur) { B(L.engine).grunt = null; return true; }
373
- return false;
374
- },
375
- apply(P, w, inst) {
376
- const x = (inst.t % 0.32) / 0.32, on = inst.t < 0.32 * inst.n ? 1 : 0;
377
- const pulse = Math.sin(Math.PI * clamp(x / 0.6, 0, 1)) * on;
378
- P.jaw = Math.max(P.jaw, 0.07 * pulse * w);
379
- P.jawOmega = Math.max(P.jawOmega, 30);
380
- P.breathAmp += 1.2 * pulse * w;
381
- },
382
- };
383
-
384
- // sniff: head up, the disc twitching fast, a look around
385
- const sniff = {
386
- kind: 'oneshot', fade: 0.35,
387
- start(inst, L) { inst.dur = 2.2 + L.engine.rand() * 1.5; inst.dir = L.engine.rand() < 0.5 ? -1 : 1; },
388
- update(inst) { return inst.t >= inst.dur; },
389
- apply(P, w, inst, L) {
390
- const u = 1 / L.engine.cfg.unit, t = inst.t;
391
- const k = smooth(0, 0.5, t) * (1 - smooth(inst.dur - 0.5, inst.dur, t)) * w;
392
- mx(P, 'headRaise', 0.06 * u, k); mx(P, 'headPitch', -0.3, k); mx(P, 'neckReach', 0.02 * u, k);
393
- // (no look to the side while the pig turns on the spot)
394
- inst.turnK = Math.max((inst.turnK ?? 0) * Math.exp(-L.engine.frameDt * 2), turning(L.engine));
395
- P.headYaw += 0.25 * inst.dir * Math.sin(t * 1.3) * k * (1 - inst.turnK);
396
- mx(P, 'lookW', 0.2, k);
397
- B(L.engine).sniff = Math.max(B(L.engine).sniff, k);
398
- },
399
- };
400
-
401
- // root: rooting in the ground (the built-in eat, looped; behaviour adds the nudges)
402
- const root = {
403
- ...ONESHOTS.eat, kind: 'oneshot',
404
- start(inst, L) { inst.tune = L.tuning.eat || {}; inst.dur = inst.opts.duration ?? Infinity; inst.fade = 0.8; },
405
- update(inst, dt, L) { return inst.t >= inst.dur || L.engine.wantSpeed > 0.05 || !!L.engine.input.follow; },
406
- apply(P, w, inst, L) { ONESHOTS.eat.apply(P, w, inst, L); B(L.engine).sniff = Math.max(B(L.engine).sniff, w); },
407
- };
408
-
409
- // lie: kneels on the carpi, drops the hindquarters, then flops onto its side (lateral), head flat,
410
- // awake (eyes open, ears and look alive)
411
- const flop = (P, w, inst, L, sleep) => {
412
- POSTURES.sleep.apply(P, w, inst, L);
413
- const c = smooth(1.0, 3.0, inst.t) * w;
414
- // (the spine a little rounded toward the up side instead of the core's lateral curl: lying on its side, the
415
- // curl sagged the middle toward the ground and the back read as a saddle between shoulder and ham;
416
- // with the wider barrel the outline over shoulder, ribs and ham varies by 2 cm, was 6.5)
417
- mx(P, 'bend', -inst.side * PIG_LIE_BEND, c);
418
- // the broad head (jowls and cheek) rests on its side higher than the engine's slim skull
419
- P.headPos.y += HEAD_REST * L.engine.cfg.s * c;
420
- // (the chin out: the deep double chin under the jaw otherwise ploughs into the ground; while the
421
- // forequarters go down the head is held up until it is laid on its side)
422
- mx(P, 'headPitch', -0.1, c);
423
- if (inst.target > 0) {
424
- const hold = smooth(0.3, 1.0, inst.t) * (1 - smooth(1.9, 2.9, inst.t)) * w;
425
- P.headRaise += 0.08 / L.engine.cfg.unit * hold; P.headPitch -= 0.3 * hold;
426
- }
427
- // the short legs stretched out from the body (the lower claws otherwise rest in the ground); the
428
- // forelegs less (PIG_FRONT_*)
429
- for (let i = 0; i < 4; i++) { P.legs[i].spread = Math.max(P.legs[i].spread, (i < 2 ? PIG_FRONT_SPREAD : 0.55) * c); }
430
- for (let i = 0; i < 2; i++) P.legs[i].side = lerp(P.legs[i].side, PIG_FRONT_SIDE, c);
431
- P.lift += 0.03 / L.engine.cfg.unit * c;
432
- if (!sleep) {
433
- // awake: eyes open, breathing normal, the head a little raised off the ground now and then
434
- mx(P, 'eyelid', 0.1, c); mx(P, 'breathRate', 0.8, c); mx(P, 'earTwitch', 0.8, c); mx(P, 'tailIdle', 0.5, c);
435
- mx(P, 'headLimp', 0.1, c);
436
- }
437
- };
438
- const lie = { kind: 'posture', fadeIn: 1.6, fadeOut: 1.4, apply(P, w, inst, L) { flop(P, w, inst, L, false); } };
439
- const sleep = { kind: 'posture', fadeIn: 1.6, fadeOut: 1.5, apply(P, w, inst, L) { flop(P, w, inst, L, true); } };
440
-
441
- // wallow: lying on the side and rocking back and forth (rubbing the flank and back in the mud),
442
- // the legs paddling in the air as it rolls toward its back
443
- const wallow = {
444
- kind: 'posture', fadeIn: 1.6, fadeOut: 1.5,
445
- apply(P, w, inst, L) {
446
- flop(P, w, inst, L, false);
447
- // (the rocking starts as soon as the pig is down on its side, ~1.8 s)
448
- const c = smooth(1.8, 3.0, inst.t) * w;
449
- const r = 0.5 - 0.5 * Math.cos(TAU * 0.22 * Math.max(0, inst.t - 1.8));
450
- P.roll += inst.side * 0.35 * r * c;
451
- for (let i = 0; i < 4; i++) { P.legs[i].spread += 0.25 * r * c; P.legs[i].fold += 0.3 * Math.max(0, Math.sin(TAU * 0.44 * inst.t + i)) * r * c; }
452
- P.headRoll += inst.side * 0.2 * r * c;
453
- },
454
- };
455
-
456
- // attack. Sow: a shoulder shove with a sideways bite (the head swipes across, mouth open). Boar (or
457
- // { style: 'slash' }): head low and turned, then an upward sideways swipe of the tusks.
458
- const attack = {
459
- kind: 'oneshot', fade: 0.15, needsStand: true,
460
- start(inst, L) {
461
- const e = L.engine;
462
- inst.style = inst.opts.style || ((e.params.boar || 0) >= 0.5 ? 'slash' : 'shove');
463
- inst.target = inst.opts.target ? inst.opts.target.clone() : null;
464
- inst.dir = e.rand() < 0.5 ? -1 : 1;
465
- if (inst.target) {
466
- const lx = (inst.target.x - e.pos.x) * Math.cos(e.heading) - (inst.target.z - e.pos.z) * Math.sin(e.heading);
467
- inst.dir = lx > 0 ? 1 : -1;
468
- }
469
- inst.dur = inst.style === 'slash' ? 1.25 : 1.1;
470
- inst.hitAt = inst.style === 'slash' ? 0.62 : 0.55;
471
- inst.hit = false;
472
- B(e).fear = 0;
473
- },
474
- update(inst, dt, L) {
475
- if (!inst.hit && inst.t >= inst.hitAt) {
476
- inst.hit = true;
477
- const e = L.engine;
478
- const p = new THREE.Vector3().copy(e.cfg.mouthLocal).applyQuaternion(e.headPose.quaternion).add(e.headPose.position);
479
- const dir = e.forward(e.heading, new THREE.Vector3()).addScaledVector(e.left(e.heading, new THREE.Vector3()), inst.dir * 0.6).normalize();
480
- if (inst.style === 'slash') dir.y = 0.5;
481
- e.emit('attackHit', { position: p, direction: dir.normalize(), style: inst.style === 'slash' ? 'tusk' : 'bite' });
482
- }
483
- return inst.t >= inst.dur;
484
- },
485
- apply(P, w, inst, L) {
486
- const e = L.engine, u = 1 / e.cfg.unit, t = inst.t, d = inst.dir;
487
- if (inst.target) { P.look = inst.target; mx(P, 'lookW', 1, w); }
488
- mx(P, 'earFlat', 0.7, w * smooth(0, 0.2, t)); mx(P, 'earTwitch', 0, w);
489
- const wind = smooth(0, 0.4, t) * (1 - smooth(inst.hitAt - 0.05, inst.hitAt + 0.1, t));
490
- const strike = smooth(inst.hitAt - 0.18, inst.hitAt, t) * (1 - smooth(inst.hitAt + 0.15, inst.dur - 0.05, t));
491
- if (inst.style === 'slash') {
492
- // head low and turned away, then swung up and across: the tusk rips upward
493
- mx(P, 'headRaise', -0.07 * u, wind * w); mx(P, 'headPitch', 0.45, wind * w); mx(P, 'headYaw', -d * 0.3, wind * w);
494
- mx(P, 'dropF', 0.15, wind * w); mx(P, 'fwd', -0.03 * u, wind * w);
495
- mx(P, 'headRaise', 0.06 * u, strike * w); mx(P, 'headPitch', -0.45, strike * w); mx(P, 'headYaw', d * 0.35, strike * w);
496
- mx(P, 'headRoll', d * 0.35, strike * w); mx(P, 'fwd', 0.06 * u, strike * w); mx(P, 'jaw', 0.25, strike * w);
497
- } else {
498
- // shoulder shove: the body lunges forward and into the target, the head swipes across, bites
499
- mx(P, 'fwd', -0.03 * u, wind * w); mx(P, 'headYaw', -d * 0.25, wind * w); mx(P, 'jaw', 0.3, wind * w);
500
- mx(P, 'fwd', 0.08 * u, strike * w); mx(P, 'side', d * 0.04 * u, strike * w); mx(P, 'roll', -d * 0.08, strike * w);
501
- mx(P, 'headYaw', d * 0.35, strike * w); mx(P, 'neckReach', 0.03 * u, strike * w);
502
- mx(P, 'jaw', 0.4 * (1 - smooth(inst.hitAt - 0.03, inst.hitAt + 0.05, t)) + 0.05, strike * w); mx(P, 'jawOmega', 30, w);
503
- }
504
- mx(P, 'gaitW', 0.4, (wind + strike > 0 ? 1 : 0) * w);
505
- },
506
- };
507
-
508
- // hit: the engine's stagger with a squeal (mouth wide), then the tail stays uncurled for a while
509
- const hit = {
510
- ...ONESHOTS.hit, kind: 'oneshot',
511
- start(inst, L) { ONESHOTS.hit.start(inst, L); B(L.engine).fear = 5; L.engine.emit('vocalize', { kind: 'squeal', position: L.engine.headPose.position.clone() }); },
512
- apply(P, w, inst, L) {
513
- ONESHOTS.hit.apply(P, w, inst, L);
514
- const k = smooth(0.02, 0.12, inst.t) * (1 - smooth(0.45, 0.7, inst.t)) * w;
515
- mx(P, 'jaw', 0.35, k); mx(P, 'jawOmega', 30, w); mx(P, 'headPitch', -0.2, k);
516
- },
517
- };
518
-
519
- // frolic (piglets): two quick stiff-legged bounces in place with a head toss and a twist of the
520
- // body (the legs stay planted: a crouch, then the body springs up on extended legs)
521
- const frolic = {
522
- kind: 'oneshot', fade: 0.1,
523
- start(inst, L) { inst.dur = 1.0; inst.toss = L.engine.rand() < 0.5 ? -1 : 1; },
524
- update(inst) { return inst.t >= inst.dur; },
525
- apply(P, w, inst) {
526
- const t = inst.t, T = 0.42;
527
- const ph = clamp(t / T, 0, 2.2);
528
- const cyc = ph < 2 ? ph % 1 : 1;
529
- // per bounce: crouch (0 - 0.35), spring up (0.35 - 0.6), settle (0.6 - 1)
530
- const crouch = smooth(0, 0.3, cyc) * (1 - smooth(0.3, 0.5, cyc));
531
- const up = smooth(0.32, 0.55, cyc) * (1 - smooth(0.6, 0.95, cyc));
532
- const env = (1 - smooth(0.85, 1.0, t / inst.dur)) * w;
533
- P.dropF += (0.14 * crouch - 0.08 * up) * env; P.dropH += (0.16 * crouch - 0.09 * up) * env;
534
- P.pitch += 0.05 * (up - crouch) * env;
535
- const k = Math.sin(Math.PI * clamp(t / inst.dur, 0, 1));
536
- P.headYaw += inst.toss * 0.35 * Math.sin(TAU * t / (2 * T)) * k * w; P.headRoll += inst.toss * 0.2 * up * env;
537
- P.roll -= inst.toss * 0.06 * up * env; P.headPitch -= 0.15 * up * env;
538
- mx(P, 'earFlat', 0.2, up * env);
539
- },
540
- };
541
-
542
- // jump: the built-in, with the forelegs tucking up more gradually after take-off (the short legs'
543
- // elbows flipped when they folded within a few frames of leaving the ground)
544
- const JUMP_TUCK = 0.4; // s (the built-in: 0.12)
545
- const jump = {
546
- ...ONESHOTS.jump, kind: 'oneshot',
547
- apply(P, w, inst, L) {
548
- ONESHOTS.jump.apply(P, w, inst, L);
549
- if (inst.phase === 'air') {
550
- const x = smooth(0, JUMP_TUCK * L.engine.cfg.sk, inst.t - inst.tAir);
551
- for (let i = 0; i < 2; i++) P.legs[i].tuck = Math.min(P.legs[i].tuck, x);
552
- }
553
- },
554
- };
555
-
556
- // death: the engine's collapse onto the side, the broad head resting on its cheek, the tail hanging
557
- const death = {
558
- ...POSTURES.death, kind: 'posture',
559
- apply(P, w, inst, L) {
560
- POSTURES.death.apply(P, w, inst, L);
561
- P.headPos.y += HEAD_REST * L.engine.cfg.s * smooth(0.35, 1.0, inst.t) * w;
562
- // the hind legs only partly in the side-lying pose (legs laid out on the ground), slightly flexed:
563
- // laid out full, the lower leg's foot target pressed the lower thigh's lateral bulge (the stifle)
564
- // 8-11 mm into bumps on a slope (death-uneven, between the torso sections the rest solver checks)
565
- const k = smooth(0.8, 1.6, inst.t) * w;
566
- for (let i = 2; i < 4; i++) P.legs[i].side = lerp(P.legs[i].side, 0.3, k);
567
- for (let i = 0; i < 2; i++) { P.legs[i].spread = lerp(P.legs[i].spread, PIG_FRONT_SPREAD, k); P.legs[i].side = lerp(P.legs[i].side, PIG_FRONT_SIDE, k); }
568
- },
569
- };
570
-
571
- export const behaviour = {
572
- update,
573
- pose,
574
- actions: { jump, lie, sleep, wallow, death, attack, hit, grunt, sniff, root, frolic },
575
- };