@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,444 +0,0 @@
1
- // The red fox's coat, painted per vertex in reference space.
2
- //
3
- // Colour fields follow the red fox's pattern (Lariviere & Pasitschniak-Arts 1996):
4
- // rufous-orange back, flanks, head and brush, brightest on the head, shoulders and flanks;
5
- // white upper lips, cheeks, chin, throat and chest bib, a pale grey-white belly; black "stockings"
6
- // from the paws up the front of the forelegs to the elbows and over the hind feet up to the hocks
7
- // (a dark stripe runs on up the front of the shin); black backs of the ears with a white-furred
8
- // inside; dark tear lines from the inner eye corners toward the lips; a black lip line and nose; the
9
- // brush has black-tipped guard hair (the shader's agouti band), a dark tail-gland spot near its
10
- // base and a white tip. Morphs: red (default), cross (a dark cross over the shoulders and down the
11
- // spine, dark legs and belly, grizzled flanks), silver (black with silver-tipped guard hair, white
12
- // tail tip), urban (duller, greyer, grizzled winter coat). Kits: woolly, sandy grey-brown with dark
13
- // legs and a pale face.
14
- // Fur length: long ruff and cheek fluff, long flanks and breeches, the huge brush; short sleek face,
15
- // muzzle and lower legs.
16
- import { HEAD_O, MUZZLE_Z0, HS } from './rig.js';
17
- import { neckS } from './regions.js';
18
- import { SDFModel } from '../../core/sdf/sdf.js';
19
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
20
- import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
21
-
22
- const P3 = {
23
- // sRGB swatches, neutral-light albedo
24
- red: {
25
- back: 0xb4582a, flank: 0xc46a32, lowFlank: 0xc97a42, head: 0xca7236, muzzleTop: 0xb46a34, shoulder: 0xc56a32,
26
- white: 0xece8e2, belly: 0xd8d2ca, stocking: 0x19140f, stockingEdge: 0x3a2418, earBack: 0x1e1814, earInner: 0xe6dccb,
27
- tail: 0xa45a2c, tailUnder: 0xc08450, tailTip: 0xf0ebe2, gland: 0x3a2618, tear: 0x3e2a1e, rump: 0xc98a58, dark: 0x2a1c14,
28
- ag: { back: 0.35, flank: 0.1, head: 0.05, leg: 0, tail: 0.55 },
29
- },
30
- cross: {
31
- back: 0x8a5634, flank: 0x9c6a44, lowFlank: 0x8a6448, head: 0xa8703e, muzzleTop: 0x7a5234, shoulder: 0x8e5a36,
32
- white: 0xcfc8bc, belly: 0x3a3430, stocking: 0x151210, stockingEdge: 0x2a2018, earBack: 0x1a1512, earInner: 0xcfc4b2,
33
- tail: 0x6e4a32, tailUnder: 0x8a6448, tailTip: 0xe8e2d8, gland: 0x2a1c14, tear: 0x2a1c14, rump: 0x8a6448, dark: 0x1e1a18,
34
- ag: { back: 0.55, flank: 0.45, head: 0.15, leg: 0, tail: 0.7 },
35
- },
36
- silver: {
37
- back: 0x8e8c8a, flank: 0x7a7876, lowFlank: 0x2e2c2a, head: 0x5e5c5a, muzzleTop: 0x201e1c, shoulder: 0x74726f,
38
- white: 0x5a5856, belly: 0x1e1c1a, stocking: 0x121010, stockingEdge: 0x181614, earBack: 0x141210, earInner: 0x6a6866,
39
- tail: 0x242220, tailUnder: 0x1c1a18, tailTip: 0xeae6de, gland: 0x141210, tear: 0x141210, rump: 0x3a3836, dark: 0x161412,
40
- ag: { back: 0.85, flank: 0.8, head: 0.6, leg: 0.1, tail: 0.3 },
41
- },
42
- urban: {
43
- back: 0x7e4a2e, flank: 0x96684a, lowFlank: 0x9a7a62, head: 0x9a6440, muzzleTop: 0x8a5a3a, shoulder: 0x86522f,
44
- white: 0xd8d2c8, belly: 0xa8a098, stocking: 0x1c1712, stockingEdge: 0x3a2a1e, earBack: 0x1e1814, earInner: 0xd6cab8,
45
- tail: 0x6e4630, tailUnder: 0x8e6a50, tailTip: 0xe0dad0, gland: 0x2a1c14, tear: 0x3a2618, rump: 0x9a7a62, dark: 0x2a1c14,
46
- ag: { back: 0.6, flank: 0.55, head: 0.15, leg: 0, tail: 0.65 },
47
- },
48
- };
49
- const KIT = 0x7a6450; // woolly sandy grey-brown natal / juvenile coat
50
-
51
- export function palette(p) {
52
- const base = P3[p.variant] || P3.red;
53
- const k = p.coatWarmth || 0, l = p.coatLightness || 0, juv = p.juv || 0;
54
- const kit = srgb(KIT).map((c) => c * (p.variant === 'silver' ? 0.45 : 1));
55
- const out = { ag: base.ag };
56
- for (const [key, hex] of Object.entries(base)) {
57
- if (key === 'ag') continue;
58
- let c = srgb(hex);
59
- // warmth shifts red against blue; the white parts stay white
60
- const wk = /white|belly|earInner|tailTip/.test(key) ? 0.25 : 1;
61
- c = [c[0] * (1 + (0.1 * k + l) * wk), c[1] * (1 + (0.01 * k + l) * wk), c[2] * (1 - (0.12 * k - l) * wk)];
62
- // kits: most of the pattern is washed into the woolly juvenile coat (face, stockings, ears and the
63
- // tail tip stay)
64
- const keep = /white|earInner|tailTip/.test(key) ? 0.6 : /stocking|earBack/.test(key) ? 0.7 : /head|muzzleTop/.test(key) ? 0.45 : 0.15;
65
- if (juv > 0) c = mix3(c, kit, juv * (1 - keep));
66
- out[key] = c;
67
- }
68
- // eyelids (the eye shader's lidColor): furred in the face colour round the eye, a little darker; the
69
- // shader darkens their margin itself, so a blink or a closed eye reads as a furred lid with a dark
70
- // line (it was near black: every blink, sleep and death showed glossy black beads)
71
- out.lid = mix3(out.head, out.tear, 0.25).map((c) => c * 0.85);
72
- return out;
73
- }
74
-
75
- export function foxCoat(ctx) {
76
- const { model, rig, pos, nrm, regionOf: partOf, weights, nV, params } = ctx;
77
- const { BONES, AXIAL } = rig;
78
- const J = rig.J;
79
- const AX_LEN = weights.axialLengths;
80
- const C = palette(params);
81
- const AG = C.ag;
82
- const juv = params.juv || 0;
83
- const hw = params.headW || 1, mz = params.muzzle || 1;
84
- const ruffK = params.ruff ?? 1;
85
- const bibK = params.bib ?? 1; // extent of the white bib / belly
86
- const stockK = params.stockings ?? 1; // how far up the black stockings reach
87
- const tipK = params.tailTip ?? 1; // length of the white tail tip (0 = none)
88
- const tearK = params.tear ?? 1;
89
- const variant = params.variant || 'red';
90
- const nOff = (params.coatSeed || 0) % 997;
91
- const { dominant, axialS, skinIndex, skinWeight, limbMember, limbWhich } = weights;
92
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
93
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
94
- const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8], sTip = AX_LEN[AX_LEN.length - 1];
95
- const tailLen = sTip - sTailBase;
96
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
97
- const nosePrim = model.prims.find((p) => p.tag === 'nose');
98
- const caninePrims = model.prims.filter((p) => p.tag === 'canine');
99
- const jawPrims = model.forPart('jaw').filter((p) => p.tag !== 'canine');
100
- const bodyPrims = model.forPart('body');
101
- const headPrims = bodyPrims.filter((p) => p.bone === 'head' && !p.carve && p.tag !== 'canine');
102
- const earPrims = model.prims.filter((p) => p.tag === 'ear');
103
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|femur|tibia|metatarsus|hpaw)/.test(b.name));
104
- const isPaw = BONES.map((b) => /^(fpaw|hpaw)/.test(b.name));
105
- const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw)/.test(b.name));
106
- // reference heights
107
- const yEl = J.elbowL[1], yHock = J.hockL[1], yKnee = J.kneeL[1], yBack = J.thoraxRear[1];
108
- const zSh = J.shoulderL[2], zHip = J.hipL[2];
109
- // head-local coordinates of a reference-space point (undo head width and muzzle stretch)
110
- const headLocal = (p) => {
111
- const x = (p[0] - HEAD_O[0]) / hw / HS, y = (p[1] - HEAD_O[1]) / HS;
112
- let z = (p[2] - HEAD_O[2]) / HS;
113
- if (z > MUZZLE_Z0) z = MUZZLE_Z0 + (z - MUZZLE_Z0) / mz;
114
- return [x, y, z];
115
- };
116
-
117
- // --- regions per vertex (0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw) and limb blend
118
- const region = new Uint8Array(nV);
119
- const legness = new Float32Array(nV);
120
- const legBone = new Int16Array(nV).fill(-1);
121
- const junction = new Float32Array(nV);
122
- const tailT = new Float32Array(nV);
123
- for (let v = 0; v < nV; v++) {
124
- const bn = BONES[dominant[v]].name;
125
- const p = P(v);
126
- let lb = -1, lw = 0;
127
- for (let k = 0; k < 4; k++) {
128
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
129
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
130
- }
131
- legBone[v] = lb;
132
- const m = limbMember[v];
133
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, m) : 0;
134
- junction[v] = lb >= 0 ? 4 * m * (1 - m) : 0;
135
- if (partOf[v] === 2) region[v] = 6;
136
- else if (bn.startsWith('ear')) region[v] = 5;
137
- else if (axialS[v] > sTailBase + 0.012 && bn.startsWith('tail')) { region[v] = 4; tailT[v] = clamp((axialS[v] - sTailBase) / tailLen, 0, 1); }
138
- else if (axialS[v] < sOcc + 0.006 || (partOf[v] === 1 && neckS(p[0], p[1], p[2]) > 0.012)) region[v] = 2;
139
- else if (axialS[v] < sNeckBase) region[v] = 1;
140
- else region[v] = 0;
141
- if (region[v] >= 2) { legness[v] = 0; junction[v] = 0; }
142
- }
143
- const underFactor = (v) => {
144
- const n = N(v), p = P(v), li = limbWhich[v];
145
- if (li < 0 || junction[v] <= 0) return 0;
146
- if (li <= 1) return junction[v] * Math.max(smoothstep(0.15, -0.55, n[2]) * smoothstep(yEl + 0.1, yEl + 0.05, p[1]), smoothstep(0.2, -0.6, n[1]));
147
- return junction[v] * smoothstep(0.35, -0.45, n[1] + 0.35 * Math.abs(n[2]));
148
- };
149
-
150
- // --- ventral (white underside) factor: throat, bib, belly, inner thighs
151
- const ventral = new Float32Array(nV);
152
- const yBib = yEl + 0.13 * bibK; // top of the white bib on the chest front
153
- for (let v = 0; v < nV; v++) {
154
- const p = P(v), n = N(v), r = region[v];
155
- let w = 0;
156
- if (r === 0) {
157
- w = smoothstep(-0.45, -0.85, n[1]);
158
- // chest front between the forelegs: the white bib, continuing the throat
159
- if (p[2] > zSh - 0.05) w = Math.max(w, smoothstep(yBib, yBib - 0.035, p[1] + 0.012 * (fbm3(p[0] * 40, p[1] * 40, p[2] * 40, 2) - 0.5)) * smoothstep(0.042, 0.018, Math.abs(p[0])) * smoothstep(-0.3, 0.35, n[2]));
160
- w *= smoothstep(yBack - 0.02, yBack - 0.08, p[1]);
161
- } else if (r === 1) {
162
- // throat and the front of the neck ruff
163
- w = smoothstep(0.4, -0.05, n[1] + 0.65 * Math.abs(n[0]) - 0.45 * Math.max(0, n[2]));
164
- } else if (r === 2 || r === 6) {
165
- const h = headLocal(p);
166
- w = smoothstep(-0.028, -0.05, h[1]) * smoothstep(-0.1, -0.6, n[1]) + (r === 6 ? 0.8 : 0);
167
- }
168
- const L = legness[v];
169
- if (L > 0) {
170
- const side = p[0] >= 0 ? 1 : -1;
171
- const inner = smoothstep(0.1, -0.6, n[0] * side);
172
- const wl = inner * 0.75 * smoothstep(yKnee - 0.01, yKnee + 0.05, p[1]) * (isFront[legBone[v]] ? 0.4 : 1) + smoothstep(-0.3, -0.85, n[1]) * 0.4 * smoothstep(yKnee - 0.02, yKnee + 0.04, p[1]);
173
- w = mix(w, wl, L);
174
- }
175
- w = Math.max(w, underFactor(v) * 0.6 * smoothstep(-0.1, -0.5, n[1]));
176
- ventral[v] = clamp(w, 0, 1);
177
- }
178
-
179
- // --- comb (hair flow), bind space
180
- const comb = new Float32Array(nV * 3);
181
- for (let v = 0; v < nV; v++) {
182
- const p = P(v), n = N(v), r = region[v];
183
- let d;
184
- const bone = BONES[dominant[v]];
185
- if (r === 5) {
186
- d = norm(sub(bone.tail, bone.head));
187
- } else if (r === 2 || r === 6) {
188
- const h = headLocal(p);
189
- // out from the nose over the face; the cheek ruff sweeps back, out and down
190
- const fromNose = norm(sub(h, [0, -0.01, 0.165]));
191
- d = norm(add(fromNose, [0, -0.2, -0.5]));
192
- if (h[2] < 0.01 && Math.abs(h[0]) > 0.03) d = norm(add(d, [Math.sign(h[0]) * 0.7, -0.6, -0.7]));
193
- } else {
194
- const seg = weights.segT[v * 2] | 0;
195
- const a = AXIAL.points[seg], b = AXIAL.points[Math.min(seg + 1, AXIAL.points.length - 1)];
196
- d = norm(sub(b, a));
197
- if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
198
- if (r === 1) d = norm(add(d, [0, -0.55, 0])); // the ruff hangs down and back
199
- const L = legness[v];
200
- if (L > 0 && legBone[v] >= 0) {
201
- const lbn = BONES[legBone[v]];
202
- let dl = norm(sub(lbn.tail, lbn.head));
203
- if (isPaw[legBone[v]]) dl = norm(add(dl, [0, 0, 0.4]));
204
- else dl = norm(add(dl, [0, -0.2, -0.3]));
205
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
206
- }
207
- }
208
- let t = sub(d, mul(n, dot(d, n)));
209
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
210
- t = norm(t);
211
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
212
- }
213
-
214
- // --- per-vertex paint
215
- const furLen = new Float32Array(nV);
216
- const tint = new Float32Array(nV * 4);
217
- const markSDF = new Float32Array(nV).fill(1);
218
- const surf = new Float32Array(nV * 4);
219
- const g3 = (h, c, rr) => Math.exp(-(((Math.abs(h[0]) - c[0]) / rr[0]) ** 2 + ((h[1] - c[1]) / rr[1]) ** 2 + ((h[2] - c[2]) / rr[2]) ** 2));
220
- // distance (head-local) from a point to the segment a-b, mirrored to the left side
221
- const segD = (h, a, b) => {
222
- const q = [Math.abs(h[0]), h[1], h[2]];
223
- const ab = sub(b, a), t = clamp(dot(sub(q, a), ab) / dot(ab, ab), 0, 1);
224
- return { d: len(sub(q, add(a, mul(ab, t)))), t };
225
- };
226
- const tailSegDorsal = (v) => {
227
- const bone = BONES[dominant[v]];
228
- const d = norm(sub(bone.tail, bone.head));
229
- return norm([0, -d[2], d[1]]);
230
- };
231
- const cross_ = variant === 'cross';
232
-
233
- for (let v = 0; v < nV; v++) {
234
- const p = P(v), n = N(v), r = region[v];
235
- let col, fl = 0.02, ag = 0, mat = MAT.FUR, mark = 1, under = 0.4;
236
- // low-frequency noise (ragged pattern edges) and a finer field
237
- const nz = fbm3(p[0] * 12 + nOff, p[1] * 12, p[2] * 12, 3) - 0.5;
238
- const nz2 = vnoise3(p[0] * 70 + nOff, p[1] * 70, p[2] * 70) - 0.5;
239
- if (r === 0 || r === 1) {
240
- const up = n[1];
241
- const vent = ventral[v];
242
- const dors = smoothstep(-0.1, 0.7, up + 0.3 * nz);
243
- col = mix3(C.lowFlank, C.flank, smoothstep(-0.4, 0.2, up + 0.25 * nz));
244
- col = mix3(col, C.back, dors * 0.85);
245
- ag = mix(AG.flank, AG.back, dors);
246
- if (r === 0) {
247
- // brighter shoulders; grizzled hips and rump in the dull coats
248
- const sh = Math.exp(-(((p[2] - zSh + 0.01) / 0.05) ** 2)) * smoothstep(-0.3, 0.3, n[0] * (p[0] >= 0 ? 1 : -1));
249
- col = mix3(col, C.shoulder, sh * 0.6);
250
- const rump = smoothstep(zHip + 0.02, zHip - 0.05, p[2]) * smoothstep(-0.2, -0.8, n[2]) * smoothstep(yBack + 0.02, yBack - 0.06, p[1]);
251
- col = mix3(col, C.rump, rump * 0.6);
252
- // cross morph: a dark stripe down the spine crossed by one over the shoulders
253
- if (cross_) {
254
- const spine = smoothstep(0.4, 0.8, up + 0.15 * nz) * smoothstep(zHip - 0.06, zHip + 0.02, p[2]);
255
- const bar = Math.exp(-(((p[2] - zSh - 0.035 + 0.02 * nz) / 0.05) ** 2)) * smoothstep(yEl + 0.02, yBack - 0.04, p[1]);
256
- const c = clamp(Math.max(spine, bar), 0, 1);
257
- col = mix3(col, C.dark, c * 0.92);
258
- ag = mix(ag, 0.3, c);
259
- }
260
- col = mix3(col, C.belly, vent);
261
- // the white bib is whiter than the belly
262
- const bib = smoothstep(zSh - 0.06, zSh + 0.01, p[2]) * vent;
263
- col = mix3(col, C.white, bib);
264
- ag *= 1 - vent;
265
- // guard-hair lengths: flanks, long over the shoulders and hips, a belly fringe
266
- fl = mix(0.017, 0.024, smoothstep(0.1, 0.85, up)) * (1 + 0.1 * nz2);
267
- fl = mix(fl, 0.014, smoothstep(0.3, 0.9, vent));
268
- fl = Math.max(fl, 0.022 * bib);
269
- if (p[2] < zHip) fl = Math.max(fl, 0.024 * rump);
270
- } else {
271
- // neck: rufous above, the white throat below, the ruff long and soft
272
- col = mix3(col, C.white, smoothstep(0.2, 0.65, vent + 0.15 * nz));
273
- ag *= 1 - smoothstep(0.2, 0.6, vent);
274
- fl = mix(0.026, 0.024, vent) * (0.85 + 0.15 * ruffK) * (1 + 0.1 * nz2);
275
- if (cross_) col = mix3(col, C.dark, smoothstep(0.6, 0.95, up) * 0.6);
276
- }
277
- const L = legness[v];
278
- if (L > 0) {
279
- const lb = legBone[v];
280
- const front = lb >= 0 && isFront[lb];
281
- const side = p[0] >= 0 ? 1 : -1;
282
- const outer = smoothstep(-0.2, 0.5, n[0] * side);
283
- // upper leg: body colour (orange thighs, white inside)
284
- let lc = mix3(col, C.belly, smoothstep(0.2, 0.7, ventral[v]) * (1 - outer));
285
- // the black stockings: forelegs from the paw up to the elbow (highest on the front), a thin
286
- // dark line up the front of the upper arm; hind feet up to the hock and a dark stripe up
287
- // the front of the shin
288
- let st;
289
- const e = 0.012 * nz;
290
- if (front) {
291
- const top = yEl + 0.01 * stockK + 0.02 * smoothstep(-0.2, 0.8, n[2]);
292
- st = smoothstep(top + 0.012, top - 0.012, p[1] + e);
293
- st = Math.max(st, 0.8 * smoothstep(0.4, 0.85, n[2]) * smoothstep(yEl + 0.075, yEl + 0.03, p[1]) * stockK);
294
- } else {
295
- const top = yHock + 0.012 + 0.03 * stockK * smoothstep(0.1, 0.8, n[2]);
296
- st = smoothstep(top + 0.012, top - 0.012, p[1] + e);
297
- // dark front of the shin (from the hock up toward the stifle)
298
- st = Math.max(st, 0.85 * smoothstep(0.35, 0.8, n[2]) * smoothstep(yKnee - 0.01, yHock + 0.03, p[1]) * stockK);
299
- }
300
- st = clamp(st * (variant === 'silver' ? 1 : 1), 0, 1);
301
- lc = mix3(lc, mix3(C.stockingEdge, C.stocking, smoothstep(0.3, 0.9, st)), st);
302
- let lf = mix(0.007, 0.018, smoothstep(front ? yEl - 0.06 : yHock, front ? yEl + 0.06 : yKnee + 0.04, p[1]));
303
- // breeches on the back of the thighs, a little feathering behind the forearm
304
- if (!front) lf = Math.max(lf, 0.024 * smoothstep(-0.1, -0.7, n[2]) * smoothstep(yKnee - 0.04, yKnee + 0.05, p[1]));
305
- else lf = Math.max(lf, 0.009 * smoothstep(-0.2, -0.8, n[2]) * smoothstep(yEl - 0.1, yEl - 0.02, p[1]));
306
- if (lb >= 0 && isPaw[lb]) { lf = 0.005; lc = C.stocking; }
307
- col = mix3(col, lc, L);
308
- ag = mix(ag, AG.leg * (1 - st), L);
309
- fl = mix(fl, lf, L);
310
- }
311
- under = 0.45;
312
- } else if (r === 4) {
313
- // the brush: rufous with black-tipped guard hair (strongest on top), a dark tail-gland spot near
314
- // the base, paler underside, white tip
315
- const t = tailT[v];
316
- const dors = dot(n, tailSegDorsal(v));
317
- const top = smoothstep(-0.45, 0.55, dors + 0.3 * nz);
318
- col = mix3(C.tailUnder, C.tail, top);
319
- ag = AG.tail * (0.55 + 0.45 * top) * smoothstep(0.02, 0.2, t);
320
- const gl = Math.exp(-(((t - 0.14) / 0.05) ** 2)) * smoothstep(0.2, 0.75, dors);
321
- col = mix3(col, C.gland, gl * 0.75);
322
- // a darker band before the white tip (dark-tipped hair crowding at the end)
323
- const tipStart = 1 - 0.17 * tipK;
324
- const band = Math.exp(-(((t - tipStart + 0.06) / 0.07) ** 2));
325
- col = mix3(col, C.dark, band * 0.35);
326
- const tip = tipK > 0 ? smoothstep(tipStart - 0.02, tipStart + 0.04, t + 0.035 * nz) : 0;
327
- col = mix3(col, C.tailTip, tip);
328
- ag *= 1 - tip;
329
- fl = mix(0.024, 0.04, smoothstep(0.0, 0.35, t)) * (1 - 0.35 * smoothstep(0.88, 1, t));
330
- under = 0.4;
331
- } else if (r === 5) {
332
- // ears: black backs, the black running round the rim; the inside furred white
333
- const earP = earPrims[p[0] > 0 ? 0 : 3];
334
- const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
335
- const upE = [earP.P[6], earP.P[7], earP.P[8]];
336
- const lat = [earP.P[3], earP.P[4], earP.P[5]];
337
- const front = dot(n, faceDir);
338
- const rel = sub(p, [earP.P[0], earP.P[1], earP.P[2]]);
339
- const ht = dot(rel, upE) / 0.095; // ~0 base .. 1 tip
340
- const edge = Math.abs(dot(rel, lat)) / (0.022 * Math.max(0.25, 1 - ht));
341
- if (front > 0.25) {
342
- col = mix3(C.head, C.earInner, smoothstep(0.25, 0.7, front) * (1 - smoothstep(0.75, 1.05, edge)));
343
- fl = 0.012 * (1 - smoothstep(0.4, 1.0, ht));
344
- } else {
345
- // the back: black, orange at the very base where the head fur runs onto it
346
- col = mix3(C.earBack, C.head, smoothstep(0.12, -0.05, ht) * 0.9);
347
- fl = 0.005;
348
- }
349
- ag = 0;
350
- } else {
351
- // ---------------- head & jaw
352
- const h = headLocal(p);
353
- col = C.head;
354
- ag = AG.head;
355
- // muzzle top a little darker and duller
356
- const mtop = smoothstep(0.05, 0.1, h[2]) * smoothstep(-0.1, 0.5, n[1]);
357
- col = mix3(col, C.muzzleTop, mtop * 0.6);
358
- // white mask: upper lips, muzzle sides low down, cheeks (the white cheek ruff), chin, throat
359
- const lipSide = smoothstep(0.035, 0.065, h[2]) * smoothstep(-0.018, -0.032, h[1] - 0.06 * (h[2] - 0.1)) * smoothstep(0.1, 0.5, Math.abs(n[0]));
360
- const cheek = smoothstep(-0.01, -0.03, h[1] + 0.25 * (h[2] - 0.02) + 0.01 * nz) * smoothstep(0.075, 0.02, h[2]) * smoothstep(0.012, 0.03, Math.abs(h[0]));
361
- const lowFace = smoothstep(-0.03, -0.05, h[1]) * smoothstep(-0.1, -0.6, n[1]);
362
- const throat = smoothstep(-0.034, -0.055, h[1]) * smoothstep(0.08, 0.03, h[2]);
363
- let mask = clamp(Math.max(lipSide, cheek, lowFace, throat, r === 6 ? 0.85 : 0), 0, 1);
364
- col = mix3(col, C.white, mask);
365
- ag *= 1 - mask;
366
- // dark tear line: from the inner eye corner down and forward toward the upper lip
367
- const tl = segD(h, [0.025, 0.012, 0.05], [0.017, -0.018, 0.085]);
368
- const tear = Math.exp(-((tl.d / (0.0045 + 0.003 * tl.t)) ** 2)) * tearK * (1 - 0.6 * juv);
369
- col = mix3(col, C.tear, tear * 0.85);
370
- // cheek ruff: long white fluff with an orange / dark edge toward the neck
371
- const ruffW = smoothstep(-0.015, -0.06, h[2]) * smoothstep(0.03, 0.05, Math.abs(h[0])) * smoothstep(0.015, -0.015, h[1]);
372
- // fur length: sleek muzzle and face, longer crown and cheeks, flaring cheek ruff
373
- fl = mix(0.009, 0.0035, smoothstep(0.02, 0.1, h[2]));
374
- if (h[1] > 0.03 && h[2] < 0.02) fl = 0.009;
375
- fl = Math.max(fl, 0.024 * (0.8 + 0.2 * ruffK) * ruffW);
376
- if (r === 6) fl = mix(0.004, 0.012, smoothstep(0.08, 0.0, h[2]));
377
- // the throat under the jaw runs into the long fur of the neck ruff (no step at the region seam)
378
- if (r === 2) fl = mix(fl, 0.024, smoothstep(-0.05, -0.075, h[1]) * smoothstep(0.06, 0.0, h[2]));
379
- // back of the head blends into the neck
380
- if (r === 2 && h[2] < -0.07) {
381
- const b = smoothstep(-0.07, -0.11, h[2]);
382
- fl = mix(fl, 0.02, b);
383
- }
384
- // eye rims (black lid margins)
385
- for (const e of eyePrims) {
386
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
387
- if (Math.abs(de) < 0.0032) mark = Math.min(mark, Math.abs(de) - 0.0013);
388
- if (Math.abs(de) < 0.0011) { mat = MAT.DARK_SKIN; fl = 0; }
389
- else if (Math.abs(de) < 0.0032) fl = Math.min(fl, 0.001);
390
- }
391
- // nose leather
392
- const dn = SDFModel.dist(nosePrim, p[0], p[1], p[2]);
393
- if (dn < 0.0015 && h[2] > 0.138) { mat = MAT.NOSE; col = srgb(0x1a1616); fl = 0; }
394
- else if (dn < 0.0035 && h[2] > 0.13) { mark = Math.min(mark, dn - 0.0022); fl = Math.min(fl, 0.0015); }
395
- // lips: head vertices near the jaw surface, jaw vertices near the head surface (black lip line)
396
- // (the lip line ends at the mouth corner, z ~0.025: behind it the cheek's jowl wraps the jaw's side and
397
- // the line along their seam ran back under the cheek fur as a dark crease. Skin facing out (the jowl's
398
- // side and underside, the jaw's outer side) is never the mouth's lining: it shows when the mouth opens,
399
- // and painted as the lining it drew a dark pocket behind the mouth corner; buried in the other surface it
400
- // keeps fur only as long as it stays inside it)
401
- const lipZ = smoothstep(0.012, 0.03, h[2]);
402
- const nOut = n[0] * Math.sign(h[0] || 1);
403
- if (partOf[v] !== 2) {
404
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
405
- const lining = h[2] > 0.025 ? nOut < 0.55 || n[1] > 0.3 : nOut < 0.2 && n[1] > -0.5;
406
- if (dj < -0.0004 && lining) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
407
- else if (dj < -0.0004) fl = Math.min(fl, Math.max(0, -dj - 0.0015));
408
- else if (dj < 0.006) { mark = Math.min(mark, mix(1, dj - 0.0018, lipZ)); fl = Math.min(fl, mix(fl, mix(0, 0.003, smoothstep(0.002, 0.006, dj)), lipZ)); }
409
- } else {
410
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
411
- const lining = nOut < 0.45 || n[1] > 0.35;
412
- if (dh < 0.0 && lining) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
413
- else if (dh < 0.0) fl = Math.min(fl, Math.max(0, -dh - 0.0015));
414
- else if (dh < 0.005) { mark = Math.min(mark, mix(1, dh - 0.0015, lipZ)); fl = Math.min(fl, mix(fl, mix(0, 0.003, smoothstep(0.0015, 0.005, dh)), lipZ)); }
415
- }
416
- // teeth
417
- for (const c of caninePrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0008) { mat = MAT.KERATIN; col = srgb(0xe8e0cc); fl = 0; mark = 1; }
418
- under = 0.3;
419
- }
420
- // kits: woolly and uniform
421
- if (juv > 0 && mat === MAT.FUR) {
422
- fl = mix(fl, Math.max(fl * 0.8, r === 2 || r === 6 ? 0.008 : 0.016), juv);
423
- ag *= 1 - 0.8 * juv;
424
- }
425
- // low-frequency colour variation
426
- const cvh = fbm3(p[0] * 6 + 5 + nOff, p[1] * 6, p[2] * 6, 2) - 0.5;
427
- if (mat === MAT.FUR) col = [col[0] * (1 + 0.14 * nz + 0.1 * cvh + 0.06 * nz2), col[1] * (1 + 0.12 * nz + 0.06 * nz2), col[2] * (1 + 0.1 * nz - 0.08 * cvh + 0.06 * nz2)];
428
- markSDF[v] = mark;
429
- furLen[v] = fl;
430
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
431
- surf[v * 4 + 2] = mat === MAT.FUR ? clamp(ag * (0.85 + 0.3 * nz2), 0, 1) : 0;
432
- surf[v * 4 + 3] = mat === MAT.FUR ? under : 0;
433
- if (mat === MAT.KERATIN) surf[v * 4] = 0.7;
434
- if (mat === MAT.NOSE) surf[v * 4] = 0.55;
435
- }
436
-
437
- // smooth fur length so the shells do not step (bare skin stays locked)
438
- smoothField(furLen, weights.neighbors, 3, (v) => tint[v * 4 + 3] > 0);
439
-
440
- void rng;
441
- const pattern = new Float32Array(nV).fill(1);
442
- const patternColor = new Float32Array(nV * 4);
443
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, region, ventral, tailT };
444
- }
@@ -1,120 +0,0 @@
1
- // Red fox (Vulpes vulpes). A small, light, long-legged canid with a narrow pointed muzzle, big black-
2
- // backed ears, vertical-slit pupils, black stockings, a white bib and a huge white-tipped brush; trots
3
- // in a near single-file line and hunts small prey with a high arcing "mousing" pounce.
4
- // Colour morphs are variants (createAnimal('fox', { variant: 'silver' })) or come from the seed.
5
- import { foxRig, HEAD_O } from './rig.js';
6
- import { sculptFox, eyeOf } from './sculpt.js';
7
- import { foxRegions } from './regions.js';
8
- import { foxCoat, palette } from './coat.js';
9
- import { motion } from './motion.js';
10
- import { rng } from '../../core/math/vec.js';
11
-
12
- // [id, name, seeded frequency]
13
- const VARIANTS = [
14
- ['red', 'Red fox', 0.7],
15
- ['cross', 'Cross fox', 0.12],
16
- ['silver', 'Silver fox', 0.08],
17
- ['urban', 'Urban fox (dark winter coat)', 0.1],
18
- ];
19
-
20
- export default {
21
- id: 'fox',
22
- name: 'Red fox',
23
- latin: 'Vulpes vulpes',
24
- group: 'paws',
25
- plan: 'quadruped',
26
- covering: 'fur',
27
- variants: VARIANTS.map(([id, name]) => ({ id, name, latin: 'Vulpes vulpes' })),
28
-
29
- // Seeded individual. Morph (red 70 %, cross 12 %, silver 8 %, urban 10 %, or opts.variant); dog
30
- // foxes ~15-20 % heavier (+4 % withers height), broader head and muzzle, fuller ruff; vixens finer;
31
- // kits (age 'juvenile', ~10-12 weeks): about half size, big domed head, short muzzle, very big ears,
32
- // short legs and brush, woolly sandy coat, blue-grey eyes. Individuals vary in leg length, body
33
- // length, muzzle, ears, brush, the extent of the white bib, stockings and tail tip, colour warmth.
34
- variation(R, o) {
35
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
36
- const age = o.age || 'adult';
37
- const juv = age === 'juvenile' ? 1 : 0;
38
- const male = sex === 'male';
39
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
40
- // morph from its own hashed stream (consecutive seeds must not correlate)
41
- const M = rng(Math.imul((o.seed ?? 1) + 0x3c6ef372, 0x9e3779b1) ^ 0x5be0cd19);
42
- for (let i = 0; i < 3; i++) M();
43
- let variant = o.variant && VARIANTS.some((v) => v[0] === o.variant) ? o.variant : null;
44
- if (!variant) {
45
- let x = M();
46
- variant = 'red';
47
- for (const [id, , p] of VARIANTS) { if (x < p) { variant = id; break; } x -= p; }
48
- } else M();
49
- const size = (male ? 1.04 : 0.96) * (1 + 0.04 * g()) * (juv ? 0.55 : 1);
50
- const legs = 1 + 0.03 * g() - 0.1 * juv;
51
- const head = (male ? 1.03 : 0.98) * (1 + 0.02 * g());
52
- const coatSeed = Math.floor(R() * 1e6);
53
- // (every draw is made whatever the morph, so that a morph never changes the individual: the silver
54
- // fox's stockings ignore theirs)
55
- const morphed = (v, x) => (variant === 'silver' ? v : x);
56
- return {
57
- sex, age, size, variant, juv,
58
- muzzle: (male ? 1.02 : 0.99) * (1 + 0.04 * g()) * (juv ? 0.7 : 1),
59
- headW: (male ? 1.05 : 0.98) * (1 + 0.025 * g()) * (juv ? 1.06 : 1),
60
- ear: (1 + 0.05 * g()) * (juv ? 1.12 : 1),
61
- eyeK: juv ? 1.18 : 1,
62
- tail: (1 + 0.05 * g()) * (juv ? 0.72 : 1),
63
- tailBrush: (1 + 0.07 * g()) * (juv ? 0.75 : 1),
64
- ruff: (male ? 1.1 : 0.9) + 0.12 * g() - 0.2 * juv,
65
- slim: (male ? 1.04 : 0.97) * (1 + 0.03 * g()) * (juv ? 1.08 : 1),
66
- boneK: juv ? 1.12 : 1,
67
- pawK: juv ? 1.15 : 1,
68
- bib: 1 + 0.25 * g(),
69
- stockings: morphed(1, Math.max(0.2, 1 + 0.45 * g())),
70
- tailTip: M() < 0.08 && variant === 'red' ? 0 : 0.75 + 0.5 * M(), // 70-90 % of foxes have a white tip
71
- tear: 0.7 + 0.5 * M(),
72
- coatWarmth: 1.3 * g(),
73
- coatLightness: 0.16 * g(),
74
- coatSeed,
75
- minThick: 0.0022,
76
- warps: [
77
- { type: 'legs', k: legs, top: 0.25 },
78
- { type: 'length', k: 1 + 0.03 * g() - 0.05 * juv, z0: -0.19, z1: 0.24 },
79
- { type: 'scaleAbout', c: HEAD_O, k: head * (juv ? 1.3 : 1), r0: 0.05, r1: 0.12 },
80
- // big kit paws
81
- ...(juv ? [[0.028, 0.2], [-0.028, 0.2], [0.033, -0.2], [-0.033, -0.2]].map(([x, z]) => ({ type: 'scaleAbout', c: [x, 0.015, z], k: 1.18, r0: 0.018, r1: 0.04 })) : []),
82
- ],
83
- };
84
- },
85
- rig: (params) => foxRig(params),
86
- sculpt: (m, rig, params) => sculptFox(m, rig, params),
87
- regions: foxRegions,
88
- coat: foxCoat,
89
- eyeSpecs: (params) => {
90
- const look = eyeLookOf(params);
91
- return [1, -1].map((side) => ({ side, spec: eyeOf(params), headOrigin: HEAD_O, bone: 'head', look }));
92
- },
93
- motion,
94
- render: {
95
- markColor: [0.012, 0.01, 0.009],
96
- strandDensity: 1400,
97
- clumpDensity: 0.9,
98
- },
99
- };
100
-
101
- // Vertical slit pupil (elliptical in bright light, dilating to round in the dark), amber to yellow-
102
- // orange iris (#c8943c with a darker ring #7a4a1c), no visible sclera, black lid margins.
103
- export const EYE_LOOK = {
104
- pupil: 'slit',
105
- lids: 'mammal',
106
- iris: { inner: [0.5, 0.26, 0.04], mid: [0.62, 0.32, 0.06], hi: [0.8, 0.5, 0.14], outer: [0.19, 0.065, 0.012] },
107
- sclera: { color: [0.2, 0.15, 0.11], visible: false },
108
- lidColor: [0.45, 0.14, 0.035], // (per individual: eyeLookOf)
109
- pupilSize: [0.12, 0.75],
110
- };
111
- // kits: blue-grey eyes (turn amber at ~4-5 months)
112
- export const KIT_EYE = {
113
- ...EYE_LOOK,
114
- iris: { inner: [0.1, 0.13, 0.17], mid: [0.2, 0.25, 0.32], hi: [0.36, 0.42, 0.5], outer: [0.06, 0.07, 0.09] },
115
- };
116
- // the lids are furred in the individual's face colour (the shader darkens the margin itself), so a
117
- // blink, the curled sleep and death show a closed, furred lid with a dark line, not a black bead
118
- function eyeLookOf(params) {
119
- return { ...(params.juv ? KIT_EYE : EYE_LOOK), lidColor: palette(params).lid };
120
- }
@@ -1,81 +0,0 @@
1
- // Red fox motion data for the quadruped engine (schema: src/core/motion/quadruped.js).
2
- // Numbers are for the reference individual (params.size = 1, withers 0.40 m, ~6 kg); the engine scales
3
- // them by dynamic similarity for kits and big dog foxes.
4
- //
5
- // Gaits (Elbroch 2003, Heglund & Taylor 1988, Alexander & Jayes 1983): the trot
6
- // is the fox's travelling gait. Lateral-sequence walk (0.4-1.0 m/s, 1.2-1.8 Hz, duty 0.60-0.70) used
7
- // when hunting and investigating; trot (1.0-3.0 m/s, typically ~2 m/s, 2.4-3.2 Hz, duty 0.38-0.48,
8
- // hind of each diagonal ~3 % first) in a near single-file line (trail straddle ~7.5 cm: `width`
9
- // narrows the stance per row); transverse lope (3-6 m/s, right lead, 3.0-3.5 Hz); rotary gallop
10
- // (LH-RH-RF-LF, 3.5-4.2 Hz, duty 0.20-0.28) with strong spinal flexion up to ~13 m/s (48 km/h).
11
- // Trot -> gallop 1.53 M^0.24 = 2.4 m/s by scaling; foxes hold the trot to ~3 m/s.
12
- import { foxHooks } from './behaviour.js';
13
-
14
- const G = (name, v, f, D, off, o) => ({ name, v, f, D, off, ...o });
15
-
16
- export const motion = {
17
- // fox behaviour: mousing pounce, listening (ear swivels, head tilt), tail as a counterweight,
18
- // curled sleep with the brush over the nose, the upright sit with the tail round the forefeet
19
- hooks: foxHooks,
20
- maxSpeed: 13,
21
- accel: [5, 9],
22
- decel: 9,
23
- turnRate: 2.8,
24
- latAccelMax: 11,
25
- gears: { walk: 0.8, trot: 2.0, canter: 3.6, gallop: 8 },
26
- gallopBlend: [3.4, 6.5],
27
- gaits: [
28
- G('walk', 0.0, 1.1, 0.72, [0.25, 0.75, 0.0, 0.5], { liftF: 0.032, liftH: 0.027, bobF: 0.003, bobH: 0.003, flex: 0.0, lat: 0.05, fold: 1.0, heel: 35, tail: 0, drop: 0.0, width: 0.85 }),
29
- G('walk', 0.85, 1.55, 0.63, [0.25, 0.75, 0.0, 0.5], { liftF: 0.04, liftH: 0.032, bobF: 0.005, bobH: 0.005, flex: 0.0, lat: 0.05, fold: 1.2, heel: 42, tail: 0.12, drop: 0.0, width: 0.7 }),
30
- G('trot', 1.2, 2.45, 0.47, [0.53, 0.03, 0.0, 0.5], { liftF: 0.053, liftH: 0.045, bobF: 0.009, bobH: 0.009, flex: 0.02, lat: 0.015, fold: 1.45, heel: 50, tail: 0.3, drop: 0.003, width: 0.5 }),
31
- G('trot', 2.8, 2.95, 0.39, [0.53, 0.03, 0.0, 0.5], { liftF: 0.064, liftH: 0.054, bobF: 0.012, bobH: 0.012, flex: 0.03, lat: 0.01, fold: 1.6, heel: 58, tail: 0.4, drop: 0.006, width: 0.48 }),
32
- G('canter', 3.6, 3.1, 0.35, [0.3, 0.45, 0.0, 0.3], { liftF: 0.085, liftH: 0.075, bobF: 0.012, bobH: 0.018, flex: 0.2, lat: 0, fold: 1.8, heel: 62, tail: 0.6, drop: 0.01, width: 0.75 }),
33
- G('gallop', 6, 3.55, 0.27, [0.52, 0.42, 0.0, 0.1], { liftF: 0.105, liftH: 0.095, bobF: 0.016, bobH: 0.024, flex: 0.34, lat: 0, fold: 1.9, heel: 68, tail: 0.8, drop: 0.016, width: 1 }),
34
- G('gallop', 12, 4.1, 0.21, [0.52, 0.42, 0.0, 0.1], { liftF: 0.125, liftH: 0.115, bobF: 0.016, bobH: 0.026, flex: 0.44, lat: 0, fold: 2.0, heel: 72, tail: 0.9, drop: 0.026, width: 1 }),
35
- ],
36
- feet: {
37
- front: { type: 'digitigrade', curl: 55 },
38
- hind: { type: 'digitigrade', curl: 65 },
39
- },
40
- stance: { width: 1, gallopWidth: 0.6, sprawl: 0, crouch: 0, scuff: [11, 18, 0] },
41
- // torso cross-sections (bind, reference size): centre height, half height, half width (incl. coat)
42
- body: {
43
- chest: { y: 0.305, hh: 0.112, hw: 0.068 },
44
- pelvis: { y: 0.31, hh: 0.11, hw: 0.076 }, // incl. the folded thighs a lying fox rests on
45
- back: 0.036,
46
- },
47
- head: {
48
- // [speed, raise (m), reach (m), pitch (rad, + = nose down)]: standing, the head is held up at or
49
- // above the withers, muzzle near level; the trot lowers it toward the back line with the muzzle
50
- // pointing down and ahead; the gallop stretches it forward
51
- carriage: [[0, 0.09, -0.02, -0.02], [0.8, -0.008, 0.01, 0.16], [2.0, -0.045, 0.026, 0.3], [4.2, -0.04, 0.032, 0.25], [8, -0.037, 0.043, 0.18]],
52
- stab: [[0, 0], [0.7, 0.22], [1.5, 0.3], [4, 0.34], [8, 0.6]],
53
- neckBend: 42, neckPivot: 0.6, yaw: 1.3, pitchUp: 0.6, pitchDown: 0.75,
54
- },
55
- tail: {
56
- // [speed, pitch per segment from horizontal (deg), base -> tip]: at rest a shallow downward curve
57
- // with the tip just off the ground; held level with a slight droop in the trot (the brush floats
58
- // behind, never curled over the back); straight out behind as a counterweight in the gallop
59
- carriage: [
60
- [0.0, [-24, -32, -38, -41, -43, -43, -42, -40, -37, -33]],
61
- [0.8, [-14, -20, -24, -27, -28, -28, -27, -25, -22, -19]],
62
- [2.0, [-8, -10, -12, -13, -13, -12, -11, -9, -7, -5]],
63
- [4.2, [-5, -7, -7, -7, -6, -5, -4, -3, -2, -1]],
64
- [8, [-3, -4, -4, -3, -2, -1, 0, 1, 1, 2]],
65
- ],
66
- stiffness: 0.85, sway: 6, flick: 0, wag: 0, swish: 0, curl: 0,
67
- balance: 0.65, // the brush is a counterweight and rudder in turns and speed changes
68
- radius: [0.05, 0.052], // (the brush: coat included)
69
- },
70
- ears: { rest: [0, 0], mobility: 1.2, speedFlatten: 0.5, twitch: 1.2, prick: 0.5 },
71
- breath: { rate: 0.35, amp: 0.012, pant: true, pantRate: 4.0, pantAmp: 0.05, heatSpeed: 5 },
72
- actions: {
73
- attack: { style: 'pounce' }, // (the mousing pounce is a species action, behaviour.js)
74
- eat: { style: 'tear', dropF: 0.3, dropH: 0.1, ahead: 0.75 },
75
- drink: { dropF: 0.34, dropH: 0.12, ahead: 0.75 },
76
- jump: { height: 0.6, distance: 2.0 },
77
- sit: { dropH: 0.9 },
78
- lie: {},
79
- sleep: { style: 'curl' },
80
- },
81
- };