@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,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
- }