@bitefoundry/retrieviewer 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,122 +0,0 @@
1
- // The snake sculpted as a signed distance field: a tube of round cones along the spine with a flat,
2
- // carved belly (loaf cross-section), a head modelled in head-local coordinates (origin at v0,
3
- // +Z forward), a separately meshed lower jaw, a forked tongue in two rigid pieces, hinged fangs
4
- // (rattlesnake) and a rattle.
5
- import { SPINE, bodyPlan } from './rig.js';
6
- import { add, sub, mul, norm, lerp } from '../../core/math/vec.js';
7
- import { sculptEyeSocket } from '../../core/sdf/eyeSocket.js';
8
-
9
- // Eye spec (core/sdf/eyeSocket.js) for this individual: round aperture (no lids: a spectacle),
10
- // iris filling the whole visible eye.
11
- export function eyeSpec(params) {
12
- const B = bodyPlan(params);
13
- const e = B.P.eye, k = B.kH * (params.age === 'juvenile' ? 1.15 : 1);
14
- const r = e.r * k;
15
- return {
16
- c: [e.at[0] * B.kH, e.at[1] * B.kH, e.at[2] * B.kH], r, back: 0.0002 * B.kH, yaw: e.yaw, pitch: e.pitch,
17
- lid: 0.00015 * B.kH, R: r * 0.96, d: 0, off: 0, tilt: 0, irisZ: r * 0.62, irisR: r * 0.9,
18
- };
19
- }
20
-
21
- export function sculptSnake(m, rig, params) {
22
- const J = rig.J, B = rig.plan, P = B.P, viper = params.variant === 'rattlesnake';
23
- const HO = J.v0;
24
- const hL = B.headLen, W = P.headW * B.kH, H = P.headH * B.kH;
25
- const yB = -HO[1]; // head-local height of the belly / jaw underside (y = 0 in the world)
26
- const hl = (v) => [HO[0] + v[0], HO[1] + v[1], HO[2] + v[2]];
27
- const HB = { bone: 'head' };
28
- const sym = (f) => { f(1); f(-1); };
29
-
30
- // ---------------------------------------------------------------- BODY
31
- // round cones of radius = half width; the axis sits at flat * hw, so the circle's lower part is
32
- // carved off by the belly plane (flat belly with ventrolateral angles, rounded back)
33
- const hwAt = [];
34
- let total = 0;
35
- const sig = [0];
36
- for (let i = 0; i < SPINE; i++) { total += Math.hypot(...sub(J['v' + (i + 1)], J['v' + i])); sig.push(total); }
37
- for (let i = 0; i <= SPINE; i++) hwAt.push(B.hw(sig[i] / total));
38
- for (let i = 0; i < SPINE; i++) {
39
- m.cone({ tag: i >= SPINE * (1 - B.tailFrac) ? 'tail' : i < 3 ? 'neck' : 'body', bone: 'spine' + i, a: J['v' + i], b: J['v' + (i + 1)], ra: hwAt[i], rb: hwAt[i + 1], k: 0 });
40
- }
41
- // thin tail tip: overlapping ellipsoids that the pipeline inflates at coarse tiers (minThick)
42
- for (let i = SPINE - 12; i < SPINE; i++) {
43
- const a = J['v' + i], b = J['v' + (i + 1)], rr = (hwAt[i] + hwAt[i + 1]) * 0.5;
44
- m.ell({ tag: 'tailtip', bone: 'spine' + i, c: lerp(a, b, 0.5), axis: norm(sub(a, b)), r: [rr * 0.98, rr * 0.98, Math.hypot(...sub(a, b)) * 0.62], k: 0, thin: true });
45
- }
46
- // ---------------------------------------------------------------- HEAD (upper jaw + braincase)
47
- if (viper) {
48
- // broad, triangular: venom-gland bulges at the back, flat crown, blunt canthus, narrow neck
49
- m.ell({ ...HB, tag: 'cranium', c: hl([0, yB + 0.62 * H, 0.36 * hL]), r: [0.33 * W, 0.34 * H, 0.36 * hL], k: 0.004 });
50
- sym((s) => m.ell({ ...HB, tag: 'gland', c: hl([0.31 * W * s, yB + 0.5 * H, 0.22 * hL]), r: [0.28 * W, 0.34 * H, 0.27 * hL], axis: norm([0.35 * s, 0, 1]), k: 0.005 }));
51
- sym((s) => m.ell({ ...HB, tag: 'cheek', c: hl([0.2 * W * s, yB + 0.5 * H, 0.52 * hL]), r: [0.22 * W, 0.3 * H, 0.3 * hL], axis: norm([-0.25 * s, 0, 1]), k: 0.005 }));
52
- m.ell({ ...HB, tag: 'snout', c: hl([0, yB + 0.58 * H, 0.7 * hL]), r: [0.29 * W, 0.28 * H, 0.25 * hL], k: 0.005 });
53
- sym((s) => m.ell({ ...HB, tag: 'lip', c: hl([0.26 * W * s, yB + 0.38 * H, 0.5 * hL]), r: [0.16 * W, 0.16 * H, 0.44 * hL], axis: norm([-0.28 * s, 0, 1]), k: 0.004 }));
54
- m.sphere({ ...HB, tag: 'rostral', c: hl([0, yB + 0.5 * H, 0.94 * hL]), rad: 0.13 * W, k: 0.004 });
55
- // supraocular scales: a brow shelf over each eye
56
- const E = eyeSpec(params);
57
- sym((s) => m.ell({ ...HB, tag: 'brow', c: hl([E.c[0] * s * 0.95, E.c[1] + E.r * 0.95, E.c[2]]), r: [E.r * 1.25, E.r * 0.5, E.r * 1.5], k: 0.0015 }));
58
- } else {
59
- // corn snake: barely wider than the neck, long rounded snout, domed crown
60
- m.ell({ ...HB, tag: 'cranium', c: hl([0, yB + 0.6 * H, 0.34 * hL]), r: [0.42 * W, 0.4 * H, 0.4 * hL], k: 0.004 });
61
- sym((s) => m.ell({ ...HB, tag: 'temporal', c: hl([0.2 * W * s, yB + 0.5 * H, 0.2 * hL]), r: [0.28 * W, 0.36 * H, 0.3 * hL], k: 0.004 }));
62
- m.ell({ ...HB, tag: 'snout', c: hl([0, yB + 0.56 * H, 0.72 * hL]), r: [0.29 * W, 0.34 * H, 0.3 * hL], axis: norm([0, -0.08, 1]), k: 0.005 });
63
- sym((s) => m.ell({ ...HB, tag: 'lip', c: hl([0.24 * W * s, yB + 0.4 * H, 0.52 * hL]), r: [0.19 * W, 0.2 * H, 0.42 * hL], axis: norm([-0.22 * s, 0, 1]), k: 0.004 }));
64
- m.sphere({ ...HB, tag: 'rostral', c: hl([0, yB + 0.5 * H, 0.92 * hL]), rad: 0.17 * W, k: 0.004 });
65
- }
66
- // neck: blend the cranium into the first body cone
67
- m.cone({ ...HB, tag: 'nape', a: hl([0, 0, 0.18 * hL]), b: hl([0, 0, -0.25 * hL]), ra: 0.3 * W, rb: hwAt[0] * 1.02, k: 0.01 });
68
- // eyes in round sockets (the spectacle sits flush), nostrils, rostral notch for the tongue
69
- const E = eyeSpec(params);
70
- sym((s) => sculptEyeSocket(m, E, HO, s, { bone: 'head', orbit: { r: [E.r * 0.95, E.r * 0.8, E.r * 0.45], at: [0, 0, E.r * 1.0], k: E.r * 0.35 } }));
71
- sym((s) => m.sphere({ ...HB, tag: 'nostril', c: hl([0.17 * W * s, yB + 0.62 * H, 0.95 * hL]), rad: 0.04 * W, k: 0.0006, carve: true }));
72
- if (viper) sym((s) => m.sphere({ ...HB, tag: 'pit', c: hl([0.33 * W * s, yB + 0.48 * H, 0.8 * hL]), rad: 0.05 * W, k: 0.0006, carve: true }));
73
- m.ell({ ...HB, tag: 'notch', c: hl([0, yB + 0.36 * H, 0.99 * hL]), r: [0.07 * W, 0.07 * H, 0.05 * hL], k: 0.0006, carve: true });
74
-
75
- // palate: the upper jaw ends at the lip line (the lower jaw is its own surface)
76
- m.ell({ ...HB, tag: 'palate', c: hl([0, yB + 0.1 * H, 0.62 * hL]), r: [0.33 * W, 0.26 * H, 0.5 * hL], k: 0.0012, carve: true });
77
- // belly plane: a very flat carving ellipsoid whose top is y = 0 (sag < 0.2 mm over the body)
78
- const zMid = (J.v0[2] + J['v' + SPINE][2]) * 0.5;
79
- m.ell({ tag: 'belly', bone: 'spine' + (SPINE >> 1), c: [0, -0.012, zMid], r: [0.4, 0.012, 3.2], k: 0.18 * hwAt[SPINE >> 1], carve: true });
80
-
81
- // ---------------------------------------------------------------- LOWER JAW (own surface)
82
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
83
- sym((s) => m.cone({ ...JW, tag: 'mandible', a: hl([(viper ? 0.34 : 0.3) * W * s, yB + 0.22 * H, 0.08 * hL]), b: hl([0.07 * W * s, yB + 0.17 * H, 0.88 * hL]), ra: viper ? 0.14 * H + 0.05 * W : 0.11 * H + 0.03 * W, rb: viper ? 0.1 * H : 0.085 * H, k: 0.003 }));
84
- m.ell({ ...JW, tag: 'jawfloor', c: hl([0, yB + 0.19 * H, 0.46 * hL]), r: [0.3 * W, 0.18 * H, 0.42 * hL], k: 0.003 });
85
- m.sphere({ ...JW, tag: 'chin', c: hl([0, yB + 0.2 * H, 0.9 * hL]), rad: 0.14 * H, k: 0.003 });
86
-
87
- // ---------------------------------------------------------------- TONGUE (two rigid pieces)
88
- const tb = J.tongueBase, tf = J.tongueFork, tt = J.tongueTip;
89
- const tr = 0.0007 * B.kH * (viper ? 1.25 : 1);
90
- m.cone({ bone: 'tongue', group: 'tongue', part: 'tongue', tag: 'tongue', a: tb, b: add(tf, [0, 0, tr]), ra: tr * 1.1, rb: tr * 0.85, k: 0 });
91
- const forkLen = Math.hypot(...sub(tt, tf));
92
- sym((s) => m.cone({ bone: 'tongueTip', group: 'tongue', part: 'fork', tag: 'fork', a: add(tf, [0, 0, -tr * 0.5]), b: add(tt, [s * forkLen * 0.28, 0, 0]), ra: tr * 0.8, rb: tr * 0.32, k: tr * 0.6 }));
93
-
94
- // ---------------------------------------------------------------- FANGS (rattlesnake)
95
- if (viper) {
96
- const fb = J.fangBase, ft = J.fangTip;
97
- sym((s) => m.cone({ bone: 'fang', group: 'fang', part: 'fang', tag: 'fang', a: add(fb, [0.2 * W * s, 0, 0]), b: add(ft, [0.2 * W * s, 0, 0]), ra: 0.0009 * B.kH, rb: 0.00015, k: 0 }));
98
- }
99
-
100
- // ---------------------------------------------------------------- RATTLE
101
- if (P.rattle) {
102
- const last = 'spine' + (SPINE - 1);
103
- const a = J['v' + (SPINE - 1)], b = J['v' + SPINE];
104
- const d = norm(sub(b, a));
105
- const nSeg = params.rattleSegs ?? P.rattle.segs;
106
- const tip = hwAt[SPINE];
107
- const segL = (P.rattle.len / P.rattle.segs) * (params.shape?.girth ?? 1);
108
- for (let k = 0; k < nSeg; k++) {
109
- const t = k / Math.max(1, P.rattle.segs - 1);
110
- const w = tip * (1.35 - 0.3 * t);
111
- const c0 = add(b, mul(d, segL * (k + 0.55)));
112
- // the rattle rests on (not in) the ground: lift a segment whose half height exceeds the tip's axis height
113
- const c = [c0[0], Math.max(c0[1], w * 0.78 + 0.0003), c0[2]];
114
- // each segment: a flattened, bi-lobed bell overlapping the next
115
- m.ell({ bone: last, part: 'rattle', group: 'rattle', tag: 'rattle', c, axis: d, r: [w, w * 0.78, segL * 0.62], k: segL * 0.25 });
116
- m.ell({ bone: last, part: 'rattle', group: 'rattle', tag: 'rattle', c: add(c, mul(d, segL * 0.35)), axis: d, r: [w * 0.82, w * 0.62, segL * 0.38], k: segL * 0.2 });
117
- }
118
- // the tail tip plugs into the button
119
- m.ell({ bone: last, part: 'rattle', group: 'rattle', tag: 'button', c: ((q) => [q[0], Math.max(q[1], tip * 0.9 + 0.0003), q[2]])(add(b, mul(d, -segL * 0.2))), axis: d, r: [tip * 1.05, tip * 0.9, segL * 0.7], k: 0 });
120
- }
121
- return m;
122
- }
@@ -1,279 +0,0 @@
1
- // The spider's coat: chitin (material 8) under a covering of setae (shell hairs), and glossy bare
2
- // cuticle around the eyes and on the fangs (keratin-like, material 5).
3
- //
4
- // Wolf spider (Hogna / Lycosa type): dark brown carapace with a pale median band and pale
5
- // submarginal bands, dark striae radiating from the fovea, black ocular area; abdomen with a dark
6
- // lanceolate heart mark in front and pale chevrons behind; banded legs; black venter.
7
- // Tarantula morphs: red-knee (Brachypelma: velvet black, orange knee bands, pale rings, tan carapace
8
- // margin, reddish abdominal setae), rosy (Grammostola: pinkish brown) and pinktoe (Avicularia: black
9
- // with pink tarsi). Colours sampled from the reference photos.
10
- //
11
- // Pattern and mark distances are stored in "cheetah-equivalent" metres (real distance x PK) so the
12
- // covering shader's anti-aliasing widths and hair-level edge jitter, which are tuned for a cheetah,
13
- // stay proportionate on a 25 mm animal.
14
- import { spiderDims } from './rig.js';
15
- import { EYES, eyeSpec } from './sculpt.js';
16
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
17
- import { srgb, mix3 } from '../../core/build/coatKit.js';
18
- import { norm, sub, dot, cross, add, mul, smoothstep, clamp, mix, fbm3, len } from '../../core/math/vec.js';
19
-
20
- const MAT = { CHITIN: 8, KERATIN: 5 };
21
-
22
- function palette(p) {
23
- const w = p.coatWarmth || 0, l = p.coatLightness || 0;
24
- // warmth (rufous .. cool), lightness, and greyness (Pardosa-like grey individuals .. brown Hogna)
25
- const gr = p.coatGrey || 0;
26
- const tw = (c) => {
27
- const r = c[0] * (1 + 0.12 * w + l), g = c[1] * (1 + 0.03 * w + l), b = c[2] * (1 - 0.12 * w + l);
28
- const y = 0.3 * r + 0.55 * g + 0.15 * b;
29
- return [mix(r, y, gr), mix(g, y, gr), mix(b, y * 1.04, gr)];
30
- };
31
- if (p.variant !== 'tarantula') {
32
- return {
33
- carapace: tw(srgb(0x3a2b1f)), stripe: tw(srgb(0xa08a6a)), margin: tw(srgb(0x8c7657)),
34
- abdomen: tw(srgb(0x4e3b2a)), heart: tw(srgb(0x261b13)), chevron: tw(srgb(0x8e7a5c)), abdSide: tw(srgb(0x5d4935)),
35
- leg: tw(srgb(0x5f4a35)), legBand: tw(srgb(0x9a8465)), legDark: tw(srgb(0x33261b)),
36
- chel: srgb(0x1f1812), chelHair: tw(srgb(0x6a4a2c)), venter: srgb(0x1a1512), ocular: srgb(0x0d0b09),
37
- fang: srgb(0x160f0b), palp: tw(srgb(0x5a4633)), spinneret: tw(srgb(0x6e5a44)),
38
- };
39
- }
40
- const morph = p.morph || 'redknee';
41
- const base = {
42
- carapace: srgb(0x17120f), stripe: srgb(0x17120f), margin: srgb(0xc3884b),
43
- abdomen: srgb(0x1a1411), heart: srgb(0x120d0b), chevron: srgb(0x6b3a22), abdSide: srgb(0x1c1512),
44
- leg: srgb(0x15110e), legBand: srgb(0xd9651e), legDark: srgb(0x0f0c0a), legRing: srgb(0xe0a068),
45
- chel: srgb(0x120e0c), chelHair: srgb(0x2a1d16), venter: srgb(0x0e0b09), ocular: srgb(0x0a0908),
46
- fang: srgb(0x0e0a08), palp: srgb(0x16120f), spinneret: srgb(0x201813), tarsusTip: null,
47
- };
48
- if (morph === 'rosy') {
49
- Object.assign(base, {
50
- carapace: srgb(0x6e4c40), margin: srgb(0x9a7462), abdomen: srgb(0x5e4036), chevron: srgb(0x8a5a4a), abdSide: srgb(0x5a3e34),
51
- leg: srgb(0x4a3530), legBand: srgb(0x86645a), legRing: srgb(0x9a7a6c), legDark: srgb(0x35261f), chelHair: srgb(0x6a4a3e), palp: srgb(0x4a3530),
52
- });
53
- } else if (morph === 'pinktoe') {
54
- Object.assign(base, {
55
- carapace: srgb(0x1b191d), margin: srgb(0x2a2630), abdomen: srgb(0x1c1a1e), chevron: srgb(0x3a3036), abdSide: srgb(0x1c1a1e),
56
- leg: srgb(0x1a181c), legBand: srgb(0x2c2830), legRing: srgb(0x2c2830), legDark: srgb(0x121014), tarsusTip: srgb(0xe07a7a), palp: srgb(0x1a181c),
57
- });
58
- }
59
- const tw2 = (c) => (c ? [c[0] * (1 + l), c[1] * (1 + l), c[2] * (1 + l)] : c);
60
- for (const k of Object.keys(base)) base[k] = tw2(base[k]);
61
- return base;
62
- }
63
-
64
- export function spiderCoat(ctx) {
65
- const { rig, pos, nrm, regionOf, regionNames, weights, nV, params } = ctx;
66
- const d = spiderDims(params);
67
- const J = rig.J, BONES = rig.BONES;
68
- const U = d.unit, F = U / 0.013, PK = 0.33 / U;
69
- const wolf = d.variant === 'wolf';
70
- const COL = palette(params);
71
- const Mm = 0.001 * F; // one "wolf millimetre" at this variant's scale
72
- const comb = new Float32Array(nV * 3), tint = new Float32Array(nV * 4), pattern = new Float32Array(nV).fill(1);
73
- const mark = new Float32Array(nV).fill(1), furLen = new Float32Array(nV), patternColor = new Float32Array(nV * 4), surf = new Float32Array(nV * 4);
74
- const dom = weights.dominant;
75
- const contrast = params.patternContrast ?? 1;
76
-
77
- // carapace landmarks
78
- const z0 = J.pedA[2], z1 = J.front[2], Lc = z1 - z0;
79
- const fovea = [0, 0, z0 + 0.36 * Lc];
80
- const eyeC = add(J.ceph, [0, 0, 0.0]);
81
- const halfW = (z) => {
82
- // carapace half width along z (pear shape: widest behind the middle, narrow head)
83
- const u = (z - z0) / Lc;
84
- const wMax = d.carapace.wid * 0.0005, wHead = d.carapace.headW * 0.0005;
85
- return u < 0.45 ? wMax * Math.sqrt(Math.max(0.05, 1 - ((0.45 - u) / 0.47) ** 2)) : mix(wMax, wHead, smoothstep(0.45, 0.85, u));
86
- };
87
- const eyes = [];
88
- for (const e of EYES[d.variant]) { const E = eyeSpec(e); for (const s of [1, -1]) eyes.push({ c: eyeFrameOf(E, J.ceph, s).c, r: E.r }); }
89
- const boneKind = BONES.map((b) => {
90
- const m = /^(coxa|troch|femur|patella|tibia|meta|tarsus)(\d)([LR])$/.exec(b.name);
91
- if (m) return { kind: 'leg', seg: m[1], leg: +m[2], S: m[3] };
92
- if (/^palp/.test(b.name)) return { kind: 'palp', seg: b.name.replace(/^palp|[LR]$/g, '').toLowerCase() };
93
- return { kind: b.name.replace(/[LR]$/, '') };
94
- });
95
- const segIdx = { coxa: 0, troch: 1, femur: 2, patella: 3, tibia: 4, meta: 5, tarsus: 6 };
96
-
97
- for (let v = 0; v < nV; v++) {
98
- const p = [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
99
- const n = [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
100
- const rn = regionNames[regionOf[v]];
101
- const b = BONES[dom[v]], bk = boneKind[dom[v]];
102
- let col = COL.carapace, mat = MAT.CHITIN, fl = 0, gloss = 0.05, pat = 1, pcol = COL.stripe, pint = 0, mk = 1;
103
- let cdir = [0, 0, -1];
104
- const noise = fbm3(p[0] / (1.6 * Mm), p[1] / (1.6 * Mm), p[2] / (1.6 * Mm), 3) - 0.5;
105
- const noiseF = fbm3(p[0] / (0.45 * Mm) + 7, p[1] / (0.45 * Mm), p[2] / (0.45 * Mm), 2) - 0.5;
106
-
107
- if (rn.startsWith('fang')) {
108
- mat = MAT.KERATIN; col = COL.fang; gloss = 0.9; fl = 0;
109
- cdir = sub(J['fangTip' + rn.slice(-1)], J['cheTip' + rn.slice(-1)]);
110
- } else if (rn.startsWith('chel')) {
111
- const S = rn.slice(-1);
112
- col = COL.chel; fl = (wolf ? 0.66 : 1.6) * Mm; gloss = 0.05;
113
- cdir = sub(J['cheTip' + S], J['cheBase' + S]);
114
- // hair on the front face, bare glossy cuticle near the tip (the fang base)
115
- const t = dot(sub(p, J['cheBase' + S]), norm(cdir)) / len(cdir);
116
- fl *= 1 - smoothstep(0.7, 0.95, t);
117
- col = mix3(col, COL.chelHair, clamp(0.5 + noise, 0, 1) * (wolf ? 0.35 : 0.5) * (1 - smoothstep(0.6, 0.9, t)));
118
- } else if (bk.kind === 'leg' || bk.kind === 'palp') {
119
- // ---- legs and palps: distal hair flow, pale joint bands
120
- const a = b.head, e = b.tail, ax = sub(e, a), L = len(ax);
121
- cdir = ax;
122
- const t = clamp(dot(sub(p, a), ax) / (L * L), 0, 1);
123
- const seg = bk.kind === 'leg' ? bk.seg : bk.seg === 'coxa' ? 'coxa' : bk.seg === 'troch' ? 'troch' : bk.seg === 'femur' ? 'femur' : bk.seg === 'patella' ? 'patella' : bk.seg === 'tibia' ? 'tibia' : 'tarsus';
124
- const si = segIdx[seg] ?? 6;
125
- const up = n[1];
126
- col = bk.kind === 'palp' ? COL.palp : COL.leg;
127
- if (si <= 1) col = mix3(COL.venter, col, smoothstep(-0.2, 0.6, up));
128
- // bands: pale rings near the distal end of femur, patella and tibia (wolf); red-knee: the
129
- // orange band covers the patella top and the distal femur, pale rings at tibia / metatarsus ends
130
- let bandD = 1e9, bandCol = COL.legBand;
131
- const along = t * L; // metres from the segment start
132
- if (wolf) {
133
- if (si === 2) bandD = Math.min(Math.abs(along - L * 0.18) - 0.35 * Mm, Math.abs(along - L * 0.62) - 0.4 * Mm);
134
- else if (si === 3) bandD = Math.abs(along - L * 0.5) - L * 0.22;
135
- else if (si === 4) bandD = Math.min(Math.abs(along - L * 0.2) - 0.45 * Mm, Math.abs(along - L * 0.72) - 0.4 * Mm);
136
- else if (si === 5) bandD = Math.abs(along - L * 0.12) - 0.3 * Mm;
137
- // diffuse, mottled banding of the setae (not crisp rings)
138
- bandD += 0.35 * Mm * noiseF + 0.25 * Mm * noise;
139
- } else if ((params.morph || 'redknee') === 'redknee' || params.morph === 'rosy') {
140
- if (si === 3) bandD = -0.2 * Mm + (0.25 - up) * 1.4 * Mm; // top of the patella
141
- else if (si === 2) bandD = (L - along) - 0.12 * L + (0.2 - up) * 1.2 * Mm;
142
- else if (si === 4) { bandD = Math.min(L - along - 0.07 * L, along - 0.04 * L) + (0.1 - up) * 0.8 * Mm; bandCol = COL.legRing; }
143
- else if (si === 5) { bandD = (L - along) - 0.06 * L + (0.1 - up) * 0.8 * Mm; bandCol = COL.legRing; }
144
- // "knee" stripes run lengthwise along the patella and tibia top in B. hamorii
145
- if (si === 3 || si === 4) bandD = Math.min(bandD, Math.abs(dot(norm(cross(ax, [0, 1, 0])), sub(p, a))) - 0.4 * Mm + (0.5 - up) * 2 * Mm);
146
- } else if (params.morph === 'pinktoe' && si === 6 && bk.kind === 'leg') {
147
- col = COL.tarsusTip;
148
- }
149
- // (wolf: soft edges, about 3x the anti-aliasing width, and a lower contrast)
150
- if (bandD < 1e8) { pat = bandD * PK * (wolf ? 0.33 : 1); pcol = bandCol; pint = contrast * (wolf ? 0.6 : 1); }
151
- // darker ventral side and joint membranes
152
- col = mix3(col, COL.legDark, smoothstep(0.1, -0.7, up) * 0.4);
153
- const lens = wolf ? [0.62, 0.64, 0.85, 0.8, 0.8, 0.72, 0.66] : [1.8, 1.8, 3.4, 3.2, 3.2, 2.8, 2.4];
154
- fl = lens[si] * Mm * (bk.kind === 'palp' ? 0.9 : 1) * (1 + 0.2 * noiseF);
155
- if (!wolf && si === 6) fl *= 0.8;
156
- gloss = 0.03;
157
- } else if (bk.kind === 'abdomen' || bk.kind === 'pedicel' || bk.kind === 'spinnerets') {
158
- // ---- abdomen: hair lying backwards, dorsal heart mark and chevrons, dark venter
159
- const c = d.abdomen.c.map((x) => x * 0.001), r = d.abdomen.r.map((x) => x * 0.001);
160
- const u = (p[2] - c[2]) / r[2]; // +1 front .. -1 rear
161
- const up = n[1];
162
- const dors = smoothstep(-0.1, 0.55, up);
163
- col = mix3(COL.venter, mix3(COL.abdSide, COL.abdomen, dors), smoothstep(-0.75, -0.2, up));
164
- cdir = [0, -0.15 * u, -1];
165
- if (wolf) {
166
- // lanceolate heart mark: a dark spear along the front half of the midline (tint), pale
167
- // chevrons over the rear half (pattern)
168
- const hw = mix(0.95, 0.15, clamp((0.95 - u) / 1.0, 0, 1)) * Mm;
169
- const heart = (Math.abs(p[0]) - hw * (u > -0.1 ? 1 : 0)) / Mm;
170
- col = mix3(col, COL.heart, smoothstep(0.3, -0.2, heart) * dors * contrast * smoothstep(-0.2, 0.2, u));
171
- mk = Math.min(mk, u > -0.05 && u < 0.95 ? (Math.abs(Math.abs(p[0]) - hw) - 0.08 * Mm) * PK / (dors > 0.3 ? 1 : 1e3) : 1);
172
- // rear half: pairs of pale spots flanking the midline, joined by faint, broken chevrons
173
- let spots = 1e9, ch = 1e9;
174
- for (let k = 0; k < 4; k++) {
175
- const uz = -0.12 - k * 0.2;
176
- spots = Math.min(spots, Math.hypot(Math.abs(p[0]) - r[0] * (0.3 - 0.03 * k), (u - uz) * r[2]) - 0.32 * Mm * (1 - 0.15 * k));
177
- const vz = uz + Math.abs(p[0]) / r[0] * 0.25;
178
- ch = Math.min(ch, Math.abs(u - vz) * r[2] - 0.1 * Mm * (1 - 0.2 * k));
179
- }
180
- ch = Math.max(ch, Math.abs(p[0]) - r[0] * 0.3, 0.25 * Mm * (noiseF + 0.2)); // broken up
181
- // pale flanks: lateral pale bands (Hogna) framing the dorsum
182
- const flank = Math.abs(Math.abs(p[0]) / r[0] - 0.72) * r[0] - 0.35 * Mm;
183
- pat = (Math.min(spots, ch, flank + 0.3 * Mm * noise) + 0.2 * Mm * noiseF) * PK * 0.6;
184
- pcol = COL.chevron;
185
- pint = dors * contrast * 0.8 * smoothstep(-0.35, 0.1, up);
186
- // mottling
187
- col = [col[0] * (1 + 0.3 * noiseF), col[1] * (1 + 0.28 * noiseF), col[2] * (1 + 0.24 * noiseF)];
188
- fl = 0.9 * Mm * (1 + 0.25 * noiseF);
189
- } else {
190
- // tarantula: long reddish setae on black; a bald urticating patch sometimes
191
- // black under the hair, the long setae reddish (strand-mottled, stronger on the dorsum)
192
- pat = (-0.5 * Mm + 0.2 * Mm * noise) * PK;
193
- pcol = COL.chevron;
194
- pint = smoothstep(-0.4, 0.3, up) * (0.4 + 0.3 * noiseF);
195
- fl = (3.8 + 1.2 * noiseF) * Mm * smoothstep(-0.9, -0.3, up) + 1.2 * Mm;
196
- if (params.bald) {
197
- const bd = len([p[0] / r[0], (p[1] - c[1] - r[1] * 0.7) / r[1], (p[2] - c[2] + r[2] * 0.35) / r[2]]);
198
- fl *= smoothstep(0.28, 0.42, bd + 0.05 * noise);
199
- }
200
- }
201
- if (bk.kind === 'spinnerets') { col = COL.spinneret; fl *= 0.3; }
202
- if (bk.kind === 'pedicel') fl *= 0.4;
203
- gloss = 0.03;
204
- } else {
205
- // ---- prosoma: carapace, head, sternum
206
- const up = n[1];
207
- const x = p[0], z = p[2];
208
- const W = halfW(z);
209
- const dors = smoothstep(-0.15, 0.35, up);
210
- col = mix3(COL.venter, COL.carapace, smoothstep(-0.55, -0.1, up));
211
- // hair radiates back and out from the eye region
212
- cdir = [x, 0, z - eyeC[2] - 0.001 * F];
213
- const u = (z - z0) / Lc;
214
- if (wolf) {
215
- // median band: narrow between the eyes, widest over the thorax, narrowing to the rear
216
- // (a narrow pale stripe between the eyes and down the face, widening behind the eyes)
217
- const mw = (u > 0.72 ? mix(0.5, 0.35, smoothstep(0.72, 0.95, u)) : mix(0.4, 0.85, smoothstep(0.1, 0.55, u))) * Mm;
218
- const median = Math.abs(x) - mw + 0.12 * Mm * noiseF;
219
- // submarginal bands along the sides of the carapace
220
- const rel = Math.abs(x) / Math.max(W, 1e-5);
221
- const sub1 = Math.abs(rel - 0.8) * W - 0.13 * W + 0.1 * Mm * noise;
222
- const subm = Math.max(sub1, (0.15 - up) * 3 * Mm, (u - 0.8) * 8 * Mm);
223
- pat = Math.min(median, subm) * PK;
224
- pint = dors * contrast;
225
- pcol = COL.stripe;
226
- // dark striae radiating from the fovea
227
- let st = 1e9;
228
- for (let k = 0; k < 4; k++) {
229
- const ang = (-0.95 + k * 0.62);
230
- const dir = [Math.sin(ang + Math.PI / 2), 0, Math.cos(ang + Math.PI / 2)];
231
- const q = sub([Math.abs(x), 0, z], fovea);
232
- const along = dot(q, dir);
233
- if (along > 0.4 * Mm && along < W * 0.95) st = Math.min(st, Math.abs(q[0] * dir[2] - q[2] * dir[0]) - 0.08 * Mm * (1 - along / W));
234
- }
235
- mk = Math.min(mk, dors > 0.2 ? st * PK : 1);
236
- } else {
237
- // velvet black centre with a tan margin and faint radiating lines
238
- const rel = Math.abs(x) / Math.max(W, 1e-5);
239
- pat = (Math.max(0.78 - rel, 0) * W * 1.0 - 0.06 * Mm + (0.25 - up) * 0.5 * Mm) * PK;
240
- if (up < -0.2) pat = 1;
241
- pcol = COL.margin; pint = (params.morph === 'pinktoe' ? 0.3 : 1) * smoothstep(-0.2, 0.2, up);
242
- }
243
- // ocular area (and the tarantula's tubercle): bare, black, glossy
244
- let eyeD = 1e9;
245
- for (const e of eyes) eyeD = Math.min(eyeD, len(sub(p, e.c)) - e.r * 1.35);
246
- if (eyeD < 0.5 * Mm * (wolf ? 1 : 0.6)) col = mix3(col, COL.ocular, smoothstep(0.5 * Mm, 0.05 * Mm, eyeD));
247
- fl = (wolf ? 0.72 : 1.1) * Mm * smoothstep(0.05 * Mm, 0.6 * Mm, eyeD);
248
- if (up < -0.5) fl *= 0.5; // sternum
249
- gloss = eyeD < 0.3 * Mm ? 0.4 : 0.05;
250
- if (b.name === 'head' && up > 0.2 && z > J.front[2] - 0.0015 * F) fl *= 0.6;
251
- }
252
-
253
- // low frequency colour noise
254
- col = [col[0] * (1 + 0.22 * noise), col[1] * (1 + 0.2 * noise), col[2] * (1 + 0.16 * noise)];
255
- // comb: tangent to the surface
256
- let cd = sub(cdir, mul(n, dot(cdir, n)));
257
- if (len(cd) < 1e-9) cd = norm(cross(n, [1, 0, 0])); else cd = norm(cd);
258
- comb.set(cd, v * 3);
259
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
260
- pattern[v] = pat; mark[v] = mk; furLen[v] = fl;
261
- patternColor[v * 4] = pcol[0]; patternColor[v * 4 + 1] = pcol[1]; patternColor[v * 4 + 2] = pcol[2]; patternColor[v * 4 + 3] = pint;
262
- // gloss, feature scale (fine cuticle striation), agouti (banded setae), undercoat
263
- surf[v * 4] = gloss; surf[v * 4 + 1] = -0.00012 * F; surf[v * 4 + 2] = wolf ? 0.35 : 0.1; surf[v * 4 + 3] = 0;
264
- }
265
-
266
- // smooth hair length so shells do not step
267
- const nb = weights.neighbors, tmp = new Float32Array(nV);
268
- for (let it = 0; it < 2; it++) {
269
- for (let v = 0; v < nV; v++) {
270
- const ns = nb[v];
271
- if (!ns.length || tint[v * 4 + 3] !== MAT.CHITIN) { tmp[v] = furLen[v]; continue; }
272
- let a = 0;
273
- for (const u of ns) a += furLen[u];
274
- tmp[v] = 0.5 * furLen[v] + (0.5 * a) / ns.length;
275
- }
276
- furLen.set(tmp);
277
- }
278
- return { comb, tint, pattern, mark, furLen, patternColor, surf, stats: { patternScale: PK } };
279
- }
@@ -1,78 +0,0 @@
1
- // Spider: wolf spider (Lycosidae, default) and tarantula (Theraphosidae, opts.variant = 'tarantula').
2
- // Body plan 'spider' (core/motion/spider.js): 8 legs x 7 segments, pedipalps, chelicerae with
3
- // fangs, eight simple eyes.
4
- import { spiderRig } from './rig.js';
5
- import { sculptSpider, EYES, eyeSpec } from './sculpt.js';
6
- import { spiderRegions } from './regions.js';
7
- import { spiderCoat } from './coat.js';
8
- import { motion } from './motion.js';
9
-
10
- // glossy black domes; the look.iris.outer colour tints the dome (dark amber-brown lens)
11
- const EYE_LOOK = {
12
- pupil: 'round',
13
- lids: 'simple',
14
- // the dome colour comes from iris.outer: near black with a faint amber tapetum glint
15
- iris: { inner: [0.02, 0.02, 0.02], mid: [0.03, 0.03, 0.025], hi: [0.05, 0.05, 0.04], outer: [0.3, 0.17, 0.05] },
16
- sclera: { color: [0.02, 0.02, 0.02], visible: false },
17
- lidColor: [0.02, 0.018, 0.015],
18
- pupilSize: [0.4, 0.5],
19
- };
20
-
21
- export default {
22
- id: 'spider',
23
- name: 'Spider',
24
- latin: 'Lycosidae / Theraphosidae',
25
- group: 'others',
26
- plan: 'spider',
27
- covering: 'chitin',
28
- variants: [
29
- { id: 'wolf', name: 'Wolf spider', latin: 'Lycosidae' },
30
- { id: 'tarantula', name: 'Tarantula', latin: 'Theraphosidae' },
31
- ],
32
-
33
- // Seeded individual. Females are larger with a heavy abdomen (gravid ones more so); males are
34
- // smaller and leggier with swollen palpal bulbs; spiderlings are small and short-legged.
35
- variation(R, o) {
36
- const variant = o.variant === 'tarantula' ? 'tarantula' : 'wolf';
37
- const sex = o.sex || (R() < 0.6 ? 'female' : 'male');
38
- const age = o.age || 'adult';
39
- const juv = age === 'juvenile';
40
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
41
- const male = sex === 'male' && !juv;
42
- const size = (male ? (variant === 'wolf' ? 0.82 : 0.88) : 1) * (1 + 0.08 * g()) * (juv ? 0.42 : 1);
43
- const p = {
44
- variant, sex, age, size,
45
- quality: o.quality,
46
- legK: (male ? 1.1 : 1) * (1 + 0.04 * g()) * (juv ? 0.9 : 1),
47
- abdK: (male ? 0.78 : 1 + 0.08 * Math.abs(g()) + 0.05 * g()) * (juv ? 0.9 : 1),
48
- palpBulb: male ? 1 : 0,
49
- coatWarmth: 0.9 * g(),
50
- coatLightness: 0.18 * g() + (male && variant === 'wolf' ? -0.08 : 0),
51
- patternContrast: variant === 'wolf' ? 0.6 + 0.55 * R() : 1,
52
- coatGrey: variant === 'wolf' ? 0.75 * R() ** 1.5 : 0,
53
- coatSeed: Math.floor(R() * 1e6),
54
- warps: [],
55
- };
56
- if (variant === 'tarantula') {
57
- const m = R();
58
- p.morph = m < 0.6 ? 'redknee' : m < 0.8 ? 'rosy' : 'pinktoe';
59
- p.bald = R() < 0.3; // kicked off its urticating hairs
60
- }
61
- return p;
62
- },
63
- rig: (params) => spiderRig(params),
64
- sculpt: (m, rig, params) => sculptSpider(m, rig, params),
65
- regions: spiderRegions,
66
- coat: spiderCoat,
67
- eyeSpecs: (params, rig) => {
68
- const out = [];
69
- for (const e of EYES[params.variant === 'tarantula' ? 'tarantula' : 'wolf']) for (const side of [1, -1]) out.push({ side, spec: eyeSpec(e), headOrigin: rig.J.ceph, bone: 'head', look: EYE_LOOK });
70
- return out;
71
- },
72
- motion,
73
- // setae: fine but still resolvable strands (a few per millimetre), loosely tufted; the tarantula's
74
- // long hair is coarser and fuzzier
75
- render: (params) => (params?.variant === 'tarantula'
76
- ? { markColor: [0.012, 0.009, 0.007], strandDensity: 3000, clumpDensity: 3, shellScale: 1, finWidth: 0.6 }
77
- : { markColor: [0.012, 0.009, 0.007], strandDensity: 5000, clumpDensity: 5, shellScale: 1, finWidth: 0.45 }),
78
- };
@@ -1,72 +0,0 @@
1
- // Spider motion data for the spider engine (schema: src/core/motion/spider.js).
2
- // Numbers are for the reference individual of each variant (params.size = 1); the engine scales
3
- // speeds by dynamic similarity (sqrt(size)) and reads every length from the joints.
4
- //
5
- // Gait: alternating tetrapod (L1 R2 L3 R4 against R1 L2 R3 L4) with a back-to-front metachronal lag
6
- // inside each tetrapod (Biancardi et al. 2011 on Grammostola: quadruped lag 16 % slow, 8 % fast;
7
- // Weihmann 2013 on Cupiennius: contralateral pairs in antiphase, body oscillation tiny). Offsets are
8
- // touchdown phases relative to L4, in the order L1 L2 L3 L4 R1 R2 R3 R4.
9
- // Duty factor falls with speed (d = 0.666 - 0.531 v for Grammostola; Cupiennius < 0.5 when fast);
10
- // stride frequency rises with speed (f = 14.4 v + 0.14 Hz for an 18 g tarantula, faster for the
11
- // smaller wolf spider by dynamic similarity).
12
- const slow = [0.74, 0.16, 0.58, 0.0, 0.24, 0.66, 0.08, 0.5];
13
- const fast = [0.62, 0.08, 0.54, 0.0, 0.12, 0.58, 0.04, 0.5];
14
- const G = (name, v, f, D, off, o = {}) => ({ name, v, f, D, off, ...o });
15
-
16
- export const motion = {
17
- // ---------------------------------------------------------------- wolf spider (default)
18
- maxSpeed: 0.32, // m/s: a dash (wolf spiders sprint at 0.3-0.6 m/s over short distances)
19
- accel: 1.6, // m/s^2: wolf spiders reach full speed within a stride or two
20
- decel: 2.4, // and freeze abruptly
21
- turnRate: 4.0, // rad/s (on the spot and at a walk)
22
- latAccelMax: 0.4, // m/s^2: at speed it turns in arcs of a few body lengths
23
- gears: { walk: 0.05, run: 0.14, sprint: 0.3 },
24
- gaits: [
25
- G('walk', 0.0, 1.6, 0.72, slow, { lift: 0.0022, bob: 0.00008, sway: 0.015 }),
26
- G('walk', 0.05, 2.4, 0.66, slow, { lift: 0.0026, bob: 0.00012, sway: 0.015 }),
27
- G('run', 0.1, 3.3, 0.58, fast, { lift: 0.003, bob: 0.00016, sway: 0.01 }),
28
- G('run', 0.18, 4.9, 0.5, fast, { lift: 0.0034, bob: 0.0002, sway: 0.008 }),
29
- G('run', 0.3, 6.0, 0.48, fast, { lift: 0.0038, bob: 0.00022, sway: 0.006 }),
30
- ],
31
- // neutral footholds: bind claw positions pulled toward the coxa (legs are not fully spread in
32
- // stance) and the stance height relative to the bind pose
33
- stance: { reach: [0.8, 0.9, 0.9, 0.78], height: 1.0, width: 1.0 },
34
- feet: { contact: 'claw', heelLift: 12 },
35
- abdomen: { stiffness: 16, lag: 0.12, droop: 0.05 },
36
- breath: { rate: 0.55, amp: 0.018 },
37
- chelicerae: { fang: 'labidognath', fangOpen: 1.25, spread: 0.35, pitch: 0.45 },
38
- palps: { twitch: 1, tap: 0.6 },
39
- idle: { twitch: 1, raiseFront: 0.12, look: 0.35 },
40
- look: { yaw: 0.45, pitch: 0.32 },
41
- actions: {
42
- jump: { height: 0.016, distance: 0.035, crouch: 0.25 },
43
- attack: { style: 'threat-lunge', rear: 0.3, lunge: 0.005, threat: 0.7 },
44
- sleep: { tuck: 0.85 },
45
- },
46
-
47
- // ---------------------------------------------------------------- tarantula
48
- variants: {
49
- tarantula: {
50
- maxSpeed: 0.18,
51
- accel: 1.2,
52
- decel: 2.4,
53
- turnRate: 3.2,
54
- gears: { walk: 0.035, run: 0.1, sprint: 0.2 },
55
- gaits: [
56
- G('walk', 0.0, 0.8, 0.7, slow, { lift: 0.005, bob: 0.00015, sway: 0.012 }),
57
- G('walk', 0.04, 1.4, 0.65, slow, { lift: 0.0055, bob: 0.0002, sway: 0.012 }),
58
- G('run', 0.11, 2.4, 0.6, fast, { lift: 0.0065, bob: 0.0003, sway: 0.008 }),
59
- G('run', 0.236, 3.6, 0.54, fast, { lift: 0.0075, bob: 0.0004, sway: 0.006 }),
60
- ],
61
- stance: { reach: [0.88, 0.93, 0.93, 0.88], height: 1.0, width: 1.0 },
62
- breath: { rate: 0.35, amp: 0.012 },
63
- chelicerae: { fang: 'orthognath', fangOpen: 1.2, spread: 0.12, pitch: 0.55 },
64
- idle: { twitch: 0.5, raiseFront: 0.05, look: 0.2 },
65
- actions: {
66
- jump: { height: 0.012, distance: 0.05, crouch: 0.2 },
67
- attack: { style: 'threat-lunge', rear: 0.7, raise2: 0.65, lunge: 0.008, threat: 0.9 },
68
- sleep: { tuck: 0.8 },
69
- },
70
- },
71
- },
72
- };
@@ -1,87 +0,0 @@
1
- // Meshing plan. A spider is mostly thin legs spread over a wide box, so one dense grid would be
2
- // wasteful: the body (prosoma, abdomen, palps, coxae, trochanters) is one region, every leg its
3
- // own region from the middle of the trochanter outward, and the chelicerae and fangs are rigid
4
- // regions of their own (they move on their own bones). Legs and body overlap across a cut plane
5
- // through the trochanter and cross-fade there (both surfaces see the same primitives near the cut).
6
- import { spiderDims } from './rig.js';
7
- import { legJoints, LEG_KEYS } from '../../core/rig/spider.js';
8
- import { lerp, norm, sub, smoothstep } from '../../core/math/vec.js';
9
-
10
- // cut planes through the trochanters (reference space)
11
- export function legCuts(J, d) {
12
- const F = d.unit / 0.013;
13
- return LEG_KEYS.map((key) => {
14
- const S = key[0], i = +key[1];
15
- const lj = legJoints(i, S);
16
- const t = J[lj[1]], f = J[lj[2]];
17
- return { key, p: lerp(t, f, 0.5), n: norm(sub(f, t)), e: J[lj[3]], r: d.legR[2][0] * 0.001 * 2.2 * (i === 4 ? d.hindR : 1) };
18
- });
19
- }
20
-
21
- function box(points, pad) {
22
- const mn = [1e9, 1e9, 1e9], mx = [-1e9, -1e9, -1e9];
23
- for (const p of points) for (let k = 0; k < 3; k++) { mn[k] = Math.min(mn[k], p[k] - pad); mx[k] = Math.max(mx[k], p[k] + pad); }
24
- return [mn, mx];
25
- }
26
-
27
- export function spiderRegions(rig, params, Q) {
28
- const J = rig.J, d = spiderDims(params);
29
- // the tarantula is stockier (thicker legs, bigger abdomen for its unit): slightly coarser cells
30
- const F = (d.unit / 0.013) * (d.variant === 'tarantula' ? 1.12 : 1), r = Q.res;
31
- const hB = 0.156e-3 * F * r, hL = 0.148e-3 * F * r, hC = 0.115e-3 * F * r;
32
- const B = 2.2 * Math.max(hB, hL);
33
- const cuts = legCuts(J, d);
34
- // s: signed distance along the cut normal; near: within reach of the leg (distance to the segment
35
- // from the cut centre to the knee)
36
- for (const c of cuts) { c.ab = sub(c.e, c.p); c.ab2 = c.ab[0] ** 2 + c.ab[1] ** 2 + c.ab[2] ** 2; }
37
- const inCut = (c, x, y, z) => {
38
- const dx = x - c.p[0], dy = y - c.p[1], dz = z - c.p[2];
39
- const s = dx * c.n[0] + dy * c.n[1] + dz * c.n[2];
40
- const t = Math.max(0, Math.min(1, (dx * c.ab[0] + dy * c.ab[1] + dz * c.ab[2]) / c.ab2));
41
- const ex = dx - c.ab[0] * t, ey = dy - c.ab[1] * t, ez = dz - c.ab[2] * t;
42
- return { s, near: ex * ex + ey * ey + ez * ez < c.r * c.r };
43
- };
44
- const bodyClip = (x, y, z) => {
45
- for (const c of cuts) { const q = inCut(c, x, y, z); if (q.near && q.s > B) return false; }
46
- return true;
47
- };
48
- const maxR = Math.max(...d.legR.map((q) => q[0])) * 0.001 * d.hindR;
49
- const minPad = 0.4e-3 * F;
50
- // body box: axial joints, palps, coxae, trochanters, the start of the femora
51
- const bodyPts = [];
52
- for (const k of Object.keys(J)) if (!/^(femur|patella|tibia|meta|tarsus|claw)/.test(k)) bodyPts.push(J[k]);
53
- for (const c of cuts) bodyPts.push(c.p);
54
- const abd = d.abdomen.r.map((x) => x * 0.001);
55
- const [bmin, bmax] = box(bodyPts, Math.max(abd[0], abd[1]) + minPad);
56
- bmin[1] = Math.max(bmin[1], -0.2e-3 * F);
57
- const regions = [{ name: 'body', part: 'body', bmin, bmax, h: hB, F: 5, clip: bodyClip }];
58
- const rigid = [];
59
- for (const S of ['L', 'R']) {
60
- const [cmin, cmax] = box([J['cheBase' + S], J['cheTip' + S]], d.chel.r[0] * 0.0013 + minPad);
61
- rigid.push({ name: 'chel' + S, part: 'chel' + S, bmin: cmin, bmax: cmax, h: hC, F: 5, rigidBone: 'chelicera' + S });
62
- const [fmin, fmax] = box([J['cheTip' + S], J['fangTip' + S]], d.chel.fangR * 0.0015 + minPad * 0.5);
63
- rigid.push({ name: 'fang' + S, part: 'fang' + S, bmin: fmin, bmax: fmax, h: hC * 0.8, F: 5, rigidBone: 'fang' + S });
64
- }
65
- const legs = { L: [], R: [] };
66
- for (const c of cuts) {
67
- const S = c.key[0], i = +c.key[1];
68
- const lj = legJoints(i, S).slice(1).map((n) => J[n]);
69
- const [lmin, lmax] = box(lj, maxR * 1.3 + minPad);
70
- legs[S].push({ name: 'leg' + c.key, part: 'leg' + c.key, bmin: lmin, bmax: lmax, h: hL, F: 5, clip: (x, y, z) => inCut(c, x, y, z).s > -B });
71
- }
72
- return {
73
- jobs: [[...regions, ...rigid], legs.L, legs.R],
74
- fade(x, y, z, name) {
75
- if (name === 'body') {
76
- let f = 1;
77
- for (const c of cuts) { const q = inCut(c, x, y, z); if (q.near) f *= 1 - smoothstep(-B, B, q.s); }
78
- return f;
79
- }
80
- if (name.startsWith('leg')) {
81
- const c = cuts.find((q) => 'leg' + q.key === name);
82
- return smoothstep(-B, B, inCut(c, x, y, z).s);
83
- }
84
- return 1;
85
- },
86
- };
87
- }