@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,529 +0,0 @@
1
- // The sheep's coat: the fleece (crimped wool staples clumped into locks: bright weathered tips on the
2
- // lock crests, dark crevices between the locks, dusty backline, stained britch and belly, matte),
3
- // the clean short-haired face, ears and legs (white, or black in the Suffolk and the black morph),
4
- // pink skin under white hair (nose, lips, inner ears), keratin horns with transverse growth ridges and
5
- // cloven hooves, hair flow following the hair tracts.
6
- // Works in the reference space (see rig.js); seeded variation picks the breed colours.
7
- import { HEAD_O, HZ, HY } from './rig.js';
8
- import { neckS, EYE_PATCH } from './regions.js';
9
- import { hornPath, WOOL_TAGS, stapleFall, lockRadius } from './sculpt.js';
10
- import { SDFModel } from '../../core/sdf/sdf.js';
11
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
12
- import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
13
-
14
- // sRGB swatches sampled from the reference photos.
15
- // tip = weathered lock crests, clean = the fleece's inside / fresh wool, crevice = the gaps between
16
- // locks, dust = backline dirt, stain = belly / britch, hair = face and legs, ear = inner ear skin,
17
- // nose = nose leather, hoof.
18
- export const COATS = {
19
- white: { tip: 0xdccdae, clean: 0xeee7d5, crevice: 0xa39886, dust: 0xcbc1ad, stain: 0xd3c6a6, hair: 0xf0efec, hairShade: 0xdedbd5, muzzle: 0xdccfca, skin: 0xcfb6b0, nose: 0xa88c8a, hoof: 0x9c8a74, earIn: 0xdcaca2, lid: 0xd8c4bf },
20
- suffolk: { tip: 0xe9e0cb, clean: 0xf5f0e4, crevice: 0xa29479, dust: 0xcdbfa5, stain: 0xd5c39e, hair: 0x4e4a4b, hairShade: 0x3c3839, muzzle: 0x534d4c, skin: 0x2a2626, nose: 0x1a1818, hoof: 0x2a2725, earIn: 0x2e2a2a, lid: 0x4a4542, black: 1 },
21
- merino: { tip: 0xd8cbad, clean: 0xf0e9d7, crevice: 0x9c8f7a, dust: 0xc4b89f, stain: 0xcdbd98, hair: 0xebe8e2, hairShade: 0xd6d1c8, muzzle: 0xe6d2cc, skin: 0xe8b4a8, nose: 0xc99a92, hoof: 0xc0ad92, earIn: 0xe8b0a4, lid: 0xe0c8c0 },
22
- black: { tip: 0x5e4c3e, clean: 0x2f2723, crevice: 0x211a16, dust: 0x5e4c3e, stain: 0x4a3c31, hair: 0x463c3a, hairShade: 0x332b29, muzzle: 0x4e4440, skin: 0x2a2424, nose: 0x1c1817, hoof: 0x2a2624, earIn: 0x3a302c, lid: 0x3e3532, black: 1 },
23
- brown: { tip: 0x7e654c, clean: 0x4a3a2e, crevice: 0x30251d, dust: 0x7a624a, stain: 0x5c4838, hair: 0x4a3a30, hairShade: 0x3a2e26, muzzle: 0x55463c, skin: 0x3a3030, nose: 0x2a2222, hoof: 0x302a26, earIn: 0x4a3a34, lid: 0x5a4a40, black: 1 },
24
- texel: { tip: 0xc9ab80, clean: 0xe7dbc4, crevice: 0x8f7352, dust: 0xa88a64, stain: 0xa98a60, hair: 0xe8e6e2, hairShade: 0xd2cec6, muzzle: 0xdcd0cc, skin: 0xe0a698, nose: 0x2a2424, hoof: 0x9c8a74, earIn: 0xe0a698, lid: 0xd8c4bf },
25
- };
26
-
27
- function palette(p) {
28
- let C = COATS[p.coat] || COATS.white;
29
- // most white-faced sheep have a dark, pigmented nose (threequarter1, face2, side2, extra/Lundy); some a
30
- // pale pink-grey one (front1): three in four dark, by the individual's coat seed (no extra draw from the
31
- // variation stream, which would reshuffle every seed)
32
- if (C === COATS.white && (Math.imul(p.coatSeed || 0, 0x9e3779b1) >>> 28) % 4 !== 0) C = { ...C, nose: 0x5a5251, muzzle: 0xd2cac6 };
33
- const k = p.coatShade || 0, l = p.coatLightness || 0;
34
- const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.05 * k + l), c[1] * (1 + l), c[2] * (1 - 0.06 * k + l)]; };
35
- const out = {};
36
- for (const [key, v] of Object.entries(C)) out[key] = typeof v === 'number' && key !== 'black' ? tw(v) : v;
37
- out.horn = srgb(p.hornColor || 0x9c8462); out.hornDark = srgb(0x6e5a42);
38
- return out;
39
- }
40
-
41
- const HOOF_TAGS = new Set(['hoof', 'heelbulb', 'dewclaw']);
42
-
43
- export function sheepCoat(ctx) {
44
- const { model, rig, pos, nrm, regionOf, regionNames, weights, nV, params, lists } = ctx;
45
- const { BONES } = rig;
46
- const J = rig.J;
47
- const R = rng(7331 + (params.coatSeed || 0));
48
- const COL = palette(params);
49
- const lamb = params.age === 'juvenile';
50
- const merino = params.variant === 'merino';
51
- const staple = params.staple ?? 0.014; // shell length on the fleece (crimped staple tips)
52
- const woolD = params.wool ?? 0.07; // sculpted wool depth
53
- const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
54
- const AX_LEN = weights.axialLengths;
55
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
56
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
57
- const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
58
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
59
- const jawPrims = model.forPart('jaw');
60
- const headPrims = model.forPart('body').filter((p) => p.bone === 'head' && !p.carve);
61
- const preorb = model.prims.filter((q) => q.tag === 'preorbital');
62
- const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril' || p.tag === 'philtrum');
63
- const fleeceBase = model.prims.filter((p) => WOOL_TAGS.has(p.tag) && p.tag !== 'lock' && p.tag !== 'lockleg' && !p.carve);
64
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
65
- const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(b.name));
66
- const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
67
- const partName = (v) => regionNames[regionOf[v]];
68
- const horns = [hornPath(params, 1), hornPath(params, -1)];
69
- const rl = lockRadius(woolD, merino); // lock radius (sculpt.js)
70
-
71
- // --- nearest primitive tag per vertex
72
- const tagOf = new Array(nV);
73
- for (let v = 0; v < nV; v++) {
74
- const L = lists[v], p = P(v);
75
- let best = 1e9, t = '';
76
- for (const pr of L) {
77
- if (pr.carve) continue;
78
- const d = SDFModel.dist(pr, p[0], p[1], p[2]);
79
- if (d < best) { best = d; t = pr.tag; }
80
- }
81
- tagOf[v] = t;
82
- }
83
-
84
- // --- regions: 0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 horn
85
- const region = new Uint8Array(nV);
86
- const legness = new Float32Array(nV);
87
- const legBone = new Int16Array(nV).fill(-1);
88
- for (let v = 0; v < nV; v++) {
89
- const bn = BONES[dominant[v]].name, p = P(v), pn = partName(v);
90
- let lb = -1, lw = 0;
91
- for (let k = 0; k < 4; k++) {
92
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
93
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
94
- }
95
- legBone[v] = lb;
96
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
97
- if (pn === 'horn') region[v] = 7;
98
- else if (pn === 'jaw') region[v] = 6;
99
- else if (bn.startsWith('ear')) region[v] = 5;
100
- else if (bn.startsWith('tail') && axialS[v] > sTailBase + 0.01) region[v] = 4;
101
- else if (axialS[v] < sOcc + 0.01 || (pn === 'head' && neckS(p[0], p[1], p[2]) > 0.02)) region[v] = 2;
102
- else if (axialS[v] < sNeckBase) region[v] = 1;
103
- else region[v] = 0;
104
- if (region[v] >= 2) legness[v] = 0;
105
- }
106
-
107
- // --- wool: which vertices grow fleece, and where they sit on a lock (crest 1 .. crevice 0)
108
- const wool = new Float32Array(nV);
109
- const crest = new Float32Array(nV);
110
- const fleeceOn = fleeceBase.length > 0;
111
- // away from any lock (a smooth stretch of fleece) the surface is a crest, not a crevice
112
- const lockPrims = model.prims.filter((q) => q.tag === 'lock' || q.tag === 'lockleg');
113
- const lockNear = (p) => {
114
- let dm = 1e9;
115
- for (const q of lockPrims) { const dx = p[0] - q.P[0], dy = p[1] - q.P[1], dz = p[2] - q.P[2]; const d = dx * dx + dy * dy + dz * dz; if (d < dm) dm = d; }
116
- return smoothstep(1.4 * rl, 2.4 * rl, Math.sqrt(dm)) * 4;
117
- };
118
- for (let v = 0; v < nV; v++) {
119
- const t = tagOf[v];
120
- const r = region[v];
121
- if (r === 7 || r === 5 || r === 6) continue;
122
- if (fleeceOn) {
123
- const p = P(v);
124
- const d = SDFModel.evalList(fleeceBase, p[0], p[1], p[2]);
125
- // (skin that pokes through a thin fleece between wool primitives is wool too)
126
- if (!WOOL_TAGS.has(t) && !(r <= 1 && d < 0.012 && legness[v] < 0.5)) continue;
127
- wool[v] = 1;
128
- crest[v] = smoothstep(-0.08 * rl, 0.3 * rl, d + 0.09 * rl * lockNear(p));
129
- } else if (r <= 1 || r === 4) {
130
- // shorn / lamb: the whole body skin is the (short) fleece, except the lower legs
131
- const p = P(v), L = legness[v];
132
- const lb = legBone[v];
133
- const kneeY = lb >= 0 && isFront[lb] ? 0.3 : 0.26;
134
- wool[v] = 1 - L * smoothstep(kneeY + 0.03, kneeY - 0.03, p[1]);
135
- crest[v] = 0.5 + 0.5 * (fbm3(p[0] * 40, p[1] * 40, p[2] * 40, 2) - 0.5) * 2;
136
- }
137
- }
138
- // the face's edge: the wool starts on a line across the head's axis just behind the ears and
139
- // the angle of the jaw (side2, side3_texel, threequarter1); the poll and the throat behind it are wool, the face
140
- // in front of it short white hair. (The throat and the back of the poll carried the face's hair back into the
141
- // collar: the white face ran on into the neck with no edge.)
142
- // (not on a black face: the Suffolk's black runs back over a clean poll and down the throat, its own edge)
143
- for (let v = 0; v < (COL.black ? 0 : nV); v++) {
144
- const r = region[v];
145
- if (r !== 1 && r !== 2) continue;
146
- const p = P(v), h = HL(p);
147
- const rag = 0.006 * (vnoise3(p[0] * 120, p[1] * 120, p[2] * 120) - 0.5);
148
- // (and below the lower edge of the mandible: the throat runs down and forward in the head's frame)
149
- const yJaw = -0.14 + 0.387 * (h[2] + 0.03);
150
- const wl = Math.max(smoothstep(-0.061, -0.071, h[2] + rag), smoothstep(-0.006, -0.016, h[1] - yJaw + rag));
151
- if (wl <= wool[v]) continue;
152
- if (wool[v] <= 0) crest[v] = 0.5 + 0.5 * (fbm3(p[0] * 40, p[1] * 40, p[2] * 40, 2) - 0.5) * 2;
153
- wool[v] = wl;
154
- }
155
- // the wool line is a little ragged
156
- smoothField(wool, weights.neighbors, 1);
157
- // (and on the head, where the poll's topknot meets the face, a soft edge rather than a line)
158
- // (only round the topknot: smoothed at the collar it lit white tufts at the ear roots of a Suffolk)
159
- smoothField(wool, weights.neighbors, 8, (v) => region[v] !== 2 || !lists[v].some((q) => q.tag === 'woolpoll'));
160
-
161
- // --- comb (hair flow). Fleece: the staples fall down the sides and back along the top, each lock
162
- // crimped its own way (a disordered flow at the lock scale); hair: nose -> tail, down the legs,
163
- // outward from the forehead whorl on the face
164
- const comb = new Float32Array(nV * 3);
165
- const whorl = [0, 0.03, -0.02]; // forehead whorl, head-local
166
- // the face's hair flow before its fine jitter: out from the forehead whorl, down the face to the nose
167
- const faceFlow = (p) => {
168
- const h = HL(p);
169
- const rel = [h[0] - whorl[0], (h[1] - whorl[1]) * 0.4, h[2] - whorl[2] + 0.05];
170
- let d = norm(add(mul(HY, rel[1] - 0.03), add(mul(HZ, rel[2]), [rel[0], 0, 0])));
171
- if (h[2] < -0.05) d = norm(add(d, [0, -0.6, -0.8]));
172
- return d;
173
- };
174
- for (let v = 0; v < nV; v++) {
175
- const p = P(v), n = N(v), r = region[v];
176
- const bone = BONES[dominant[v]];
177
- let d;
178
- if (r === 5) d = norm(sub(bone.tail, bone.head));
179
- else if (r === 7) d = [0, 1, 0];
180
- else if (r === 2 || r === 6) {
181
- d = faceFlow(p);
182
- // (the hairs lie a little unevenly: breaks the sheen of a short coat into hair texture)
183
- const f = 220;
184
- d = norm(add(d, [vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 7, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 3, p[1] * f + 11, p[2] * f + 5) - 0.5].map((x) => x * 0.9)));
185
- } else if (r === 4) {
186
- d = norm(sub(bone.tail, bone.head));
187
- } else {
188
- const seg = weights.segT[v * 2] | 0;
189
- const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
190
- d = norm(sub(b, a));
191
- if (r === 0) d = norm(add(d, [0, -0.8 * Math.abs(n[0]), 0]));
192
- if (r === 1) d = norm(add(d, [0, -0.6, 0.1]));
193
- const L = legness[v];
194
- if (L > 0 && legBone[v] >= 0) {
195
- const lbn = BONES[legBone[v]];
196
- let dl = norm(sub(lbn.tail, lbn.head));
197
- if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
198
- else dl = norm(add(dl, [0, -0.3, 0]));
199
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
200
- }
201
- }
202
- if (wool[v] <= 0.5 && legness[v] > 0.3) {
203
- // short leg hair lies a little unevenly too (matte, not a sheen)
204
- const f = 220;
205
- d = norm(add(d, [vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 7, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 3, p[1] * f + 11, p[2] * f + 5) - 0.5].map((x) => x * 0.8)));
206
- }
207
- if (wool[v] > 0.5) {
208
- // the staples' fibres fall together: one continuous field over the whole fleece (sculpt.js
209
- // stapleFall: away from the head and down). (An earlier field turned it a little lock by lock: the fur
210
- // shading follows the hair direction, and under a low sun the fleece lit in grey blotches the size of
211
- // a few locks; the locks now show as the crevices between them, below.) The fine crimp is added after
212
- // smoothing.
213
- d = stapleFall(n, p, J);
214
- }
215
- let t = sub(d, mul(n, dot(d, n)));
216
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
217
- t = norm(t);
218
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
219
- }
220
-
221
- // fleece flow: smoothed, then the fine crimp (the fibres wave every few millimetres)
222
- {
223
- const cx = new Float32Array(nV), cy = new Float32Array(nV), cz = new Float32Array(nV);
224
- for (let v = 0; v < nV; v++) { cx[v] = comb[v * 3]; cy[v] = comb[v * 3 + 1]; cz[v] = comb[v * 3 + 2]; }
225
- const keep = (v) => wool[v] <= 0.5;
226
- for (const f of [cx, cy, cz]) smoothField(f, weights.neighbors, 3, keep);
227
- for (let v = 0; v < nV; v++) {
228
- if (wool[v] <= 0.5) continue;
229
- const p = P(v), n = N(v), f = 140;
230
- let d = add([cx[v], cy[v], cz[v]], mul([vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 17, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 5, p[1] * f + 31, p[2] * f + 9) - 0.5], 0.3));
231
- let t = sub(d, mul(n, dot(d, n)));
232
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
233
- t = norm(t);
234
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
235
- }
236
- }
237
-
238
- // --- per-vertex fields
239
- const pattern = new Float32Array(nV).fill(0.02);
240
- const patternColor = new Float32Array(nV * 4);
241
- const markSDF = new Float32Array(nV).fill(1);
242
- const furLen = new Float32Array(nV);
243
- const tint = new Float32Array(nV * 4);
244
- const surf = new Float32Array(nV * 4);
245
- const hairLen = lamb ? 0.007 : 0.0045;
246
-
247
- for (let v = 0; v < nV; v++) {
248
- const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
249
- // (short hair: matte; 0.45 gave the black face a plastic sheen)
250
- let col, mat = MAT.FUR, fl = hairLen, mark = 1, gloss = 0.22, undercoat = 0;
251
- const lb = legBone[v];
252
- const lbName = lb >= 0 ? BONES[lb].name : '';
253
- const cv = fbm3(p[0] * 6, p[1] * 6, p[2] * 6, 3) - 0.5;
254
- if (r === 0 || r === 1 || r === 4) {
255
- // clean hair (legs below the wool, and wherever the fleece stops)
256
- col = mix3(COL.hair, COL.hairShade, smoothstep(0.2, -0.6, n[1]) * 0.6);
257
- const L = legness[v];
258
- // (neck skin showing between the wool primitives behind the head is the fleece's colour, not the
259
- // face's: on a Suffolk it showed as black tufts in the white throat wool)
260
- if (fleeceOn && r === 1 && L < 0.3) col = mix3(col, COL.clean, 1 - L / 0.3);
261
- fl = mix(0.006, 0.004, L);
262
- if (tag === 'udder' || tag === 'teat') {
263
- mat = MAT.SKIN; fl = tag === 'teat' ? 0 : 0.002; gloss = 0.35;
264
- col = COL.black ? mix3(COL.skin, [0.1, 0.08, 0.08], 0.3) : COL.skin;
265
- }
266
- if (tag === 'scrotum') { fl = 0.005; col = mix3(col, COL.skin, 0.25); }
267
- if (/hoof|paw/.test(lbName) && HOOF_TAGS.has(tag)) {
268
- const Cj = J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side];
269
- if (tag === 'dewclaw' || p[1] < Cj[1] + 0.006 + 0.35 * (p[2] - Cj[2])) {
270
- mat = MAT.KERATIN; fl = 0; gloss = 0.45;
271
- col = COL.hoof;
272
- // unpigmented hooves are often striped (a dark vertical band in a pale wall)
273
- if (!COL.black) col = mix3(col, [0.06, 0.05, 0.045], smoothstep(0.3, 0.7, vnoise3(p[0] * 300, 3.1, p[2] * 90)) * 0.7 * (params.hoofStripe ?? 0.5));
274
- if (tag === 'heelbulb') col = mix3(col, [0.1, 0.08, 0.07], 0.4);
275
- }
276
- }
277
- } else if (r === 5) {
278
- // ears: short hair outside, sparse inside with the skin showing
279
- const earP = model.prims.find((q) => q.tag === 'ear' && (q.bone === (p[0] > 0 ? 'earL' : 'earR')));
280
- const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
281
- const front = dot(n, faceDir);
282
- col = COL.hair;
283
- fl = 0.0035;
284
- // (the inside of the cup: on the carved hollow, whichever way its walls face)
285
- const cup = model.prims.find((q) => q.tag === 'earinner' && q.bone === earP.bone);
286
- const dc = cup ? Math.abs(SDFModel.dist(cup, p[0], p[1], p[2])) : 1;
287
- // (pink skin inside the cup; the face of the leaf round it and its rim keep their white hair, so the
288
- // ear seen from the side or above reads as a white leaf, not a pink saucer)
289
- const inner = Math.max(0.2 * smoothstep(0.35, 0.8, front), smoothstep(0.0025, 0.0005, dc) * smoothstep(-0.3, 0.0, front));
290
- fl = mix(fl, 0.002, inner); col = mix3(col, COL.earIn, 0.55 * inner);
291
- } else if (r === 7) {
292
- // horn: pale ridged keratin, darker toward the base, transverse growth ridges (the "corrugations")
293
- const S = p[0] >= 0 ? 0 : 1, Hh = horns[S];
294
- let bt = 0, bd = 1e9;
295
- for (let i = 0; i + 1 < Hh.length; i++) {
296
- const a = Hh[i].p, b = Hh[i + 1].p, ab = sub(b, a);
297
- const u = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1);
298
- const q = add(a, mul(ab, u)), dd = len(sub(p, q));
299
- if (dd < bd) { bd = dd; bt = Hh[i].t + (Hh[i + 1].t - Hh[i].t) * u; }
300
- }
301
- mat = MAT.KERATIN; fl = 0; gloss = 0.3;
302
- col = mix3(COL.hornDark, COL.horn, smoothstep(0.0, 0.35, bt));
303
- col = mix3(col, mul(COL.horn, 1.12), smoothstep(0.75, 1, bt) * 0.5);
304
- const hlen = (params.horns?.turns || 1) * 0.5 * bt;
305
- const ring = 0.5 + 0.5 * Math.sin(hlen * 2 * Math.PI / 0.012);
306
- col = mul(col, 1 - 0.14 * ring * (1 - smoothstep(0.8, 1, bt)));
307
- col = mul(col, 0.9 + 0.2 * vnoise3(p[0] * 300, p[1] * 60, p[2] * 300));
308
- } else {
309
- // head & jaw: short, fine, matte hair (5-7 mm on the skull and cheeks, 3 mm on the muzzle and round
310
- // the eyes); a black face is charcoal rather than ink, greyer round the muzzle and the eyes (face1,
311
- // extra/Tete_de_brebie_Suffolk), with a fine, uneven tone that keeps it from reading as plastic
312
- const h = HL(p);
313
- col = mix3(COL.hair, COL.hairShade, smoothstep(0.1, -0.7, dot(n, HY)) * 0.5);
314
- fl = (lamb ? 0.0065 : 0.0055) * (1 + 0.25 * smoothstep(0.02, -0.06, h[2]));
315
- const muz = smoothstep(0.12, 0.175, h[2]);
316
- col = mix3(col, COL.muzzle, muz * 0.7);
317
- fl = mix(fl, 0.0018, muz);
318
- const eyeD = EYE_PATCH.centres.reduce((a, c) => Math.min(a, Math.hypot(p[0] - c[0], p[1] - c[1], p[2] - c[2])), 1);
319
- col = mix3(col, COL.muzzle, smoothstep(0.024, 0.012, eyeD) * 0.2);
320
- fl = Math.min(fl, mix(0.003, fl, smoothstep(0.012, 0.022, eyeD)));
321
- const tn = vnoise3(p[0] * 160, p[1] * 160, p[2] * 160) - 0.5;
322
- col = mul(col, 1 + 0.16 * tn + 0.08 * (fbm3(p[0] * 30, p[1] * 30, p[2] * 30, 2) - 0.5));
323
- // the short coat's texture: streaks drawn out along the hair flow (partings and tufts lying together a
324
- // few millimetres wide, a centimetre or two long), so the face reads as short hair, not a smooth skin
325
- // (without it: smooth white or black rubber, hard to tell from the fleece)
326
- {
327
- const c = faceFlow(p), al = dot(p, c);
328
- const q = [p[0] - c[0] * al * 0.85, p[1] - c[1] * al * 0.85, p[2] - c[2] * al * 0.85];
329
- const st = vnoise3(q[0] * 230 + 5, q[1] * 230, q[2] * 230 + 9) - 0.5;
330
- col = mul(col, 1 + (COL.black ? 0.5 : 0.22) * st * smoothstep(0.0025, 0.004, fl));
331
- }
332
- // a white face is a little mottled grey where the skin shows through the short hair (threequarter1)
333
- if (!COL.black) col = mul(col, 1 - 0.1 * smoothstep(0.55, 0.8, fbm3(p[0] * 22 + 3, p[1] * 22, p[2] * 22 + 1, 2)));
334
- const nearEye = eyeD < 0.019;
335
- // the lids: a thin dark margin (the lash line) and round it a few millimetres of pale, almost bare lid
336
- // skin (threequarter1, face2, extra/Lundy: the eye reads as an almond in a pale rim, not a black bead)
337
- // (only on the lids themselves, the skin over the globe (r + lid 16.2 mm from its centre): the almond's cut
338
- // runs on outward and passes close to the brow, and the margin drawn there was a dark frown line over the eye)
339
- const onLid = smoothstep(0.0205, 0.0178, eyeD);
340
- for (const e of nearEye ? eyePrims : []) {
341
- const de = Math.abs(SDFModel.dist(e, p[0], p[1], p[2]));
342
- if (de < 0.0026 && eyeD < 0.0178) mark = Math.min(mark, de - 0.0011);
343
- if (de < 0.001 && eyeD < 0.0178) { mat = MAT.DARK_SKIN; fl = 0; }
344
- else if (de < 0.005) {
345
- const lid = smoothstep(0.005, 0.0025, de) * onLid;
346
- col = mix3(col, COL.lid, lid);
347
- fl = Math.min(fl, mix(0.0018, 0.0006, lid));
348
- }
349
- }
350
- // nostrils: comma slits in a deeper shade of the nose, soft-edged; the philtrum a fine groove (front1: on a
351
- // pink nose the slits are a deeper pink-mauve, the philtrum a pink line). The front of the nose is meshed at
352
- // ~1.2 mm (regions.js NOSE_PATCH), so the carved slits come out clean. (the dark leather
353
- // material and a black mark inside them, on the 4-5 mm face cells, read as ragged black blotches with a
354
- // glint, and the philtrum as a black T)
355
- const paleNose = COL.nose[0] + COL.nose[1] + COL.nose[2] > 0.45;
356
- for (const e of nostrilPrims) {
357
- const dn = SDFModel.dist(e, p[0], p[1], p[2]);
358
- if (e.tag === 'philtrum') continue;
359
- if (dn < 0.005) {
360
- // (how deep the skin here lies inside the nose's uncarved form: the slit's walls; the carve's own distance
361
- // is loose for so thin an ellipsoid)
362
- const depth = -SDFModel.evalList(headPrims, p[0], p[1], p[2]), inside = smoothstep(0.0002, 0.0012, depth);
363
- const ins = Math.max(inside, 0.5 * smoothstep(0.002, 0, dn));
364
- col = mix3(col, paleNose ? [COL.nose[0] * 0.36, COL.nose[1] * 0.22, COL.nose[2] * 0.26] : mul(COL.nose, COL.black ? 0.4 : 0.28), ins);
365
- // (bare inside the slit: the 1 mm hair's lit tips drew pale streaks there; the fine hair runs on to its
366
- // edge: a bare band round it showed the fur base, shaded as if under the coat, as a ragged dark ring)
367
- fl = Math.min(fl, 0.0012 * (1 - smoothstep(0.25, 0.6, inside)));
368
- }
369
- }
370
- // the philtrum: a fine line from between the nostrils down to the lip (front1, face2, threequarter1)
371
- {
372
- const pl = smoothstep(0.0012, 0.0004, Math.abs(h[0])) * smoothstep(-0.026, -0.03, h[1]) * smoothstep(0.19, 0.196, h[2]) * smoothstep(0.1, 0.4, dot(n, HZ));
373
- if (pl > 0) { col = mix3(col, mul(COL.nose, paleNose ? 0.75 : 0.7), pl * 0.85); fl = Math.min(fl, mix(fl, 0.0004, pl)); }
374
- }
375
- // the small bare nose pad between the slit nostrils (soft-edged; covered in very fine hair: only
376
- // the nostrils are bare)
377
- // (a rounded pad over the nose front between the nostrils, running down the philtrum, its edge soft and
378
- // mottled into the muzzle hair: a square block of colour read as a rubber cap)
379
- const fwdN = smoothstep(0.15, 0.5, dot(n, HZ)) * smoothstep(0.17, 0.182, h[2]);
380
- const dpad = Math.hypot(h[0] / 0.021, (h[1] + 0.017) / 0.014);
381
- const phil = smoothstep(0.006, 0.003, Math.abs(h[0])) * smoothstep(-0.04, -0.033, h[1]) * smoothstep(-0.012, -0.02, h[1]);
382
- const mott = vnoise3(p[0] * 260, p[1] * 260, p[2] * 260);
383
- const pad = fwdN * Math.max(smoothstep(1.2 + 0.25 * mott, 0.7, dpad), phil);
384
- if (pad > 0) { col = mix3(col, COL.nose, pad); fl = mix(fl, Math.min(fl, 0.0012), pad); }
385
- // (a dark nose dusts the muzzle round it grey)
386
- if (!COL.black) col = mix3(col, mix3(COL.nose, col, 0.5), muz * smoothstep(1.9, 1.1, dpad) * smoothstep(0.35, 0.75, mott) * 0.5 * (1 - pad));
387
- // the lips meet in a dark line; the pink inside of the mouth only where it faces into the mouth
388
- const into = dot(n, HY);
389
- // (the line only along the front of the mouth, fading out 4 cm back from the nose: drawn round the rear
390
- // of the lower jaw it turned down at the corner, a sharply down-turned mouth (side2: the mouth is short,
391
- // its corner level or a little up))
392
- const lipF = smoothstep(0.15, 0.165, h[2]);
393
- // (the lips overlap: what one lip hides is lip skin, so a chewing jaw that slides aside shows lip,
394
- // not a hole; the line where they meet is a dark mark)
395
- // (a dark line where the lips meet, as in the photos; not a pink gash)
396
- // (on a white face the hidden lip skin is a pale pink-grey: a dark lip surface showed as a dark gap wherever
397
- // chewing parted the lips)
398
- const lipCol = COL.black ? mul(COL.muzzle, 0.7) : mix3(COL.muzzle, mix3(COL.skin, COL.nose, 0.3), 0.3);
399
- if (regionNames[regionOf[v]] !== 'jaw') {
400
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
401
- if (h[2] > 0.135 && dj < 0.004) {
402
- mark = Math.min(mark, Math.abs(dj) - 0.0014 * lipF);
403
- fl = Math.min(fl, 0.0012);
404
- if (dj < 0) { col = lipCol; fl = 0; if (dj < -0.004 && into < -0.6) mat = MAT.MOUTH; }
405
- }
406
- } else {
407
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
408
- if (dh < 0.0035) {
409
- mark = Math.min(mark, Math.abs(dh) - 0.0014 * lipF);
410
- fl = Math.min(fl, 0.0012);
411
- if (dh < 0) { col = lipCol; fl = 0; if (dh < -0.004 && into > 0.6) mat = MAT.MOUTH; }
412
- }
413
- }
414
- // the preorbital gland: a bare dark slit
415
- const dp = preorb.reduce((a, q) => Math.min(a, SDFModel.dist(q, p[0], p[1], p[2])), 1);
416
- if (dp < 0.003) { col = mul(col, COL.black ? 0.8 : 0.75); fl = Math.min(fl, 0.002); } // (0.55 drew a dark stroke like a frowning brow)
417
- }
418
-
419
- // ---- fleece
420
- const w = wool[v];
421
- if (w > 0.01 && mat === MAT.FUR) {
422
- const c = crest[v];
423
- const up = clamp(n[1], -1, 1);
424
- // clean wool inside, weathered tips on the lock crests, the crevices between the locks only a little
425
- // darker (the shells' own depth shading darkens them further): an even, creamy fleece
426
- let wc = mix3(mix3(COL.crevice, COL.clean, 0.62), COL.clean, smoothstep(0.0, 0.45, c));
427
- wc = mix3(wc, COL.tip, smoothstep(0.3, 1, c) * (COL.black ? 0.35 : 0.55));
428
- // a faint dusting along the top line, light stains on the belly and the britch
429
- wc = mix3(wc, COL.dust, smoothstep(0.6, 0.95, up) * 0.22 * smoothstep(0.2, 0.8, c));
430
- const low = smoothstep(0.34, 0.2, p[1]) * (1 - legness[v] * 0.5);
431
- const britch = smoothstep(-0.35, -0.5, p[2]) * smoothstep(0.55, 0.35, p[1]);
432
- wc = mix3(wc, COL.stain, Math.max(low, britch) * 0.3);
433
- // (no large-scale grey blotches: the tone varies a little from staple to staple only)
434
- const sn = vnoise3(p[0] * 45, p[1] * 45, p[2] * 45) - 0.5;
435
- wc = [wc[0] * (1 + 0.035 * cv + 0.05 * sn), wc[1] * (1 + 0.03 * cv + 0.05 * sn), wc[2] * (1 + 0.025 * cv + 0.045 * sn)];
436
- col = mix3(col, wc, w);
437
- // staple tips: crests longer, crevices short (the gaps open between the locks)
438
- // (shorter close behind the head, where the poll and the throat fold the wool in every pose)
439
- const nearHead = smoothstep(-0.09, -0.01, neckS(p[0], p[1], p[2]));
440
- const flW = staple * mix(0.75, 1.1, c) * (1 - 0.3 * nearHead);
441
- fl = mix(fl, flW, w);
442
- gloss = mix(gloss, merino ? 0.22 : 0.15, w);
443
- undercoat = w * 0.08; // (greyer roots greyed the cream)
444
- } else if (mat === MAT.FUR) {
445
- const tn = r <= 1 ? vnoise3(p[0] * 160, p[1] * 160, p[2] * 160) - 0.5 : 0;
446
- col = [col[0] * (1 + 0.08 * cv + 0.14 * tn), col[1] * (1 + 0.07 * cv + 0.14 * tn), col[2] * (1 + 0.06 * cv + 0.14 * tn)];
447
- }
448
- markSDF[v] = mark;
449
- furLen[v] = fl;
450
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
451
- surf[v * 4] = gloss;
452
- surf[v * 4 + 3] = undercoat;
453
- }
454
- smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.FUR);
455
-
456
- // --- the head's form in the coat: the hollows of the face (round the eye, the groove
457
- // between cheek and muzzle, under the jaw, the throat behind it and the face's edge where the wool collar
458
- // stands over it) lie in the shadow of the forms round them, and on a black face the short hair of the
459
- // ridges (nasal bone, brow, cheek, the edge of the jaw) catches the sky's light. Lit only by the sky (the
460
- // shade side, a low sun), a uniform black face read as one featureless shape and a white face ran into the
461
- // neck with no break at the jaw. Occlusion from the sculpt (SDF samples 5-30 mm out along the normal), the
462
- // ridges from samples inside (a form thinner than ~1-2 cm), the sheen from the hair facing up and forward.
463
- {
464
- const occl = model.prims.filter((q) => !q.carve && q.part !== 'horn' && (q.bone === 'head' || q.bone === 'jaw' || q.bone === 'neck2' || (q.bone === 'neck1' && /neck|throat/.test(q.tag))));
465
- // (at the mouth each lip is shaded by its own part's form only: the other lip's occlusion drew the crease
466
- // where they meet as a dark gap at the corner of the mouth, an open mouth)
467
- const occlUpper = occl.filter((q) => q.part !== 'jaw'), occlLower = occl.filter((q) => q.part === 'jaw');
468
- const skull = model.prims.filter((q) => !q.carve && (q.bone === 'head' || q.bone === 'jaw') && q.part !== 'horn');
469
- const dark = !!COL.black;
470
- for (let v = 0; v < nV; v++) {
471
- const r = region[v];
472
- if ((r !== 2 && r !== 6) || tint[v * 4 + 3] !== MAT.FUR || wool[v] > 0.5) continue;
473
- const p = P(v), n = N(v);
474
- const hm = HL(p), atMouth = hm[2] > 0.1 && hm[1] < -0.025;
475
- const ol = r === 6 ? occlLower : atMouth ? occlUpper : occl;
476
- let occ = 0;
477
- for (const [i, d] of [[1, 0.005], [2, 0.011], [3, 0.019], [4, 0.03]]) {
478
- const sd = SDFModel.evalList(ol, p[0] + n[0] * d, p[1] + n[1] * d, p[2] + n[2] * d);
479
- occ += (Math.max(0, d - sd) / d) / i;
480
- }
481
- let ridge = 0;
482
- for (const d of [0.007, 0.014]) {
483
- const si = SDFModel.evalList(skull, p[0] - n[0] * d, p[1] - n[1] * d, p[2] - n[2] * d);
484
- ridge += 0.5 * clamp((d + si) / d, 0, 1);
485
- }
486
- const h = HL(p);
487
- const nh = [n[0], dot(n, HY), dot(n, HZ)];
488
- // (the forehead and the nasal bone face the sky; the underside of the jaw does not)
489
- const sky = smoothstep(-0.2, 0.8, nh[1] * 0.8 + nh[2] * 0.35);
490
- const ao = clamp(1 - (dark ? 0.75 : 0.6) * occ, dark ? 0.45 : 0.6, 1);
491
- const lift = dark ? 1 + 0.9 * smoothstep(0.08, 0.5, ridge) + 0.9 * sky : 1 + 0.04 * smoothstep(0.1, 0.5, ridge);
492
- // (the muzzle and nose keep their own tone)
493
- const k = ao * mix(lift, 1, smoothstep(0.165, 0.185, h[2]));
494
- tint[v * 4] *= k; tint[v * 4 + 1] *= k; tint[v * 4 + 2] *= k;
495
- }
496
- }
497
-
498
- // --- the locks: thin, broken dark crevices between the staple blocks (a crisp pattern line along the
499
- // border of each lock's cell; the cells are the sculpted locks, stretched down the fall), so the fleece
500
- // reads as locks by its texture rather than by big soft bumps (whose shading under a low sun made the
501
- // grey blotches)
502
- if (lockPrims.length) {
503
- // (on a dark fleece the gaps between the locks hardly show: the tips are the lighter part)
504
- // (COL.black marks a black face; the fleece is dark only in the black / brown morph)
505
- const darkFleece = COL.clean[0] + COL.clean[1] + COL.clean[2] < 0.6;
506
- const crev = darkFleece ? mix3(COL.crevice, COL.clean, 0.4) : mix3(COL.crevice, COL.clean, 0.15);
507
- for (let v = 0; v < nV; v++) {
508
- if (wool[v] < 0.5 || tint[v * 4 + 3] !== MAT.FUR) continue;
509
- const p = P(v);
510
- let d1 = 1e9, d2 = 1e9;
511
- for (const q of lists[v]) {
512
- if (q.tag !== 'lock' && q.tag !== 'lockleg') continue;
513
- const dx = p[0] - q.P[0], dy = p[1] - q.P[1], dz = p[2] - q.P[2];
514
- const al = dx * q.P[9] + dy * q.P[10] + dz * q.P[11];
515
- const dd = Math.sqrt(Math.max(0, dx * dx + dy * dy + dz * dz - 0.55 * al * al));
516
- if (dd < d1) { d2 = d1; d1 = dd; } else if (dd < d2) d2 = dd;
517
- }
518
- if (d2 > 1e8) continue;
519
- const brk = vnoise3(p[0] * 30 + 3, p[1] * 30, p[2] * 30 + 7);
520
- const wdt = 0.0011 + 0.0012 * vnoise3(p[0] * 90, p[1] * 90 + 5, p[2] * 90);
521
- pattern[v] = clamp((d2 - d1) * 0.5 - wdt, -0.02, 0.02);
522
- const k = wool[v] * smoothstep(0.3, 0.6, brk) * (merino ? 0.4 : darkFleece ? 0.35 : 0.5);
523
- patternColor[v * 4] = crev[0]; patternColor[v * 4 + 1] = crev[1]; patternColor[v * 4 + 2] = crev[2]; patternColor[v * 4 + 3] = k;
524
- }
525
- }
526
- if (globalThis.__sheepDbg) globalThis.__sheepDbg.push({ region, wool, tint, furLen, pos, HL, nV });
527
- void R; void mix;
528
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: {}, region, wool };
529
- }