@bitefoundry/retrieviewer 0.0.3 → 0.0.5

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 (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. 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
- }