@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,495 +0,0 @@
1
- // The dog's coat, painted per vertex in reference space, by variant and colour.
2
- //
3
- // shepherd black-and-tan saddle (FCI 166): black saddle from the withers over the back and into the
4
- // tail top, reaching down the flanks; black mask on the muzzle and round the eyes, dark
5
- // forehead and ear backs; rich tan legs, cheeks, chest and breeches; pale cream inner
6
- // thighs and belly. Sable: grey-tan hair with black tips (the shader's agouti band) over
7
- // the whole body, darker along the back, a dark mask. Solid black (rare). Medium double
8
- // coat: ruff, breeches and a bushy tail are longer.
9
- // retriever solid yellow (cream to fox red), black or chocolate (FCI 122); a little darker on the
10
- // ears and along the back, paler on the belly and the feathering in yellows. Short, dense,
11
- // glossy coat; liver nose and lids in chocolates.
12
- // terrier white ground with crisp patches (pattern SDF): a tan head mask over the eyes and ears split
13
- // by a white blaze, and in tricolours black body patches (saddle spot, tail root) with a tan
14
- // rim. Smooth short coat.
15
- // Pups: softer, fluffier, a little paler; shepherd pups are darker (sooty) until the tan comes in.
16
- import { HEAD_O, MUZZLE_Z0, HS } from './rig.js';
17
- import { neckS, NECK_CUT } from './regions.js';
18
- import { harmonizeSeamWeights } from '../../core/build/seams.js';
19
- import { SDFModel } from '../../core/sdf/sdf.js';
20
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
21
- import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
22
-
23
- // sRGB swatches, neutral-light albedo
24
- const PAL = {
25
- blackTan: {
26
- saddle: 0x221b1a, tan: 0xb57a45, tanLight: 0xcb9d6c, cream: 0xd9c2a0, mask: 0x1c1615, headTop: 0x3c2a22,
27
- earBack: 0x1d1715, earInner: 0xa07e5c, tailTop: 0x201a19, tailUnder: 0xc39a6c, paw: 0xc99a66,
28
- ag: { back: 0.15, side: 0.1, head: 0.1, leg: 0, tail: 0.1 }, gloss: 0.25,
29
- },
30
- sable: {
31
- saddle: 0x4a3d33, tan: 0x9c7f5e, tanLight: 0xb89c78, cream: 0xcfbd9f, mask: 0x241d19, headTop: 0x5a4a3c,
32
- earBack: 0x2a221e, earInner: 0x9c8468, tailTop: 0x3a302a, tailUnder: 0xb49e80, paw: 0xb39776,
33
- ag: { back: 0.85, side: 0.6, head: 0.45, leg: 0.15, tail: 0.75 }, gloss: 0.15,
34
- },
35
- black: {
36
- saddle: 0x1b1817, tan: 0x221e1c, tanLight: 0x2a2522, cream: 0x332d29, mask: 0x171413, headTop: 0x1d1a18,
37
- earBack: 0x171413, earInner: 0x2a2522, tailTop: 0x1a1716, tailUnder: 0x26221f, paw: 0x221e1c,
38
- ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.35,
39
- },
40
- yellow: {
41
- saddle: 0xcfae78, tan: 0xd9bb88, tanLight: 0xe2c89c, cream: 0xebdcbc, mask: 0xd6b886, headTop: 0xd2b27e,
42
- earBack: 0xbf9a64, earInner: 0xdcc094, tailTop: 0xcfae78, tailUnder: 0xe2caa0, paw: 0xe0c79c,
43
- ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.3,
44
- },
45
- choc: {
46
- saddle: 0x5c3626, tan: 0x683f2b, tanLight: 0x734a34, cream: 0x7c5340, mask: 0x5f3828, headTop: 0x5d3727,
47
- earBack: 0x4f2e20, earInner: 0x6c4634, tailTop: 0x5c3626, tailUnder: 0x6e4533, paw: 0x6c4533,
48
- ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.45, liver: 0x6e4234,
49
- },
50
- labBlack: {
51
- saddle: 0x19191b, tan: 0x1c1c1f, tanLight: 0x202023, cream: 0x232327, mask: 0x1a1a1d, headTop: 0x19191b,
52
- earBack: 0x161618, earInner: 0x202023, tailTop: 0x19191b, tailUnder: 0x1e1e21, paw: 0x1e1e21,
53
- ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.6,
54
- },
55
- terrier: {
56
- saddle: 0xf0ede5, tan: 0xefece4, tanLight: 0xf2efe8, cream: 0xf4f1ea, mask: 0xefebe2, headTop: 0xeeeae1,
57
- earBack: 0xefebe2, earInner: 0xe8dcd2, tailTop: 0xf0ede5, tailUnder: 0xf2efe8, paw: 0xeeebe3,
58
- ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.2,
59
- patchTan: 0xa35f2c, patchBlack: 0x1d1b1d,
60
- },
61
- };
62
- const PAL_OF = { shepherd: { blackTan: 'blackTan', sable: 'sable', black: 'black' }, retriever: { yellow: 'yellow', black: 'labBlack', chocolate: 'choc' }, terrier: { white: 'terrier', tan: 'terrier', tricolour: 'terrier' } };
63
-
64
- function palette(p) {
65
- const key = (PAL_OF[p.variant] || PAL_OF.shepherd)[p.colour] || 'blackTan';
66
- const base = PAL[key];
67
- const k = p.coatWarmth || 0, l = p.coatLightness || 0, juv = p.juv || 0;
68
- const out = { ag: base.ag, gloss: base.gloss, key };
69
- for (const [name, hex] of Object.entries(base)) {
70
- if (name === 'ag' || name === 'gloss') continue;
71
- let c = srgb(hex);
72
- // warmth / lightness within the colour's range (yellow labs: cream to fox red)
73
- const kk = key === 'yellow' ? 2.2 * k : key === 'terrier' && !/patch/.test(name) ? 0.15 * k : k;
74
- c = [c[0] * (1 + 0.1 * kk + l), c[1] * (1 + 0.01 * kk + l), c[2] * (1 - 0.14 * kk + l)];
75
- // shepherd pups: sooty until the tan comes in
76
- if (juv > 0 && key === 'blackTan' && /tan|cream|paw/.test(name)) c = mix3(c, srgb(0x6a5040), 0.45 * juv);
77
- out[name] = c;
78
- }
79
- return out;
80
- }
81
-
82
- export function dogCoat(ctx) {
83
- const { model, rig, pos, nrm, regionOf: partOf, weights, nV, params } = ctx;
84
- const { BONES, AXIAL } = rig;
85
- const AX_LEN = weights.axialLengths;
86
- const C = palette(params);
87
- const AG = C.ag;
88
- const key = C.key;
89
- const juv = params.juv || 0;
90
- const hw = params.headW || 1, mz = params.muzzle || 1;
91
- const variant = params.variant || 'shepherd';
92
- const saddleExt = params.saddle ?? 0; // how far the saddle reaches down the flanks (-1..1)
93
- const maskK = params.mask ?? 1;
94
- const FK = params.furK || 1; // fur length factor of the variant
95
- const nOff = (params.coatSeed || 0) % 997;
96
- const R = rng(9173 + (params.coatSeed || 0));
97
- const { dominant, axialS, skinIndex, skinWeight, limbMember, limbWhich } = weights;
98
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
99
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
100
- const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8], sTip = AX_LEN[AX_LEN.length - 1];
101
- const tailLen = sTip - sTailBase;
102
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
103
- const nosePrim = model.prims.find((p) => p.tag === 'nose');
104
- const toothPrims = model.prims.filter((p) => p.tag === 'canine' || p.tag === 'lowercanine');
105
- const jawPrims = model.forPart('jaw').filter((p) => p.tag !== 'lowercanine');
106
- const bodyPrims = model.forPart('body');
107
- const headPrims = bodyPrims.filter((p) => p.bone === 'head' && !p.carve && p.tag !== 'canine');
108
- const clawPrims = model.prims.filter((p) => p.tag === 'claw');
109
- const padPrims = model.prims.filter((p) => p.tag === 'pad' || p.tag === 'carpalpad');
110
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|femur|tibia|metatarsus|hpaw)/.test(b.name));
111
- const isPaw = BONES.map((b) => /^(fpaw|hpaw)/.test(b.name));
112
- const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw)/.test(b.name));
113
- const regionNames = ctx.regionNames;
114
- const jawRegion = regionNames.indexOf('jaw'), tongueRegion = regionNames.indexOf('tongue');
115
- const liver = key === 'choc';
116
- const noseCol = liver ? srgb(PAL.choc.liver) : key === 'yellow' && (params.noseFade || 0) > 0.5 ? srgb(0x5a4238) : srgb(0x161414);
117
- const headLocal = (p) => {
118
- const x = (p[0] - HEAD_O[0]) / hw / HS, y = (p[1] - HEAD_O[1]) / HS;
119
- let z = (p[2] - HEAD_O[2]) / HS;
120
- if (z > MUZZLE_Z0) z = MUZZLE_Z0 + (z - MUZZLE_Z0) / mz;
121
- return [x, y, z];
122
- };
123
-
124
- // --- regions per vertex (0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 tongue) and limb blend
125
- const region = new Uint8Array(nV);
126
- const legness = new Float32Array(nV);
127
- const legBone = new Int16Array(nV).fill(-1);
128
- const junction = new Float32Array(nV);
129
- const tailT = new Float32Array(nV);
130
- for (let v = 0; v < nV; v++) {
131
- const bn = BONES[dominant[v]].name;
132
- const p = P(v);
133
- let lb = -1, lw = 0;
134
- for (let k = 0; k < 4; k++) {
135
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
136
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
137
- }
138
- legBone[v] = lb;
139
- const m = limbMember[v];
140
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, m) : 0;
141
- junction[v] = lb >= 0 ? 4 * m * (1 - m) : 0;
142
- if (partOf[v] === tongueRegion) region[v] = 7;
143
- else if (partOf[v] === jawRegion) region[v] = 6;
144
- else if (bn.startsWith('ear')) region[v] = 5;
145
- else if (axialS[v] > sTailBase + 0.015 && bn.startsWith('tail')) { region[v] = 4; tailT[v] = clamp((axialS[v] - sTailBase) / tailLen, 0, 1); }
146
- else if (axialS[v] < sOcc + 0.01 || (partOf[v] === 1 && neckS(p[0], p[1], p[2]) > 0.02)) region[v] = 2;
147
- else if (axialS[v] < sNeckBase) region[v] = 1;
148
- else region[v] = 0;
149
- if (region[v] >= 2) { legness[v] = 0; junction[v] = 0; }
150
- }
151
- const underFactor = (v) => {
152
- const n = N(v), p = P(v), li = limbWhich[v];
153
- if (li < 0 || junction[v] <= 0) return 0;
154
- if (li <= 1) return junction[v] * Math.max(smoothstep(0.15, -0.55, n[2]) * smoothstep(0.42, 0.32, p[1]), smoothstep(0.2, -0.6, n[1]));
155
- return junction[v] * smoothstep(0.35, -0.45, n[1] + 0.35 * Math.abs(n[2]));
156
- };
157
-
158
- // --- ventral (pale underside) factor
159
- const ventral = new Float32Array(nV);
160
- for (let v = 0; v < nV; v++) {
161
- const p = P(v), n = N(v), r = region[v];
162
- let w = 0;
163
- if (r === 0) {
164
- w = smoothstep(-0.05, -0.65, n[1]);
165
- // chest front between the forelegs
166
- if (p[2] > 0.2) w = Math.max(w, smoothstep(0.46, 0.36, p[1]) * smoothstep(0.07, 0.03, Math.abs(p[0])) * smoothstep(-0.3, 0.4, n[2]));
167
- w *= smoothstep(0.5, 0.4, p[1]) * 0.55 + 0.45;
168
- } else if (r === 1) {
169
- w = smoothstep(0.1, -0.5, n[1]) * smoothstep(-0.4, 0.3, n[2]) + smoothstep(0.52, 0.44, p[1]) * 0.6;
170
- } else if (r === 2 || r === 6) {
171
- const h = headLocal(p);
172
- w = smoothstep(-0.03, -0.06, h[1]) * smoothstep(-0.2, -0.7, n[1]) + (r === 6 ? 0.7 : 0);
173
- }
174
- const L = legness[v];
175
- if (L > 0) {
176
- const side = p[0] >= 0 ? 1 : -1;
177
- const inner = smoothstep(0.1, -0.6, n[0] * side);
178
- const wl = inner * 0.8 * smoothstep(0.06, 0.25, p[1]) + smoothstep(-0.3, -0.85, n[1]) * 0.5;
179
- w = mix(w, wl, L);
180
- }
181
- w = Math.max(w, underFactor(v) * 0.95);
182
- ventral[v] = clamp(w, 0, 1);
183
- }
184
-
185
- // --- comb (hair flow), bind space
186
- const comb = new Float32Array(nV * 3);
187
- for (let v = 0; v < nV; v++) {
188
- const p = P(v), n = N(v), r = region[v];
189
- let d;
190
- const bone = BONES[dominant[v]];
191
- if (r === 5 || r === 7) {
192
- d = norm(sub(bone.tail, bone.head));
193
- } else if (r === 2 || r === 6) {
194
- const h = headLocal(p);
195
- // out from the nose over the face, back over the skull and down the cheeks
196
- const fromNose = norm(sub(h, [0, -0.01, 0.16]));
197
- d = norm(add(fromNose, [0, -0.2, -0.5]));
198
- if (h[2] < 0.0 && Math.abs(h[0]) > 0.035) d = norm(add(d, [Math.sign(h[0]) * 0.4, -0.5, -0.8]));
199
- } else {
200
- // axial direction, blended across the joints (a per-segment direction steps at every joint:
201
- // visible as bands in the fur of a bent tail)
202
- const seg = weights.segT[v * 2] | 0, st = weights.segT[v * 2 + 1];
203
- const nP = AXIAL.points.length;
204
- const dirOf = (i) => { i = clamp(i, 0, nP - 2); return norm(sub(AXIAL.points[i + 1], AXIAL.points[i])); };
205
- const d0 = dirOf(seg);
206
- d = st < 0.5 ? norm(add(mul(norm(add(dirOf(seg - 1), d0)), 0.5 - st), mul(d0, 0.5 + st))) : norm(add(mul(d0, 1.5 - st), mul(norm(add(d0, dirOf(seg + 1))), st - 0.5)));
207
- if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
208
- if (r === 1) d = norm(add(d, [0, -0.5, 0]));
209
- const L = legness[v];
210
- if (L > 0 && legBone[v] >= 0) {
211
- const lbn = BONES[legBone[v]];
212
- let dl = norm(sub(lbn.tail, lbn.head));
213
- if (isPaw[legBone[v]]) dl = norm(add(dl, [0, 0, 0.4]));
214
- else dl = norm(add(dl, [0, -0.2, -0.3]));
215
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
216
- }
217
- }
218
- let t = sub(d, mul(n, dot(d, n)));
219
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
220
- t = norm(t);
221
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
222
- }
223
-
224
- // --- terrier patches (crisp pattern): tan head mask split by a white blaze, black body patches
225
- // (tricolour) with a tan rim; seeded layout. Distances are approximate metres.
226
- const patches = [];
227
- if (variant === 'terrier') {
228
- const J = rig.J;
229
- const tri = params.colour === 'tricolour';
230
- const nb = params.patches ?? 1;
231
- // body patches, in reference space: saddle spot over the back / flank, tail-root patch, head
232
- if (tri || R() < 0.35) {
233
- const z0 = mix(J.thoraxRear[2], J.lumbarMid[2], R()), side = R() < 0.5 ? 1 : -1;
234
- patches.push({ c: [0.03 * side * R(), J.thoraxRear[1] + 0.02, z0], r: [0.09 + 0.05 * R() * nb, 0.1 + 0.05 * R(), 0.07 + 0.06 * R() * nb], black: tri });
235
- }
236
- if (tri ? R() < 0.85 : R() < 0.4) patches.push({ c: [0, J.tailBase[1] + 0.01, J.tailBase[2] + 0.015], r: [0.07 + 0.02 * R(), 0.06, 0.06 + 0.03 * R()], black: tri });
237
- if (R() < 0.3 * nb) patches.push({ c: [0.06 * (R() < 0.5 ? 1 : -1), J.chestMid[1] - 0.02, mix(J.chestMid[2], J.thoraxRear[2], R())], r: [0.05, 0.06 + 0.03 * R(), 0.06 + 0.03 * R()], black: tri && R() < 0.6 });
238
- }
239
- const blaze = params.blaze ?? 0.5; // width of the white blaze (terrier)
240
- const maskSide = params.maskSide ?? 0; // asymmetric head mask (-1..1)
241
- const patchD = (p) => {
242
- let d = 1, black = 0, dB = 1;
243
- for (const q of patches) {
244
- const u = [(p[0] - q.c[0]) / q.r[0], (p[1] - q.c[1]) / q.r[1], (p[2] - q.c[2]) / q.r[2]];
245
- const nz = fbm3(p[0] * 18 + nOff, p[1] * 18, p[2] * 18, 2) - 0.5;
246
- const di = (Math.hypot(...u) - 1 + 0.5 * nz) * Math.min(...q.r) * 0.8;
247
- if (di < d) d = di;
248
- if (q.black && di < dB) { dB = di; }
249
- }
250
- // tricolour: black patches carry a thin tan rim (~5 mm)
251
- if (dB < 0.02) black = smoothstep(-0.002, -0.009, dB);
252
- return { d, black };
253
- };
254
-
255
- // --- per-vertex paint
256
- const furLen = new Float32Array(nV);
257
- const tint = new Float32Array(nV * 4);
258
- const markSDF = new Float32Array(nV).fill(1);
259
- const surf = new Float32Array(nV * 4);
260
- const pattern = new Float32Array(nV).fill(1);
261
- const patCol = new Float32Array(nV * 4);
262
- const g3 = (h, c, rr) => Math.exp(-(((Math.abs(h[0]) - c[0]) / rr[0]) ** 2 + ((h[1] - c[1]) / rr[1]) ** 2 + ((h[2] - c[2]) / rr[2]) ** 2));
263
- // (the tail direction blended by the skin weights: no colour step at the joints)
264
- const tailSegDorsal = (v) => {
265
- let d = [0, 0, 0];
266
- for (let k = 0; k < 4; k++) {
267
- const bone = BONES[skinIndex[v * 4 + k]], w = skinWeight[v * 4 + k];
268
- if (w > 0 && bone.name.startsWith('tail')) d = add(d, mul(norm(sub(bone.tail, bone.head)), w));
269
- }
270
- if (len(d) < 1e-6) { const bone = BONES[dominant[v]]; d = sub(bone.tail, bone.head); }
271
- d = norm(d);
272
- return norm([0, -d[2], d[1]]);
273
- };
274
- const tan = C.tan, black = C.saddle;
275
- const shep = key === 'blackTan' || key === 'sable';
276
-
277
- for (let v = 0; v < nV; v++) {
278
- const p = P(v), n = N(v), r = region[v];
279
- let col, fl = 0.02, ag = 0, mat = MAT.FUR, mark = 1, under = 0.35, gloss = C.gloss;
280
- const nz = fbm3(p[0] * 7 + nOff, p[1] * 7, p[2] * 7, 3) - 0.5;
281
- const nz2 = vnoise3(p[0] * 38 + nOff, p[1] * 38, p[2] * 38) - 0.5;
282
- if (r === 0 || r === 1) {
283
- const up = n[1];
284
- const vent = ventral[v];
285
- col = mix3(C.tan, C.tanLight, smoothstep(0.2, -0.4, up + 0.2 * nz) * 0.5);
286
- ag = mix(AG.side * 0.7, AG.side, smoothstep(-0.3, 0.3, up));
287
- if (r === 0) {
288
- // saddle: from the withers over the back into the tail top, down the flanks (ragged edge)
289
- const dors = smoothstep(-0.08, 0.12, up + 0.3 * nz + 0.22 * saddleExt + 0.2 * smoothstep(0.42, 0.55, p[1]));
290
- const along = smoothstep(0.3, 0.2, p[2]) * smoothstep(-0.36, -0.26, p[2]);
291
- if (key === 'blackTan') {
292
- col = mix3(col, black, dors * along);
293
- // the tan runs up in front of the thigh and on the shoulder
294
- } else {
295
- col = mix3(col, C.saddle, dors * along * (key === 'sable' ? 0.7 : 0.25));
296
- ag = mix(ag, AG.back, dors * along);
297
- }
298
- col = mix3(col, C.cream, vent);
299
- ag *= 1 - vent;
300
- fl = mix(0.02, 0.027, smoothstep(0.2, 0.85, up)) * (1 + 0.1 * nz2);
301
- fl = mix(fl, 0.02, smoothstep(0.3, 0.9, vent));
302
- // longer hair behind the thighs (breeches) and over the withers
303
- if (p[2] < -0.28) fl = Math.max(fl, 0.036 * smoothstep(-0.2, -0.7, n[2]));
304
- } else {
305
- // neck: dark nape (saddle into the neck top in black-and-tans), tan / cream throat
306
- const top = smoothstep(0.2, 0.85, up + 0.25 * nz);
307
- if (key === 'blackTan') col = mix3(col, mix3(black, C.headTop, 0.5), top * 0.9);
308
- else col = mix3(col, C.saddle, top * (key === 'sable' ? 0.55 : 0.2));
309
- ag = mix(AG.side * 0.7, AG.back, top);
310
- col = mix3(col, C.cream, smoothstep(0.35, 0.8, vent) * (shep ? 0.8 : 0.6));
311
- ag *= 1 - smoothstep(0.3, 0.7, vent);
312
- fl = mix(0.03, 0.026, vent) * (1 + 0.12 * nz2);
313
- }
314
- const L = legness[v];
315
- if (L > 0) {
316
- const lb = legBone[v];
317
- const front = lb >= 0 && isFront[lb];
318
- let lc = mix3(C.tan, C.cream, smoothstep(0.2, 0.8, ventral[v]) * 0.6);
319
- lc = mix3(lc, col, smoothstep(0.26, 0.42, p[1]) * 0.6);
320
- let lf = mix(0.008, 0.018, smoothstep(0.08, 0.34, p[1]));
321
- if (!front) lf = Math.max(lf, 0.04 * smoothstep(-0.1, -0.7, n[2]) * smoothstep(0.2, 0.38, p[1]));
322
- else lf = Math.max(lf, 0.02 * smoothstep(-0.2, -0.8, n[2]) * smoothstep(0.12, 0.24, p[1]));
323
- if (lb >= 0 && isPaw[lb]) { lf = 0.004; lc = C.paw; }
324
- col = mix3(col, lc, L);
325
- ag = mix(ag, AG.leg * smoothstep(0.12, 0.35, p[1]), L);
326
- fl = mix(fl, lf, L);
327
- }
328
- } else if (r === 4) {
329
- const t = tailT[v];
330
- const dors = dot(n, tailSegDorsal(v));
331
- const top = smoothstep(-0.35, 0.55, dors + 0.3 * nz);
332
- col = mix3(C.tailUnder, C.tailTop, top);
333
- ag = AG.tail * top;
334
- if (key === 'blackTan') col = mix3(col, black, smoothstep(0.75, 0.92, t + 0.05 * nz));
335
- if (key === 'sable') col = mix3(col, srgb(0x1c1716), smoothstep(0.82, 0.95, t));
336
- fl = variant === 'shepherd' ? mix(0.03, 0.045, smoothstep(0.05, 0.4, t)) * (1 - 0.25 * smoothstep(0.85, 1, t)) : mix(0.016, 0.02, t);
337
- under = 0.4;
338
- } else if (r === 5) {
339
- // ears: short fine hair, dark backs (shepherd), inner pinna paler with fine hair
340
- const facing = params.earType === 'prick' ? dot(n, norm([0.5 * Math.sign(p[0]), 0.05, 1])) : -n[0] * Math.sign(p[0]);
341
- col = C.earBack;
342
- if (facing > 0.3) { col = mix3(C.earBack, C.earInner, smoothstep(0.3, 0.75, facing)); fl = 0.006; }
343
- else fl = params.earType === 'drop' ? 0.009 : 0.006;
344
- ag = AG.head * 0.5;
345
- } else if (r === 7) {
346
- mat = MAT.MOUTH; col = srgb(0xc86070); fl = 0;
347
- } else {
348
- // ---------------- head & jaw
349
- const h = headLocal(p);
350
- col = mix3(C.headTop, C.tan, smoothstep(0.03, -0.02, h[1] + 0.1 * nz));
351
- ag = AG.head;
352
- // mask: muzzle and round the eyes (shepherds), darker forehead line
353
- const muzzle = smoothstep(0.035, 0.075, h[2]);
354
- const eyeRing = g3(h, [0.033, 0.022, 0.038], [0.02, 0.014, 0.02]);
355
- const cheek = g3(h, [0.04, -0.02, 0.0], [0.022, 0.022, 0.035]);
356
- const brow = g3(h, [0.025, 0.04, 0.036], [0.01, 0.007, 0.01]); // tan "eyebrow" spots
357
- if (key === 'blackTan' || key === 'sable') {
358
- let mk = clamp(Math.max(muzzle * (0.9 - 0.4 * smoothstep(-0.03, -0.06, h[1])), eyeRing * 0.9) * maskK, 0, 1);
359
- col = mix3(col, C.tan, cheek * 0.9);
360
- col = mix3(col, C.mask, mk);
361
- col = mix3(col, C.tanLight, brow * 0.6 * (1 - juv));
362
- ag *= 1 - 0.6 * mk;
363
- // pale chin / lower lips
364
- col = mix3(col, C.cream, smoothstep(-0.05, -0.07, h[1]) * smoothstep(-0.2, -0.6, n[1]) * 0.5);
365
- } else if (key === 'yellow') {
366
- col = mix3(col, C.tanLight, cheek * 0.4);
367
- col = mix3(col, C.cream, smoothstep(-0.04, -0.065, h[1]) * 0.4);
368
- }
369
- // fur length: sleek muzzle and face, longer at the back of the head
370
- fl = mix(0.009, 0.004, smoothstep(0.02, 0.1, h[2]));
371
- if (r === 6) fl = mix(0.005, 0.01, smoothstep(0.08, 0.0, h[2]));
372
- if (r === 2 && h[2] < -0.07) {
373
- const b = smoothstep(-0.07, -0.11, h[2]);
374
- fl = mix(fl, 0.03, b);
375
- }
376
- if (r === 2 && (params.ruff || 0) > 0.2) fl = Math.max(fl, 0.022 * params.ruff * smoothstep(-0.02, -0.06, h[2]) * smoothstep(0.035, 0.055, Math.abs(h[0])) * smoothstep(0.02, -0.01, h[1]));
377
- // eye rims (dark lid margins; liver in chocolates)
378
- for (const e of eyePrims) {
379
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
380
- if (Math.abs(de) < 0.0042) mark = Math.min(mark, Math.abs(de) - 0.0017);
381
- if (Math.abs(de) < 0.0015) { mat = liver ? MAT.SKIN : MAT.DARK_SKIN; col = srgb(0x4a2a22); fl = 0; }
382
- else if (Math.abs(de) < 0.0042) fl = Math.min(fl, 0.0015);
383
- }
384
- // nose leather (liver in chocolates: bare skin with the liver colour)
385
- const dn = SDFModel.dist(nosePrim, p[0], p[1], p[2]);
386
- if (dn < 0.0022 && h[2] > 0.135) { mat = liver || key === 'yellow' ? MAT.SKIN : MAT.NOSE; col = noseCol; fl = 0; gloss = 0.55; }
387
- else if (dn < 0.005 && h[2] > 0.13) { mark = Math.min(mark, dn - 0.0032); fl = Math.min(fl, 0.002); }
388
- // lips: head vertices near the jaw surface, jaw vertices near the head surface (dark lip line)
389
- if (r !== 6) {
390
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
391
- if (dj < -0.0005) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
392
- else if (dj < 0.0045 && h[2] > -0.02) { mark = Math.min(mark, dj - 0.0032); fl = Math.min(fl, 0.002); }
393
- } else {
394
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
395
- if (dh < 0.0) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
396
- else if (dh < 0.0035) { mark = Math.min(mark, dh - 0.0026); fl = Math.min(fl, 0.002); }
397
- }
398
- for (const c of toothPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0012) { mat = MAT.KERATIN; col = srgb(0xe8e0cc); fl = 0; mark = 1; }
399
- under = 0.25;
400
- }
401
- // claws and pads (keratin claws; dark pads)
402
- if (legness[v] > 0.5 && legBone[v] >= 0 && isPaw[legBone[v]]) {
403
- for (const c of clawPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0009) { mat = MAT.KERATIN; col = key === 'terrier' || key === 'yellow' ? srgb(0xb8a894) : srgb(0x2a2420); fl = 0; }
404
- if (mat === MAT.FUR) for (const c of padPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0012 && n[1] < -0.3) { mat = MAT.SKIN; col = liver ? srgb(0x5a3a30) : srgb(0x2a2524); fl = 0; gloss = 0.2; }
405
- }
406
- // terrier patches
407
- if (patches.length || variant === 'terrier') {
408
- let pd = 1, pc = C.patchTan, blackK = 0;
409
- if (patches.length && (r === 0 || r === 1 || r === 4)) { const q = patchD(p); pd = q.d; blackK = q.black; }
410
- if (r === 2 || r === 5) {
411
- // tan head mask: the ears, round the eyes, the cheeks and the skull; the muzzle, chin and
412
- // throat stay white, and a white blaze runs up the middle from the nose (seeded width, length
413
- // and asymmetry). 'white' terriers keep tan only on the ears and round one or both eyes.
414
- const h = headLocal(p);
415
- const side = Math.sign(h[0]) || 1;
416
- const ext = 1 + 0.35 * maskSide * side;
417
- const wob = 0.005 * (fbm3(p[0] * 30 + nOff, p[1] * 30, p[2] * 30, 2) - 0.5);
418
- const mostlyWhite = params.colour === 'white';
419
- const zFront = (mostlyWhite ? 0.03 : 0.068) * ext, yLow = mostlyWhite ? 0.0 : -0.022 - 0.006 * ext;
420
- const dMask = Math.max(h[2] - zFront, yLow - h[1], -0.09 - h[2]) + wob;
421
- const bw = (0.004 + 0.01 * blaze) * (0.5 + 0.8 * smoothstep(0.0, 0.07, h[2]));
422
- const dBlaze = Math.max(Math.abs(h[0]) - bw, (params.blazeEnd ?? -0.02) - h[2]);
423
- pd = r === 5 ? -0.02 : Math.max(dMask, -dBlaze);
424
- if (mostlyWhite && r === 2 && maskSide * side < -0.3) pd = Math.max(pd, 0.01); // one eye patch only
425
- }
426
- if (pd < 1) {
427
- pattern[v] = pd;
428
- pc = mix3(C.patchTan, C.patchBlack, blackK);
429
- patCol[v * 4] = pc[0]; patCol[v * 4 + 1] = pc[1]; patCol[v * 4 + 2] = pc[2]; patCol[v * 4 + 3] = mat === MAT.FUR ? 1 : 0;
430
- }
431
- }
432
- // pups: fluffy and soft
433
- if (juv > 0 && mat === MAT.FUR) {
434
- fl = mix(fl, Math.max(fl * 0.8, r === 2 || r === 6 ? 0.01 : 0.018), juv);
435
- ag *= 1 - 0.6 * juv;
436
- }
437
- if (mat === MAT.FUR) fl *= FK;
438
- // low-frequency colour variation
439
- const cvh = fbm3(p[0] * 4 + 5 + nOff, p[1] * 4, p[2] * 4, 2) - 0.5;
440
- if (mat === MAT.FUR) {
441
- const a = key === 'terrier' ? 0.25 : 1;
442
- col = [col[0] * (1 + a * (0.12 * nz + 0.08 * cvh + 0.06 * nz2)), col[1] * (1 + a * (0.1 * nz + 0.06 * nz2)), col[2] * (1 + a * (0.08 * nz - 0.06 * cvh + 0.06 * nz2))];
443
- }
444
- markSDF[v] = mark;
445
- furLen[v] = fl;
446
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
447
- surf[v * 4] = mat === MAT.FUR ? gloss : mat === MAT.KERATIN ? 0.7 : mat === MAT.NOSE || mat === MAT.SKIN ? gloss : 0;
448
- surf[v * 4 + 2] = mat === MAT.FUR ? clamp(ag * (0.85 + 0.3 * nz2), 0, 1) : 0;
449
- surf[v * 4 + 3] = mat === MAT.FUR ? under : 0;
450
- }
451
-
452
- smoothField(furLen, weights.neighbors, 3, (v) => tint[v * 4 + 3] > 0);
453
- // the neck's long ruff and the head's short throat coat meet at the neck cut, where the head and body surfaces
454
- // overlap and the coat's regions switch: the hair went from 25 to 9 mm within 15 mm of the cut, and the two
455
- // surfaces took different lengths at one place, so the ruff ended in a crisp collar line round the throat. Near the
456
- // cut the hair length and the colour (the head's and the neck's colour rules also stepped there, 3 mm wide) are
457
- // averaged in space over both surfaces (Gaussian, 13 mm): one field, fading over a few cm
458
- {
459
- const R = 0.013, cell = 2 * R, grid = new Map(), near = [];
460
- const ck = (x, y, z) => `${Math.floor(x / cell)},${Math.floor(y / cell)},${Math.floor(z / cell)}`;
461
- for (let v = 0; v < nV; v++) {
462
- if (tint[v * 4 + 3] !== MAT.FUR) continue;
463
- const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
464
- if (Math.abs(neckS(x, y, z)) > 0.09) continue;
465
- const k = ck(x, y, z); let l = grid.get(k); if (!l) grid.set(k, (l = [])); l.push(v);
466
- if (Math.abs(neckS(x, y, z)) < 0.06) near.push(v);
467
- }
468
- const out = new Float32Array(near.length), outC = new Float32Array(near.length * 3);
469
- near.forEach((v, i) => {
470
- const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
471
- const gx = Math.floor(x / cell), gy = Math.floor(y / cell), gz = Math.floor(z / cell);
472
- let a = 0, w = 0, cr = 0, cg = 0, cb = 0;
473
- for (let ia = -1; ia <= 1; ia++) for (let ib = -1; ib <= 1; ib++) for (let ic = -1; ic <= 1; ic++) {
474
- const l = grid.get(`${gx + ia},${gy + ib},${gz + ic}`); if (!l) continue;
475
- for (const u of l) {
476
- const d2 = (pos[u * 3] - x) ** 2 + (pos[u * 3 + 1] - y) ** 2 + (pos[u * 3 + 2] - z) ** 2;
477
- if (d2 > 4 * R * R) continue;
478
- const wt = Math.exp(-d2 / (2 * R * R)); a += wt * furLen[u]; w += wt;
479
- cr += wt * tint[u * 4]; cg += wt * tint[u * 4 + 1]; cb += wt * tint[u * 4 + 2];
480
- }
481
- }
482
- out[i] = w > 0 ? a / w : furLen[v];
483
- if (w > 0) { outC[i * 3] = cr / w; outC[i * 3 + 1] = cg / w; outC[i * 3 + 2] = cb / w; } else { outC[i * 3] = tint[v * 4]; outC[i * 3 + 1] = tint[v * 4 + 1]; outC[i * 3 + 2] = tint[v * 4 + 2]; }
484
- });
485
- near.forEach((v, i) => {
486
- const k = 1 - smoothstep(0.035, 0.06, Math.abs(neckS(pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2])));
487
- furLen[v] = furLen[v] + (out[i] - furLen[v]) * k;
488
- for (let c = 0; c < 3; c++) tint[v * 4 + c] += (outC[i * 3 + c] - tint[v * 4 + c]) * k;
489
- });
490
- }
491
- // the head and body surfaces' skin weights made equal across the neck cut (regions.js): they parted at the throat
492
- // and the fur rooted on the hidden surface drew a crisp line across the ruff
493
- harmonizeSeamWeights(ctx, neckS, NECK_CUT.band);
494
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor: patCol, surf, region, ventral, tailT };
495
- }
@@ -1,144 +0,0 @@
1
- // Domestic dog (Canis familiaris). Digitigrade trotter with three variants (opts.variant):
2
- // 'shepherd' (the default: a medium shepherd / mixed type), 'retriever' (Labrador type) and 'terrier'
3
- // (a small Jack Russell type). One canid rig; the variants differ in size, proportions, head, ears,
4
- // tail, coat and colours.
5
- import { dogRig, HEAD_O } from './rig.js';
6
- import { sculptDog, eyeOf } from './sculpt.js';
7
- import { dogRegions } from './regions.js';
8
- import { dogCoat } from './coat.js';
9
- import { motion } from './motion.js';
10
- import { rng } from '../../core/math/vec.js';
11
-
12
- export const VARIANTS = {
13
- shepherd: { name: 'Shepherd dog', latin: 'Canis familiaris (shepherd type)' },
14
- retriever: { name: 'Retriever', latin: 'Canis familiaris (Labrador type)' },
15
- terrier: { name: 'Terrier', latin: 'Canis familiaris (Jack Russell type)' },
16
- };
17
- const COLOURS = {
18
- shepherd: [['blackTan', 0.68], ['sable', 0.27], ['black', 0.05]],
19
- retriever: [['yellow', 0.45], ['black', 0.4], ['chocolate', 0.15]],
20
- terrier: [['tricolour', 0.45], ['tan', 0.4], ['white', 0.15]],
21
- };
22
- const pick = (R, table) => {
23
- let x = R(), acc = 0;
24
- for (const [k, w] of table) { acc += w; if (x < acc) return k; }
25
- return table[table.length - 1][0];
26
- };
27
-
28
- export default {
29
- id: 'dog',
30
- name: 'Dog',
31
- latin: 'Canis familiaris',
32
- group: 'paws',
33
- plan: 'quadruped',
34
- covering: 'fur',
35
- variants: Object.entries(VARIANTS).map(([id, v]) => ({ id, name: v.name, latin: v.latin })),
36
-
37
- // Seeded individual: variant (from opts.variant, default 'shepherd'), colour within the
38
- // variant, sex (males larger, broader head, heavier neck and ruff), puppies (age 'juvenile', 8-12
39
- // weeks: ~half size, big domed head, short muzzle, big paws, short legs, soft ears, fluffy coat),
40
- // and individual proportions: leg length, body length, head size, muzzle, ears, tail, colour tones,
41
- // saddle extent, mask, the terrier's patch layout and blaze.
42
- variation(R, o) {
43
- const variant = o.variant && VARIANTS[o.variant] ? o.variant : 'shepherd';
44
- R();
45
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
46
- const age = o.age || 'adult';
47
- const juv = age === 'juvenile' ? 1 : 0;
48
- const male = sex === 'male';
49
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
50
- // colour from its own hashed stream (consecutive seeds must not correlate)
51
- const M = rng(Math.imul((o.seed ?? 1) + 0x2b7e1516, 0x9e3779b1) ^ 0x68e31da4);
52
- for (let i = 0; i < 3; i++) M();
53
- const colour = pick(M, COLOURS[variant]);
54
- const shep = variant === 'shepherd', ret = variant === 'retriever', ter = variant === 'terrier';
55
- // withers height relative to the 0.60 m reference: shepherd M 0.62 / F 0.575, Labrador M 0.57 /
56
- // F 0.55, Jack Russell 0.25-0.30; pups (10 weeks) about half
57
- const base = shep ? (male ? 1.035 : 0.96) : ret ? (male ? 0.95 : 0.915) : 0.46 * (male ? 1.02 : 0.98);
58
- const size = base * (1 + (ter ? 0.06 : 0.03) * g()) * (juv ? (ter ? 0.6 : 0.5) : 1);
59
- const legs = 1 + 0.03 * g() - 0.16 * juv + (ret ? -0.04 : ter ? 0.1 : 0);
60
- const head = (male ? 1.03 : 0.985) * (1 + 0.02 * g()) * (ret ? 1.15 : ter ? 1.12 : 1) * (juv ? 1.3 : 1);
61
- const earType = ret ? 'drop' : ter ? 'button' : juv ? 'button' : 'prick';
62
- const tailK = (1 + 0.05 * g()) * (juv ? 0.7 : 1) * (ter ? (M() < 0.35 ? 0.62 : 0.9 + 0.1 * g()) : 1);
63
- const warps = [
64
- { type: 'legs', k: legs, top: 0.36 },
65
- { type: 'length', k: 1 + 0.03 * g() - 0.05 * juv + (ret ? -0.09 : ter ? -0.03 : 0), z0: -0.26, z1: 0.3 },
66
- { type: 'scaleAbout', c: HEAD_O, k: head, r0: 0.075, r1: 0.19 },
67
- ];
68
- // a shorter, thicker-looking neck (the head sits closer over the forechest)
69
- if (ret || ter) warps.push({ type: 'length', k: ret ? 0.78 : 0.78, z0: 0.3, z1: 0.44 });
70
- if (ret) warps.push({ type: 'girth', k: (male ? 1.1 : 1.07) * (1 + 0.03 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
71
- if (ter) warps.push({ type: 'girth', k: 0.95 * (1 + 0.03 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
72
- if (shep) warps.push({ type: 'girth', k: (male ? 1.02 : 0.98) * (1 + 0.02 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
73
- // chubby pups
74
- if (juv) warps.push({ type: 'girth', k: 1.12, cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
75
- // big pup paws
76
- if (juv) for (const [x, z] of [[0.046, 0.26], [-0.046, 0.26], [0.052, -0.28], [-0.052, -0.28]]) warps.push({ type: 'scaleAbout', c: [x, 0.025, z], k: 1.22, r0: 0.03, r1: 0.06 });
77
- return {
78
- sex, age, size, variant, colour, juv, headScale: head,
79
- earType, tailType: shep ? 'brush' : ret ? 'otter' : 'thin',
80
- muzzle: (1 + 0.04 * g()) * (ret ? 0.96 : ter ? 0.88 : 1.02) * (juv ? 0.72 : 1),
81
- headW: (male ? 1.04 : 0.99) * (1 + 0.02 * g()) * (ret ? 1.12 : ter ? 1.12 : 1.05) * (juv ? 1.05 : 1),
82
- stop: shep ? 0.35 + 0.1 * g() : ret ? 0.75 : 1.0,
83
- flews: ret ? 0.8 + 0.2 * g() : 0,
84
- muzzleDepth: ret ? 1.15 : ter ? 1.08 : 1,
85
- zyg: ret ? 0.7 : 1,
86
- dome: ret ? 0.6 : ter ? 0.2 : 0,
87
- ruff: shep ? (male ? 0.7 : 0.5) + 0.1 * g() : ret ? 0.12 : 0,
88
- neckK: (ret ? 1.22 : ter ? 1.04 : 1) * (male ? 1.03 : 0.98),
89
- chestK: ret ? 1.06 : 1,
90
- boneK: (ret ? 1.35 : ter ? 1.1 : 1) * (juv ? 1.1 : 1),
91
- pawK: (ret ? 1.1 : ter ? 0.92 : 1),
92
- tuck: shep ? 1 : ter ? 1.0 : 0.2,
93
- eyeK: (ter ? 1.3 : 1) * (juv ? 1.25 : 1),
94
- eyeRound: ret ? 0.6 : ter ? 0.5 : 0.1 * juv,
95
- ear: (1 + 0.05 * g()) * (juv ? 1.08 : 1) * (shep && !juv ? 0.95 : ter ? 1.35 : 1),
96
- tail: tailK,
97
- tailBrush: (1 + 0.06 * g()) * (juv ? 0.85 : 1) * (ret ? 1.2 : 1),
98
- furK: (shep ? 1 : ret ? 0.32 : 0.3) * (1 + 0.06 * g()),
99
- saddle: 1.1 * g(),
100
- mask: 1 + 0.15 * g(),
101
- coatWarmth: 0.9 * g(),
102
- coatLightness: 0.1 * g(),
103
- noseFade: ret && colour === 'yellow' ? M() : 0,
104
- patches: 0.7 + 0.6 * M(),
105
- blaze: 0.2 + 0.8 * M(),
106
- blazeEnd: -0.05 + 0.08 * M(),
107
- maskSide: 2 * M() - 1,
108
- coatSeed: Math.floor(R() * 1e6),
109
- minThick: 0.003,
110
- warps,
111
- };
112
- },
113
- rig: (params) => dogRig(params),
114
- sculpt: (m, rig, params) => sculptDog(m, rig, params),
115
- regions: dogRegions,
116
- coat: dogCoat,
117
- eyeSpecs: (params) => [1, -1].map((side) => ({ side, spec: eyeOf(params), headOrigin: HEAD_O, bone: 'head', look: eyeLook(params) })),
118
- motion,
119
- render: (params) => ({
120
- markColor: params.colour === 'chocolate' ? [0.06, 0.028, 0.02] : [0.012, 0.01, 0.009],
121
- strandDensity: params.variant === 'shepherd' ? 720 : 1200,
122
- clumpDensity: params.variant === 'shepherd' ? 0.8 : 0.3,
123
- }),
124
- };
125
-
126
- // Round pupil; brown iris (dark brown in shepherds and terriers, hazel / amber in chocolates), no
127
- // visible sclera at rest; black lid margins (liver in chocolates); pups blue-grey.
128
- const IRIS = {
129
- dark: { inner: [0.08, 0.035, 0.012], mid: [0.16, 0.075, 0.025], hi: [0.3, 0.16, 0.06], outer: [0.035, 0.016, 0.006] },
130
- brown: { inner: [0.12, 0.055, 0.018], mid: [0.24, 0.12, 0.04], hi: [0.42, 0.24, 0.08], outer: [0.05, 0.022, 0.008] },
131
- amber: { inner: [0.22, 0.11, 0.03], mid: [0.42, 0.24, 0.07], hi: [0.6, 0.38, 0.12], outer: [0.1, 0.05, 0.015] },
132
- pup: { inner: [0.1, 0.12, 0.15], mid: [0.2, 0.24, 0.3], hi: [0.34, 0.4, 0.48], outer: [0.06, 0.07, 0.09] },
133
- };
134
- function eyeLook(params) {
135
- const iris = params.juv ? IRIS.pup : params.colour === 'chocolate' ? IRIS.amber : params.variant === 'retriever' ? IRIS.brown : IRIS.dark;
136
- return {
137
- pupil: 'round',
138
- lids: 'mammal',
139
- iris,
140
- sclera: { color: [0.55, 0.45, 0.38], visible: false },
141
- lidColor: params.colour === 'chocolate' ? [0.08, 0.04, 0.03] : [0.02, 0.017, 0.015],
142
- pupilSize: [0.3, 0.6],
143
- };
144
- }