@bitefoundry/retrieviewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +75 -0
  6. package/INSTALLING.md +8 -5
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +110 -0
  11. package/README.md +119 -58
  12. package/RELEASING.md +27 -14
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/desktop/platform.mjs +27 -0
  17. package/integrations/linux-overlay/README.md +83 -0
  18. package/integrations/linux-overlay/overlay.py +1094 -0
  19. package/integrations/macos-overlay/README.md +83 -0
  20. package/integrations/macos-overlay/overlay.jxa +639 -0
  21. package/integrations/opencode/runtime.mjs +4 -4
  22. package/integrations/opencode/tui.tsx +1 -1
  23. package/integrations/windows-overlay/Overlay.cs +4 -4
  24. package/integrations/windows-overlay/desktop.mjs +40 -8
  25. package/integrations/windows-overlay/protocol.mjs +11 -7
  26. package/integrations/windows-overlay/worker.mjs +13 -7
  27. package/package.json +41 -14
  28. package/puppy-comfort-sprites.mjs +25 -0
  29. package/puppy-companion-comfort-q5.bin.gz +0 -0
  30. package/puppy-companion-comfort.json +77 -0
  31. package/puppy-companion-life.mjs +49 -16
  32. package/puppy-companion-objects.mjs +12 -0
  33. package/puppy-frame-predictive.mjs +15 -9
  34. package/puppy-frame-store.mjs +2 -2
  35. package/puppy-paper-world.mjs +14 -0
  36. package/puppy-paper.mjs +65 -14
  37. package/retrieviewer-cli.mjs +10 -3
  38. package/retrieviewer-plugin.d.mts +1 -1
  39. package/retrieviewer-plugin.mjs +1 -1
  40. package/retrieviewer.d.mts +10 -4
  41. package/retrieviewer.mjs +20 -14
  42. package/locomotion-core.mjs +0 -21
  43. package/locomotion-puppy.animal +0 -0
  44. package/vendor/procedural-animals/package.json +0 -40
  45. package/vendor/procedural-animals/src/animal.js +0 -146
  46. package/vendor/procedural-animals/src/bake.js +0 -51
  47. package/vendor/procedural-animals/src/build.js +0 -81
  48. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  49. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  50. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  51. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  52. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  53. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  54. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  55. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  56. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  57. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  58. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  59. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  60. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  61. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  62. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  63. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  64. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  65. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  66. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  67. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  68. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  69. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  70. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  71. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  72. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  73. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  74. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  75. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  76. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  77. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  79. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  80. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  81. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  82. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  83. package/vendor/procedural-animals/src/index.d.ts +0 -98
  84. package/vendor/procedural-animals/src/index.js +0 -29
  85. package/vendor/procedural-animals/src/registry.js +0 -48
  86. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  87. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  88. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  89. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  90. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  91. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  92. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  94. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  95. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  96. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  97. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  98. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  99. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  100. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  101. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  102. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  103. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  104. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  105. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  106. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  107. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  108. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  109. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  110. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  111. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  112. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  113. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  114. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  115. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  116. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  117. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  118. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  119. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  120. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  121. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  122. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  123. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  124. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  125. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  126. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  127. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  128. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  129. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  130. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  131. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  132. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  133. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  134. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  135. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  136. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  137. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  138. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  139. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  140. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  141. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  142. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  143. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  144. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  145. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  146. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  147. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  148. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  149. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  150. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  151. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  152. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  153. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  154. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  155. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  156. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  157. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  158. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  159. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  160. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  161. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  162. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  163. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  164. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  165. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  166. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  167. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  168. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  169. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  170. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  171. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  172. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  173. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  174. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  175. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  176. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  177. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  178. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  179. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  180. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  181. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  182. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  183. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  184. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  185. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  186. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  187. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  188. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  189. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  190. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  191. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  192. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  193. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  194. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  195. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  196. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  197. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  198. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  199. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  200. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  201. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  202. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  203. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  204. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  205. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  206. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  207. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  208. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  209. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  210. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  211. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  212. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  213. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  214. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  215. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  216. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  217. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  218. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  219. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  220. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  221. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  222. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  223. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  224. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  225. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  226. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  227. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  228. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  229. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  230. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  231. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  232. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  233. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  234. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  235. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  236. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  237. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  238. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  239. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  240. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  241. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  242. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  243. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  244. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  245. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  246. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  247. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  248. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  249. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  250. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  251. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  252. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  253. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  254. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  255. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  256. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  257. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  258. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  259. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  260. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  261. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,463 +0,0 @@
1
- // Brown bear 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 lumbering walk (head swinging with
4
- // the forelegs, shoulders and hump rolling), nose working while idle, automatic
5
- // idle variants (sniffing the air, standing up to look, digging), standing up
6
- // on the hind legs to look at a far target (input.look)
7
- // pose(engine, dt) nostril / muzzle twitch while sniffing (a small head-bone nod is enough)
8
- // actions standup (bipedal: looks and sniffs), sniff, dig (alternate forepaw rakes),
9
- // sit (on the rump like a person, hind legs forward, forepaws in the lap),
10
- // sleep (curled on the side, or prone with the head on the forepaws),
11
- // attack (charge, rear up, swat with a forepaw, come down biting),
12
- // hit (flinch, huff and a head swing, jaw popping), eat (rooting / grazing)
13
- //
14
- // Distances are bear metres at the reference size (x cfg.s); posture lengths that the engine
15
- // multiplies by cfg.k (fwd, side, lift, headRaise, neckReach) are divided by cfg.unit.
16
- import * as THREE from 'three';
17
- import { clamp, lerp, smooth, TAU, tv, tableAt } from '../../core/motion/common.js';
18
- import { mx, mxLeg, ONESHOTS, POSTURES } from '../../core/motion/actions.js';
19
-
20
- const PI = Math.PI;
21
-
22
- function B(e) {
23
- if (!e._bear) e._bear = { idleT: 8 + e.rand() * 8, lookStandT: 0, sniff: 0, sway: 0, huffT: -1, standW: 0 };
24
- return e._bear;
25
- }
26
- // A bear looking at something keeps its muzzle below its line of sight (face1 / face2 / tq1 / front1: looking at
27
- // the camera, the nose pad's bottom 0.91-0.98 eye separations under the eye line; face3_profile: the nose centre
28
- // 0.48 of the eye-nose distance under it). The engine aims the head bone's long axis at a look target and so
29
- // replaced the nose-down carriage with a level head whenever the bear looked at anything, ~70 % of a standing
30
- // bear's time (the nose pad then sat 0.19 / 0.58 eye separations under the eyes). The look pitch gets this offset.
31
- const GAZE = 0.25;
32
- // A bear looking to the side bends its short, thick neck toward the look as well as turning its head (m of lateral
33
- // neck bend at the poll per rad of head yaw, reference size). The engine carries the neck with the chest and yaws the
34
- // head bone alone: a 45 deg look turned the head ~40 deg about the poll, the skin behind the ear on the look side
35
- // folded into a crease and the nape's ruff stood along it as a hard edge over a bald-looking trench
36
- const NECK_SWAY = 0.18;
37
- const busy = (L) => L.postures.length > 0 || L.oneshots.some((a) => !a.stopping);
38
-
39
- // ------------------------------------------------------------------------------------------------
40
- // bipedal stand: the body rotates up about the hips (hind feet flat), forelegs hang in front of the
41
- // chest, head up and looking, nose working. `w` is the rise (0..1).
42
- const STAND_PITCH = 1.2; // torso pitch standing up (rad, ~69 deg)
43
- const SIT_PITCH = 0.95; // sitting on the rump
44
- const HEAD_SIDE = 0.09; // extra height of the occiput of a head lying on its side (m)
45
-
46
- // Pitches the torso up about the hips to pT (body frame pitch) with the hips displaced dispH (m) from
47
- // their bind height and carried to body-local z hipZt. The girdle height targets are set to agree
48
- // with that pitch (the engine pitches the body by atan2(shoulder - hip displacement, girdle distance)
49
- // + P.pitch), and the body is carried forward (P.fwd) so the hips land where asked.
50
- function tiltBody(P, g, e, pT, dispH, hipZt) {
51
- const cfg = e.cfg;
52
- const dz = cfg.zS - cfg.zH;
53
- const restH = cfg.pelvis.hh - (cfg.pelvis.y - cfg.yH);
54
- const dispS = dispH + (cfg.yS - cfg.yH) * (Math.cos(pT) - 1) + dz * Math.sin(pT);
55
- if (dispH !== null) mx(P, 'dropH', -dispH / (cfg.yH - restH), g);
56
- // (the shoulders by absolute height: the engine's resting height behind dropF changes with the pitch)
57
- mx(P, 'frontH', cfg.yS + dispS, g / Math.max(1e-3, P.frontHW + g)); mx(P, 'frontHW', 1, g);
58
- mx(P, 'pitch', pT - Math.atan2(dispS - dispH, dz), g);
59
- const hipZ = cfg.zH * Math.cos(pT) - (cfg.yH + dispH) * Math.sin(pT);
60
- mx(P, 'fwd', (hipZt - hipZ) / cfg.k, g);
61
- }
62
-
63
- function applyStand(P, w, e, headUp = 1) {
64
- if (w <= 1e-4) return;
65
- const cfg = e.cfg, s = cfg.s;
66
- const g = smooth(0, 0.2, w);
67
- mx(P, 'gaitW', 0, g); mx(P, 'legGait', 0, g);
68
- // the hips stay over the planted hind feet (heels down), the torso rises toward vertical
69
- const r = smooth(0.3, 1, w);
70
- // (over where the hind feet actually stand: the midpoint of their plants in the heading frame)
71
- const zPl = 0.5 * (e.worldToBody(e.legs[2].plant, tv()).z + e.worldToBody(e.legs[3].plant, tv()).z);
72
- // (the hips a little behind the heels: the knees flex slightly and the hip does not over-extend)
73
- tiltBody(P, g, e, STAND_PITCH * r, -0.02 * s * r, zPl - (0.04 + 0.08 * r) * s);
74
- mx(P, 'bodyOmega', 1.4, g);
75
- // forelegs hang in front of the chest, paws at the belly; they leave from and come back down to
76
- // their plants (the reach target starts and ends there, so nothing slides when the gait takes the
77
- // feet back)
78
- const hang = smooth(0.45, 0.9, w), kR = smooth(0, 0.3, w);
79
- const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
80
- for (let i = 0; i < 2; i++) {
81
- const Ld = e.legs[i].def;
82
- const tp = tv().copy(e.fr.shC).addScaledVector(fwd, 0.26 * s).addScaledVector(lat, Ld.s * 0.1 * s);
83
- tp.y -= 0.34 * s;
84
- tp.lerp(e.legs[i].plant, 1 - hang);
85
- P.legs[i].target.lerp(tp, kR / Math.max(1e-3, P.legs[i].reach + kR));
86
- mxLeg(P, i, 'reach', 1, kR);
87
- }
88
- // head held up and level, looking ahead (detail_bipedal); the neck carries it forward of the chest
89
- // (at a 0.85 rad nose-down pitch the chin was tucked to the chest and the nape stretched over the poll)
90
- // (the stand sets its own head pitch: the gaze offset of a look is taken back while it rises, update())
91
- mx(P, 'headRaise', 0.03 / cfg.unit * headUp, w); mx(P, 'headPitch', 0.35 * headUp, w);
92
- const b = B(e); b.standW = Math.max(b.standW, w);
93
- mx(P, 'earFlat', -0.1, w); mx(P, 'tailIdle', 0.2, w);
94
- }
95
-
96
- const standup = {
97
- kind: 'oneshot', fade: 0.6, needsStand: true,
98
- start(inst, L) {
99
- const e = L.engine;
100
- inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 4 + e.rand() * 3);
101
- const a = e.heading + (e.rand() - 0.5) * 1.4, d = 25 * e.cfg.s;
102
- inst.look = inst.opts.target ? inst.opts.target.clone() : new THREE.Vector3(e.pos.x + Math.sin(a) * d, e.pos.y + 1.5 * e.cfg.s, e.pos.z + Math.cos(a) * d);
103
- },
104
- update(inst) { return inst.t >= inst.dur; },
105
- apply(P, w, inst, L) {
106
- const e = L.engine, t = inst.t;
107
- const up = w * smooth(0, 1.1, t) * (1 - smooth(inst.dur - 0.9, inst.dur, t));
108
- applyStand(P, up, e);
109
- if (!e.input.look) { P.look = inst.look; mx(P, 'lookW', 1, up); }
110
- // scenting: short nose-up sniffs
111
- const sn = Math.max(0, Math.sin(t * TAU * 0.45)) * up;
112
- mx(P, 'headPitch', P.headPitch - 0.25, sn);
113
- mx(P, 'breathRate', 3, sn); mx(P, 'breathAmp', 0.5, sn);
114
- B(e).sniff = Math.max(B(e).sniff, sn);
115
- },
116
- };
117
-
118
- // sniffing the air on all fours: head raised, nose up, working
119
- const sniff = {
120
- kind: 'oneshot', fade: 0.4,
121
- start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 3 + L.engine.rand() * 2); inst.yaw = (L.engine.rand() - 0.5) * 0.8; },
122
- update(inst) { return inst.t >= inst.dur; },
123
- apply(P, w, inst, L) {
124
- const e = L.engine, t = inst.t;
125
- const k = w * smooth(0, 0.6, t);
126
- mx(P, 'headRaise', 0.1 / e.cfg.unit, k); mx(P, 'headPitch', -0.35 + 0.08 * Math.sin(t * 2.3), k);
127
- mx(P, 'headYaw', inst.yaw + 0.25 * Math.sin(t * 0.9), k); mx(P, 'lookW', 0, k);
128
- mx(P, 'breathRate', 3.5, k); mx(P, 'breathAmp', 0.5, k);
129
- mx(P, 'jaw', 0.02 + 0.02 * Math.max(0, Math.sin(t * 17)), k);
130
- B(e).sniff = Math.max(B(e).sniff, k);
131
- },
132
- };
133
-
134
- // digging / rooting: head low, alternate forepaw rakes toward the body (1.4 Hz), nose in the hole.
135
- // The paws stay in reach mode for the whole dig (claws down, wrist raised): between strokes a paw rests
136
- // where its rake ended. (Fading the reach out after every stroke swung the metacarpus from the claws-
137
- // down rake to the flat plantigrade palm, 93 deg in 55 ms: an 84 rad/s* pop.)
138
- function rakeTargets(P, k, e, t, rate) {
139
- const cfg = e.cfg, s = cfg.s;
140
- const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
141
- for (let i = 0; i < 2; i++) {
142
- const L = e.legs[i];
143
- // each paw in turn: reach forward and up, strike down, rake back along the ground; the path is
144
- // continuous from stroke to stroke (it starts and ends at the resting point 2 cm ahead of the plant)
145
- const ph = (t * rate + (i === 0 ? 0 : 0.5)) % 1;
146
- const u = Math.min(1, ph / 0.55);
147
- const z = 0.02 + 0.3 * (smooth(0, 0.35, u) - smooth(0.45, 1, u));
148
- const y = 0.13 * smooth(0, 0.22, u) * (1 - smooth(0.28, 0.55, u));
149
- const tp = tv().copy(e.legs[i].plant).addScaledVector(fwd, z * s).addScaledVector(lat, -L.def.s * 0.03 * s);
150
- tp.y = e.terrainH(tp.x, tp.z) + (y + 0.025) * s;
151
- P.legs[i].target.lerp(tp, k / Math.max(1e-3, P.legs[i].reach + k));
152
- mxLeg(P, i, 'reach', 1, k);
153
- }
154
- }
155
- const dig = {
156
- kind: 'oneshot', fade: 0.5,
157
- start(inst, L) { inst.dur = inst.opts.loop ? Infinity : (inst.opts.duration ?? 5); },
158
- update(inst) { return inst.t >= inst.dur; },
159
- apply(P, w, inst, L) {
160
- const e = L.engine, t = inst.t;
161
- const k = w * smooth(0, 0.6, t);
162
- mx(P, 'gaitW', 0, k); mx(P, 'legGait', 0, k);
163
- mx(P, 'dropF', 0.25, k); mx(P, 'dropH', -0.05, k);
164
- mx(P, 'headRaise', -0.2 / e.cfg.unit, k); mx(P, 'headPitch', 0.9, k); mx(P, 'lookW', 0, k);
165
- mx(P, 'headYaw', 0.15 * Math.sin(t * 1.4 * PI), k);
166
- const ks = k * smooth(0.5, 1.0, t);
167
- if (ks > 0) rakeTargets(P, ks, e, Math.max(0, t - 0.5), 1.4);
168
- mx(P, 'breathRate', 2, k);
169
- },
170
- };
171
-
172
- // sit on the rump like a person: hips down on the ground, the torso up at ~55 deg, the hind legs
173
- // stretched forward along the ground, the forepaws resting in the lap between them
174
- const sit = {
175
- kind: 'posture', fadeIn: 1.6, fadeOut: 1.3,
176
- apply(P, w, inst, L) {
177
- const e = L.engine, cfg = e.cfg, s = cfg.s;
178
- const wH = smooth(0, 0.7, w), wF = smooth(0.3, 1, w);
179
- mx(P, 'gaitW', 0, w); mx(P, 'legGait', 0, w);
180
- // the rump goes down first, then the torso comes up (the hips roll back a little onto the rump)
181
- const restH = cfg.pelvis.hh - (cfg.pelvis.y - cfg.yH);
182
- const pT = SIT_PITCH * wF;
183
- tiltBody(P, w, e, pT, -(cfg.yH - restH) * wH + 0.08 * s * Math.sin(pT), cfg.zH - 0.06 * s * wH);
184
- mx(P, 'groundW', 1, wH);
185
- const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
186
- // hind feet: forward of the hips, soles facing forward-up (heels on the ground)
187
- const wl = smooth(0.1, 0.8, w);
188
- for (let i = 2; i < 4; i++) {
189
- const Ld = e.legs[i].def;
190
- const tp = e.bodyToWorld(Ld.cx * Ld.s * 1.25, 0, cfg.zH + Ld.len * 0.95, e.heading, e.pos, tv());
191
- tp.y = e.terrainH(tp.x, tp.z) + 0.03 * s;
192
- P.legs[i].target.copy(tp);
193
- mxLeg(P, i, 'reach', 1, wl);
194
- }
195
- // forepaws in the lap
196
- const wp = smooth(0.45, 1, w);
197
- for (let i = 0; i < 2; i++) {
198
- const Ld = e.legs[i].def;
199
- const tp = e.bodyToWorld(Ld.cx * Ld.s * 0.7, 0, cfg.zH + Ld.len * 0.55, e.heading, e.pos, tv());
200
- tp.y = e.terrainH(tp.x, tp.z) + 0.28 * s;
201
- P.legs[i].target.lerp(tp, wp / Math.max(1e-3, P.legs[i].reach + wp));
202
- mxLeg(P, i, 'reach', 1, wp);
203
- }
204
- void fwd; void lat;
205
- mx(P, 'headRaise', 0.02 / cfg.unit, wF); mx(P, 'headPitch', 0.45, wF);
206
- mx(P, 'tailGround', 1, wH);
207
- },
208
- };
209
-
210
- // sleep: curled on the side (built-in curl), or prone on the belly with the chin on the forepaws
211
- const sleep = {
212
- kind: 'posture', fadeIn: 1.4, fadeOut: 1.2,
213
- apply(P, w, inst, L) {
214
- if (inst.style === undefined) inst.style = inst.opts.style || (L.engine.rand() < 0.5 ? 'curl' : 'prone');
215
- if (inst.style === 'curl') { POSTURES.sleep.apply(P, w, { ...inst, tune: { ...inst.tune, style: 'curl' } }, L); return; }
216
- POSTURES.lie.apply(P, w, inst, L);
217
- const e = L.engine, cfg = e.cfg, s = cfg.k, fr = e.fr;
218
- const c = smooth(0.8, 2.6, inst.t) * w;
219
- // chin resting on the crossed forepaws in front of the chest
220
- const hp = tv().copy(fr.neckBase).addScaledVector(fr.F, cfg.neckLen * 0.8);
221
- hp.y = e.terrainH(hp.x, hp.z) + 0.1 * s;
222
- P.headPos.lerp(hp, c / Math.max(1e-3, P.headW + c)); mx(P, 'headW', 1, c);
223
- mx(P, 'headPitch', 0.3, c); mx(P, 'headYaw', 0, c); mx(P, 'headLimp', 0.2, c);
224
- mx(P, 'eyelid', 1, smooth(1.6, 3.2, inst.t) * w);
225
- mx(P, 'breathRate', 0.45, c); mx(P, 'breathAmp', 1.4, c);
226
- mx(P, 'earFlat', 0.2, c); mx(P, 'earTwitch', 0.3, c); mx(P, 'lookW', 0, c);
227
- },
228
- };
229
-
230
- // attack: a charge, then it rears up, swats with one forepaw, comes down and bites
231
- const attack = {
232
- kind: 'oneshot', fade: 0.15, needsStand: true,
233
- start(inst, L) {
234
- const e = L.engine;
235
- inst.run = smooth(1.5, 3.5, e.speed / e.cfg.sq);
236
- inst.target = inst.opts.target ? inst.opts.target.clone() : null;
237
- // (from standing a lunge on the spot, the forefeet planted, then straight up: a rush from standing cost
238
- // 1.5-2 s of stepping to a stop before the rear-up could start, and the swat came 3.4 s after the
239
- // command; now ~1.2 s. Already running: it brakes and rears, ~1.7 s)
240
- inst.charge = inst.run > 0.5 ? 0.2 : 0;
241
- inst.lunge = inst.run > 0.5 ? 0 : 1;
242
- inst.dur = Infinity;
243
- inst.T0 = Infinity; // rear-up start: once the charge has stopped
244
- inst.side = e.rand() < 0.5 ? 0 : 1; // swatting paw
245
- inst.swat = false; inst.bit = false;
246
- },
247
- update(inst, dt, L) {
248
- const e = L.engine, cfg = e.cfg, t = inst.t;
249
- // (a short lumbering rush at the amble: the canter's footfall order would have to change twice)
250
- const vC = cfg.gears.amble || 2.4 * cfg.sq;
251
- if (t < inst.charge) e.wantSpeed = Math.max(e.wantSpeed, vC);
252
- else {
253
- // the charge brakes hard; it rears up once it has (nearly) stopped
254
- // (braking over ~0.4 s from the rush: the feet keep up; from a run it stops like stop())
255
- if (inst.v0 === undefined) inst.v0 = Math.max(vC, e.speed);
256
- e.wantSpeed = inst.run > 0.5 || inst.lunge ? 0 : inst.v0 * (1 - smooth(inst.charge, inst.charge + 0.4, t));
257
- // (standing, all four feet down: the gait has gathered the feet under the body first; the reach that
258
- // lifts the forepaws starts from their plants, so a forefoot still in the air snapped to it: a
259
- // 160-320 rad/s* pop. A quarter second of stillness is not needed: 0.08 s)
260
- if (e.speed > 0.08 * cfg.sq || !e.legs.every((l) => l.state === 'stance')) inst.still = 0;
261
- else inst.still = (inst.still || 0) + dt;
262
- if (inst.T0 === Infinity && inst.still > 0.08 && t > 0.3 * inst.lunge) inst.T0 = t;
263
- if (inst.T0 === Infinity && t > inst.charge + 4) inst.T0 = t;
264
- }
265
- const T0 = inst.T0;
266
- if (t >= T0 + 2.6) return true;
267
- if (!inst.swat && t >= T0 + 0.85) {
268
- inst.swat = true;
269
- const p = e.legs[inst.side].contact.clone();
270
- const dir = inst.target ? inst.target.clone().sub(e.pos).setY(0).normalize() : e.forward(e.heading, new THREE.Vector3());
271
- e.emit('attackHit', { position: p, direction: dir, style: 'swat' });
272
- }
273
- if (!inst.bit && t >= T0 + 1.85) {
274
- inst.bit = true;
275
- const p = new THREE.Vector3().copy(cfg.mouthLocal).applyQuaternion(e.headPose.quaternion).add(e.headPose.position);
276
- e.emit('attackHit', { position: p, direction: e.forward(e.heading, new THREE.Vector3()), style: 'bite' });
277
- }
278
- return false;
279
- },
280
- apply(P, w, inst, L) {
281
- const e = L.engine, cfg = e.cfg, s = cfg.s, t = inst.t, T0 = inst.T0;
282
- if (inst.target) { P.look = inst.target; mx(P, 'lookW', 1, w); }
283
- mx(P, 'earFlat', 1, w * smooth(0, 0.2, t)); mx(P, 'earTwitch', 0, w);
284
- // charge: head low, ears back
285
- const ch = w * (1 - smooth(inst.charge - 0.1, inst.charge + 0.4, t));
286
- mx(P, 'headRaise', -0.06 / cfg.unit, ch); mx(P, 'headPitch', 0.2, ch);
287
- // lunge from standing: the body surges forward over the planted feet, head low, a huff
288
- if (inst.lunge) {
289
- const lg = w * smooth(0, 0.25, t) * (1 - smooth(0.35, 0.7, t));
290
- mx(P, 'fwd', 0.1 / cfg.unit, lg); mx(P, 'dropF', 0.08, lg); mx(P, 'headRaise', -0.06 / cfg.unit, lg); mx(P, 'headPitch', 0.25, lg);
291
- mx(P, 'jaw', 0.2, lg * smooth(0.1, 0.25, t)); mx(P, 'breathAmp', 2.5, lg);
292
- }
293
- // rear up (not as high as the bipedal stand), swat, come down into the bite
294
- const u = Number.isFinite(T0) ? t - T0 : -10;
295
- const rear = w * smooth(0.1, 0.6, u) * (1 - smooth(1.0, 1.8, u));
296
- applyStand(P, rear * 0.8, e, 0.4);
297
- mx(P, 'jaw', 0.35, rear * smooth(0.3, 0.6, u));
298
- // the swat: the paw goes out and up to the side, then sweeps across and down
299
- const sw = rear * smooth(0.45, 0.7, u) * (1 - smooth(0.95, 1.15, u));
300
- if (sw > 0) {
301
- const i = inst.side, Ld = e.legs[i].def, sd = Ld.s;
302
- const fwd = e.forward(e.heading, tv()), lat = e.left(e.heading, tv());
303
- const a = smooth(0.6, 0.9, u); // 0 wound up to the side .. 1 swept across
304
- const tp = tv().copy(e.fr.shC).addScaledVector(fwd, lerp(0.25, 0.55, a) * s).addScaledVector(lat, sd * lerp(0.4, -0.15, a) * s);
305
- tp.y += lerp(0.25, -0.2, a) * s;
306
- P.legs[i].target.lerp(tp, sw / Math.max(1e-3, P.legs[i].reach + sw));
307
- mxLeg(P, i, 'reach', 1, sw);
308
- mx(P, 'bend', -sd * 0.25 * (a - 0.5), sw); mx(P, 'headYaw', sd * 0.3 * (0.5 - a), sw);
309
- }
310
- // bite: lunge forward and down, jaws open then snap shut, a shake of the head
311
- const bite = w * smooth(1.5, 1.75, u) * (1 - smooth(2.0, 2.5, u));
312
- mx(P, 'fwd', 0.18 / cfg.unit * 0.3, bite); mx(P, 'neckReach', 0.06 / cfg.unit, bite); mx(P, 'headRaise', -0.05 / cfg.unit, bite);
313
- mx(P, 'jaw', 0.55 * (1 - smooth(1.8, 1.9, u)), bite); mx(P, 'jawOmega', 30, w);
314
- mx(P, 'headRoll', 0.25 * Math.sin((u - 1.85) * 22) * smooth(1.85, 1.95, u), bite);
315
- void s;
316
- },
317
- };
318
-
319
- // hit: flinch back, then a loud huff with the head swung and the jaw popping
320
- const hit = {
321
- ...ONESHOTS.hit,
322
- kind: 'oneshot',
323
- start(inst, L) {
324
- ONESHOTS.hit.start(inst, L);
325
- inst.dur = 1.5;
326
- inst.opts = { ...inst.opts, strength: (inst.opts.strength ?? 1) * 0.6 };
327
- },
328
- update(inst) { return inst.t >= inst.dur; },
329
- apply(P, w, inst, L) {
330
- const t = inst.t;
331
- const k = smooth(0, 0.1, t) * Math.exp(-Math.max(0, t - 0.1) * 4) * w;
332
- mx(P, 'roll', inst.lx * 0.12, k); mx(P, 'bend', -inst.lx * 0.2, k);
333
- mx(P, 'pitch', inst.lz * 0.06, k); mx(P, 'dropF', 0.15, k); mx(P, 'dropH', 0.1, k);
334
- P.headLocal.set(inst.lx * 0.06, 0.02, inst.lz * 0.06 - 0.03); mx(P, 'headLocalW', 1, k);
335
- mx(P, 'eyelid', 0.6, k);
336
- // huff: head swings side to side (the swing that follows the blow), jaw pops twice
337
- const hs = w * smooth(0.25, 0.45, t) * (1 - smooth(1.1, 1.5, t));
338
- mx(P, 'headYaw', 0.45 * Math.sin((t - 0.25) * 7.5) * (inst.lx >= 0 ? -1 : 1), hs);
339
- mx(P, 'headRaise', -0.02, hs); mx(P, 'headPitch', 0.3, hs); mx(P, 'lookW', 0, hs);
340
- const pop = Math.max(smooth(0.35, 0.42, t) * (1 - smooth(0.46, 0.55, t)), smooth(0.7, 0.77, t) * (1 - smooth(0.81, 0.9, t)));
341
- mx(P, 'jaw', 0.3 * pop, w); mx(P, 'jawOmega', 28, w);
342
- mx(P, 'earFlat', 1, w * (1 - smooth(0.9, 1.5, t)));
343
- mx(P, 'breathAmp', 3, hs); mx(P, 'breathRate', 2.5, hs);
344
- },
345
- };
346
-
347
- // ------------------------------------------------------------------------------------------------
348
- function update(P, dt, L) {
349
- const e = L.engine, b = B(e), cfg = e.cfg;
350
- const vn = e.speed / cfg.sq;
351
- b.sniff *= Math.exp(-dt * 3);
352
- // the big broad head (cheeks ~15 cm from the midline with the ruff) rests higher on its side than
353
- // the engine's default dead-head height; the grazing / drinking mouth works just above the ground
354
- for (const p of L.postures) {
355
- if (p.name === 'death' && P.headW > 0) P.headPos.y += HEAD_SIDE * cfg.s * smooth(0.3, 1.0, p.t) * p.w;
356
- if (p.name === 'death') {
357
- // the broad paws lie on their sides: the engine's side-lying leg rest (3 cm up) put their edges
358
- // into the ground; rest them a paw's half width up (same place, reach mode)
359
- const fr = e.fr;
360
- for (let i = 0; i < 4; i++) {
361
- const pl = P.legs[i], k = pl.side;
362
- if (k < 1e-3) continue;
363
- const Ld = e.legs[i].def, gy = Ld.front ? cfg.yS : cfg.yH, out = pl.spread;
364
- const lz = (Ld.front ? cfg.zS + 0.18 * Ld.len : cfg.zH - 0.02 * Ld.len) + (Ld.s * Math.sign(P.roll || 1) > 0 ? 0.12 : 0) * Ld.len * out;
365
- const tp = tv().set(Ld.cx * Ld.s * 1.2, gy - Ld.len * lerp(0.85, 0.95, out), lz).applyQuaternion(fr.qB).add(fr.origin);
366
- const gh = e.terrainH(tp.x, tp.z) + 0.09 * cfg.s;
367
- tp.y = Math.max(gh, lerp(tp.y, gh, 0.85));
368
- pl.target.copy(tp);
369
- mxLeg(P, i, 'reach', 1, k);
370
- }
371
- }
372
- }
373
- for (const a of L.oneshots) {
374
- if ((a.name === 'eat' || a.name === 'drink') && P.headW > 0) P.headPos.y += 0.02 * cfg.s * a.w;
375
- }
376
- // lumbering walk: the head swings with the forelegs, the shoulders and hump roll over each
377
- // planted foreleg (walk and amble only)
378
- const walk = smooth(0.1, 0.5, vn) * (1 - smooth(2.4, 3.2, vn)) * P.gaitW;
379
- if (walk > 1e-3) {
380
- const ph = TAU * (e.phase - 0.25);
381
- // (+-6 deg with the forelegs, NOTES: the engine's own head stabilisation takes back half of it)
382
- mx(P, 'headYaw', P.headYaw + 0.2 * Math.sin(ph), walk);
383
- P.roll += 0.06 * Math.sin(ph) * walk;
384
- P.headRoll += 0.05 * Math.sin(ph) * walk;
385
- }
386
- // looking up raises the neck with the head: the head hangs low at rest (0.34 rad nose down, 8 cm under its
387
- // bind height), and a head pitched up about the poll alone to look level extended the poll ~18 deg past its
388
- // bind angle: the nape skin folded and its long hair stood up as a crack across the crown. The neck rises
389
- // 0.25 m per rad the look is above the carriage (about the neck's length: the poll keeps its angle)
390
- // (eased like the head's own pitch: a look target that jumps must not step the neck, 105 rad/s* pops)
391
- // The look pitch carries the gaze offset (GAZE: the muzzle below the line of sight), except while the bear
392
- // rises onto its hind legs, whose pose sets the head's pitch itself; the neck rises only for the part of the
393
- // look above the carriage that the offset leaves
394
- const look = e.input.look || P.look;
395
- const standW = b.standW; b.standW = 0;
396
- let upT = 0;
397
- if (look && P.lookW > 0.01) {
398
- const lw = clamp(P.lookW, 0, 1), gz = GAZE * (1 - smooth(0, 0.3, standW));
399
- P.headPitch += gz * lw;
400
- const hp = e.headPose.position;
401
- const lp = -Math.atan2(look.y - hp.y, Math.hypot(look.x - hp.x, look.z - hp.z));
402
- const cp = tableAt(cfg.headCarry, vn, b.carry || (b.carry = [0, 0, 0]))[2];
403
- upT = clamp(cp - (Math.max(lp, -cfg.head.pitchUp) + gz), 0, 0.5) * lw;
404
- }
405
- b.up = (b.up || 0) + (upT - (b.up || 0)) * (1 - Math.exp(-dt * 3));
406
- P.headRaise += 0.25 * b.up / cfg.unit;
407
- // the neck bends toward the head's yaw (the engine's eased head yaw: looks, the walk's head swing, turns), so the turn
408
- // is shared between the neck and the poll (added to any posture's occiput offset, e.g. the hit's flinch)
409
- const yawH = e.lookYaw ? e.lookYaw.x : 0;
410
- if (Math.abs(yawH) > 1e-4) {
411
- P.headLocal.multiplyScalar(P.headLocalW);
412
- P.headLocal.x += NECK_SWAY * clamp(yawH, -1.2, 1.2) / cfg.unit;
413
- P.headLocalW = 1;
414
- }
415
- // any other action interrupts grazing / drinking / digging
416
- if (L.oneshots.some((a) => !a.stopping && !/^(eat|drink|dig|hit|sniff)$/.test(a.name))) for (const a of L.oneshots) if (/^(eat|drink|dig)$/.test(a.name)) a.stopping = true;
417
- // a far look target: stand up on the hind legs to see over the grass (now and then)
418
- b.lookStandT -= dt;
419
- if (e.input.look && L.idle.w > 0.9 && !busy(L) && b.lookStandT <= 0) {
420
- const d = Math.hypot(e.input.look.x - e.pos.x, e.input.look.z - e.pos.z);
421
- b.lookStandT = 6 + e.rand() * 6;
422
- if (d > 12 * cfg.s && e.rand() < 0.7) L.play('standup', { target: e.input.look, duration: 3.5 + e.rand() * 2 });
423
- }
424
- // automatic idle variants
425
- if (e.idleVariants !== false && L.idle.w > 0.98 && !busy(L) && !e.input.look && !e.input.follow) {
426
- b.idleT -= dt;
427
- if (b.idleT <= 0) {
428
- b.idleT = 9 + e.rand() * 12;
429
- const r = e.rand();
430
- if (r < 0.35) L.play('sniff');
431
- else if (r < 0.6) L.play('standup');
432
- else if (r < 0.8) L.play('dig', { duration: 3 + e.rand() * 3 });
433
- }
434
- } else if (busy(L)) b.idleT = Math.max(b.idleT, 4);
435
- }
436
-
437
- // jump: the built-in jump, but the push-off never extends the legs past standing (the flat
438
- // plantigrade feet cannot roll up onto their toes while the gait is held: the knee snapped straight).
439
- // In the air a bear bounds (research 4.2: a poor jumper): the hind legs trail behind after the push-off
440
- // and swing forward only for the landing, the forelegs reach ahead to land first (the built-in flight
441
- // tucked all four feet under the body like a cat)
442
- const jump = {
443
- ...ONESHOTS.jump,
444
- kind: 'oneshot', airborne: true,
445
- apply(P, w, inst, L) {
446
- ONESHOTS.jump.apply(P, w, inst, L);
447
- if (inst.phase === 'crouch' || inst.phase === 'push') { P.dropH = Math.max(P.dropH, 0); P.dropF = Math.max(P.dropF, 0); }
448
- if (inst.phase === 'air') {
449
- const T = (2 * inst.vy) / 9.81, x = clamp((inst.t - inst.tAir) / T, 0, 1);
450
- const k = smooth(0, 0.12, inst.t - inst.tAir);
451
- // (both end at the built-in landing's 0.8, which the landing phase starts from: a hind leg arriving
452
- // at 0.35 snapped to it, 225 rad/s* at the hind paw)
453
- const extF = 0.3 + 0.5 * smooth(0.15, 0.85, x);
454
- const extH = 1 - 0.2 * smooth(0.4, 1, x);
455
- for (let i = 0; i < 4; i++) P.legs[i].extend = lerp(P.legs[i].extend, i < 2 ? extF : extH, k);
456
- }
457
- },
458
- };
459
-
460
- export const hooks = {
461
- update,
462
- actions: { standup, sniff, dig, sit, sleep, attack, hit, jump },
463
- };
@@ -1,147 +0,0 @@
1
- // Claws: real geometry (species.surfaces, appended to the base mesh), rigidly skinned to the paw bones.
2
- //
3
- // A brown bear's fore claws are 5-6 cm straight / 7-10 cm along the curve (x1.15 in the grizzly, x0.65 in the
4
- // black bear), the hind claws ~4 cm [NOTES 1]: thick horn at the base, laterally compressed (taller than wide),
5
- // tapering to a blunt point, curving down from the toe so they lie close along the ground in front of the toes
6
- // (side1, side3, tq2, detail_bipedal); dark horn with paler tips (in grizzlies paler horn, still darker at the base).
7
- // (As SDF cones in the body mesh, at its 7.8-15.6 mm cells, they were faceted spikes 0.6-0.9 cm thick sticking
8
- // straight out 5-6 cm ahead of the toes at toe height, with the mouth's pink interpolated round their bases where
9
- // the material id switched from fur to keratin.)
10
- // Each claw is a closed tube along a circular arc in the paw's sagittal plane: an elliptical cross-section
11
- // (height 1.5 x width at the base) that tapers to an apex, its root buried in the toe.
12
- import { srgb, MAT } from '../../core/build/coatKit.js';
13
- import { add, mul, norm, cross } from '../../core/math/vec.js';
14
- import { TOE_F, TOE_H } from './sculpt.js';
15
- import { clawColour } from './coat.js';
16
-
17
- // digits I..V of the left paw (x from the midline out: I is medial), toe sphere offsets as in sculpt.js
18
- const FORE = { x: [-0.056, -0.028, 0.0, 0.028, 0.056], z: [-0.02, -0.004, 0.002, -0.004, -0.02], y: 0.024, len: [0.78, 0.94, 1, 0.97, 0.84] };
19
- const HIND = { x: [-0.048, -0.024, 0.0, 0.024, 0.048], z: [-0.018, -0.004, 0.002, -0.004, -0.016], y: 0.022, len: [0.8, 0.95, 1, 0.97, 0.85] };
20
-
21
- // the claw's centre line: pitch below horizontal from th0 at the root to th1 at the tip, along an arc of length L;
22
- // th1 is chosen so the tip ends `drop` below the root (clamped to 75 deg: short black-bear claws hook more steeply)
23
- function arcFor(L, drop, th0) {
24
- const dropOf = (th1) => (Math.abs(th1 - th0) < 1e-4 ? L * Math.sin(th0) : (L * (Math.cos(th0) - Math.cos(th1))) / (th1 - th0));
25
- let lo = th0, hi = (75 * Math.PI) / 180;
26
- if (dropOf(hi) < drop) return hi;
27
- if (dropOf(lo) > drop) return lo;
28
- for (let i = 0; i < 30; i++) { const m = (lo + hi) / 2; if (dropOf(m) < drop) lo = m; else hi = m; }
29
- return (lo + hi) / 2;
30
- }
31
-
32
- export function clawSpecs(rig, params = {}) {
33
- const J = rig.J, k = params.claw ?? 1;
34
- const out = [];
35
- for (const S of ['L', 'R']) {
36
- const s = S === 'L' ? 1 : -1;
37
- for (const fore of [true, false]) {
38
- const D = fore ? FORE : HIND;
39
- const T = fore ? J['ftoe' + S] : J['htoe' + S];
40
- const back = fore ? TOE_F : TOE_H;
41
- for (let i = 0; i < 5; i++) {
42
- const tp = [T[0] + D.x[i] * s, D.y, T[2] - back - 0.012 + D.z[i]];
43
- // root inside the toe, at its front, at the toe's mid height (the toe's hair is 8 mm: the claw shows from there on)
44
- // (rooted at the top of the toe the claws dropped ~3 cm to the ground and hung from the toes like talons)
45
- const root = add(tp, [D.x[i] * s * 0.08, 0.001, fore ? 0.012 : 0.01]);
46
- // fore middle claw 5.8 cm along the curve (~4.2 cm of it outside the toe), hind 2.6 cm (~1.4 cm showing: the hind
47
- // claws are much shorter, mostly hidden in the toe hair)
48
- const L = (fore ? 0.058 : 0.026) * k * D.len[i];
49
- // the tip 5 mm above the ground (standing), at most: in the stance the claws lie along the ground
50
- const th0 = ((fore ? 3 : 10) * Math.PI) / 180;
51
- const th1 = arcFor(L, root[1] - (fore ? 0.006 : 0.008), th0);
52
- // splay: the outer claws turn a little outward with their toes
53
- const yaw = D.x[i] * s * 2.4;
54
- // (the width grows nearly with the length: at sqrt(claw) the grizzly's long claws read as thin needles)
55
- const kk = k ** 0.75;
56
- out.push({
57
- bone: (fore ? 'fpaw' : 'hpaw') + S, root, L, th0, th1, yaw,
58
- // (thick at the base: at 1.05 cm wide the fore claws of a lifted paw read as thin spikes in the live page)
59
- h0: (fore ? 0.0175 : 0.0125) * kk * (0.85 + 0.15 * D.len[i]), w0: (fore ? 0.012 : 0.009) * kk * (0.85 + 0.15 * D.len[i]),
60
- });
61
- }
62
- }
63
- }
64
- return out;
65
- }
66
-
67
- // centre-line point, tangent and arc normal (toward the claw's dorsal side) at t in [0, 1]
68
- function frameAt(c, t) {
69
- const n = 16;
70
- let x = 0, y = 0;
71
- const thAt = (u) => c.th0 + (c.th1 - c.th0) * u;
72
- for (let i = 0; i < n; i++) { const u = ((i + 0.5) / n) * t; x += Math.cos(thAt(u)) * (c.L * t) / n; y -= Math.sin(thAt(u)) * (c.L * t) / n; }
73
- const th = thAt(t);
74
- const cy = Math.cos(c.yaw), sy = Math.sin(c.yaw);
75
- const fwd = [sy, 0, cy];
76
- const P = add(c.root, add(mul(fwd, x), [0, y, 0]));
77
- const Tn = norm(add(mul(fwd, Math.cos(th)), [0, -Math.sin(th), 0]));
78
- const Up = norm(add(mul(fwd, Math.sin(th)), [0, Math.cos(th), 0]));
79
- return { P, T: Tn, U: Up, X: norm(cross(Up, Tn)) };
80
- }
81
-
82
- export function bearClaws({ rig, params, Q }) {
83
- if (!Q || Q.shells <= 0) return null;
84
- const SEG = Q.res <= 1.05 ? 8 : Q.res <= 1.5 ? 7 : 4;
85
- const SIDES = Q.res <= 1.05 ? 8 : Q.res <= 1.5 ? 7 : Q.res <= 2.5 ? 6 : 5;
86
- const { base, tip } = clawColour(params);
87
- const pos = [], nrm = [], idx = [], comb = [], skinIndex = [], tint = [];
88
- for (const c of clawSpecs(rig, params)) {
89
- const bi = rig.BONE[c.bone].index;
90
- const v0 = pos.length / 3;
91
- const push = (p, n, T, t) => {
92
- pos.push(...p); nrm.push(...n); comb.push(...T); skinIndex.push(bi, 0, 0, 0);
93
- // dark horn at the base, paler toward the tip (worn keratin; the tip's last third turns down out of the sun, so the
94
- // paler horn starts a third of the way out)
95
- const w = Math.min(1, Math.max(0, (t - 0.3) / 0.7)) ** 1.2;
96
- tint.push(base[0] + (tip[0] - base[0]) * w, base[1] + (tip[1] - base[1]) * w, base[2] + (tip[2] - base[2]) * w, MAT.KERATIN);
97
- };
98
- // root cap centre (buried in the toe)
99
- const f0 = frameAt(c, 0);
100
- push(f0.P, mul(f0.T, -1), f0.X, 0);
101
- for (let k = 0; k < SEG; k++) {
102
- // rings closer together toward the tip, where the claw turns and narrows
103
- const t = 1 - (1 - k / SEG) ** 1.25;
104
- const f = frameAt(c, t);
105
- // the section tapers to a blunt point (height 1.8 mm, width 1.4 mm, at the last ring)
106
- const taper = (1 - t) ** 0.6;
107
- const a = Math.max(0.0007, c.w0 * 0.5 * taper), b = Math.max(0.0009, c.h0 * 0.5 * taper);
108
- for (let q = 0; q < SIDES; q++) {
109
- const ang = (q / SIDES) * Math.PI * 2;
110
- const ca = Math.cos(ang), sa = Math.sin(ang);
111
- // (the dorsal edge rounder, the sole a little flatter: the centre sits slightly below the section's middle)
112
- const p = add(f.P, add(mul(f.X, a * ca), mul(f.U, b * sa - (sa < 0 ? 0.1 * b * sa : 0))));
113
- const n = norm(add(mul(f.X, ca / a), mul(f.U, sa / b)));
114
- // the comb runs round the claw, not along it: the keratin's anisotropic lobe (a sheen wherever the half vector
115
- // is square to the comb) then lies in one thin line instead of silvering the claw's whole lit side
116
- push(p, n, norm(add(mul(f.X, -a * sa), mul(f.U, b * ca))), t);
117
- }
118
- }
119
- const f1 = frameAt(c, 1);
120
- push(f1.P, f1.T, f1.X, 1);
121
- const ring = (k) => v0 + 1 + k * SIDES;
122
- for (let q = 0; q < SIDES; q++) { const q1 = (q + 1) % SIDES; idx.push(v0, ring(0) + q1, ring(0) + q); }
123
- for (let k = 0; k < SEG - 1; k++) {
124
- for (let q = 0; q < SIDES; q++) {
125
- const q1 = (q + 1) % SIDES;
126
- const a0 = ring(k) + q, b0 = ring(k) + q1, a1 = ring(k + 1) + q, b1 = ring(k + 1) + q1;
127
- idx.push(a0, b0, a1, b0, b1, a1);
128
- }
129
- }
130
- const apex = pos.length / 3 - 1, last = ring(SEG - 1);
131
- for (let q = 0; q < SIDES; q++) { const q1 = (q + 1) % SIDES; idx.push(last + q, last + q1, apex); }
132
- }
133
- const nV = pos.length / 3;
134
- const surf = new Float32Array(nV * 4);
135
- // matte horn (the keratin's roughest, 0.55): at 0.6 (the default) the tips flashed white in the sun, at 0.35 the
136
- // anisotropic sheen ran along each claw as a silver stripe and the sky's reflection silvered their sides
137
- for (let v = 0; v < nV; v++) surf[v * 4] = 0.001;
138
- const skinWeight = new Float32Array(nV * 4);
139
- for (let v = 0; v < nV; v++) skinWeight[v * 4] = 1;
140
- return {
141
- nV, pos: new Float32Array(pos), nrm: new Float32Array(nrm), index: new Uint32Array(idx),
142
- skinIndex: new Uint16Array(skinIndex), skinWeight, comb: new Float32Array(comb), tint: new Float32Array(tint),
143
- pattern: new Float32Array(nV).fill(1), mark: new Float32Array(nV).fill(1), furLen: new Float32Array(nV),
144
- patternColor: new Float32Array(nV * 4), surf,
145
- };
146
- }
147
- void srgb;