@bitefoundry/retrieviewer 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/PLUGIN.md +13 -10
  7. package/PREREQUISITES.md +95 -0
  8. package/PUPPY-MEMORY.md +102 -1
  9. package/README.ko.md +110 -0
  10. package/README.md +117 -59
  11. package/RELEASING.md +23 -12
  12. package/THIRD-PARTY-NOTICES.md +5 -3
  13. package/braille.mjs +35 -9
  14. package/examples/live.mjs +2 -2
  15. package/integrations/linux-overlay/overlay.py +2 -2
  16. package/integrations/macos-overlay/overlay.jxa +8 -6
  17. package/integrations/opencode/runtime.mjs +4 -4
  18. package/integrations/opencode/tui.tsx +1 -1
  19. package/integrations/windows-overlay/Overlay.cs +4 -4
  20. package/integrations/windows-overlay/protocol.mjs +11 -7
  21. package/package.json +34 -13
  22. package/puppy-comfort-sprites.mjs +25 -0
  23. package/puppy-companion-comfort-q5.bin.gz +0 -0
  24. package/puppy-companion-comfort.json +77 -0
  25. package/puppy-companion-life.mjs +49 -16
  26. package/puppy-companion-objects.mjs +12 -0
  27. package/puppy-frame-predictive.mjs +15 -9
  28. package/puppy-frame-store.mjs +2 -2
  29. package/puppy-paper-world.mjs +14 -0
  30. package/puppy-paper.mjs +65 -14
  31. package/retrieviewer-plugin.d.mts +1 -1
  32. package/retrieviewer-plugin.mjs +1 -1
  33. package/retrieviewer.d.mts +10 -4
  34. package/retrieviewer.mjs +20 -14
  35. package/locomotion-core.mjs +0 -21
  36. package/locomotion-puppy.animal +0 -0
  37. package/vendor/procedural-animals/package.json +0 -40
  38. package/vendor/procedural-animals/src/animal.js +0 -146
  39. package/vendor/procedural-animals/src/bake.js +0 -51
  40. package/vendor/procedural-animals/src/build.js +0 -81
  41. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  42. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  43. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  44. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  45. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  46. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  47. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  48. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  49. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  50. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  51. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  52. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  53. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  54. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  55. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  56. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  57. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  58. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  59. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  60. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  61. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  62. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  63. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  64. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  65. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  66. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  67. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  68. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  69. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  70. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  71. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  72. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  73. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  74. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  75. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  76. package/vendor/procedural-animals/src/index.d.ts +0 -98
  77. package/vendor/procedural-animals/src/index.js +0 -29
  78. package/vendor/procedural-animals/src/registry.js +0 -48
  79. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  80. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  81. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  82. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  83. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  84. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  85. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  86. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  87. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  88. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  89. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  90. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  91. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  92. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  93. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  94. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  95. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  96. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  97. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  98. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  99. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  100. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  101. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  102. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  103. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  104. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  105. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  106. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  107. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  108. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  109. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  110. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  111. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  112. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  113. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  114. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  115. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  116. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  117. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  118. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  119. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  120. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  121. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  122. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  123. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  124. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  125. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  126. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  127. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  128. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  129. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  130. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  131. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  132. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  133. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  134. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  135. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  136. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  137. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  138. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  139. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  140. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  141. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  142. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  143. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  144. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  145. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  146. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  147. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  148. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  149. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  150. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  151. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  152. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  153. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  154. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  155. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  156. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  157. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  158. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  159. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  160. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  161. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  162. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  163. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  164. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  165. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  166. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  167. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  168. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  169. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  170. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  171. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  172. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  173. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  174. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  175. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  176. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  177. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  178. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  179. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  180. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  181. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  182. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  183. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  184. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  185. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  186. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  187. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  188. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  189. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  190. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  191. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  192. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  193. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  194. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  195. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  196. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  197. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  198. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  199. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  200. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  201. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  202. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  203. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  204. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  205. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  206. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  207. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  208. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  209. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  210. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  211. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  212. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  213. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  214. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  215. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  216. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  217. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  218. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  219. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  220. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  221. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  222. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  223. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  224. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  225. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  226. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  227. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  228. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  229. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  230. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  231. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  232. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  233. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  234. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  235. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  236. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  237. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  238. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  239. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  240. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  241. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  242. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  243. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  244. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  245. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  246. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  247. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  248. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  249. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  250. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  251. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  252. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  253. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  254. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,315 +0,0 @@
1
- // Snake coat: overlapping dorsal scales (keeled on the rattlesnake), wide ventral scutes, head plates;
2
- // corn snake saddles with dark borders, lateral blotches and the checkerboard belly; rattlesnake
3
- // diamonds with pale edges, flank blotches, face stripes and the black-and-white "coon tail".
4
- // Everything is laid out in body coordinates: arc length sigma along the spine from the occiput and
5
- // the arc u around the body from the dorsal midline, so the pattern wraps the tube like the real one.
6
- import { SPINE } from './rig.js';
7
- import { eyeSpec } from './sculpt.js';
8
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
9
- import { srgb, mix3, MAT, distPolyline } from '../../core/build/coatKit.js';
10
- import { clamp, smoothstep, mix, fbm3, rng } from '../../core/math/vec.js';
11
-
12
- // Colour sets (sRGB hex sampled from reference photos in neutral light).
13
- export const MORPHS = {
14
- normal: { ground: 0xc9763f, flank: 0xd99a5c, saddle: 0xa8321f, lateral: 0x983a26, border: 0x1b1310, belly: 0xe9e2d2, check: 0x171412, mark: [0.012, 0.009, 0.008] },
15
- okeetee: { ground: 0xd6843f, flank: 0xe0a45e, saddle: 0xb52f1c, lateral: 0xa23421, border: 0x100b09, belly: 0xece4d2, check: 0x141110, mark: [0.006, 0.004, 0.004], borderW: 2.6 },
16
- wild: { ground: 0xa68a6c, flank: 0xb89c7c, saddle: 0x8a3a24, lateral: 0x7c3a28, border: 0x1d1512, belly: 0xe4ddcc, check: 0x1a1715, mark: [0.013, 0.01, 0.009] },
17
- amel: { ground: 0xeb9656, flank: 0xf0b070, saddle: 0xd9482a, lateral: 0xd65a36, border: 0xf2ece0, belly: 0xf3eee4, check: 0xe8a070, mark: [0.86, 0.82, 0.74], borderW: 1.2 },
18
- anery: { ground: 0xa9a6a2, flank: 0xbdbab4, saddle: 0x4a4847, lateral: 0x55524f, border: 0x151414, belly: 0xe6e4e0, check: 0x161515, mark: [0.01, 0.01, 0.01] },
19
- juvenile: { ground: 0x9c8672, flank: 0xae9a86, saddle: 0x6e2a1c, lateral: 0x5e2a20, border: 0x140e0c, belly: 0xe6e0d2, check: 0x151211, mark: [0.008, 0.006, 0.006] },
20
- };
21
- export const DIAMONDBACK = {
22
- ground: 0x8a7560, flank: 0x9a846a, diamond: 0x56422f, centre: 0x76604a, edge: 0xcdbd9c, blotch: 0x5e4a38,
23
- belly: 0xe2d6bc, ringW: 0xe6e0d4, ringB: 0x1a1715, rattle: 0xbfae8c, mark: [0.69, 0.6, 0.45],
24
- };
25
-
26
- export function markColorOf(params) {
27
- if (params.variant === 'rattlesnake') return DIAMONDBACK.mark;
28
- return (MORPHS[params.coat?.morph] || MORPHS.normal).mark;
29
- }
30
-
31
- export function snakeCoat(ctx) {
32
- const { rig, params, pos, nrm, nV, regionOf, regionNames, weights } = ctx;
33
- const J = rig.J, B = rig.plan, viper = params.variant === 'rattlesnake';
34
- const cp = params.coat || {};
35
- const R = rng(9173 + (cp.seed || 0));
36
- const AXL = weights.axialLengths;
37
- const s0 = AXL[1]; // arc of v0 from the nose
38
- const total = AXL[AXL.length - 1] - s0;
39
- const sVent = total * (1 - B.tailFrac);
40
- const hL = B.headLen, W = B.P.headW * B.kH, H = B.P.headH * B.kH;
41
- const yB = -J.v0[1];
42
- // axis samples: y and half width at arc sigma
43
- const axY = (sg) => {
44
- let i = 1;
45
- while (i < AXL.length - 2 && sg + s0 > AXL[i + 1]) i++;
46
- const t = clamp((sg + s0 - AXL[i]) / (AXL[i + 1] - AXL[i]), 0, 1);
47
- const a = J['v' + (i - 1)], b = J['v' + i];
48
- return a[1] + (b[1] - a[1]) * t;
49
- };
50
- const hwAt = (sg) => B.hw(clamp(sg / total, 0, 1));
51
-
52
- // ---- colours
53
- const juv = params.age === 'juvenile';
54
- const morph = viper ? null : juv && (cp.morph === 'normal' || cp.morph === 'wild') ? MORPHS.juvenile : MORPHS[cp.morph] || MORPHS.normal;
55
- const warm = cp.warm || 0, light = cp.light || 0;
56
- const tweak = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.1 * warm + light), c[1] * (1 + 0.02 * warm + light), c[2] * (1 - 0.1 * warm + light)]; };
57
- const C0 = viper
58
- ? Object.fromEntries(Object.entries(DIAMONDBACK).filter(([k]) => k !== 'mark').map(([k, v]) => [k, tweak(v)]))
59
- : Object.fromEntries(Object.entries(morph).filter(([k]) => typeof morph[k] === 'number' && k !== 'borderW').map(([k, v]) => [k, tweak(v)]));
60
- // individuals range from bright orange to duller, browner / greyer grounds
61
- const C = C0;
62
- if (!viper && (cp.morph === 'normal' || cp.morph === 'okeetee')) {
63
- const d = clamp(cp.dull || 0, 0, 0.7);
64
- C.ground = mix3(C.ground, srgb(MORPHS.wild.ground), d); C.flank = mix3(C.flank, srgb(MORPHS.wild.flank), d);
65
- C.saddle = mix3(C.saddle, srgb(0x8e2c1c), d * 0.6);
66
- }
67
- if (viper) {
68
- // wild C. atrox range from pale grey through grey-brown to reddish-brown, with bolder or fainter
69
- // diamonds (seeded tone: < 0 greyer, > 0 redder; light: paler / darker)
70
- const tone = clamp(cp.tone || 0, -1, 1), lt = cp.light || 0;
71
- const tgt = tone < 0 ? srgb(0x8f8c86) : srgb(0x9a6a4e);
72
- const k = Math.abs(tone) * 0.75;
73
- for (const key of ['ground', 'flank', 'centre']) C[key] = mix3(C[key], [tgt[0] * (key === 'flank' ? 1.08 : 1), tgt[1] * (key === 'flank' ? 1.08 : 1), tgt[2] * (key === 'flank' ? 1.08 : 1)], k);
74
- C.diamond = mix3(C.diamond, tone < 0 ? srgb(0x4a4744) : srgb(0x5a3526), k * 0.7);
75
- for (const key of ['ground', 'flank', 'centre', 'edge']) C[key] = C[key].map((c) => c * (1 + 1.6 * lt));
76
- C.diamond = mix3(C.diamond, C.ground, clamp(0.3 - 0.3 * ((cp.blotch || 1) - 1) / 0.18, 0, 0.55));
77
- }
78
- const bk = cp.blotch || 1;
79
-
80
- // ---- pattern features (body coordinates)
81
- const sStart = viper ? 0.045 * total : Math.max(0, 1.5 * hL - (0.5 * (total * (1 - B.tailFrac) - 1.5 * hL)) / 36);
82
- const feats = []; // { s, a, b, u (centre across), kind, col }
83
- if (viper) {
84
- const n = Math.round((cp.count || 1) * 29);
85
- const sp = (sVent - sStart) / n;
86
- for (let k = 0; k < n; k++) {
87
- const s = sStart + (k + 0.5) * sp + (R() - 0.5) * 0.08 * sp;
88
- feats.push({ s, a: 0.56 * sp, b: 0.9, u: 0, kind: 'diamond' });
89
- // flank blotches below the lateral corners of the diamonds
90
- for (const side of [1, -1]) feats.push({ s: s + 0.5 * sp + (R() - 0.5) * 0.1 * sp, a: 0.2 * sp, b: 0.28, u: side * 1.55, kind: 'blotch' });
91
- }
92
- } else {
93
- const n = Math.round((cp.count || 1) * 36);
94
- const sp = (sVent - sStart) / n;
95
- for (let k = 0; k < n; k++) {
96
- const s = sStart + (k + 0.5) * sp + (R() - 0.5) * 0.14 * sp;
97
- feats.push({ s, a: (0.25 + 0.06 * R()) * sp * bk, b: (1.0 + 0.14 * R()) * bk, u: (R() - 0.5) * 0.12, kind: 'saddle' });
98
- for (const side of [1, -1]) if (R() < 0.85) feats.push({ s: s + 0.5 * sp + (R() - 0.5) * 0.2 * sp, a: (0.15 + 0.05 * R()) * sp * bk, b: 0.3 + 0.08 * R(), u: side * (1.72 + 0.1 * R()), kind: 'lateral' });
99
- }
100
- // tail: saddles continue, closer
101
- const nt = Math.round(11 * (cp.count || 1));
102
- const spt = (total * 0.985 - sVent) / nt;
103
- for (let k = 0; k < nt; k++) feats.push({ s: sVent + (k + 0.5) * spt, a: 0.27 * spt * bk, b: 1.2, u: 0, kind: 'saddle' });
104
- }
105
- feats.sort((p, q) => p.s - q.s);
106
- // belly checks (corn): blocks on alternating sides, scute by scute
107
- const checks = [];
108
- const scute = viper ? total / 182 : total / 215;
109
- if (!viper) {
110
- for (let s = 0.09 * total; s < sVent; s += scute) {
111
- if (R() < 0.5) checks.push({ s: s + scute * (0.4 + 0.2 * R()), a: scute * (0.45 + 0.5 * R()), side: R() < 0.5 ? 1 : -1, w: 0.3 + 0.25 * R() });
112
- }
113
- }
114
-
115
- // head and neck features, as 2-D polylines in a projection of the (straight) bind pose:
116
- // top view (x, z) for the crown and nape, side view (z, y) for the flanks of the head
117
- const E = eyeSpec(params);
118
- const eyeC = eyeFrameOf(E, J.v0, 1).c; // left eye centre (x > 0)
119
- const z0 = J.v0[2];
120
- const ez = eyeC[2] - z0, ey = eyeC[1]; // eye position along the head, height (world)
121
- const seg2 = (px, py, P, Wd) => {
122
- let best = 1e9;
123
- for (let i = 0; i < P.length - 1; i++) {
124
- const ax = P[i][0], ay = P[i][1], bx = P[i + 1][0] - ax, by = P[i + 1][1] - ay;
125
- const t = clamp(((px - ax) * bx + (py - ay) * by) / (bx * bx + by * by || 1e-12), 0, 1);
126
- const d = Math.hypot(px - ax - bx * t, py - ay - by * t) - (Wd[i] + (Wd[i + 1] - Wd[i]) * t);
127
- if (d < best) best = d;
128
- }
129
- return best;
130
- };
131
- const kH = B.kH;
132
- // corn: spear point (two blotches meeting in a point between the eyes, running back onto the neck),
133
- // the interocular bar, and the post-ocular stripe through the eye to the jaw angle
134
- const spearL = [[0.0, z0 + ez + 0.003 * kH], [0.06 * W, z0 + 0.4 * hL], [0.11 * W, z0 + 0.0 * hL], [0.14 * W, z0 - 0.35 * hL], [0.1 * W, z0 - 0.75 * hL]];
135
- const spearW = [0.0006, 0.0011, 0.0013, 0.0014, 0.001].map((w) => w * kH);
136
- const barP = [[0, z0 + ez + 0.1 * E.r], [eyeC[0] * 0.85, z0 + ez + 0.4 * E.r]];
137
- const postP = [[z0 + ez - 1.0 * E.r, ey - 0.3 * E.r], [z0 + 0.2 * hL, 0.45 * H], [z0 + 0.02 * hL, 0.36 * H]];
138
- // rattlesnake: pale pre-ocular stripe (front of the eye down to the lip) and post-ocular stripe
139
- // (behind the eye diagonally to the mouth corner), with the dark mask between them
140
- const preV = [[z0 + ez + 1.7 * E.r, ey + 1.1 * E.r], [z0 + 0.78 * hL, 0.62 * H], [z0 + 0.66 * hL, 0.4 * H]];
141
- const postV = [[z0 + ez - 0.5 * E.r, ey - 1.5 * E.r], [z0 + 0.3 * hL, 0.34 * H], [z0 + 0.02 * hL, 0.26 * H]];
142
- const maskV = [[z0 + ez + 0.4 * E.r, ey], [z0 + 0.3 * hL, 0.5 * H], [z0 + 0.05 * hL, 0.42 * H]];
143
- // fine vertical sutures between the labial scales (corn)
144
- const labialMark = (x, z, k) => {
145
- const u = ((z - J.v0[2]) / hL - 0.12) / 0.11; // ~8 labials from the jaw angle to the snout
146
- if (u < 0 || u > 8) return 1;
147
- const f = Math.abs(u - Math.round(u));
148
- return f * 0.11 * hL - 0.00008 * B.kH + (1 - k) * 0;
149
- };
150
- // ---- per-vertex outputs
151
- const comb = new Float32Array(nV * 3), tint = new Float32Array(nV * 4), pattern = new Float32Array(nV).fill(1);
152
- const mark = new Float32Array(nV).fill(1), furLen = new Float32Array(nV), patternColor = new Float32Array(nV * 4), surf = new Float32Array(nV * 4);
153
- const bw = ((morph && morph.borderW) || 1) * 0.0007;
154
- const idx = { body: 0, head: 1, jaw: 2, tongue: 3, fork: 4, fang: 5, rattle: 6 };
155
- for (let v = 0; v < nV; v++) {
156
- const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
157
- const nx = nrm[v * 3], ny = nrm[v * 3 + 1], nz = nrm[v * 3 + 2];
158
- const reg = idx[regionNames[regionOf[v]]] ?? 0;
159
- // comb: scales overlap tail-ward
160
- let cx = -nx * -nz, cy = -ny * -nz, cz = -1 - nz * -nz;
161
- const cl = Math.hypot(cx, cy, cz) || 1;
162
- comb[v * 3] = cx / cl; comb[v * 3 + 1] = cy / cl; comb[v * 3 + 2] = cz / cl;
163
- const sg = weights.axialS[v] - s0;
164
- const hw = hwAt(Math.max(0, sg));
165
- const ya = axY(Math.max(0, sg));
166
- const th = Math.atan2(x, y - ya); // 0 dorsal, +-pi ventral
167
- const u = th; // angular coordinate (radians around the body)
168
- const nzF = fbm3(x * 60, y * 60, z * 60, 3) - 0.5;
169
- const nLo = fbm3(x * 9, y * 9, z * 9, 3) - 0.5;
170
- let col, mat = MAT.SCALES, gloss = viper ? 0.28 : 0.55, size = (viper ? 0.0042 : 0.0024) * (hw / (viper ? 0.0265 : 0.0135)) ** 0.35, keel = viper ? 0.85 : 0.05, irid = viper ? -0.15 : -0.4;
171
- let pat = 1, pcol = null, mk = 1;
172
- const belly = reg <= 1 && y < 0.1 * hw && ny < -0.75 && sg > -0.2 * hL;
173
- // ---------------- body (and neck)
174
- if (reg === 0 || reg === 1) {
175
- const lat = smoothstep(0.9, 2.2, Math.abs(u)); // 0 dorsal .. 1 low flank
176
- const t = clamp(sg / total, 0, 1);
177
- if (viper) {
178
- col = mix3(C.ground, C.flank, lat);
179
- if (sg > sVent) {
180
- // coon tail: alternating rings from the vent to the rattle
181
- const rl = (total - sVent) / 9.5;
182
- const q = (sg - sVent) / rl;
183
- col = C.ringW;
184
- const f = q - Math.floor(q);
185
- if (Math.floor(q) % 2 === 1) { pat = -Math.min(f, 1 - f) * rl; pcol = C.ringB; }
186
- else pat = Math.min(f, 1 - f) * rl;
187
- pat += nzF * 0.0015;
188
- }
189
- } else {
190
- col = mix3(C.ground, C.flank, lat);
191
- }
192
- // dorsal features near sigma
193
- let lo = 0, hi = feats.length;
194
- while (lo < hi) { const md = (lo + hi) >> 1; if (feats[md].s < sg - 0.06) lo = md + 1; else hi = md; }
195
- for (let k = lo; k < feats.length && feats[k].s < sg + 0.06; k++) {
196
- const F = feats[k];
197
- if (sg > sVent && viper) break;
198
- const ds = sg - F.s, du = (u - F.u) * hw; // metres across the surface
199
- const bb = F.b * hw;
200
- let d;
201
- if (F.kind === 'diamond') {
202
- const L1 = Math.abs(ds) / F.a + Math.abs(du) / bb;
203
- d = (L1 - 1) * (F.a * bb) / Math.hypot(F.a, bb);
204
- } else {
205
- const rr = Math.min(F.a, bb) * 0.55;
206
- const qx = Math.abs(ds) - (F.a - rr), qy = Math.abs(du) - (bb - rr);
207
- d = Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) + Math.min(Math.max(qx, qy), 0) - rr;
208
- }
209
- d += nzF * (viper ? 0.0012 : 0.0016);
210
- if (d < pat) {
211
- pat = d;
212
- if (F.kind === 'diamond') pcol = mix3(C.diamond, C.centre, smoothstep(-0.003, -0.009, d) * 0.8);
213
- else pcol = F.kind === 'saddle' ? C.saddle : F.kind === 'lateral' ? C.lateral : C.blotch;
214
- }
215
- }
216
- if (viper && sg <= sVent) {
217
- // pale diamond edges (one scale wide), fading into the ground colour
218
- col = mix3(C.edge, col, smoothstep(0.0022, 0.0034, pat + 0.0006 * nzF));
219
- mk = 1;
220
- } else if (!viper) {
221
- mk = Math.abs(pat) - bw * (0.8 + 0.4 * (0.5 + nzF));
222
- }
223
- // belly
224
- if (belly || Math.abs(u) > 2.55) {
225
- const bcol = viper ? C.belly : C.belly;
226
- const e = belly ? 1 : smoothstep(2.55, 2.75, Math.abs(u));
227
- col = mix3(col, bcol, e);
228
- if (e > 0.5) { pat = 1; pcol = null; mk = 1; }
229
- if (!viper && belly) {
230
- // checkerboard blocks, and two dark stripes under the tail
231
- let best = 1;
232
- for (const c of checks) {
233
- if (Math.abs(c.s - sg) > scute * 3) continue;
234
- const qx = Math.abs(sg - c.s) - c.a, xc = c.side * hw * (0.2 + c.w * 0.5), qy = Math.abs(x - xc) - hw * c.w * 0.5;
235
- const d = Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) + Math.min(Math.max(qx, qy), 0);
236
- if (d < best) best = d;
237
- }
238
- if (sg > sVent) best = Math.min(best, Math.abs(Math.abs(x) - hw * 0.35) - hw * 0.14);
239
- if (best < 1) { pat = best + nzF * 0.0006; pcol = C.check; }
240
- }
241
- if (belly) { mat = MAT.SCALES; gloss = 0.7; size = scute; keel = -1; irid = -0.3; }
242
- }
243
- // head and nape: the scales grow into large head plates over ~1 head length, and their relief
244
- // fades in the same span, so head and neck are one continuous covering (no seam)
245
- // (measured straight back from the occiput in the bind pose, identical on the overlapping head
246
- // and body meshes, so their cross-fade shows no seam)
247
- const sgG = J.v0[2] - z;
248
- const hq = smoothstep(-0.1 * hL, 1.2 * hL, sgG); // 0 on the head .. 1 on the body
249
- if (!belly && sgG < 1.2 * hL) {
250
- const plate = viper ? 0.0027 * kH : 0.009 * kH;
251
- size = size + (plate - size) * (1 - hq);
252
- keel = keel * (viper ? 0.5 + 0.5 * hq : 1);
253
- irid = mix(viper ? -0.5 : -0.75, irid, hq);
254
- gloss = mix(viper ? 0.35 : 0.75, gloss, hq);
255
- }
256
- const upper = ny > -0.15 && y > 0.35 * hw;
257
- if (!viper && sgG < 1.3 * hL) {
258
- // spear point on the crown and nape (top view), with black borders
259
- let d = 1;
260
- if (upper) {
261
- const ax = Math.abs(x);
262
- d = Math.min(seg2(ax, z, spearL, spearW), seg2(ax, z, barP, [0.0012 * kH, 0.0009 * kH])) + nzF * 0.0005;
263
- }
264
- // post-ocular stripe on the side of the head (side view)
265
- if (Math.abs(x) > 0.2 * W && sgG < 0.1 * hL) d = Math.min(d, seg2(z, y, postP, [0.0009 * kH, 0.0012 * kH, 0.0009 * kH]) + nzF * 0.0004);
266
- if (d < pat) { pat = d; pcol = C.saddle; mk = Math.abs(d) - bw * 0.9; }
267
- }
268
- if (viper && sgG < 0.3 * hL && Math.abs(x) > 0.15 * W) {
269
- const dm = seg2(z, y, maskV, [0.0022 * kH, 0.0022 * kH, 0.0016 * kH]);
270
- if (dm < 0) col = mix3(col, C.diamond, 0.75 * smoothstep(0, -0.0015, dm));
271
- mk = Math.min(mk, seg2(z, y, preV, [0.0011 * kH, 0.0018 * kH, 0.0015 * kH]) + nzF * 0.0004, seg2(z, y, postV, [0.0011 * kH, 0.002 * kH, 0.0016 * kH]) + nzF * 0.0004);
272
- }
273
- if (reg === 1 && sgG < 0.02 * hL) {
274
- const hy = y - J.v0[1];
275
- if (viper) {
276
- col = mix3(col, C.edge, 0.8 * smoothstep(yB + 0.46 * H, yB + 0.34 * H, hy));
277
- } else {
278
- // labials: belly cream with thin, faint sutures
279
- if (hy < yB + 0.48 * H) col = mix3(col, C.belly, 0.7 * smoothstep(yB + 0.48 * H, yB + 0.38 * H, hy));
280
- if (hy < yB + 0.48 * H && Math.abs(x) > 0.3 * W) mk = Math.min(mk, labialMark(x, z, 0.1));
281
- }
282
- // mouth: the palate, well inside the lips (no lining shows while the mouth is shut)
283
- if (ny < -0.6 && hy < yB + 0.4 * H && Math.abs(x) < 0.15 * W && z > J.v0[2] + 0.12 * hL && !belly) { mat = MAT.MOUTH; col = srgb(0x8a5a58); pat = 1; mk = 1; }
284
- }
285
- // low-frequency mottling
286
- col = [col[0] * (1 + 0.1 * nLo), col[1] * (1 + 0.1 * nLo), col[2] * (1 + 0.08 * nLo)];
287
- void t;
288
- } else if (reg === 2) {
289
- // lower jaw: pale labials outside (plates, faint relief), mouth lining well inside
290
- const hy = y - J.v0[1];
291
- col = viper ? mix3(C.edge, C.belly, 0.5) : mix3(C.belly, C.flank, 0.3);
292
- size = viper ? 0.003 * kH : 0.008 * kH;
293
- keel = 0; gloss = viper ? 0.5 : 0.75; irid = viper ? -0.5 : -0.75;
294
- if (ny > 0.6 && hy > yB + 0.32 * H && Math.abs(x) < 0.12 * W) { mat = MAT.MOUTH; col = srgb(0x8a5a58); }
295
- } else if (reg === 3 || reg === 4) {
296
- mat = MAT.SKIN;
297
- col = viper ? srgb(0x141212) : reg === 4 ? srgb(0x1a0f0f) : srgb(0x5a1414);
298
- gloss = 0.85;
299
- } else if (reg === 5) {
300
- mat = MAT.KERATIN; col = srgb(0xece4d0); gloss = 0.8;
301
- } else if (reg === 6) {
302
- mat = MAT.KERATIN;
303
- col = mix3(C.rattle, [C.rattle[0] * 0.55, C.rattle[1] * 0.5, C.rattle[2] * 0.45], 0.5 + 0.5 * Math.sin(-z * 900));
304
- gloss = 0.55;
305
- }
306
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
307
- pattern[v] = pat;
308
- mark[v] = mk;
309
- const pc = pcol || col;
310
- patternColor[v * 4] = pc[0]; patternColor[v * 4 + 1] = pc[1]; patternColor[v * 4 + 2] = pc[2]; patternColor[v * 4 + 3] = pcol ? 1 : 0;
311
- surf[v * 4] = gloss; surf[v * 4 + 1] = size; surf[v * 4 + 2] = keel; surf[v * 4 + 3] = irid;
312
- }
313
- void SPINE; void mix;
314
- return { comb, tint, pattern, mark, furLen, patternColor, surf, stats: { features: feats.length, checks: checks.length } };
315
- }
@@ -1,73 +0,0 @@
1
- // Snake: corn snake (Pantherophis guttatus) by default; western diamondback rattlesnake
2
- // (Crotalus atrox) with `variant: 'rattlesnake'`. Proving species for the snake body plan
3
- // (core/motion/snake.js, core/rig/snake.js).
4
- import { snakeRig } from './rig.js';
5
- import { sculptSnake, eyeSpec } from './sculpt.js';
6
- import { snakeRegions } from './regions.js';
7
- import { snakeCoat, markColorOf } from './coat.js';
8
- import { motion } from './motion.js';
9
-
10
- const VARIANTS = { corn: 'corn', cornsnake: 'corn', rattlesnake: 'rattlesnake', rattler: 'rattlesnake', diamondback: 'rattlesnake', viper: 'rattlesnake' };
11
-
12
- export default {
13
- id: 'snake',
14
- name: 'Snake',
15
- latin: 'Pantherophis guttatus',
16
- group: 'others',
17
- plan: 'snake',
18
- covering: 'scales',
19
- variants: [
20
- { id: 'corn', name: 'Corn snake', latin: 'Pantherophis guttatus' },
21
- { id: 'rattlesnake', name: 'Western diamondback rattlesnake', latin: 'Crotalus atrox' },
22
- ],
23
-
24
- // Seeded individual. Size: corn adults 0.9-1.5 m (reference 1.2 m), rattlesnake 0.9-1.6 m, males
25
- // larger with longer tails; hatchlings ~0.3 m with relatively bigger heads and eyes.
26
- variation(R, o) {
27
- const variant = VARIANTS[String(o.variant || 'corn').toLowerCase()] || 'corn';
28
- const viper = variant === 'rattlesnake';
29
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
30
- const age = o.age || 'adult';
31
- const juv = age === 'juvenile';
32
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
33
- const male = sex === 'male';
34
- const size = (male ? 1.04 : 0.96) * (1 + 0.1 * g()) * (juv ? 0.27 : 1) * (viper && male ? 1.05 : 1);
35
- const shape = {
36
- length: 1,
37
- girth: (1 + 0.07 * g()) * (juv ? 0.92 : 1) * (viper && !male ? 1.04 : 1),
38
- head: (1 + 0.04 * g()) * (juv ? 1.4 : 1),
39
- tail: (male ? 1.08 : 0.94) * (1 + 0.04 * g()),
40
- };
41
- const r = R();
42
- const morph = viper ? 'diamondback' : r < 0.52 ? 'normal' : r < 0.68 ? 'okeetee' : r < 0.84 ? 'wild' : r < 0.94 ? 'amel' : 'anery';
43
- return {
44
- variant, sex, age, size, shape,
45
- warps: [],
46
- minThick: 0.0005,
47
- rattleSegs: viper ? (juv ? 1 : 5 + Math.floor(R() * 4)) : 0,
48
- coat: { morph, seed: Math.floor(R() * 1e6), warm: 1.1 * g(), light: 0.1 * g(), count: 1 + 0.1 * g(), dull: Math.max(0, 0.45 * g() + 0.1), blotch: 1 + 0.18 * g(), tone: viper ? 2 * R() - 1 : 0 },
49
- };
50
- },
51
- rig: (params) => snakeRig(params),
52
- sculpt: (m, rig, params) => sculptSnake(m, rig, params),
53
- regions: snakeRegions,
54
- coat: snakeCoat,
55
- eyeSpecs: (params, rig) => [1, -1].map((side) => ({ side, spec: eyeSpec(params), headOrigin: rig.J.v0, bone: 'head', look: lookOf(params) })),
56
- motion,
57
- // render hints may depend on the individual (the mark colour follows the morph)
58
- render: (params) => ({ markColor: markColorOf(params || {}) }),
59
- };
60
-
61
- function lookOf(params) {
62
- if (params.variant === 'rattlesnake') {
63
- return { pupil: 'slit', lids: 'none', pupilSize: [0.1, 0.55], iris: { inner: [0.12, 0.1, 0.07], mid: [0.33, 0.27, 0.19], hi: [0.5, 0.42, 0.3], outer: [0.16, 0.13, 0.09] }, sclera: { color: [0.2, 0.17, 0.12], visible: false }, lidColor: [0.2, 0.17, 0.13] };
64
- }
65
- const amel = params.coat?.morph === 'amel';
66
- return {
67
- pupil: 'round', lids: 'none', pupilSize: [0.34, 0.5],
68
- iris: amel
69
- ? { inner: [0.35, 0.02, 0.03], mid: [0.62, 0.06, 0.07], hi: [0.8, 0.2, 0.16], outer: [0.4, 0.03, 0.04] }
70
- : { inner: [0.2, 0.06, 0.02], mid: [0.62, 0.22, 0.06], hi: [0.82, 0.42, 0.14], outer: [0.16, 0.05, 0.02] },
71
- sclera: { color: [0.3, 0.12, 0.05], visible: false }, lidColor: [0.3, 0.12, 0.05],
72
- };
73
- }
@@ -1,51 +0,0 @@
1
- // Snake motion data for the snake engine (schema: src/core/motion/snake.js). Numbers are for the
2
- // reference individual (params.size = 1, total length 1.2 m); the engine scales lengths by size and
3
- // speeds by sqrt(size).
4
- //
5
- // Lateral undulation (Jayne 1986, Hu et al. 2009, Fu et al. 2022): every point follows the
6
- // head's path; 1.5-2.5 waves on the body; wavelength 0.35-0.45 of the total length, amplitude
7
- // 0.06-0.10 of it, shrinking at a slow crawl; cruise 0.1-0.3 m/s, sprint ~0.6-0.8 m/s.
8
- // Strike (Penning et al. 2016): peak head speed ~2.7-3 m/s, ~190 m/s^2, 50-90 ms to the target.
9
- export const motion = {
10
- maxSpeed: 0.7,
11
- accel: 0.9,
12
- decel: 1.6,
13
- turnRate: 2.2,
14
- minTurnRadius: 0.1, // x total length
15
- gears: { crawl: 0.07, slither: 0.24, dash: 0.6 },
16
- // [name, speed (m/s), wavelength (x TL), amplitude (x TL), head lift (m), head yaw stabilisation 0..1]
17
- gaits: [
18
- { name: 'crawl', v: 0, wave: 0.5, amp: 0.028, headLift: 0.012, stab: 0.75 },
19
- { name: 'crawl', v: 0.1, wave: 0.46, amp: 0.05, headLift: 0.012, stab: 0.7 },
20
- { name: 'slither', v: 0.16, wave: 0.43, amp: 0.066, headLift: 0.009, stab: 0.65 },
21
- { name: 'slither', v: 0.35, wave: 0.4, amp: 0.078, headLift: 0.006, stab: 0.6 },
22
- { name: 'dash', v: 0.45, wave: 0.37, amp: 0.085, headLift: 0.004, stab: 0.55 },
23
- { name: 'dash', v: 0.8, wave: 0.35, amp: 0.09, headLift: 0.003, stab: 0.5 },
24
- ],
25
- body: { neck: 0.12, forebody: 0.3 }, // x TL: neck (raised when looking), strike forebody
26
- tongue: { rate: 0.7, length: 0.6, flicks: [3, 6], freq: 7.5 },
27
- breath: { rate: 0.28, roll: 0.03 },
28
- actions: {
29
- strike: { reach: 0.33, speed: 2.8, gape: 75, coilHeight: 0.07, rise: 0.12 },
30
- coil: { pitch: 0.88, speed: 0.34 },
31
- sit: { raise: 0.13 },
32
- eat: { swallow: 3.2 },
33
- drink: { pumps: 4 },
34
- },
35
- variants: {
36
- // heavy-bodied viper: slower, stiffer undulation, big gape, fangs, rattle
37
- // C. atrox: heavy and slow; slow travel is nearly straight (rectilinear-looking, 1-5 cm/s),
38
- // a fast escape tops out ~0.35 m/s; the rattle is carried just off the ground while moving
39
- rattlesnake: {
40
- maxSpeed: 0.38, gears: { crawl: 0.04, slither: 0.16, dash: 0.34 }, tailCarry: 0.012,
41
- gaits: [
42
- { name: 'crawl', v: 0, wave: 0.6, amp: 0.012, headLift: 0.006, stab: 0.85 },
43
- { name: 'crawl', v: 0.06, wave: 0.55, amp: 0.022, headLift: 0.008, stab: 0.8 },
44
- { name: 'slither', v: 0.1, wave: 0.46, amp: 0.05, headLift: 0.008, stab: 0.7 },
45
- { name: 'slither', v: 0.22, wave: 0.42, amp: 0.062, headLift: 0.006, stab: 0.65 },
46
- { name: 'dash', v: 0.28, wave: 0.4, amp: 0.07, headLift: 0.004, stab: 0.6 },
47
- { name: 'dash', v: 0.38, wave: 0.38, amp: 0.075, headLift: 0.004, stab: 0.55 },
48
- ],
49
- tongue: { length: 0.85, amp: 0.55 }, strike: { gape: 115, reach: 0.36, coilHeight: 0.1 }, rattle: true },
50
- },
51
- };
@@ -1,51 +0,0 @@
1
- // Meshing plan: the long body at ~7 % of the max half width, the head at half that (overlapping the
2
- // body across the neck and cross-faded), the lower jaw, tongue, fangs and rattle as rigid regions.
3
- // Thin parts (tongue, fangs) are dropped at the low and crowd tiers.
4
- import { SPINE } from './rig.js';
5
- import { smoothstep } from '../../core/math/vec.js';
6
-
7
- export function snakeRegions(rig, params, Q) {
8
- const J = rig.J, B = rig.plan, P = B.P, r = Q.res;
9
- let hwMax = 0;
10
- for (let i = 0; i <= 40; i++) hwMax = Math.max(hwMax, B.hw(i / 40));
11
- const hL = B.headLen, W = P.headW * B.kH, H = P.headH * B.kH;
12
- const hBody = 0.078 * hwMax * r;
13
- const hHead = Math.min(hBody * 0.5, 0.012 * hL * r);
14
- const zCut = -0.35 * hL, band = Math.max(0.0025, 1.5 * hBody);
15
- const zTail = J['v' + SPINE][2];
16
- const pad = 3 * hBody;
17
- const y0 = J.v0[1];
18
- const jobs = [
19
- [{ name: 'body', part: 'body', bmin: [-hwMax - pad, -pad, zTail - pad - 0.01], bmax: [hwMax + pad, 2.2 * hwMax + pad, zCut + band + pad], h: hBody, F: 4, clip: (x, y, z) => z < zCut + band }],
20
- ];
21
- const head = [
22
- { name: 'head', part: 'body', bmin: [-0.7 * W, -pad, zCut - band - 0.004], bmax: [0.7 * W, y0 + 1.2 * H, hL * 1.12], h: hHead, F: 5, clip: (x, y, z) => z > zCut - band },
23
- { name: 'jaw', part: 'jaw', bmin: [-0.6 * W, -0.003, -0.1 * hL], bmax: [0.6 * W, y0 + 0.2 * H, hL * 1.05], h: hHead, F: 5, rigidBone: 'jaw' },
24
- ];
25
- if (r < 2.5) {
26
- const tr = 0.0012 * B.kH;
27
- const hT = Math.min(0.00022 * B.kH * r, hHead);
28
- head.push(
29
- { name: 'tongue', part: 'tongue', bmin: [-tr * 2, J.tongueBase[1] - tr * 2, J.tongueBase[2] - tr * 2], bmax: [tr * 2, J.tongueBase[1] + tr * 2, J.tongueFork[2] + tr * 3], h: hT, F: 4, rigidBone: 'tongue' },
30
- { name: 'fork', part: 'fork', bmin: [-0.3 * hL, J.tongueFork[1] - tr * 2, J.tongueFork[2] - tr * 2], bmax: [0.3 * hL, J.tongueFork[1] + tr * 2, J.tongueTip[2] + tr * 2], h: hT, F: 4, rigidBone: 'tongueTip' },
31
- );
32
- if (params.variant === 'rattlesnake') {
33
- head.push({ name: 'fang', part: 'fang', bmin: [-0.4 * W, J.fangTip[1] - 0.004, J.fangTip[2] - 0.004], bmax: [0.4 * W, J.fangBase[1] + 0.003, J.fangBase[2] + 0.003], h: hT, F: 4, rigidBone: 'fang' });
34
- }
35
- }
36
- jobs.push(head);
37
- if (P.rattle) {
38
- const b = J['v' + SPINE];
39
- const L = P.rattle.len * 1.3 * (params.shape?.girth ?? 1);
40
- const w = B.hw(1) * 2;
41
- jobs[0].push({ name: 'rattle', part: 'rattle', bmin: [-w, b[1] - w, b[2] - L - 0.01], bmax: [w, b[1] + w, b[2] + 0.01], h: Math.min(hBody * 0.5, 0.00035 * r), F: 4, rigidBone: 'spine' + (SPINE - 1) });
42
- }
43
- return {
44
- jobs,
45
- fade(x, y, z, name) {
46
- if (name === 'body') return 1 - smoothstep(zCut - band, zCut + band, z);
47
- if (name === 'head') return smoothstep(zCut - band, zCut + band, z);
48
- return 1;
49
- },
50
- };
51
- }
@@ -1,108 +0,0 @@
1
- // Snake skeleton in bind pose (metres): lying straight along -Z, belly on y = 0, head at +Z.
2
- // Two builds share the code: the corn snake (default) and the western diamondback rattlesnake
3
- // (variant). Numbers: total length 1.2 m (reference adults), corn head 3.4 cm and
4
- // max body width 2.7 cm, rattlesnake head 5.2 cm (3.8 cm wide) and max body width 5.2 cm;
5
- // tail 17 % (corn) / 7.5 % (rattlesnake) of the total length.
6
- import { buildRig } from '../../core/rig/rig.js';
7
- import { snakeSpine, snakeBones, snakeAxial, snakeSegments } from '../../core/rig/snake.js';
8
-
9
- export const SPINE = 50; // spine bones (each ~4-5 vertebrae)
10
-
11
- // Per-variant proportions (reference individual). hw = half width of the body (m) at arc fraction t
12
- // (0 = occiput, 1 = tail tip); flat = axis height / half width (loaf cross-section: flat belly).
13
- export const PROFILES = {
14
- corn: {
15
- TL: 1.2, headLen: 0.034, headW: 0.0185, headH: 0.0112, tail: 0.17, flat: 0.74,
16
- hw: [[0, 0.0071], [0.05, 0.0076], [0.22, 0.0112], [0.45, 0.0135], [0.66, 0.0128], [0.8, 0.0098], [0.88, 0.0068], [0.95, 0.0036], [1, 0.0011]],
17
- eye: { r: 0.0026, at: [0.006, 0.0027, 0.0205], yaw: 1.05, pitch: 0.2 },
18
- },
19
- rattlesnake: {
20
- TL: 1.2, headLen: 0.052, headW: 0.038, headH: 0.02, tail: 0.075, flat: 0.64,
21
- hw: [[0, 0.0092], [0.04, 0.0105], [0.2, 0.019], [0.46, 0.0265], [0.7, 0.0238], [0.86, 0.016], [0.92, 0.0112], [0.97, 0.0078], [1, 0.0062]],
22
- eye: { r: 0.0029, at: [0.0133, 0.0052, 0.032], yaw: 1.0, pitch: 0.22 },
23
- rattle: { len: 0.05, segs: 7 },
24
- },
25
- };
26
-
27
- // monotone cubic (Fritsch-Carlson) through [[t, v]...]
28
- export function pchip(keys) {
29
- const n = keys.length, x = keys.map((k) => k[0]), y = keys.map((k) => k[1]);
30
- const d = [], m = new Array(n).fill(0);
31
- for (let i = 0; i < n - 1; i++) d.push((y[i + 1] - y[i]) / (x[i + 1] - x[i]));
32
- m[0] = d[0]; m[n - 1] = d[n - 2];
33
- for (let i = 1; i < n - 1; i++) m[i] = d[i - 1] * d[i] <= 0 ? 0 : (d[i - 1] + d[i]) / 2;
34
- for (let i = 0; i < n - 1; i++) {
35
- if (d[i] === 0) { m[i] = m[i + 1] = 0; continue; }
36
- const a = m[i] / d[i], b = m[i + 1] / d[i], s = a * a + b * b;
37
- if (s > 9) { const t = 3 / Math.sqrt(s); m[i] = t * a * d[i]; m[i + 1] = t * b * d[i]; }
38
- }
39
- return (t) => {
40
- if (t <= x[0]) return y[0];
41
- if (t >= x[n - 1]) return y[n - 1];
42
- let i = 0;
43
- while (i < n - 2 && t > x[i + 1]) i++;
44
- const h = x[i + 1] - x[i], u = (t - x[i]) / h;
45
- const h00 = 2 * u * u * u - 3 * u * u + 1, h10 = u * u * u - 2 * u * u + u, h01 = -2 * u * u * u + 3 * u * u, h11 = u * u * u - u * u;
46
- return h00 * y[i] + h10 * h * m[i] + h01 * y[i + 1] + h11 * h * m[i + 1];
47
- };
48
- }
49
-
50
- // Individual body plan from the variant profile and the seeded proportions in params:
51
- // params.shape = { length, girth, head, tail } multipliers (1 = reference).
52
- export function bodyPlan(params) {
53
- const P = PROFILES[params.variant] || PROFILES.corn;
54
- const sh = params.shape || {};
55
- const kL = sh.length ?? 1, kG = sh.girth ?? 1, kH = sh.head ?? 1, kT = sh.tail ?? 1;
56
- const headLen = P.headLen * kH;
57
- const bodyLen = P.TL * kL - headLen;
58
- const tailFrac = Math.min(0.3, (P.tail * kT * P.TL * kL) / bodyLen);
59
- // re-map the profile's tail portion to this individual's tail fraction
60
- const refTail = (P.tail * P.TL) / (P.TL - P.headLen);
61
- const remap = (t) => (t <= 1 - tailFrac ? (t / (1 - tailFrac)) * (1 - refTail) : 1 - refTail + ((t - (1 - tailFrac)) / tailFrac) * refTail);
62
- const base = pchip(P.hw);
63
- const neckK = kH; // a bigger head comes with a slightly thicker neck
64
- const hw = (t) => {
65
- const r = base(remap(t));
66
- const neck = 1 - Math.min(1, t / 0.1);
67
- return r * (kG * (1 - neck) + (0.5 * kG + 0.5 * neckK) * neck);
68
- };
69
- return { P, headLen, bodyLen, tailFrac, hw, flat: P.flat, kH };
70
- }
71
-
72
- export function snakeRig(params) {
73
- const B = bodyPlan(params);
74
- const P = B.P;
75
- const J = {};
76
- const lens = snakeSegments(SPINE, B.bodyLen, { tipShrink: P.rattle ? 0.8 : 0.5, tipBones: 10 });
77
- snakeSpine(J, 0, lens, (s, t) => B.hw(t) * B.flat);
78
- const y0 = J.v0[1];
79
- const kH = B.kH;
80
- const hL = B.headLen, hH = P.headH * kH;
81
- // head landmarks (head-local: origin v0, +Z forward). The jaw's underside rests on y = 0.
82
- // heights are from the belly plane (the jaw's underside rests on y = 0)
83
- J.nose = [0, 0.56 * hH, hL];
84
- J.jawHinge = [0, 0.34 * hH, 0.06 * hL];
85
- J.jawTip = [0, 0.16 * hH, 0.9 * hL];
86
- // tongue lies in its sheath in the floor of the mouth, tips just behind the chin
87
- J.tongueBase = [0, 0.2 * hH, 0.18 * hL];
88
- J.tongueFork = [0, 0.2 * hH, 0.62 * hL];
89
- J.tongueTip = [0, 0.2 * hH, 0.84 * hL];
90
- // fangs hinge at the front of the upper jaw, folded back along the palate
91
- J.fangBase = [0, 0.38 * hH, 0.72 * hL];
92
- J.fangTip = [0, 0.32 * hH, 0.55 * hL];
93
- const rig = buildRig({
94
- joints: J,
95
- unit: (B.headLen + B.bodyLen) / 1.3,
96
- headOrigin: J.v0,
97
- bones: snakeBones(SPINE, { neck: 4, tail: Math.round(B.tailFrac * SPINE) }),
98
- axial: {
99
- ...snakeAxial(SPINE),
100
- // the skull is rigid right up to the occiput; the neck blends in just behind it
101
- blend: { 1: [-0.004 * kH, 0.006 * kH] },
102
- },
103
- limbs: {},
104
- appendages: [],
105
- });
106
- rig.plan = B;
107
- return rig;
108
- }