@bitefoundry/retrieviewer 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/ASSET-PROVENANCE.md +6 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +65 -0
  5. package/DESKTOP-PLATFORMS.md +75 -0
  6. package/INSTALLING.md +8 -5
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +110 -0
  11. package/README.md +119 -58
  12. package/RELEASING.md +27 -14
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/desktop/platform.mjs +27 -0
  17. package/integrations/linux-overlay/README.md +83 -0
  18. package/integrations/linux-overlay/overlay.py +1094 -0
  19. package/integrations/macos-overlay/README.md +83 -0
  20. package/integrations/macos-overlay/overlay.jxa +639 -0
  21. package/integrations/opencode/runtime.mjs +4 -4
  22. package/integrations/opencode/tui.tsx +1 -1
  23. package/integrations/windows-overlay/Overlay.cs +4 -4
  24. package/integrations/windows-overlay/desktop.mjs +40 -8
  25. package/integrations/windows-overlay/protocol.mjs +11 -7
  26. package/integrations/windows-overlay/worker.mjs +13 -7
  27. package/package.json +41 -14
  28. package/puppy-comfort-sprites.mjs +25 -0
  29. package/puppy-companion-comfort-q5.bin.gz +0 -0
  30. package/puppy-companion-comfort.json +77 -0
  31. package/puppy-companion-life.mjs +49 -16
  32. package/puppy-companion-objects.mjs +12 -0
  33. package/puppy-frame-predictive.mjs +15 -9
  34. package/puppy-frame-store.mjs +2 -2
  35. package/puppy-paper-world.mjs +14 -0
  36. package/puppy-paper.mjs +65 -14
  37. package/retrieviewer-cli.mjs +10 -3
  38. package/retrieviewer-plugin.d.mts +1 -1
  39. package/retrieviewer-plugin.mjs +1 -1
  40. package/retrieviewer.d.mts +10 -4
  41. package/retrieviewer.mjs +20 -14
  42. package/locomotion-core.mjs +0 -21
  43. package/locomotion-puppy.animal +0 -0
  44. package/vendor/procedural-animals/package.json +0 -40
  45. package/vendor/procedural-animals/src/animal.js +0 -146
  46. package/vendor/procedural-animals/src/bake.js +0 -51
  47. package/vendor/procedural-animals/src/build.js +0 -81
  48. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  49. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  50. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  51. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  52. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  53. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  54. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  55. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  56. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  57. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  58. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  59. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  60. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  61. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  62. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  63. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  64. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  65. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  66. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  67. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  68. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  69. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  70. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  71. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  72. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  73. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  74. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  75. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  76. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  77. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  79. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  80. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  81. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  82. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  83. package/vendor/procedural-animals/src/index.d.ts +0 -98
  84. package/vendor/procedural-animals/src/index.js +0 -29
  85. package/vendor/procedural-animals/src/registry.js +0 -48
  86. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  87. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  88. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  89. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  90. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  91. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  92. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  94. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  95. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  96. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  97. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  98. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  99. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  100. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  101. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  102. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  103. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  104. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  105. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  106. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  107. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  108. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  109. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  110. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  111. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  112. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  113. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  114. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  115. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  116. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  117. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  118. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  119. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  120. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  121. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  122. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  123. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  124. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  125. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  126. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  127. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  128. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  129. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  130. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  131. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  132. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  133. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  134. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  135. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  136. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  137. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  138. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  139. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  140. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  141. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  142. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  143. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  144. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  145. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  146. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  147. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  148. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  149. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  150. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  151. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  152. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  153. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  154. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  155. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  156. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  157. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  158. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  159. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  160. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  161. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  162. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  163. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  164. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  165. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  166. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  167. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  168. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  169. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  170. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  171. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  172. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  173. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  174. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  175. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  176. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  177. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  178. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  179. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  180. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  181. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  182. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  183. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  184. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  185. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  186. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  187. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  188. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  189. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  190. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  191. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  192. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  193. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  194. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  195. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  196. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  197. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  198. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  199. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  200. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  201. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  202. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  203. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  204. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  205. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  206. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  207. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  208. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  209. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  210. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  211. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  212. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  213. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  214. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  215. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  216. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  217. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  218. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  219. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  220. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  221. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  222. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  223. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  224. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  225. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  226. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  227. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  228. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  229. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  230. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  231. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  232. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  233. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  234. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  235. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  236. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  237. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  238. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  239. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  240. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  241. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  242. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  243. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  244. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  245. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  246. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  247. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  248. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  249. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  250. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  251. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  252. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  253. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  254. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  255. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  256. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  257. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  258. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  259. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  260. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  261. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,391 +0,0 @@
1
- // The white-tailed deer's coat: summer red-brown or winter grey-brown (hollow guard hair, longer),
2
- // darker along the back and on the forehead, white belly / inner legs / groin, white throat patch,
3
- // white eye ring, white band behind the black nose, white chin and lower lip with dark lip-corner
4
- // spots, ears grey-brown outside with a dark rim and white hair inside, the tail brown above with a
5
- // white fringe and a white underside, tan lower legs with dark hooves, the tarsal gland tuft (dark in
6
- // bucks) and the fawn's rows of white spots. Hair flow follows the cervid hair tracts. Antlers:
7
- // bone-coloured keratin, dark at the burr, polished pale tips (velvet when growing).
8
- // Works in the reference space (see rig.js); seeded variation picks the season, shade and spots.
9
- import { HEAD_O, HZ, HY, hl, bz } from './rig.js';
10
- import { neckS } from './regions.js';
11
- import { SDFModel } from '../../core/sdf/sdf.js';
12
- const lerp3 = (a, b, t) => [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t];
13
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
14
- import { srgb, mix3, MAT, poissonFeatures, featureDistance, smoothField, distPolyline } from '../../core/build/coatKit.js';
15
-
16
- // sRGB swatches sampled from the reference photos in neutral light
17
- export const COATS = {
18
- summer: { body: 0x9a6f50, dorsal: 0x825a3e, side: 0xa87e5e, head: 0x8e6c50, forehead: 0x684a34, face: 0x8e7a68, legs: 0x9e7c5c, fur: 0.007 },
19
- winter: { body: 0x988670, dorsal: 0x7a6a56, side: 0xa8977f, head: 0x928675, forehead: 0x645240, face: 0xa39d94, legs: 0x9e8a6e, fur: 0.012 },
20
- fawn: { body: 0x93603e, dorsal: 0x7c5034, side: 0xa06e4c, head: 0x8c6646, forehead: 0x6e4a30, face: 0x8c7462, legs: 0x9a7656, fur: 0.008 },
21
- };
22
- const WHITE = 0xefeae0, SPOT = 0xeee4d0, NOSE = 0x1a1a1a, BAND = 0x1e1a18, HOOF = 0x262220, EAR_RIM = 0x3a2e26, EAR_IN = 0xede7dc;
23
- const ANTLER = { burr: 0x5e4a36, beam: 0x7e6650, tip: 0xd8ccb4, velvet: 0x6b5445 };
24
-
25
- function palette(p) {
26
- const C = COATS[p.age === 'juvenile' ? 'fawn' : p.coat] || COATS.winter;
27
- const k = p.coatShade || 0, l = p.coatLightness || 0;
28
- // shade: - greyer / + redder, lightness +-
29
- const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.07 * k + l), c[1] * (1 + 0.01 * k + l), c[2] * (1 - 0.08 * k + l)]; };
30
- const out = {};
31
- for (const key of ['body', 'dorsal', 'side', 'head', 'forehead', 'face', 'legs']) out[key] = tw(C[key]);
32
- out.fur = C.fur;
33
- out.white = srgb(WHITE); out.spot = srgb(SPOT); out.nose = srgb(NOSE); out.band = srgb(BAND); out.hoof = srgb(HOOF);
34
- out.earRim = srgb(EAR_RIM); out.earIn = srgb(EAR_IN);
35
- return out;
36
- }
37
-
38
- const HOOF_TAGS = new Set(['hoof', 'heelbulb', 'dewclaw']);
39
-
40
- export function deerCoat(ctx) {
41
- const { model, rig, pos, nrm, regionOf, regionNames, weights, nV, params, lists } = ctx;
42
- const { BONES } = rig;
43
- const J = rig.J;
44
- const R = rng(4243 + (params.coatSeed || 0));
45
- const COL = palette(params);
46
- const fawn = params.age === 'juvenile';
47
- const buck = params.sex === 'male' && !fawn;
48
- const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
49
- const AX_LEN = weights.axialLengths;
50
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
51
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
52
- const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
53
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
54
- const jawPrims = model.forPart('jaw');
55
- const headPrims = model.forPart('body').filter((p) => p.bone === 'head' && !p.carve);
56
- const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril');
57
- const glandPrims = model.prims.filter((p) => p.tag === 'preorbital');
58
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
59
- const tailIdx = (name) => (name.startsWith('tail') ? +name.slice(4) : -1);
60
- const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
61
- const rn = regionNames.map((n) => n);
62
- const JAW_R = rn.indexOf('jaw'), ANT_R = rn.indexOf('antler'), HEAD_R = rn.indexOf('head');
63
-
64
- // --- nearest primitive tag per vertex
65
- const tagOf = new Array(nV);
66
- for (let v = 0; v < nV; v++) {
67
- const L = lists[v], p = P(v);
68
- let best = 1e9, t = '';
69
- for (const pr of L) {
70
- if (pr.carve) continue;
71
- const d = SDFModel.dist(pr, p[0], p[1], p[2]);
72
- if (d < best) { best = d; t = pr.tag; }
73
- }
74
- tagOf[v] = t;
75
- }
76
-
77
- // --- regions: 0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 8 antler
78
- const region = new Uint8Array(nV);
79
- const legness = new Float32Array(nV);
80
- const legBone = new Int16Array(nV).fill(-1);
81
- for (let v = 0; v < nV; v++) {
82
- const bn = BONES[dominant[v]].name, p = P(v);
83
- let lb = -1, lw = 0;
84
- for (let k = 0; k < 4; k++) {
85
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
86
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
87
- }
88
- legBone[v] = lb;
89
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
90
- const ti = tailIdx(bn);
91
- if (regionOf[v] === ANT_R) region[v] = 8;
92
- else if (regionOf[v] === JAW_R) region[v] = 6;
93
- else if (bn.startsWith('ear')) region[v] = 5;
94
- else if (ti >= 0 && axialS[v] > sTailBase + 0.01 || tagOf[v] === 'tail') region[v] = 4;
95
- else if (axialS[v] < sOcc + 0.01 || (regionOf[v] === HEAD_R && neckS(p[0], p[1], p[2]) > 0.015)) region[v] = 2;
96
- else if (axialS[v] < sNeckBase) region[v] = 1;
97
- else region[v] = 0;
98
- if (region[v] >= 2) legness[v] = 0;
99
- }
100
-
101
- // --- tail frame: which side of the flat tail a vertex is on (dorsal = brown, ventral = white)
102
- const tailSide = (v) => {
103
- const bone = BONES[dominant[v]];
104
- const d = norm(sub(bone.tail, bone.head));
105
- const lat = [1, 0, 0];
106
- const back = norm(cross(lat, d)); // points away from the rump (the tail's outer / dorsal face)
107
- return dot(N(v), back);
108
- };
109
-
110
- // --- white areas as one signed field (< 0 inside), with softly jagged edges
111
- const throatC = lerp3(hl([0, -0.068, -0.045]), [0, 0.64, 0.39], 0.45); // white throat patch on the upper throat, below the jaw
112
- const throatN = norm([0, -0.35, 1]);
113
- const throatD = (p, n, rx, ry) => (dot(n, throatN) < 0.1 ? 1 : Math.hypot(p[0] / rx, (p[1] - throatC[1]) / ry, (p[2] - throatC[2]) / 0.06) * 0.03 - 0.03);
114
- const whiteSDF = (v) => {
115
- const p = P(v), r = region[v], n = N(v);
116
- const jag = (fbm3(p[0] * 70, p[1] * 70, p[2] * 70, 2) - 0.5) * 0.012;
117
- let d = 1;
118
- if (r === 2 || r === 6) {
119
- const h = HL(p), ax = Math.abs(h[0]);
120
- // chin and lower lip (the jaw), the white band behind the nose, around the muzzle
121
- if (r === 6) d = -0.006;
122
- const band = Math.max(Math.abs(h[2] - 0.158) - 0.011 - 0.004 * smoothstep(0.0, -0.03, h[1]), -0.03 - h[1]);
123
- d = Math.min(d, band);
124
- // lower lip line / under the chin on the head surface too
125
- if (h[1] < -0.036 && h[2] > 0.12) d = Math.min(d, (h[1] + 0.036) * 0.5 - 0.002);
126
- // eye ring
127
- for (const e of eyePrims) {
128
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
129
- d = Math.min(d, Math.abs(de + 0.0005) - 0.0075);
130
- }
131
- // throat patch continues under the jaw angle (head region skin of the throat latch)
132
- d = Math.min(d, throatD(p, n, 0.034, 0.04));
133
- if (ax > 0.05 && h[2] < 0.1) d = Math.max(d, 0.02);
134
- return d + jag * 0.5;
135
- }
136
- if (r === 1 || r === 0) {
137
- // throat patch: an oval bib on the upper throat
138
- d = Math.min(d, throatD(p, n, 0.036, 0.045));
139
- // belly, groin and the insides of the legs: below the flank line on the underside
140
- const belly = r === 0 ? (p[1] - (0.5 + 0.05 * smoothstep(0.1, bz(-0.35), p[2]) + 0.06 * smoothstep(bz(-0.2), bz(-0.45), p[2]))) + 0.06 * (n[1] + 0.5) + 0.12 * smoothstep(0.1, 0.24, p[2]) : 1;
141
- if (legness[v] < 0.5) d = Math.min(d, belly);
142
- // buttocks: a white band along the inside of the thighs under the tail
143
- if (r === 0 && p[2] < bz(-0.5)) d = Math.min(d, Math.abs(p[0]) - 0.04 * smoothstep(0.72, 0.6, p[1]) + (p[1] > 0.72 ? 1 : 0));
144
- const L = legness[v];
145
- if (L > 0) {
146
- const side = p[0] >= 0 ? 1 : -1;
147
- const inner = n[0] * side; // < 0: faces the other leg
148
- const lbn = BONES[legBone[v]].name;
149
- const hind = /femur|tibia|metatarsus|hpaw|hhoof/.test(lbn);
150
- // inside of the upper legs white down to the knee / hock
151
- // (hind: the inner thigh down to the hock; fore: only the inside of the cannon, below the
152
- // carpus: a white edge over the elbow crease smeared as the elbow folds)
153
- const top = hind ? 0.5 : 0.26, bottom = hind ? 0.36 : 0.12;
154
- const dIn = inner + 0.35;
155
- d = Math.min(d, mix(1, dIn * 0.05, L) + (p[1] > top ? (p[1] - top) * 0.5 : 0) + (p[1] < bottom ? (bottom - p[1]) * 0.5 : 0));
156
- // white fetlock band above the hooves (front of the pasterns)
157
- if (/metacarpus|metatarsus|fpaw|hpaw/.test(lbn) && p[1] < 0.075 && p[1] > 0.035) d = Math.min(d, 0.012 - (0.075 - p[1]) * 0.5 * smoothstep(-0.2, 0.3, n[2]));
158
- }
159
- return d + jag;
160
- }
161
- if (r === 4) {
162
- // tail: white underside and a white fringe on the edges
163
- const ts = tailSide(v);
164
- return Math.min(ts + 0.25, 0.012 - Math.abs(n[0]) * 0.02) + jag * 0.5;
165
- }
166
- return 1;
167
- };
168
-
169
- // --- fawn spots: rows along the spine, scattered on the flanks
170
- let spots = null;
171
- if (fawn) {
172
- spots = poissonFeatures({
173
- pos, nrm, nV, R, cell: 0.05, spacing: 2.6, gap: 0.006,
174
- radius: (v) => {
175
- const p = P(v), n = N(v);
176
- if (region[v] > 0 || legness[v] > 0.3 || n[1] < 0.0 || p[2] < bz(-0.52) || p[2] > 0.3) return 0;
177
- // rows either side of the spine, thinning out down the flanks
178
- const ax = Math.abs(p[0]);
179
- const row = Math.min(Math.abs(ax - 0.035), Math.abs(ax - 0.075));
180
- if (p[1] < 0.66 || (row > 0.012 && R() < 0.55)) return 0;
181
- return 0.006 + 0.002 * R();
182
- },
183
- });
184
- }
185
-
186
- // --- comb (hair flow)
187
- const comb = new Float32Array(nV * 3);
188
- const whorl = [0, 0.03, 0.02]; // forehead whorl, head-local
189
- for (let v = 0; v < nV; v++) {
190
- const p = P(v), n = N(v), r = region[v];
191
- const bone = BONES[dominant[v]];
192
- let d;
193
- if (r === 5 || r === 8) d = norm(sub(bone.tail, bone.head));
194
- else if (r === 2 || r === 6) {
195
- const h = HL(p);
196
- const rel = [h[0] - whorl[0], (h[1] - whorl[1]) * 0.4 - 0.04, h[2] - whorl[2] + 0.05];
197
- d = norm(add(mul(HY, rel[1]), add(mul(HZ, rel[2]), [rel[0], 0, 0])));
198
- if (h[2] < -0.06) d = norm(add(d, [0, -0.6, -0.8]));
199
- } else if (r === 4) {
200
- d = norm(sub(bone.tail, bone.head));
201
- d = norm(add(d, [0, 0, 0]));
202
- } else {
203
- const seg = weights.segT[v * 2] | 0;
204
- const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
205
- d = norm(sub(b, a));
206
- if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
207
- if (r === 1) d = norm(add(d, [0, -0.6, 0.05]));
208
- const L = legness[v];
209
- if (L > 0 && legBone[v] >= 0) {
210
- const lbn = BONES[legBone[v]];
211
- let dl = norm(sub(lbn.tail, lbn.head));
212
- if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
213
- else dl = norm(add(dl, [0, -0.3, 0]));
214
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
215
- }
216
- }
217
- let t = sub(d, mul(n, dot(d, n)));
218
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
219
- t = norm(t);
220
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
221
- }
222
-
223
- // --- antler colour along its length (distance from the burr)
224
- const A = params.antlers;
225
- const burrs = A ? [1, -1].map((s) => hl([A.burr[0] * s, A.burr[1], A.burr[2]])) : [];
226
-
227
- // --- per-vertex fields
228
- const pattern = new Float32Array(nV).fill(1);
229
- const patInt = new Float32Array(nV);
230
- const patCol = new Float32Array(nV * 3);
231
- const markSDF = new Float32Array(nV).fill(1);
232
- const furLen = new Float32Array(nV);
233
- const tint = new Float32Array(nV * 4);
234
- const surf = new Float32Array(nV * 4);
235
- const coatFur = COL.fur;
236
-
237
- for (let v = 0; v < nV; v++) {
238
- const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
239
- let col, mat = MAT.FUR, fl = coatFur, mark = 1, gloss = 0.5;
240
- let wsdf = whiteSDF(v);
241
- const lb = legBone[v];
242
- const lbName = lb >= 0 ? BONES[lb].name : '';
243
- if (r === 0 || r === 1) {
244
- const up = clamp(n[1], -1, 1);
245
- // countershading: dark along the spine, lighter low on the flanks
246
- col = mix3(COL.body, COL.dorsal, smoothstep(0.35, 0.95, up) * 0.85);
247
- col = mix3(col, COL.side, smoothstep(0.0, -0.6, up) * 0.6);
248
- if (r === 1) col = mix3(col, COL.head, 0.25);
249
- fl = coatFur * (r === 1 ? 1.1 : 1);
250
- // chest between the forelegs: darker
251
- if (r === 0 && p[2] > 0.3 && p[1] < 0.66) col = mix3(col, COL.dorsal, 0.35 * smoothstep(0.3, 0.42, p[2]));
252
- const L = legness[v];
253
- if (L > 0) {
254
- let lc = mix3(col, COL.legs, smoothstep(0.55, 0.3, p[1]));
255
- // front of the cannons a little darker, backs of the legs paler
256
- if (p[1] < 0.35) lc = mix3(lc, COL.dorsal, 0.25 * smoothstep(0.1, 0.8, n[2]));
257
- col = mix3(col, lc, L);
258
- fl = mix(fl, mix(0.004, coatFur * 0.8, smoothstep(0.2, 0.55, p[1])), L);
259
- // tarsal gland: a tuft on the inside of the hock, stained dark in bucks
260
- const hind = /tibia|metatarsus/.test(lbName);
261
- if (hind) {
262
- const side = p[0] >= 0 ? 1 : -1;
263
- const Hk = J['hock' + BONES[lb].side];
264
- const dg = Math.hypot((p[1] - Hk[1] - 0.015) / 0.028, (p[2] - Hk[2] + 0.012) / 0.02);
265
- if (dg < 1.2 && n[0] * side < -0.2) { col = mix3(col, buck ? srgb(0x2e2218) : srgb(0x6a5440), smoothstep(1.2, 0.7, dg)); fl = Math.max(fl, 0.02 * smoothstep(1.2, 0.5, dg)); }
266
- // metatarsal gland: a small pale-rimmed tuft on the outside of the hind cannon
267
- const dm = Math.hypot((p[1] - (Hk[1] - 0.12)) / 0.018, (p[2] - Hk[2] - 0.004) / 0.012);
268
- if (dm < 1.3 && n[0] * side > 0.3) { col = mix3(col, dm < 0.7 ? srgb(0x4a3a2a) : srgb(0xd8d0c0), smoothstep(1.3, 0.9, dm)); fl = Math.max(fl, 0.012); }
269
- }
270
- }
271
- // hoof wall, dew claws
272
- const Cj = /hoof|paw/.test(lbName) ? J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side] : null;
273
- if (Cj && HOOF_TAGS.has(tag) && (tag === 'dewclaw' || p[1] < Cj[1] + 0.006 + 0.35 * (p[2] - Cj[2]))) {
274
- mat = MAT.KERATIN; fl = 0; gloss = 0.5; col = COL.hoof;
275
- if (tag === 'heelbulb') col = mix3(col, srgb(0x3a3430), 0.5);
276
- wsdf = 1;
277
- }
278
- } else if (r === 4) {
279
- // tail: brown on top (darker along the midline), long white hair on the edges and below
280
- const ts = tailSide(v);
281
- col = mix3(COL.dorsal, COL.body, smoothstep(0.3, 0.9, Math.abs(n[0])));
282
- fl = 0.02 + 0.03 * smoothstep(0.2, 0.9, Math.abs(n[0])) + 0.015 * smoothstep(0.2, -0.5, ts);
283
- const ti = tailIdx(BONES[dominant[v]].name);
284
- if (ti >= 4) col = mix3(col, srgb(0x3a2c20), 0.4 * smoothstep(0.2, 0.9, ts)); // dark tip above
285
- } else if (r === 5) {
286
- // ears: coat colour outside with a dark rim, white hair inside
287
- const earP = model.prims.find((q) => q.tag === 'ear' && (q.bone === (p[0] > 0 ? 'earL' : 'earR')));
288
- const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
289
- const front = dot(n, faceDir);
290
- col = mix3(COL.head, COL.face, 0.4);
291
- fl = 0.005;
292
- // rim: distance from the pinna's edge ~ where the normal turns sideways
293
- const bone = BONES[dominant[v]];
294
- const ax = norm(sub(bone.tail, bone.head));
295
- const along = dot(sub(p, bone.head), ax) / Math.max(1e-4, len(sub(bone.tail, bone.head)));
296
- const edge = 1 - Math.abs(front);
297
- col = mix3(col, COL.earRim, smoothstep(0.55, 0.9, edge) * 0.7 + 0.5 * smoothstep(0.8, 1.0, along));
298
- if (front > 0.25) { fl = 0.011 * smoothstep(0.95, 0.5, along); col = mix3(col, COL.earIn, smoothstep(0.25, 0.6, front) * 0.85 * smoothstep(1.0, 0.75, along)); }
299
- } else if (r === 8) {
300
- // antler: dark rough burr and beam base, lighter beam, polished pale tips
301
- mat = MAT.KERATIN; fl = 0; gloss = 0.35;
302
- let db = 1e9;
303
- for (const b of burrs) db = Math.min(db, len(sub(p, b)));
304
- const tipness = smoothstep(0.02, 0.01, SDFModel.evalList(model.forPart('antler').filter((q) => q.tag === 'tine' || q.tag === 'beam'), p[0], p[1], p[2]) + 0.012);
305
- col = mix3(srgb(ANTLER.burr), srgb(ANTLER.beam), smoothstep(0.012, 0.05, db));
306
- const tipT = smoothstep(0.1, 0.35, db);
307
- col = mix3(col, srgb(ANTLER.tip), tipT * 0.45);
308
- if (tag === 'tine') col = mix3(col, srgb(ANTLER.tip), 0.2);
309
- void tipness;
310
- if (A && A.velvet) { mat = MAT.FUR; fl = 0.003; col = srgb(ANTLER.velvet); gloss = 0.3; }
311
- // grooves / pearling: fine dark streaks along the beam
312
- const st = vnoise3(p[0] * 300, p[1] * 90, p[2] * 300);
313
- col = mul(col, 0.85 + 0.3 * st);
314
- wsdf = 1;
315
- } else {
316
- // head & jaw
317
- const h = HL(p);
318
- col = mix3(COL.head, COL.face, smoothstep(-0.02, 0.1, h[2]) * 0.6);
319
- fl = 0.0045;
320
- // darker forehead patch (bucks especially) and the dark bridge of the nose
321
- const fh = smoothstep(0.015, 0.035, h[1]) * smoothstep(0.09, 0.0, h[2]) * smoothstep(-0.1, -0.04, h[2]);
322
- col = mix3(col, COL.forehead, fh * (buck ? 0.9 : 0.55));
323
- col = mix3(col, COL.forehead, smoothstep(0.012, 0.024, h[1]) * smoothstep(0.02, 0.1, h[2]) * smoothstep(0.17, 0.12, h[2]) * 0.35 * smoothstep(0.02, 0.0, Math.abs(h[0])));
324
- if (h[2] < -0.07) col = mix3(col, COL.body, smoothstep(-0.07, -0.11, h[2]));
325
- // black nose leather (rhinarium)
326
- if (h[2] > 0.168 && h[1] > -0.03 && tag !== 'upperlip' || (tag === 'nose' && h[2] > 0.16)) {
327
- const nz = smoothstep(0.168, 0.176, h[2]);
328
- if (nz > 0.5) { mat = MAT.NOSE; fl = 0; col = COL.nose; gloss = 0.8; wsdf = 1; }
329
- else col = mix3(col, COL.band, nz * 2);
330
- }
331
- // dark spots at the corners of the lower lip / chin sides
332
- if (r === 6 || h[1] < -0.035) {
333
- const dc = Math.hypot((Math.abs(h[0]) - 0.014) / 0.01, (h[2] - 0.145) / 0.014);
334
- if (dc < 1 && Math.abs(h[0]) > 0.006) { col = mix3(col, COL.band, smoothstep(1, 0.6, dc)); wsdf = Math.max(wsdf, 0.004); }
335
- }
336
- // eye rims (dark skin), preorbital gland
337
- for (const e of eyePrims) {
338
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
339
- if (Math.abs(de) < 0.0035) mark = Math.min(mark, Math.abs(de) - 0.002);
340
- if (Math.abs(de) < 0.0014) { mat = MAT.DARK_SKIN; fl = 0; wsdf = Math.max(wsdf, 0.003); }
341
- else if (Math.abs(de) < 0.0035) fl = Math.min(fl, 0.0015);
342
- // upper lashes
343
- }
344
- for (const g of glandPrims) {
345
- const dg = SDFModel.dist(g, p[0], p[1], p[2]);
346
- if (dg < 0.003) { col = srgb(0x2e2622); fl = 0.001; mark = Math.min(mark, dg - 0.002); wsdf = Math.max(wsdf, 0.003); }
347
- }
348
- // nostrils: dark rims
349
- for (const e of nostrilPrims) {
350
- const dn = SDFModel.dist(e, p[0], p[1], p[2]);
351
- if (dn < 0.0025) { mat = MAT.NOSE; col = COL.nose; fl = 0; mark = Math.min(mark, dn - 0.0015); wsdf = 1; }
352
- }
353
- // lips
354
- if (r !== 6) {
355
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
356
- if (dj < 0.0012 && h[2] > 0.1) { mat = MAT.MOUTH; fl = 0; wsdf = 1; }
357
- else if (dj < 0.0035 && h[2] > 0.1) { mark = Math.min(mark, dj - 0.0028); fl = Math.min(fl, 0.0015); }
358
- } else {
359
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
360
- if (dh < 0.0) { mat = MAT.MOUTH; fl = 0; wsdf = 1; }
361
- else if (dh < 0.003) { mark = Math.min(mark, dh - 0.0025); fl = Math.min(fl, 0.0015); }
362
- }
363
- // whiskers area on the muzzle: very short hair
364
- if (h[2] > 0.13) fl = Math.min(fl, 0.003);
365
- }
366
- // white areas and fawn spots through the crisp pattern field (continuous: capped just above 0)
367
- const CAP = 0.02;
368
- let pc = COL.white, pi = 1, pd = CAP;
369
- if (mat === MAT.FUR && r !== 8) pd = Math.min(CAP, wsdf);
370
- if (spots && (r === 0 || r === 1) && mat === MAT.FUR) {
371
- const [sd] = featureDistance(spots, p, n, 1 + 0.2 * (vnoise3(p[0] * 60, p[1] * 60, p[2] * 60) - 0.5));
372
- if (sd < pd) { pd = Math.min(CAP, sd); pc = COL.spot; pi = 0.92; }
373
- }
374
- // white hair is longer on the belly, the tail and the throat patch
375
- if (pd < 0 && mat === MAT.FUR && r !== 2 && r !== 6) fl = Math.max(fl, r === 4 ? fl : coatFur * 1.3);
376
- // low-frequency colour variation
377
- const cv = fbm3(p[0] * 7, p[1] * 7, p[2] * 7, 3) - 0.5;
378
- if (mat === MAT.FUR && r !== 8) col = [col[0] * (1 + 0.14 * cv), col[1] * (1 + 0.12 * cv), col[2] * (1 + 0.1 * cv)];
379
- pattern[v] = pd; patInt[v] = pi;
380
- patCol[v * 3] = pc[0]; patCol[v * 3 + 1] = pc[1]; patCol[v * 3 + 2] = pc[2];
381
- markSDF[v] = mark;
382
- furLen[v] = fl;
383
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
384
- surf[v * 4] = gloss;
385
- }
386
- smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.FUR);
387
-
388
- const patternColor = new Float32Array(nV * 4);
389
- for (let v = 0; v < nV; v++) { patternColor[v * 4] = patCol[v * 3]; patternColor[v * 4 + 1] = patCol[v * 3 + 1]; patternColor[v * 4 + 2] = patCol[v * 3 + 2]; patternColor[v * 4 + 3] = patInt[v]; }
390
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: { spots: spots ? spots.spots.length : 0 }, region, ventral: new Float32Array(nV) };
391
- }
@@ -1,106 +0,0 @@
1
- // White-tailed deer (Odocoileus virginianus). Hooves with hoof bones (the horse's anatomy at deer
2
- // proportions: very slender long legs), large mobile ears, the white flag of a tail, antlers on bucks
3
- // (rigid bone-coloured geometry on the head, grown by age: spike yearling, 6-8 points, 8-10
4
- // points), spotted fawns, summer red-brown and winter grey-brown coats.
5
- import { deerRig, HEAD_O } from './rig.js';
6
- import { sculptDeer, EYE } from './sculpt.js';
7
- import { deerRegions } from './regions.js';
8
- import { deerCoat, COATS } from './coat.js';
9
- import { srgb } from '../../core/build/coatKit.js';
10
- import { motion } from './motion.js';
11
-
12
- const pick = (R, table) => {
13
- let x = R() * table.reduce((s, [, w]) => s + w, 0);
14
- for (const [k, w] of table) { x -= w; if (x <= 0) return k; }
15
- return table[table.length - 1][0];
16
- };
17
-
18
- // Antlers by age class: burr on the frontal pedicle, main beam curving out,
19
- // back and then forward and in, tines rising near-vertically from its top. Tine rows: [position
20
- // along the beam 0..1, length (m), outward lean].
21
- function antlersFor(R, ageClass, g) {
22
- const burr = [0.024, 0.042, -0.022];
23
- if (ageClass === 'yearling') {
24
- if (R() < 0.6) return { spike: true, beam: 0.11 + 0.07 * R(), base: 0.0105, burr, seed: Math.floor(R() * 1e6), points: 2 };
25
- return { spike: false, beam: 0.2 + 0.05 * R(), base: 0.012, spread: 0.85 + 0.1 * g(), rise: 1.15, curl: 0.8, burr, tines: [[0.45, 0.07 + 0.03 * R(), 0.2]], seed: Math.floor(R() * 1e6), points: 4 };
26
- }
27
- const mature = ageClass === 'mature';
28
- const beam = mature ? 0.44 + 0.08 * R() : 0.33 + 0.07 * R();
29
- const tines = [];
30
- const brow = mature ? R() < 0.9 : R() < 0.6;
31
- if (brow) tines.push([0.09, 0.04 + 0.05 * R(), 0.15]);
32
- tines.push([0.37, (mature ? 0.13 : 0.1) + 0.05 * R(), 0.3]);
33
- if (mature || R() < 0.55) tines.push([0.6, (mature ? 0.11 : 0.08) + 0.04 * R(), 0.3]);
34
- if (mature && R() < 0.65) tines.push([0.8, 0.06 + 0.04 * R(), 0.25]);
35
- return {
36
- spike: false, beam, base: mature ? 0.022 : 0.018,
37
- spread: (mature ? 1.2 : 1.08) + 0.1 * g(), rise: 1 + 0.12 * g(), curl: 1 + 0.12 * g(),
38
- burr, tines, seed: Math.floor(R() * 1e6), points: 2 * (tines.length + 1),
39
- };
40
- }
41
-
42
- export default {
43
- id: 'deer',
44
- name: 'White-tailed deer',
45
- latin: 'Odocoileus virginianus',
46
- group: 'hooves',
47
- plan: 'quadruped',
48
- covering: 'fur',
49
- // coat seasons as variants (createAnimal('deer', { variant: 'winter' }))
50
- variants: [
51
- { id: 'summer', name: 'White-tailed deer (summer coat)', latin: 'Odocoileus virginianus' },
52
- { id: 'winter', name: 'White-tailed deer (winter coat)', latin: 'Odocoileus virginianus' },
53
- ],
54
-
55
- // Seeded individual. Sex: doe or buck; age: adult (bucks by age class: yearling, 2.5 years,
56
- // mature) or juvenile (fawn: spotted reddish coat, long legs, big head and ears); size and
57
- // proportions within the species' range; summer red-brown or winter grey-brown coat (o.variant
58
- // forces 'summer' / 'winter'); bucks in summer carry antlers in velvet.
59
- variation(R, o) {
60
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
61
- const age = o.age || (R() < 0.12 ? 'juvenile' : 'adult');
62
- const fawn = age === 'juvenile';
63
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
64
- const coat = o.variant === 'summer' || o.variant === 'winter' ? o.variant : R() < 0.5 ? 'summer' : 'winter';
65
- const ageClass = fawn ? 'fawn' : sex === 'male' ? pick(R, [['yearling', 30], ['prime', 35], ['mature', 35]]) : 'adult';
66
- const buckSize = { yearling: 0.97, prime: 1.05, mature: 1.12 }[ageClass] || 1;
67
- const size = (sex === 'male' ? buckSize : 0.98) * (1 + 0.04 * g()) * (fawn ? 0.52 : 1);
68
- const antlers = sex === 'male' && !fawn ? antlersFor(R, ageClass, g) : null;
69
- if (antlers) antlers.velvet = coat === 'summer' && R() < 0.75;
70
- const type = g(); // - leggier / lighter, + stockier
71
- return {
72
- sex, age, ageClass, size, coat, antlers,
73
- variant: coat, // the variant this individual was built as (the showcase and tools read params.variant)
74
- neck: sex === 'male' && !fawn ? (ageClass === 'mature' ? 0.7 : ageClass === 'prime' ? 0.45 : 0.15) * (coat === 'winter' ? 1 : 0.5) : 0,
75
- coatShade: g(), coatLightness: 0.06 * g(),
76
- coatSeed: Math.floor(R() * 1e6),
77
- minThick: 0.004,
78
- warps: [
79
- { type: 'legs', k: (1 + 0.03 * g() - 0.02 * type) * (fawn ? 1.22 : 1), top: 0.5 },
80
- { type: 'length', k: (1 + 0.03 * g() + ({ prime: 0.02, mature: 0.04 }[ageClass] || 0)) * (fawn ? 0.86 : 1), z0: -0.5, z1: 0.35 },
81
- { type: 'girth', k: 1 + 0.045 * type + ({ yearling: 0.02, prime: 0.05, mature: 0.08 }[ageClass] || 0), cy: 0.68, z0: -0.55, z1: 0.42 },
82
- fawn
83
- ? { type: 'scaleAbout', c: HEAD_O, k: 1.28, r0: 0.1, r1: 0.26 }
84
- : { type: 'scaleAbout', c: HEAD_O, k: 1 + 0.025 * g(), r0: 0.2, r1: 0.5 },
85
- ],
86
- };
87
- },
88
- rig: () => deerRig(),
89
- sculpt: (m, rig, params) => sculptDeer(m, rig, params),
90
- regions: deerRegions,
91
- coat: deerCoat,
92
- eyeSpecs: () => [1, -1].map((side) => ({ side, spec: EYE, headOrigin: HEAD_O, bone: 'head', look: EYE_LOOK })),
93
- motion,
94
- render: { markColor: srgb(0x1c1816), strandDensity: 1000, clumpDensity: 1, shellScale: 1, finWidth: 0.45 },
95
- };
96
- void COATS;
97
-
98
- // Horizontal bar pupil, dark brown iris (looks black), sclera hidden, dark lids.
99
- export const EYE_LOOK = {
100
- pupil: 'bar',
101
- lids: 'mammal',
102
- iris: { inner: srgb(0x160d08), mid: srgb(0x2a1a10), hi: srgb(0x40281a), outer: srgb(0x0e0805) },
103
- sclera: { color: [0.5, 0.45, 0.4], visible: false },
104
- lidColor: [0.02, 0.018, 0.016],
105
- pupilSize: [0.12, 0.38],
106
- };
@@ -1,97 +0,0 @@
1
- // White-tailed deer motion data for the quadruped engine (schema: src/core/motion/quadruped.js).
2
- // Numbers are for the reference individual (params.size = 1, 0.92 m at the withers); the engine
3
- // scales them by dynamic similarity for fawns and big bucks.
4
- //
5
- // Gaits: lateral-sequence walk (hind foot into the fore track),
6
- // a brief diagonal trot with high knee action when wary, the transverse gallop, and the bounding
7
- // gallop of the escape (long suspension, forefeet landing almost together, hind feet planted ahead
8
- // of the fore tracks, tail flagged). Walk -> trot ~1.6 m/s (Froude ~0.4), trot -> gallop ~4.3 m/s,
9
- // gallop -> bound ~9 m/s; top speed 13.5 m/s (48 km/h).
10
- import { hooks } from './behaviour.js';
11
-
12
- const G = (name, v, f, D, off, o) => ({ name, v, f, D, off, ...o });
13
-
14
- export const motion = {
15
- maxSpeed: 13.5,
16
- accel: [4.5, 6.5],
17
- decel: 8,
18
- // turns: a walking deer comes round over 2-3 steps on a path of one to two body lengths (~1.2 rad/s
19
- // at 1.2 m/s); at speed the cornering limit (~0.5 g) keeps the lean of a sharp escape turn near
20
- // 23 deg (0.85 atan(a / g)) instead of the 34 deg clamp (a long-legged body rolled like a motorbike)
21
- turnRate: 1.5,
22
- latAccelMax: 5,
23
- gears: { walk: 1.1, trot: 2.9, gallop: 7, bound: 11.5 },
24
- gallopBlend: [3.8, 7.5],
25
- // name v f D [FL, FR, HL, HR]
26
- gaits: [
27
- G('walk', 0.0, 0.85, 0.72, [0.25, 0.75, 0.0, 0.5], { liftF: 0.06, liftH: 0.05, bobF: 0.015, bobH: 0.015, flex: 0, lat: 0.02, fold: 0.7, heel: 30, tail: 0, drop: 0 }),
28
- G('walk', 1.5, 1.15, 0.62, [0.25, 0.75, 0.0, 0.5], { liftF: 0.085, liftH: 0.065, bobF: 0.03, bobH: 0.03, flex: 0, lat: 0.025, fold: 0.9, heel: 40, tail: 0.1, drop: 0.02 }),
29
- G('trot', 2.1, 1.85, 0.45, [0.51, 0.01, 0.0, 0.5], { liftF: 0.12, liftH: 0.09, bobF: 0.03, bobH: 0.03, flex: 0.01, lat: 0.0, fold: 1.2, heel: 50, tail: 0.3, drop: 0.03 }),
30
- G('trot', 3.8, 2.15, 0.41, [0.51, 0.01, 0.0, 0.5], { liftF: 0.14, liftH: 0.11, bobF: 0.035, bobH: 0.035, flex: 0.015, lat: 0.0, fold: 1.3, heel: 55, tail: 0.4, drop: 0.035 }),
31
- // (the fast gaits run low: the long, near-straight forelegs only sweep through a real stance, fore
32
- // duty 0.2-0.25 in the gallop and ~0.13 in the bound, when the forequarters ride ~10 % of the
33
- // withers height lower than they stand; at the standing height a forefoot touched down for one or
34
- // two frames and the deer floated on its hind legs)
35
- G('gallop', 4.8, 2.35, 0.3, [0.4, 0.52, 0.0, 0.12], { liftF: 0.14, liftH: 0.12, bobF: 0.015, bobH: 0.04, flex: 0.12, lat: 0, fold: 1.35, heel: 45, tail: 0.6, drop: 0.08 }),
36
- G('gallop', 8, 2.55, 0.25, [0.4, 0.52, 0.0, 0.12], { liftF: 0.16, liftH: 0.14, bobF: 0.015, bobH: 0.045, flex: 0.17, lat: 0, fold: 1.4, heel: 40, tail: 0.8, drop: 0.1 }),
37
- // bound: the back gathers and stretches with each leap (bounding.jpg)
38
- G('bound', 10, 2.5, 0.21, [0.45, 0.5, 0.0, 0.06], { liftF: 0.2, liftH: 0.17, bobF: 0.02, bobH: 0.055, flex: 0.27, lat: 0, fold: 1.45, heel: 40, tail: 1.0, drop: 0.12 }),
39
- G('bound', 13.5, 2.6, 0.18, [0.45, 0.5, 0.0, 0.06], { liftF: 0.22, liftH: 0.19, bobF: 0.02, bobH: 0.06, flex: 0.3, lat: 0, fold: 1.5, heel: 40, tail: 1.0, drop: 0.13 }),
40
- ],
41
- feet: {
42
- // small cloven hooves: the fetlock gives under load, the digit flips in swing, breakover at the toe
43
- front: { type: 'unguligrade', contact: 0.95, curl: 75, flex: 4, sink: 16, hoofFlex: 20, swingBack: 0.12 },
44
- hind: { type: 'unguligrade', contact: 0.95, curl: 75, flex: 4, sink: 10, hoofFlex: 20, swingBack: 0.03 },
45
- },
46
- stance: { width: 1, gallopWidth: 0.8, sprawl: 0, crouch: 0, scuff: [10, 20, 0] },
47
- // torso cross-sections (bind pose, reference size): centre height, half height, half width
48
- body: {
49
- // (half heights include the legs folded under the chest and thighs when lying down)
50
- chest: { y: 0.66, hh: 0.26, hw: 0.19 },
51
- mid: { y: 0.67, hh: 0.22, hw: 0.18 },
52
- pelvis: { y: 0.74, hh: 0.3, hw: 0.2 },
53
- back: 0.08,
54
- },
55
- head: {
56
- // [speed, raise (m), reach (m), pitch (rad, + = nose down)]: lowered and nodding at the walk, up
57
- // at the wary trot, stretched forward in the gallop and bound
58
- carriage: [[0, 0, 0, 0], [1.2, -0.07, 0.04, 0.05], [3, -0.01, 0.02, 0.0], [6, -0.1, 0.08, 0.06], [12, -0.16, 0.12, 0.1]],
59
- stab: [[0, 0], [1.2, 0.1], [3, 0.4], [6, 0.3], [12, 0.35]],
60
- neckBend: 45, neckPivot: 0.6, yaw: 0.9, pitchUp: 0.6, pitchDown: 0.9, mouth: 'jawTip',
61
- },
62
- tail: {
63
- // [speed, pitch per segment from horizontal (deg), root -> tip]: flat on the rump at rest, lifting
64
- // with speed (the flag itself is the behaviour layer)
65
- carriage: [
66
- [0.0, [-45, -62, -72, -78, -80, -80]],
67
- [1.5, [-40, -58, -68, -74, -78, -80]],
68
- [3.5, [-25, -45, -58, -66, -72, -76]],
69
- [7, [0, -20, -35, -45, -52, -58]],
70
- ],
71
- stiffness: 1.1, sway: 4, flick: 0.6, wag: 0, swish: 0.45, curl: 0, balance: 0,
72
- radius: [0.035, 0.03],
73
- },
74
- ears: { rest: [0, 0], mobility: 1.1, speedFlatten: 0.35, flattenSpeeds: [5, 12], twitch: 1.3, prick: 0.55 },
75
- breath: { rate: 0.28, amp: 0.006, pant: false, pantRate: 1.5, pantAmp: 0.015, heatSpeed: 11 },
76
- actions: {
77
- attack: { style: 'headbutt', kick: 'rear' }, // (hooks: buck antler thrust, doe rears and flails)
78
- // grazing / drinking on straight forelegs (drinking.jpg): the long neck swings down from the
79
- // chest, the head hangs near vertical (bind 50 deg + 43 deg), the muzzle a little ahead of the
80
- // forefeet; the forehand stays at its standing height (it dropped 30-36 cm, a play bow, when the
81
- // neck could not reach)
82
- eat: { style: 'graze', dropF: 0, dropH: 0, pitch: 0.75, ahead: 0.25 },
83
- drink: { dropF: 0, dropH: 0, pitch: 0.7, ahead: 0.25 },
84
- // an everyday obstacle jump: the body rises ~1.1 m, 3.8 m long (play('jump', { height: 2 }) is the
85
- // record escape leap: clears ~2.4 m, 6.5 m standing, up to ~9 m from a gallop); lands on
86
- // near-straight forelegs, the hinds swing under
87
- jump: { height: 1.1, distance: 3.8, land: [0.14, 0.14] },
88
- // sit: deer do not sit like a dog (only briefly while rising, or hurt): a sit is sternal bedding
89
- // with the head up and alert (hooks: behaviour.js), lie the same, relaxed
90
- sit: { down: 'front', up: 'hind' },
91
- // kneels on both carpi first, then lowers the hindquarters; rises hind end first
92
- lie: { down: 'front', up: 'hind' },
93
- sleep: { style: 'curl', down: 'front', up: 'hind' },
94
- fold: { front: { z: -0.24, k: 1.5, beta: 0.9 } },
95
- },
96
- hooks,
97
- };
@@ -1,60 +0,0 @@
1
- // Meshing plan: body at 6.2 mm (0.67 % of the withers height), head at 2.9 mm overlapping the body
2
- // across the upper neck and cross-faded, eyelid patches at 0.9 mm, the lower lip / chin as its own
3
- // rigid surface (the mouth opens) and, in bucks, the antlers as a rigid region on the head bone.
4
- import { EYE, antlerCurves } from './sculpt.js';
5
- import { HEAD_O, deerJoints } from './rig.js';
6
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
7
- import { norm, smoothstep } from '../../core/math/vec.js';
8
-
9
- const J = deerJoints();
10
- // the head is carried nose-down on a steep neck: cut just behind the poll - throat latch line
11
- export const NECK_CUT = { c: [0, J.occiput[1] - 0.1, J.occiput[2] - 0.02], d: norm([0, 1, 0.35]), band: 0.013 };
12
- export const neckS = (x, y, z) => (x - NECK_CUT.c[0]) * NECK_CUT.d[0] + (y - NECK_CUT.c[1]) * NECK_CUT.d[1] + (z - NECK_CUT.c[2]) * NECK_CUT.d[2];
13
-
14
- export const EYE_PATCH = { R: 0.024, B: 0.004, h: 0.0009, centres: [1, -1].map((s) => eyeFrameOf(EYE, HEAD_O, s).c) };
15
- export function eyePatchWeight(x, y, z) {
16
- let dmin = 1e9;
17
- for (const c of EYE_PATCH.centres) dmin = Math.min(dmin, Math.hypot(x - c[0], y - c[1], z - c[2]));
18
- return 1 - smoothstep(EYE_PATCH.R - EYE_PATCH.B, EYE_PATCH.R + EYE_PATCH.B, dmin);
19
- }
20
-
21
- function antlerBox(A) {
22
- const lo = [1e9, 1e9, 1e9], hi = [-1e9, -1e9, -1e9];
23
- for (const side of antlerCurves(A)) {
24
- for (const p of [...side.beam, ...side.tines.flatMap((t) => t.pts)]) for (let i = 0; i < 3; i++) { lo[i] = Math.min(lo[i], p[i]); hi[i] = Math.max(hi[i], p[i]); }
25
- for (let i = 0; i < 3; i++) { lo[i] = Math.min(lo[i], side.burr[i]); hi[i] = Math.max(hi[i], side.burr[i]); }
26
- }
27
- const pad = A.base * 1.6 + 0.01;
28
- return { bmin: lo.map((v) => v - pad), bmax: hi.map((v) => v + pad) };
29
- }
30
-
31
- export function deerRegions(rig, params, Q) {
32
- const B = NECK_CUT.band, r = Q.res;
33
- const patches = Q.eyePatches ? EYE_PATCH.centres.map((c) => ({ c, R: EYE_PATCH.R, B: EYE_PATCH.B, h: EYE_PATCH.h * Math.max(1, r) })) : [];
34
- const headJobs = [
35
- { name: 'head', part: 'body', bmin: [-0.2, HEAD_O[1] - 0.22, HEAD_O[2] - 0.2], bmax: [0.2, HEAD_O[1] + 0.24, HEAD_O[2] + 0.2], h: 0.003 * r, F: 6, clip: (x, y, z) => neckS(x, y, z) > -B, patches },
36
- { name: 'jaw', part: 'jaw', bmin: [-0.04, HEAD_O[1] - 0.24, HEAD_O[2] + 0.02], bmax: [0.04, HEAD_O[1] - 0.02, HEAD_O[2] + 0.2], h: 0.0029 * r, F: 6, rigidBone: 'jaw' },
37
- ];
38
- const jobs = [
39
- [{ name: 'body', part: 'body', bmin: [-0.26, -0.01, -0.84], bmax: [0.26, 1.3, 0.66], h: 0.0064 * r, F: 4, clip: (x, y, z) => neckS(x, y, z) < B }],
40
- headJobs,
41
- ];
42
- if (params.antlers) {
43
- // (antler cell 3.5 mm at the hero tier, coarsening a little more slowly than the body)
44
- const bx = antlerBox(params.antlers);
45
- jobs.push([{ name: 'antler', part: 'antler', ...bx, h: 0.0035 * Math.pow(r, 0.85), F: 6, rigidBone: 'head' }]);
46
- }
47
- return {
48
- jobs,
49
- fade(x, y, z, name, patch) {
50
- const sN = neckS(x, y, z);
51
- if (name === 'body') return 1 - smoothstep(-B, B, sN);
52
- if (name === 'head') {
53
- let f = smoothstep(-B, B, sN);
54
- if (patches.length) { const ew = eyePatchWeight(x, y, z); f *= patch ? ew : 1 - ew; }
55
- return f;
56
- }
57
- return 1;
58
- },
59
- };
60
- }