@bitefoundry/retrieviewer 0.0.0-stage → 0.0.1

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 (292) hide show
  1. package/ASSET-PROVENANCE.md +43 -0
  2. package/ASSETS-LICENSE.md +58 -0
  3. package/ATTRIBUTION.md +54 -0
  4. package/EMBEDDING.md +96 -0
  5. package/INSTALLING.md +107 -0
  6. package/LICENSE +16 -0
  7. package/LICENSE-CODE +21 -0
  8. package/PLUGIN.md +110 -0
  9. package/PUPPY-MEMORY.md +57 -0
  10. package/PUPPY-Q5-COMPARISON.md +73 -0
  11. package/README.md +61 -2
  12. package/RELEASING.md +61 -0
  13. package/THIRD-PARTY-NOTICES.md +17 -0
  14. package/TRADEMARKS.md +21 -0
  15. package/braille.mjs +45 -0
  16. package/examples/bridge.py +62 -0
  17. package/examples/embed.mjs +15 -0
  18. package/examples/live.mjs +40 -0
  19. package/examples/plugin.mjs +21 -0
  20. package/integrations/opencode/runtime.mjs +195 -0
  21. package/integrations/opencode/tui.tsx +59 -0
  22. package/locomotion-core.mjs +21 -0
  23. package/locomotion-puppy.animal +0 -0
  24. package/package.json +125 -4
  25. package/pet-q5-manifest.json +118 -0
  26. package/puppy-clean.mjs +51 -0
  27. package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
  28. package/puppy-companion-interactions-v3.json +1 -0
  29. package/puppy-companion-life.mjs +182 -0
  30. package/puppy-companion-objects.mjs +30 -0
  31. package/puppy-companion-sprites.mjs +14 -0
  32. package/puppy-continuous.mjs +57 -0
  33. package/puppy-face.mjs +46 -0
  34. package/puppy-frame-predictive.mjs +147 -0
  35. package/puppy-frame-store.mjs +108 -0
  36. package/puppy-mouth-anchor.json +1 -0
  37. package/puppy-mouth-anchor.mjs +33 -0
  38. package/puppy-paper-actions-v9-q5.bin.gz +0 -0
  39. package/puppy-paper-actions-v9.json +1 -0
  40. package/puppy-paper-actions.mjs +48 -0
  41. package/puppy-paper-approach-q5.bin.gz +0 -0
  42. package/puppy-paper-approach.json +1 -0
  43. package/puppy-paper-area.mjs +15 -0
  44. package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
  45. package/puppy-paper-arrival-seat.json +1 -0
  46. package/puppy-paper-behaviors-q5.bin.gz +0 -0
  47. package/puppy-paper-behaviors.json +1 -0
  48. package/puppy-paper-behaviors.mjs +8 -0
  49. package/puppy-paper-contact.mjs +103 -0
  50. package/puppy-paper-director.mjs +21 -0
  51. package/puppy-paper-paw-transport.mjs +76 -0
  52. package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
  53. package/puppy-paper-rear-gallop.json +1 -0
  54. package/puppy-paper-seat-q5.bin.gz +0 -0
  55. package/puppy-paper-seat.json +14 -0
  56. package/puppy-paper-seat.mjs +13 -0
  57. package/puppy-paper-trick-timing.mjs +58 -0
  58. package/puppy-paper-world.mjs +108 -0
  59. package/puppy-paper.mjs +271 -0
  60. package/puppy-stable-meta.json +1606 -0
  61. package/puppy-stable.mjs +72 -0
  62. package/puppy-stride-meta.json +343 -0
  63. package/puppy-stride-tweens-q5.bin.gz +0 -0
  64. package/puppy-zoomies.mjs +27 -0
  65. package/puppy-zoomies30.mjs +12 -0
  66. package/retrieviewer-cli.mjs +81 -0
  67. package/retrieviewer-config.mjs +49 -0
  68. package/retrieviewer-install.mjs +157 -0
  69. package/retrieviewer-plugin.d.mts +44 -0
  70. package/retrieviewer-plugin.mjs +97 -0
  71. package/retrieviewer-stdio.mjs +38 -0
  72. package/retrieviewer.d.mts +85 -0
  73. package/retrieviewer.mjs +142 -0
  74. package/vendor/procedural-animals/LICENSE +21 -0
  75. package/vendor/procedural-animals/package.json +40 -0
  76. package/vendor/procedural-animals/src/animal.js +146 -0
  77. package/vendor/procedural-animals/src/bake.js +51 -0
  78. package/vendor/procedural-animals/src/build.js +81 -0
  79. package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
  80. package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
  81. package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
  82. package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
  83. package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
  84. package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
  85. package/vendor/procedural-animals/src/core/build/seams.js +183 -0
  86. package/vendor/procedural-animals/src/core/build/warp.js +112 -0
  87. package/vendor/procedural-animals/src/core/build/weights.js +393 -0
  88. package/vendor/procedural-animals/src/core/math/vec.js +81 -0
  89. package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
  90. package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
  91. package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
  92. package/vendor/procedural-animals/src/core/motion/common.js +158 -0
  93. package/vendor/procedural-animals/src/core/motion/index.js +22 -0
  94. package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
  95. package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
  96. package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
  97. package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
  98. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
  99. package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
  100. package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
  101. package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
  102. package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
  103. package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
  104. package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
  105. package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
  106. package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
  107. package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
  108. package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
  109. package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
  110. package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
  111. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
  112. package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
  113. package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
  114. package/vendor/procedural-animals/src/index.d.ts +98 -0
  115. package/vendor/procedural-animals/src/index.js +29 -0
  116. package/vendor/procedural-animals/src/registry.js +48 -0
  117. package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
  118. package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
  119. package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
  120. package/vendor/procedural-animals/src/species/bear/index.js +139 -0
  121. package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
  122. package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
  123. package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
  124. package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
  125. package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
  126. package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
  127. package/vendor/procedural-animals/src/species/boar/index.js +110 -0
  128. package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
  129. package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
  130. package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
  131. package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
  132. package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
  133. package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
  134. package/vendor/procedural-animals/src/species/cat/index.js +211 -0
  135. package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
  136. package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
  137. package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
  138. package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
  139. package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
  140. package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
  141. package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
  142. package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
  143. package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
  144. package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
  145. package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
  146. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
  147. package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
  148. package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
  149. package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
  150. package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
  151. package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
  152. package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
  153. package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
  154. package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
  155. package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
  156. package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
  157. package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
  158. package/vendor/procedural-animals/src/species/cow/index.js +118 -0
  159. package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
  160. package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
  161. package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
  162. package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
  163. package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
  164. package/vendor/procedural-animals/src/species/crow/index.js +70 -0
  165. package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
  166. package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
  167. package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
  168. package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
  169. package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
  170. package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
  171. package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
  172. package/vendor/procedural-animals/src/species/deer/index.js +106 -0
  173. package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
  174. package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
  175. package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
  176. package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
  177. package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
  178. package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
  179. package/vendor/procedural-animals/src/species/dog/index.js +144 -0
  180. package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
  181. package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
  182. package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
  183. package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
  184. package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
  185. package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
  186. package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
  187. package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
  188. package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
  189. package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
  190. package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
  191. package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
  192. package/vendor/procedural-animals/src/species/fish/index.js +88 -0
  193. package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
  194. package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
  195. package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
  196. package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
  197. package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
  198. package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
  199. package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
  200. package/vendor/procedural-animals/src/species/fox/index.js +120 -0
  201. package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
  202. package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
  203. package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
  204. package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
  205. package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
  206. package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
  207. package/vendor/procedural-animals/src/species/frog/index.js +97 -0
  208. package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
  209. package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
  210. package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
  211. package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
  212. package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
  213. package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
  214. package/vendor/procedural-animals/src/species/goat/index.js +149 -0
  215. package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
  216. package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
  217. package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
  218. package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
  219. package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
  220. package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
  221. package/vendor/procedural-animals/src/species/horse/index.js +80 -0
  222. package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
  223. package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
  224. package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
  225. package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
  226. package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
  227. package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
  228. package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
  229. package/vendor/procedural-animals/src/species/lion/index.js +111 -0
  230. package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
  231. package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
  232. package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
  233. package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
  234. package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
  235. package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
  236. package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
  237. package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
  238. package/vendor/procedural-animals/src/species/pig/index.js +161 -0
  239. package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
  240. package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
  241. package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
  242. package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
  243. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
  244. package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
  245. package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
  246. package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
  247. package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
  248. package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
  249. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
  250. package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
  251. package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
  252. package/vendor/procedural-animals/src/species/rat/index.js +111 -0
  253. package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
  254. package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
  255. package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
  256. package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
  257. package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
  258. package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
  259. package/vendor/procedural-animals/src/species/shark/index.js +104 -0
  260. package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
  261. package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
  262. package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
  263. package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
  264. package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
  265. package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
  266. package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
  267. package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
  268. package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
  269. package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
  270. package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
  271. package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
  272. package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
  273. package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
  274. package/vendor/procedural-animals/src/species/snake/index.js +73 -0
  275. package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
  276. package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
  277. package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
  278. package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
  279. package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
  280. package/vendor/procedural-animals/src/species/spider/index.js +78 -0
  281. package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
  282. package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
  283. package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
  284. package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
  285. package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
  286. package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
  287. package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
  288. package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
  289. package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
  290. package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
  291. package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
  292. package/vendor/procedural-animals/src/worker.js +29 -0
@@ -0,0 +1,118 @@
1
+ // Spider skeleton in bind pose (metres; dimensions below are written in millimetres).
2
+ // Forward = +Z, up = +Y, the animal's left = +X. Two variants share the rig: the wolf spider
3
+ // (Lycosidae, reference female Hogna carolinensis, body 25 mm) and the tarantula (Theraphosidae,
4
+ // reference female Brachypelma hamorii, body 60 mm).
5
+ import { buildRig } from '../../core/rig/rig.js';
6
+ import { spiderBones, spiderLimbs, planarChain, solveBindElevations, legJoints, palpJoints, AXIAL_POINTS, AXIAL_BONES } from '../../core/rig/spider.js';
7
+
8
+ const D2R = Math.PI / 180;
9
+ const mm = (v) => v.map((x) => x * 0.001);
10
+
11
+ // Per-variant anatomy (mm, degrees). leg: [I, II, III, IV] total lengths; seg: segment fractions
12
+ // coxa, trochanter, femur, patella, tibia, metatarsus, tarsus; el: bind elevations of the segments
13
+ // (the metatarsus is solved so the claw rests on the ground); r: segment radii (base, end).
14
+ export const VARIANTS = {
15
+ wolf: {
16
+ unit: 0.013,
17
+ bodyLen: 25,
18
+ axial: { front: [0, 7.4, 6.5], ceph: [0, 7.6, 1.6], pedA: [0, 6.3, -4.5], pedB: [0, 6.1, -5.7], abdEnd: [0, 5.4, -17.2], spinTip: [0, 4.7, -18.5] },
19
+ carapace: { len: 11, wid: 8.6, top: 9.9, rearTop: 7.3, bottom: 4.5, headW: 5.2 },
20
+ abdomen: { c: [0, 6.5, -11.4], r: [3.9, 3.35, 6.1] },
21
+ chel: { base: [0.95, 7.0, 6.1], tip: [0.9, 3.8, 7.0], fang: [0.15, 4.25, 6.75], r: [0.95, 0.72], fangR: 0.2 },
22
+ coxa: { y: 5.1, x: [2.35, 2.75, 2.75, 2.45], z: [3.3, 1.4, -0.6, -2.6], az: [30, 66, 112, 150] },
23
+ leg: [35, 33, 31, 43],
24
+ seg: [0.08, 0.05, 0.26, 0.12, 0.2, 0.19, 0.1],
25
+ el: [-14, 4, 30, -6, -28, 0, -14],
26
+ legR: [[1.05, 0.95], [0.84, 0.8], [0.8, 0.68], [0.7, 0.63], [0.63, 0.53], [0.5, 0.4], [0.39, 0.29]],
27
+ hindR: 1.06,
28
+ tipY: 0.32,
29
+ palp: { base: [1.75, 5.2, 5.4], az: 14, len: 16, seg: [0.12, 0.08, 0.3, 0.15, 0.18, 0.17], el: [-30, -10, 15, -30, 0, -50], r: [[0.62, 0.55], [0.5, 0.48], [0.46, 0.42], [0.42, 0.4], [0.4, 0.37], [0.37, 0.28]], tipY: 0.55 },
30
+ spinR: 0.45,
31
+ },
32
+ tarantula: {
33
+ unit: 0.028,
34
+ bodyLen: 60,
35
+ axial: { front: [0, 13.2, 12.8], ceph: [0, 14.2, 3.5], pedA: [0, 12.0, -11.6], pedB: [0, 11.8, -13.6], abdEnd: [0, 10.8, -41.5], spinTip: [0, 9.2, -46] },
36
+ carapace: { len: 25, wid: 23.5, top: 16.2, rearTop: 14.0, bottom: 7.4, headW: 13 },
37
+ abdomen: { c: [0, 12.4, -27.6], r: [12.0, 9.6, 14.4] },
38
+ chel: { base: [3.0, 12.6, 11.2], tip: [2.9, 9.4, 18.0], fang: [2.6, 4.9, 16.4], r: [3.1, 2.4], fangR: 0.75 },
39
+ coxa: { y: 8.6, x: [5.8, 6.7, 6.7, 6.0], z: [6.2, 2.0, -2.6, -7.0], az: [26, 60, 114, 150] },
40
+ leg: [65, 60, 55, 72.5],
41
+ seg: [0.09, 0.06, 0.25, 0.14, 0.2, 0.16, 0.1],
42
+ el: [-10, 6, 34, -4, -30, 0, -13],
43
+ legR: [[3.0, 2.7], [2.4, 2.3], [2.3, 2.0], [2.05, 1.95], [1.95, 1.75], [1.65, 1.45], [1.45, 1.25]],
44
+ hindR: 1.04,
45
+ tipY: 1.3,
46
+ palp: { base: [4.6, 8.4, 10.8], az: 16, len: 37, seg: [0.14, 0.08, 0.28, 0.16, 0.18, 0.16], el: [-30, -10, 15, -30, 0, -40], r: [[2.2, 2.0], [1.9, 1.8], [1.8, 1.65], [1.65, 1.55], [1.55, 1.45], [1.45, 1.2]], tipY: 1.3 },
47
+ spinR: 1.3,
48
+ },
49
+ };
50
+
51
+ // Anatomy of one individual: the variant's reference numbers with the seeded proportions of
52
+ // params (legK: leg length factor, abdK: abdomen size, palpBulb: male palpal bulbs).
53
+ export function spiderDims(params = {}) {
54
+ const V = VARIANTS[params.variant] || VARIANTS.wolf;
55
+ const legK = params.legK || 1, abdK = params.abdK || 1;
56
+ const d = JSON.parse(JSON.stringify(V));
57
+ d.variant = VARIANTS[params.variant] ? params.variant : 'wolf';
58
+ d.leg = V.leg.map((l) => l * legK);
59
+ d.palp.len = V.palp.len * (0.5 + 0.5 * legK);
60
+ d.abdomen.r = V.abdomen.r.map((r) => r * abdK);
61
+ // a bigger abdomen hangs further back from the pedicel
62
+ d.abdomen.c = [0, V.abdomen.c[1] + (abdK - 1) * 0.4 * V.abdomen.r[1], V.abdomen.c[2] - (abdK - 1) * V.abdomen.r[2]];
63
+ d.axial.abdEnd = [0, V.axial.abdEnd[1] - (abdK - 1) * 0.3 * V.abdomen.r[1], V.axial.abdEnd[2] - 2 * (abdK - 1) * V.abdomen.r[2]];
64
+ d.axial.spinTip = [0, V.axial.spinTip[1] - (abdK - 1) * 0.3 * V.abdomen.r[1], V.axial.spinTip[2] - 2 * (abdK - 1) * V.abdomen.r[2]];
65
+ d.palpBulb = params.palpBulb || 0;
66
+ return d;
67
+ }
68
+
69
+ export function spiderJoints(params = {}) {
70
+ const d = spiderDims(params);
71
+ const J = {};
72
+ for (const [k, v] of Object.entries(d.axial)) J[k] = mm(v);
73
+ J.cheBaseL = mm(d.chel.base);
74
+ J.cheTipL = mm(d.chel.tip);
75
+ J.fangTipL = mm(d.chel.fang);
76
+ for (let i = 1; i <= 4; i++) {
77
+ const L = d.leg[i - 1];
78
+ const lens = d.seg.map((f) => f * L * 0.001);
79
+ const base = mm([d.coxa.x[i - 1], d.coxa.y, d.coxa.z[i - 1]]);
80
+ const el = solveBindElevations(base[1], lens, d.el.map((e) => e * D2R), 5, d.tipY * 0.001);
81
+ planarChain(J, legJoints(i, 'L'), base, d.coxa.az[i - 1] * D2R, el, lens, 1);
82
+ }
83
+ {
84
+ const P = d.palp;
85
+ const lens = P.seg.map((f) => f * P.len * 0.001);
86
+ const base = mm(P.base);
87
+ // the tibia takes up the height so the tarsus meets the ground slanting forward
88
+ const el = solveBindElevations(base[1], lens, P.el.map((e) => e * D2R), 4, P.tipY * 0.001);
89
+ planarChain(J, palpJoints('L'), base, P.az * D2R, el, lens, 1);
90
+ }
91
+ return J;
92
+ }
93
+
94
+ export function spiderRig(params = {}) {
95
+ const d = spiderDims(params);
96
+ const J = spiderJoints(params);
97
+ const U = d.unit;
98
+ return buildRig({
99
+ joints: J,
100
+ unit: U,
101
+ headOrigin: J.ceph,
102
+ meta: { dims: d },
103
+ bones: spiderBones(),
104
+ axial: {
105
+ points: AXIAL_POINTS,
106
+ bones: AXIAL_BONES,
107
+ // the carapace is rigid: head and prosoma share it (the engine never bends them apart); the
108
+ // pedicel is a short stalk, blended narrowly into the abdomen
109
+ blend: { 1: [-0.1, 0.1], 2: [-0.012, 0.004], 3: [-0.004, 0.02] },
110
+ },
111
+ limbs: spiderLimbs({
112
+ leg: { tCore: 0.5, aCore: 0.05, aBody: -0.05, midX: 0.06 },
113
+ palp: { tCore: 0.45, aCore: 0.04, aBody: -0.05, midX: 0.04 },
114
+ }),
115
+ appendages: [],
116
+ webTags: [],
117
+ });
118
+ }
@@ -0,0 +1,182 @@
1
+ // The spider, sculpted as a signed distance field (reference space, metres).
2
+ //
3
+ // Parts (meshed separately, see regions.js): 'body' (prosoma, pedicel, abdomen, spinnerets, palps,
4
+ // coxae and trochanters), one part per leg ('legL1' .. 'legR4': coxa -> tarsus, the coxa and
5
+ // trochanter duplicated so both surfaces agree across the cut), 'chelL'/'chelR' (rigid chelicerae)
6
+ // and 'fangL'/'fangR' (rigid fangs). Eyes are glossy domes (look.lids 'simple') set in shallow
7
+ // sockets on the head.
8
+ import { spiderDims } from './rig.js';
9
+ import { legJoints, legBones, palpJoints, palpBones } from '../../core/rig/spider.js';
10
+ import { add, sub, mul, lerp, norm, cross, len } from '../../core/math/vec.js';
11
+ import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
12
+ import { QUALITY } from '../../core/build/pipeline.js';
13
+
14
+ const M = 0.001;
15
+ const mm = (v) => v.map((x) => x * M);
16
+
17
+ // Eyes per variant (mm, head-local: relative to the ceph joint; left side). r: radius; yaw: outward
18
+ // divergence of the view axis (rad, > PI/2 looks backward); pitch: upward tilt; back: how far the
19
+ // dome centre sits inside the surface point.
20
+ export const EYES = {
21
+ // Lycosidae 4-2-2: a straight anterior row of 4 small eyes above the chelicerae, 2 huge posterior
22
+ // median eyes looking forward, 2 posterior lateral eyes on top looking up and back
23
+ wolf: [
24
+ // (set into the carapace surface: the domes bulge ~0.4 r; the PME sit on the front of the face)
25
+ { name: 'PME', c: [1.02, 1.2, 4.57], r: 0.47, yaw: 0.14, pitch: 0.02, back: 0.2 },
26
+ { name: 'PLE', c: [1.78, 1.54, 2.72], r: 0.3, yaw: 2.1, pitch: 0.75, back: 0.14 },
27
+ { name: 'AME', c: [0.42, 0.3, 5.26], r: 0.22, yaw: 0.05, pitch: -0.05, back: 0.07 },
28
+ { name: 'ALE', c: [1.0, 0.3, 5.05], r: 0.17, yaw: 0.55, pitch: -0.05, back: 0.05 },
29
+ ],
30
+ // Theraphosidae: 8 small eyes clustered on a raised ocular tubercle
31
+ tarantula: [
32
+ { name: 'AME', c: [0.62, 2.55, 6.1], r: 0.5, yaw: 0.08, pitch: 0.5, back: 0.2 },
33
+ { name: 'ALE', c: [1.62, 2.05, 6.25], r: 0.52, yaw: 0.6, pitch: 0.25, back: 0.2 },
34
+ { name: 'PME', c: [0.85, 2.6, 5.05], r: 0.3, yaw: 0.5, pitch: 1.0, back: 0.12 },
35
+ { name: 'PLE', c: [1.72, 2.2, 4.75], r: 0.42, yaw: 1.7, pitch: 0.45, back: 0.16 },
36
+ ],
37
+ };
38
+
39
+ export function eyeSpec(e) {
40
+ const r = e.r * M;
41
+ return { c: mm(e.c), r, back: e.back * M, yaw: e.yaw, pitch: e.pitch, tilt: 0, lid: 0, R: r * 1.1, d: r * 0.6, off: 0, irisZ: r * 0.6, irisR: r * 0.8, name: e.name };
42
+ }
43
+
44
+ // minimum radius of thin parts per quality tier (coarse meshes would lose the tarsi otherwise)
45
+ const MIN_R = { hero: 0, high: 0, medium: 0.26, low: 0.4, crowd: 0.58 }; // ~ one cell: legs stay >= 2 voxels thick
46
+
47
+ export function sculptSpider(m, rig, params) {
48
+ const J = rig.J, d = spiderDims(params);
49
+ const F = d.unit / 0.013; // size relative to the wolf spider
50
+ const minR = (MIN_R[params.quality] ?? 0) * M * F;
51
+ const R = (r) => Math.max(r * M, minR);
52
+ const HO = J.ceph;
53
+ const hl = (v) => add(HO, mm(v));
54
+ const X = [1, 0, 0];
55
+ const ellY = (o) => {
56
+ const y = norm(o.ydir);
57
+ const z = norm(cross(o.lateral || X, y));
58
+ return m.ell({ ...o, axis: z, up: y });
59
+ };
60
+ const wolf = d.variant === 'wolf';
61
+ const C = d.carapace;
62
+ const z0 = J.pedA[2] / M, z1 = J.front[2] / M, Lc = z1 - z0;
63
+ const B = { bone: 'prosoma', group: 'axial', part: 'body' };
64
+ const H = { bone: 'head', group: 'axial', part: 'body' };
65
+
66
+ // ------------------------------------------------------------------ PROSOMA
67
+ // carapace: thoracic dome, raised cephalic region, sloping rear; sternum below
68
+ const thTop = C.rearTop + (C.top - C.rearTop) * (wolf ? 0.55 : 0.75);
69
+ m.ell({ ...B, tag: 'thorax', c: mm([0, (thTop + C.bottom) / 2, z0 + 0.42 * Lc]), r: mm([C.wid / 2, (thTop - C.bottom) / 2, 0.41 * Lc]), k: 0 });
70
+ m.ell({ ...B, tag: 'thoraxRear', c: mm([0, (C.rearTop + C.bottom) / 2 + 0.2 * F, z0 + 0.2 * Lc]), r: mm([C.wid * 0.36, (C.rearTop - C.bottom) / 2, 0.2 * Lc]), k: 0.9 * M * F });
71
+ m.ell({ ...H, tag: 'cephalic', c: mm([0, C.top - (wolf ? 2.3 : 3.2) * F, z0 + 0.78 * Lc]), r: mm([C.headW / 2, (wolf ? 2.3 : 3.2) * F, 0.26 * Lc]), axis: norm([0, wolf ? 0.18 : 0.05, 1]), k: 1.2 * M * F });
72
+ // clypeus: the face between the anterior eyes and the chelicerae
73
+ m.ell({ ...H, tag: 'clypeus', c: add(J.front, mm([0, -0.2 * F, -0.9 * F])), r: mm([C.headW * 0.36, 1.1 * F, 0.9 * F]), k: 0.8 * M * F });
74
+ m.ell({ ...B, tag: 'sternum', c: mm([0, C.bottom + 0.45 * F, z0 + 0.5 * Lc]), r: mm([C.wid * 0.27, 0.7 * F, 0.3 * Lc]), k: 0.8 * M * F });
75
+ // labium and endites (mouthparts between the palp coxae)
76
+ m.ell({ ...B, tag: 'labium', c: mm([0, C.bottom + 0.6 * F, z0 + 0.86 * Lc]), r: mm([0.9 * F, 0.55 * F, 0.9 * F]), k: 0.5 * M * F });
77
+ // fovea: a short median groove on the thoracic dome, and the cephalic groove
78
+ m.ell({ ...B, tag: 'fovea', c: mm([0, thTop + 0.02 * F, z0 + 0.36 * Lc]), r: mm([0.16 * F, 0.3 * F, 0.9 * F]), k: 0.25 * M * F, carve: true });
79
+ if (!wolf) {
80
+ // tarantula: low wide carapace with a raised ocular tubercle
81
+ m.ell({ ...H, tag: 'tubercle', c: hl([0, 1.7, 5.4]), r: mm([2.3, 1.3, 1.9]), k: 1.1 * M * F });
82
+ }
83
+
84
+ // ------------------------------------------------------------------ EYES (sockets; the domes are eye meshes)
85
+ for (const e of EYES[d.variant]) {
86
+ const E = eyeSpec(e);
87
+ for (const s of [1, -1]) {
88
+ const ef = eyeFrameOf(E, HO, s);
89
+ // a raised rim around the lens, cut open by the socket the dome sits in
90
+ m.sphere({ ...H, tag: 'eyerim', c: add(ef.c, mul(ef.z, -E.r * 0.25)), rad: E.r * 1.05, k: E.r * 0.6 });
91
+ m.sphere({ ...H, tag: 'eyesocket', c: add(ef.c, mul(ef.z, E.r * 0.1)), rad: E.r * 0.98, k: E.r * 0.15, carve: true });
92
+ }
93
+ }
94
+
95
+ // ------------------------------------------------------------------ PEDICEL, ABDOMEN, SPINNERETS
96
+ const A = { bone: 'abdomen', group: 'axial', part: 'body' };
97
+ m.cone({ bone: 'pedicel', group: 'axial', part: 'body', tag: 'pedicel', a: J.pedA, b: J.pedB, ra: (wolf ? 0.75 : 1.9) * M * F / F * (wolf ? 1 : 1), rb: (wolf ? 0.7 : 1.8) * M, k: 0.3 * M * F });
98
+ const ac = mm(d.abdomen.c), ar = mm(d.abdomen.r);
99
+ m.ell({ ...A, tag: 'abdomen', c: ac, r: ar, axis: norm([0, 0.06, 1]), k: 0 });
100
+ m.ell({ ...A, tag: 'abdomenFront', c: add(ac, [0, -ar[1] * 0.05, ar[2] * 0.52]), r: [ar[0] * 0.7, ar[1] * 0.72, ar[2] * 0.45], k: 1.0 * M * F });
101
+ m.ell({ ...A, tag: 'abdomenRear', c: add(ac, [0, -ar[1] * 0.12, -ar[2] * 0.6]), r: [ar[0] * 0.72, ar[1] * 0.72, ar[2] * 0.42], k: 1.2 * M * F });
102
+ // spinnerets: wolf 3 short pairs; tarantula 2 visible pairs, the posterior pair long and finger-like
103
+ const SP = { bone: 'spinnerets', group: 'axial', part: 'body', tag: 'spinneret' };
104
+ const st = J.abdEnd, sd = norm(sub(J.spinTip, J.abdEnd)), sl = len(sub(J.spinTip, J.abdEnd));
105
+ for (const s of [1, -1]) {
106
+ if (wolf) {
107
+ m.cone({ ...SP, a: add(st, mm([0.45 * s, -0.25, 0.3])), b: add(add(st, mul(sd, sl * 0.9)), mm([0.5 * s, -0.2, 0])), ra: d.spinR * M, rb: d.spinR * 0.7 * M, k: 0.25 * M });
108
+ m.cone({ ...SP, a: add(st, mm([0.2 * s, -0.6, 0.3])), b: add(add(st, mul(sd, sl * 0.7)), mm([0.2 * s, -0.55, 0])), ra: d.spinR * 0.8 * M, rb: d.spinR * 0.55 * M, k: 0.25 * M });
109
+ } else {
110
+ m.cone({ ...SP, a: add(st, mm([1.1 * s, -0.4, 0.8])), b: add(add(st, mul(sd, sl * 1.15)), mm([1.9 * s, 0.9, 0])), ra: d.spinR * M, rb: d.spinR * 0.62 * M, k: 0.8 * M });
111
+ m.cone({ ...SP, a: add(st, mm([0.55 * s, -1.2, 0.8])), b: add(add(st, mul(sd, sl * 0.45)), mm([0.6 * s, -1.4, 0])), ra: d.spinR * 0.6 * M, rb: d.spinR * 0.45 * M, k: 0.6 * M });
112
+ }
113
+ }
114
+
115
+ // ------------------------------------------------------------------ CHELICERAE AND FANGS (rigid parts)
116
+ for (const S of ['L', 'R']) {
117
+ const s = S === 'L' ? 1 : -1;
118
+ const b0 = J['cheBase' + S], b1 = J['cheTip' + S], ft = J['fangTip' + S];
119
+ const CH = { bone: 'chelicera' + S, group: 'chel' + S, part: 'chel' + S };
120
+ const cr = d.chel.r.map((r) => r * M);
121
+ const dir = norm(sub(b1, b0));
122
+ m.cone({ ...CH, tag: 'chelicera', a: b0, b: add(b1, mul(dir, -cr[1] * 0.4)), ra: cr[0], rb: cr[1], k: 0 });
123
+ // the stout basal boss bulging forward (wolf) / the long forward-projecting paturon (tarantula)
124
+ ellY({ ...CH, tag: 'chelicera', c: add(lerp(b0, b1, 0.35), mm([0.1 * s * F, 0, (wolf ? 0.35 : 0.8) * F])), ydir: dir, r: [cr[0] * 0.92, len(sub(b1, b0)) * 0.4, cr[0] * 0.95], k: 0.5 * M * F });
125
+ // fang groove with its row of teeth: a shallow furrow on the inner face
126
+ ellY({ ...CH, tag: 'fangGroove', c: add(lerp(b0, b1, 0.8), mm([-0.55 * s * F, 0, 0.1 * F])), ydir: dir, r: [cr[1] * 0.35, len(sub(b1, b0)) * 0.22, cr[1] * 0.4], k: 0.2 * M * F, carve: true });
127
+ const FG = { bone: 'fang' + S, group: 'fang' + S, part: 'fang' + S, tag: 'fang' };
128
+ const fr = d.chel.fangR * M;
129
+ // a curved fang: base, then bending toward the tip
130
+ const fd = norm(sub(ft, b1)), fl = len(sub(ft, b1));
131
+ const bend = norm(cross(fd, [s, 0, 0]));
132
+ const mid = add(lerp(b1, ft, 0.5), mul(bend, fl * 0.12));
133
+ m.cone({ ...FG, a: add(b1, mul(fd, -fr * 0.5)), b: mid, ra: fr, rb: fr * 0.62, k: 0.08 * M * F });
134
+ m.cone({ ...FG, a: mid, b: ft, ra: fr * 0.62, rb: fr * 0.12, k: 0.08 * M * F });
135
+ }
136
+
137
+ // ------------------------------------------------------------------ PALPS (body part)
138
+ for (const S of ['L', 'R']) {
139
+ const pj = palpJoints(S), pb = palpBones(S);
140
+ const g = { group: 'P' + S, part: 'body' };
141
+ for (let k = 0; k < pb.length; k++) {
142
+ const [ra, rb] = d.palp.r[k];
143
+ const a = J[pj[k]], b = J[pj[k + 1]];
144
+ m.cone({ ...g, bone: pb[k], tag: k === 0 ? 'palpcoxa' : 'palp', a, b, ra: R(ra * F / F), rb: R(rb), k: (k === 0 ? 0.6 : 0.09) * M * F });
145
+ if (k > 0 && k < pb.length - 1) m.sphere({ ...g, bone: pb[k], tag: 'knuckle', c: b, rad: R(rb * 1.08), k: 0.08 * M * F });
146
+ }
147
+ if (d.palpBulb > 0) {
148
+ // male: the swollen cymbium / palpal bulb ("boxing gloves")
149
+ const a = J[pj[5]], b = J[pj[6]];
150
+ // (swollen but compact: about the width of the palp tibia plus a third)
151
+ ellY({ ...g, bone: pb[5], tag: 'palpbulb', c: lerp(a, b, 0.5), ydir: sub(b, a), r: mm([0.65 * (0.62 * F * d.palpBulb + 0.3 * F), len(sub(b, a)) / M * 0.36, 0.65 * (0.7 * F * d.palpBulb + 0.3 * F)]), k: 0.2 * M * F });
152
+ }
153
+ }
154
+
155
+ // ------------------------------------------------------------------ LEGS
156
+ for (const S of ['L', 'R']) {
157
+ for (let i = 1; i <= 4; i++) {
158
+ const lj = legJoints(i, S), lb = legBones(i, S), key = S + i;
159
+ const thick = i === 4 ? d.hindR : i === 3 ? (1 + d.hindR) / 2 : 1;
160
+ for (let k = 0; k < 7; k++) {
161
+ const [ra, rb] = d.legR[k];
162
+ const a = J[lj[k]], b = J[lj[k + 1]];
163
+ const parts = k <= 2 ? ['body', 'leg' + key] : ['leg' + key];
164
+ for (const part of parts) {
165
+ const g = { group: key, part, bone: lb[k] };
166
+ if (k === 0) {
167
+ // coxa: a stout cylinder set into the prosoma between carapace margin and sternum
168
+ m.cone({ ...g, tag: 'coxa', a: add(a, mul(norm(sub(b, a)), -0.6 * M * F)), b, ra: R(ra * thick), rb: R(rb * thick), k: part === 'body' ? 0.55 * M * F : 0.1 * M * F });
169
+ } else {
170
+ m.cone({ ...g, tag: ['coxa', 'troch', 'femur', 'patella', 'tibia', 'meta', 'tarsus'][k], a, b, ra: R(ra * thick), rb: R(rb * thick), k: 0.07 * M * F });
171
+ // swollen joint condyles: the knee (femur-patella) is the biggest
172
+ if (k < 6) m.sphere({ ...g, tag: 'knuckle', c: add(b, mul(norm(sub(b, a)), -rb * 0.25 * M)), rad: R(rb * thick * (k === 2 ? 1.12 : 1.06)), k: 0.07 * M * F });
173
+ }
174
+ }
175
+ }
176
+ // claw tuft: the rounded tarsus tip
177
+ const tip = J[lj[7]], ta = J[lj[6]];
178
+ m.sphere({ group: key, part: 'leg' + key, bone: lb[6], tag: 'claws', c: add(tip, mul(norm(sub(ta, tip)), d.legR[6][1] * 0.3 * M)), rad: R(d.legR[6][1] * thick * 1.02), k: 0.06 * M * F });
179
+ }
180
+ }
181
+ return m;
182
+ }
@@ -0,0 +1,160 @@
1
+ // Wolf body language, layered over the core actions every frame (species.motion.hooks.update, see
2
+ // core/motion/actions.js).
3
+ //
4
+ // Tail carriage by mood (Schenkel 1947 in Mech & Boitani 2003): hangs at rest, ~level in the
5
+ // travelling trot (the carriage table in motion.js), raised stiff above the back in a threat or an
6
+ // attack, tucked between the hind legs when hit or afraid. Ears: pricked forward when alert (a look
7
+ // target), pinned flat in an attack (core) and when howling. Hackles: the long guard hair of the
8
+ // mane, cape and saddle lifts during an attack (P.furRaise -> the coat shader). Snarl: the jaw opens
9
+ // on the crouch before the bite so the canines show. Howling: an idle variant every 25-60 s of
10
+ // standing still (or on demand via `motion.input.howl = true`): head thrown back 50-60 deg, ears
11
+ // back, jaw half open in an "O", lasting 3-6 s.
12
+ import { clamp, smooth, lerp, angDiff } from '../../core/motion/common.js';
13
+ import { mx, mxLegs } from '../../core/motion/actions.js';
14
+ const sm = (w) => w * w * (3 - 2 * w);
15
+
16
+ // moveTo: a wolf slows down to turn onto a point close beside or behind it. The engine caps the speed
17
+ // only by the heading error (1.5 sk beyond 1.2 rad), so with the wolf's calm turn rate (1.8 rad/s) a
18
+ // target inside its turning circle was circled without end. A point is reachable
19
+ // on a turn of radius R only while it lies outside the turning circle on its side: R < d / (2 sin err)
20
+ // (d distance, err bearing from the heading). The speed is capped so the radius at the engine's yaw
21
+ // limit, v / w(v), keeps a margin inside that bound; near the point and well off the heading the wolf
22
+ // all but pivots, then goes. (Engine-wide, the same cap belongs in quadruped.js drive().)
23
+ const ARRIVE_MARGIN = 0.8;
24
+ function arriveCap(e, st) {
25
+ const inp = e.input, cfg = e.cfg;
26
+ // arrived (drive() dropped the target and zeroed the speed): stand facing the way it went instead of
27
+ // pivoting on to the last bearing of the point (a front paw dragged ~2 % S in that pivot)
28
+ if (st.tgt && !inp.target && !inp.follow && !inp.speed && Math.hypot(st.tgt.x - e.pos.x, st.tgt.z - e.pos.z) < 0.5 * cfg.k + 0.03 * e.speed) {
29
+ inp.heading = e.heading + 0.25 * e.yawRate;
30
+ e.wantHeading = inp.heading;
31
+ }
32
+ st.tgt = inp.target && !inp.follow ? (st.tgt || { x: 0, z: 0 }) : null;
33
+ if (!st.tgt) return;
34
+ st.tgt.x = inp.target.x; st.tgt.z = inp.target.z;
35
+ if (!(e.wantSpeed > 0.05) || e.air?.active) return;
36
+ const dx = inp.target.x - e.pos.x, dz = inp.target.z - e.pos.z, d = Math.hypot(dx, dz);
37
+ const err = Math.abs(angDiff(e.heading, Math.atan2(dx, dz))), sn = Math.sin(err);
38
+ if (sn < 1e-3) return;
39
+ const rMax = (ARRIVE_MARGIN * d) / (2 * sn);
40
+ // the engine's yaw-rate limit at speed v (quadruped.js step: pivots are slower than turns on the move)
41
+ const wAt = (v) => Math.min(cfg.turnRate * lerp(0.55, 1, smooth(0.3, 2, v / cfg.sq)), cfg.latAccelMax / Math.max(v, 0.5 * cfg.sk));
42
+ if (e.wantSpeed / wAt(e.wantSpeed) <= rMax) return;
43
+ // v / w(v) grows with v: bisect for the fastest speed whose turning radius fits
44
+ let lo = 0, hi = e.wantSpeed;
45
+ for (let i = 0; i < 12; i++) { const m = 0.5 * (lo + hi); if (m / wAt(m) <= rMax) lo = m; else hi = m; }
46
+ // (a point ahead that the straight line passes within the stop radius needs no turn: the cap fades out)
47
+ const stopR = 0.35 * cfg.k + 0.02 * e.speed;
48
+ e.wantSpeed = lerp(e.wantSpeed, lo, err < 1.2 ? smooth(0.4 * stopR, 0.8 * stopR, d * sn) : 1);
49
+ }
50
+
51
+ export function wolfUpdate(P, dt, L) {
52
+ const e = L.engine;
53
+ const st = L._wolf || (L._wolf = { howlIn: 25 + e.rand() * 20, howlT: -1, howlDur: 4, howlW: 0 });
54
+ arriveCap(e, st);
55
+ // (the sleeping forepaw pull-back below overrides this animal's fold placement only while it sleeps)
56
+ if (st.acts && st.acts.fold && !L.postures.some((p) => p.name === 'sleep')) st.acts.fold = undefined;
57
+ let raise = 0;
58
+
59
+ for (const a of L.oneshots) {
60
+ const w = sm(a.w);
61
+ if (w <= 0) continue;
62
+ if (a.name === 'attack') {
63
+ const t = a.t;
64
+ // threat -> lunge: hackles up, tail raised stiff above the back, snarl before the bite
65
+ raise = Math.max(raise, w * (0.5 + 0.5 * smooth(0, 0.2, t)));
66
+ mx(P, 'tailLift', 0.75, w); mx(P, 'tailStiff', 1.8, w);
67
+ const snarl = smooth(0.0, 0.12, t) * (1 - smooth(0.22, 0.3, t));
68
+ P.jaw = Math.max(P.jaw, 0.3 * snarl * w); // (the lower fangs come clear of the upper lip)
69
+ mx(P, 'headRaise', -0.035, snarl * w);
70
+ } else if (a.name === 'hit') {
71
+ // flinch: tail clamped between the hind legs, ears flat (core), body drops (core)
72
+ const k = w * (1 - smooth(0.45, 0.75, a.t) * 0.6);
73
+ mx(P, 'tailLift', -0.8, k); mx(P, 'tailStiff', 1.6, k); mx(P, 'tailIdle', 0, k);
74
+ }
75
+ }
76
+ // dying / dead: ears folded back flat against the neck
77
+ for (const p of L.postures) {
78
+ if (p.name === 'death') { const w = sm(clamp(p.w, 0, 1)) * smooth(0.3, 1.0, p.t); mx(P, 'earFlat', 1.15, w); mx(P, 'earTwitch', 0, w); mx(P, 'headRoll', p.side * 0.8, w); P.headPos.y += 0.05 * e.cfg.s * w; }
79
+ }
80
+ // curled sleep (curled1: a ring on one hip, the nose tucked by the tail). The spine
81
+ // bends into a C (2.2 rad, rolled 0.45 onto the hip, the hind legs to the inside); the head turns back
82
+ // along the inside of the curl and rests on the ground by the hind feet (yaw 2.3 rad; the occiput 20 cm
83
+ // inside and 5 cm ahead of the turned chest's shoulders, 10 cm up: further back, the throat's stretch
84
+ // > 3x rose past 4 % of its triangles); the brush sweeps round the outside of the hind feet and forward
85
+ // to the nose (tailSide 3.4 curves it progressively, tailCurl -0.5 holds the root back: the old 2.4 rad
86
+ // turned it near the root and tucked it under the belly). The forelegs stay forward on the ground, as in
87
+ // curled1: tucked (the core's tuck places them in the unbent body frame, ~26 cm off, and the elbow
88
+ // flipped on the way out), reached (the paw stands up) or turned with the chest (the inner elbow dropped
89
+ // into the ground: 7-23 mm) they failed the metrics. (roll 0.45: 0.65 pressed the lower forearm 9-14 mm
90
+ // into the ground, 0.5 up to 6 mm)
91
+ for (const p of L.postures) {
92
+ if (p.name === 'sleep' && p.tune.style !== 'lateral') {
93
+ const c = sm(clamp(p.w, 0, 1)) * smooth(1.2, 3.2, p.t), side = p.side, k = e.cfg.k, fr = e.fr;
94
+ mx(P, 'roll', side * 0.45, c); mx(P, 'bend', side * 2.2, c);
95
+ mxLegs(P, 'side', 0, 0.55, c); mxLegs(P, 'spread', 0, 0, c);
96
+ // forelegs drawn back under the head (curled1: the paws just show at the front of the ring, not a leg's
97
+ // length ahead of it like a sphinx's): the engine's fold places the forepaw at zS + actions.fold.front.z
98
+ // leg lengths, flat on the ground; this animal's own copy of that tuning moves it 25 cm back once the
99
+ // curl has settled, the forearm and paw kept flat and the elbows folding up beside the chest. (A 'reach'
100
+ // blend stood the paw up and swung the elbow 10 mm into the ground; a 'sit' blend stood the forelegs up
101
+ // like posts; the core's tuck places the paw in the unbent body frame, ~26 cm off.) Skin penetration
102
+ // 0.30-0.62 % S (seeds 1-4, every morph, both sides).
103
+ const cF = sm(clamp(p.w, 0, 1)) * smooth(2.6, 4.4, p.t);
104
+ if (!st.acts) { st.acts = { ...e.cfg.actions }; e.cfg.actions = st.acts; }
105
+ const L0 = e.legs[0].def, z0 = (0.5 * L0.Lr + L0.Lmc) / L0.len;
106
+ // (k -1.45, beta 0 and z0 are the engine's own fold values: continuous as the pull-back fades)
107
+ st.acts.fold = cF > 1e-3 ? { front: { z: z0 - (cF * 0.25 * k) / L0.len, k: -1.45, beta: 0 } } : undefined;
108
+ mx(P, 'headYaw', side * 2.3, c);
109
+ // the chest's flat forward and left (its frame is the last frame's: the posture changes slowly)
110
+ const q = fr.qCh;
111
+ let fx = 2 * (q.x * q.z + q.w * q.y), fz = 1 - 2 * (q.x * q.x + q.y * q.y);
112
+ const fl = Math.hypot(fx, fz) || 1; fx /= fl; fz /= fl;
113
+ const lat = side * 0.2 * k, fwd = 0.05 * k;
114
+ const tx = fr.shC.x + fz * lat + fx * fwd, tz = fr.shC.z - fx * lat + fz * fwd;
115
+ const hp = P.headPos;
116
+ hp.x += (tx - hp.x) * c; hp.z += (tz - hp.z) * c; hp.y += (e.terrainH(tx, tz) + 0.1 * k - hp.y) * c;
117
+ mx(P, 'tailCurl', side * -0.5, c); mx(P, 'tailSide', side * 3.4, c); mx(P, 'tailStiff', 0.7, c);
118
+ }
119
+ }
120
+
121
+ // lying: rolled onto one hip, both hind legs to that side (lying1), not a symmetric sphinx
122
+ for (const p of L.postures) {
123
+ if (p.name === 'lie') {
124
+ const w = sm(clamp(p.w, 0, 1)) * smooth(0.6, 1.8, p.t);
125
+ mx(P, 'roll', p.side * 0.25, w);
126
+ mxLegs(P, 'side', 0, 0.5, w);
127
+ }
128
+ }
129
+
130
+ // alert: ears pricked forward while watching something
131
+ const idleW = L.idle.w;
132
+ if (e.input.look && raise < 0.01) mx(P, 'earFlat', -0.18, clamp(P.lookW, 0, 1));
133
+
134
+ // howling (idle variant)
135
+ const still = idleW > 0.95 && !L.postures.length && !L.oneshots.some((a) => !a.stopping && a.name !== 'hit');
136
+ if (st.howlT < 0) {
137
+ if (still) st.howlIn -= dt;
138
+ if (e.input.howl && idleW > 0.3) { e.input.howl = false; st.howlIn = 0; }
139
+ if (st.howlIn <= 0 && still) { st.howlT = 0; st.howlDur = 3 + e.rand() * 3; }
140
+ } else {
141
+ st.howlT += dt;
142
+ if (!still && idleW < 0.6) st.howlT = Math.max(st.howlT, st.howlDur);
143
+ if (st.howlT > st.howlDur + 1.2) { st.howlT = -1; st.howlIn = 25 + e.rand() * 35; }
144
+ }
145
+ const hw = st.howlT < 0 ? 0 : smooth(0, 0.9, st.howlT) * (1 - smooth(st.howlDur, st.howlDur + 1.2, st.howlT));
146
+ st.howlW = hw;
147
+ if (hw > 0.001) {
148
+ const w = hw * idleW;
149
+ mx(P, 'lookW', 0, w);
150
+ mx(P, 'headRaise', 0.07, w); mx(P, 'neckReach', -0.03, w);
151
+ mx(P, 'headPitch', -1.0, w);
152
+ // the "O" opens after the head is up, with a slight waver
153
+ const o = smooth(0.5, 1.2, st.howlT) * (1 - smooth(st.howlDur - 0.3, st.howlDur + 0.4, st.howlT));
154
+ mx(P, 'jaw', 0.2 + 0.03 * Math.sin(st.howlT * 5.1), o * w); mx(P, 'jawOmega', 6, w);
155
+ mx(P, 'earFlat', 0.55, w); mx(P, 'earTwitch', 0, w);
156
+ mx(P, 'breathAmp', 0.4, w); mx(P, 'eyelid', 0.45, o * w);
157
+ mx(P, 'dropH', 0.05, w);
158
+ }
159
+ P.furRaise = Math.max(P.furRaise || 0, raise);
160
+ }