@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,361 +0,0 @@
1
- // The horse's coat: base colours (bay, dark bay, chestnut, black, grey, palomino, dun, buckskin),
2
- // black points, dorsal stripe and leg bars (dun), dapples (grey), white face and leg markings with
3
- // pink skin and pale hooves under them, long mane / forelock / tail hair, hair flow following the
4
- // equine hair tracts (forehead whorl, down the face, backward and down the barrel, down the legs),
5
- // bare muzzle skin with whiskers, keratin hooves.
6
- // Works in the reference space (see rig.js); seeded variation picks the colour and the markings.
7
- import { HEAD_O, HZ, HY, DOCK_SEGS } from './rig.js';
8
- import { neckS } from './regions.js';
9
- import { unifyNeckWeights, spreadThroatWeights } from './seamWeights.js';
10
- import { blendHeadNeckCoat } from './coatSeam.js';
11
- import { SDFModel } from '../../core/sdf/sdf.js';
12
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
13
- import { srgb, mix3, MAT, poissonFeatures, featureDistance, smoothField } from '../../core/build/coatKit.js';
14
-
15
- // sRGB swatches sampled from the reference photos
16
- export const COATS = {
17
- bay: { body: 0x6b3a1f, dorsal: 0x57301a, belly: 0x7a4527, head: 0x62351d, points: 0x17120f, mane: 0x121010, blackPoints: 1 },
18
- darkbay: { body: 0x3e2418, dorsal: 0x2e1b13, belly: 0x5a3421, head: 0x3a2217, points: 0x141010, mane: 0x100d0c, blackPoints: 1 },
19
- // (chestnut: #8A4A22 desaturated ~25 % and lifted a little: rendered flanks read as brick red otherwise)
20
- chestnut: { body: 0x845231, dorsal: 0x77492b, belly: 0x93613a, head: 0x7d4d2e, points: 0x74452a, mane: 0x70432a, blackPoints: 0 },
21
- black: { body: 0x1f1b1a, dorsal: 0x1a1716, belly: 0x2a221e, head: 0x1f1b1a, points: 0x131010, mane: 0x100e0d, blackPoints: 0 },
22
- grey: { body: 0x9a9893, dorsal: 0x8e8c87, belly: 0xb4b2ac, head: 0xa8a6a0, points: 0x5a5856, mane: 0xcfcdc7, blackPoints: 0, dapple: 0xc9c7c1 },
23
- palomino: { body: 0xd4a95a, dorsal: 0xc99d50, belly: 0xdcb46a, head: 0xcfa355, points: 0xc09450, mane: 0xefe3c2, blackPoints: 0 },
24
- dun: { body: 0xb89a6a, dorsal: 0xa98c5e, belly: 0xc6aa7c, head: 0x7a6040, points: 0x1c1712, mane: 0x16120f, blackPoints: 1, dunFactor: 1 },
25
- buckskin: { body: 0xb98a4f, dorsal: 0xac7e46, belly: 0xc49a60, head: 0xa87c45, points: 0x1a1410, mane: 0x141110, blackPoints: 1 },
26
- };
27
- const WHITE = 0xf0ece4, PINK = 0xc9a09a, MUZZLE = 0x2c2a2a, HOOF = 0x3a3029, HOOF_PALE = 0xb8a88a;
28
-
29
- function palette(p) {
30
- const C = COATS[p.coat] || COATS.bay;
31
- const k = p.coatShade || 0, l = p.coatLightness || 0;
32
- const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.08 * k + l), c[1] * (1 + l), c[2] * (1 - 0.08 * k + l)]; };
33
- const out = { body: tw(C.body), dorsal: tw(C.dorsal), belly: tw(C.belly), head: tw(C.head), points: srgb(C.points), mane: srgb(C.mane), blackPoints: C.blackPoints, dunFactor: C.dunFactor || 0 };
34
- if (p.coat === 'chestnut' && p.flaxen) out.mane = srgb(0xcfb37e);
35
- if (p.coat === 'grey') {
36
- // greys whiten with age: 0 = dark dapple grey, 1 = almost white
37
- const g = p.greyLevel ?? 0.4;
38
- const lift = (c, t) => mix3(c, srgb(0xe4e2dc), t);
39
- out.body = lift(out.body, g * 0.8); out.dorsal = lift(out.dorsal, g * 0.75); out.belly = lift(out.belly, g * 0.8); out.head = lift(out.head, g * 0.85);
40
- out.points = lift(out.points, g * 0.6); out.mane = lift(out.mane, g * 0.5);
41
- out.dapple = lift(srgb(C.dapple), g * 0.6);
42
- }
43
- out.white = srgb(WHITE); out.pink = srgb(PINK); out.muzzle = srgb(MUZZLE); out.hoof = srgb(HOOF); out.hoofPale = srgb(HOOF_PALE);
44
- return out;
45
- }
46
-
47
- const HAIR_TAGS = new Set(['mane', 'manecrest', 'forelock']);
48
- const HOOF_TAGS = new Set(['hoof', 'heelbulb']);
49
-
50
- export function horseCoat(ctx) {
51
- const { model, rig, pos, nrm, regionOf: partOf, weights, nV, params, lists } = ctx;
52
- const { BONES } = rig;
53
- const J = rig.J;
54
- const R = rng(4242 + (params.coatSeed || 0));
55
- const COL = palette(params);
56
- const foal = params.age === 'juvenile';
57
- const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
58
- const AX_LEN = weights.axialLengths;
59
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
60
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
61
- const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
62
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
63
- const jawPrims = model.forPart('jaw');
64
- const headPrims = model.forPart('body').filter((p) => p.bone === 'head' && !p.carve);
65
- const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril');
66
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
67
- const legOf = BONES.map((b) => (/^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(b.name) ? 'F' : /^(femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name) ? 'H' : '') + (b.side || ''));
68
- const tailIdx = (name) => (name.startsWith('tail') ? +name.slice(4) : -1);
69
- // head-local coordinates (x lateral, y dorsal, z along the nasal line)
70
- const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
71
-
72
- // --- nearest primitive tag per vertex
73
- const tagOf = new Array(nV);
74
- for (let v = 0; v < nV; v++) {
75
- const L = lists[v], p = P(v);
76
- let best = 1e9, t = '';
77
- for (const pr of L) {
78
- if (pr.carve) continue;
79
- const d = SDFModel.dist(pr, p[0], p[1], p[2]);
80
- if (d < best) { best = d; t = pr.tag; }
81
- }
82
- tagOf[v] = t;
83
- }
84
-
85
- // --- regions: 0 torso, 1 neck, 2 head, 3 mane / forelock, 4 dock, 5 ear, 6 jaw, 7 tail hair
86
- const region = new Uint8Array(nV);
87
- const legness = new Float32Array(nV);
88
- const legBone = new Int16Array(nV).fill(-1);
89
- for (let v = 0; v < nV; v++) {
90
- const bn = BONES[dominant[v]].name, p = P(v), tag = tagOf[v];
91
- let lb = -1, lw = 0;
92
- for (let k = 0; k < 4; k++) {
93
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
94
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
95
- }
96
- legBone[v] = lb;
97
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
98
- const ti = tailIdx(bn);
99
- if (partOf[v] === 2) region[v] = 6;
100
- else if (bn.startsWith('ear')) region[v] = 5;
101
- else if (HAIR_TAGS.has(tag)) region[v] = 3;
102
- else if (tag === 'tailhair') region[v] = 7;
103
- else if (ti >= 0 && axialS[v] > sTailBase + 0.02) region[v] = ti >= DOCK_SEGS ? 7 : 4;
104
- else if (axialS[v] < sOcc + 0.02 || (partOf[v] === 1 && neckS(p[0], p[1], p[2]) > 0.03)) region[v] = 2;
105
- else if (axialS[v] < sNeckBase) region[v] = 1;
106
- else region[v] = 0;
107
- if (region[v] >= 2) legness[v] = 0;
108
- }
109
-
110
- // --- white markings (face + legs), jagged edges
111
- const face = params.face || 'none';
112
- const socks = params.socks || [0, 0, 0, 0]; // FL FR HL HR marking height above ground (m), 0 = none
113
- const LEGI = { FL: 0, FR: 1, HL: 2, HR: 3 };
114
- const whiteSDF = (v) => {
115
- const p = P(v), r = region[v];
116
- const jag = (fbm3(p[0] * 60, p[1] * 60, p[2] * 60, 2) - 0.5) * 0.02;
117
- let d = 1;
118
- if (r === 2 || r === 6) {
119
- const h = HL(p), ax = Math.abs(h[0]);
120
- const front = (dot(N(v), HY) > -0.2 && h[1] > -0.07 + 0.12 * smoothstep(0.1, -0.1, h[2])) || h[2] > 0.3;
121
- if (!front) return 1;
122
- // (star: on the forehead at the eyes' level, just below the forelock's tip, which ends at z ~0)
123
- if (face === 'star' || face === 'starsnip' || face === 'stripe') d = Math.min(d, Math.hypot(ax / 0.028, (h[2] - 0.028) / 0.034) * 0.028 - 0.028);
124
- if (face === 'stripe') d = Math.min(d, Math.max(ax - 0.014 - 0.006 * smoothstep(0, 0.3, h[2]), -0.02 - h[2], h[2] - 0.3));
125
- if (face === 'blaze') d = Math.min(d, Math.max(ax - (0.03 + 0.03 * smoothstep(0.15, 0.4, h[2])), -0.07 - h[2]));
126
- if (face === 'snip' || face === 'starsnip') d = Math.min(d, Math.hypot(ax / 0.022, (h[2] - 0.36) / 0.035) * 0.022 - 0.022);
127
- return d + jag;
128
- }
129
- const lb = legBone[v];
130
- if (lb >= 0 && legness[v] > 0.3) {
131
- const hgt = socks[LEGI[legOf[lb]]] || 0;
132
- if (hgt > 0) d = p[1] - hgt + jag * 1.5;
133
- }
134
- return d;
135
- };
136
-
137
- // --- ventral factor (lighter belly, inner legs, flank)
138
- const ventral = new Float32Array(nV);
139
- for (let v = 0; v < nV; v++) {
140
- const n = N(v), r = region[v], p = P(v);
141
- let w = 0;
142
- if (r === 0) w = smoothstep(-0.2, -0.8, n[1]) * smoothstep(1.25, 1.0, p[1]);
143
- else if (r === 1) w = smoothstep(0.0, -0.7, n[1]) * 0.5;
144
- const L = legness[v];
145
- if (L > 0) { const side = p[0] >= 0 ? 1 : -1; w = mix(w, smoothstep(0.1, -0.7, n[0] * side) * 0.6 * smoothstep(0.5, 0.9, p[1]), L); }
146
- ventral[v] = w;
147
- }
148
-
149
- // --- comb (hair flow)
150
- const comb = new Float32Array(nV * 3);
151
- const whorl = [0, 0.055, -0.02]; // forehead whorl, head-local
152
- for (let v = 0; v < nV; v++) {
153
- const p = P(v), n = N(v), r = region[v];
154
- const bone = BONES[dominant[v]];
155
- let d;
156
- if (r === 5) d = norm(sub(bone.tail, bone.head));
157
- else if (r === 2 || r === 6) {
158
- const h = HL(p);
159
- // from the whorl: down the face toward the muzzle, back and down on the cheeks and jowl
160
- const rel = [h[0] - whorl[0], h[1] - whorl[1], h[2] - whorl[2]];
161
- const lh = [rel[0], rel[1] * 0.4, rel[2] + 0.08];
162
- const w = [lh[0], lh[1] - 0.05, lh[2]];
163
- d = norm(add(mul(HY, w[1]), add(mul(HZ, w[2]), [w[0], 0, 0])));
164
- if (h[2] < -0.1) d = norm(add(d, [0, -0.6, -0.8]));
165
- } else if (r === 3) {
166
- // mane falls down the side of the neck; the forelock down the face; foal mane stands up
167
- if (tagOf[v] === 'forelock') d = HZ;
168
- else if (foal) d = [0, 1, 0.2];
169
- else {
170
- // straight down the mane side; on the far side of the crest up and over to the mane side
171
- const ms = params.maneSide ?? -1;
172
- d = p[0] * ms >= 0 ? norm([ms * 0.35, -1, -0.2]) : norm([ms * 0.6, 0.8, -0.2]);
173
- }
174
- } else if (r === 7 || r === 4) {
175
- d = norm(sub(bone.tail, bone.head));
176
- if (r === 4) d = norm(add(d, [0, 0, -0.2]));
177
- } else {
178
- const seg = weights.segT[v * 2] | 0;
179
- const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
180
- d = norm(sub(b, a));
181
- if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
182
- if (r === 1) d = norm(add(d, [0, -0.5, 0.1]));
183
- const L = legness[v];
184
- if (L > 0 && legBone[v] >= 0) {
185
- const lbn = BONES[legBone[v]];
186
- let dl = norm(sub(lbn.tail, lbn.head));
187
- if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
188
- else dl = norm(add(dl, [0, -0.3, 0]));
189
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
190
- }
191
- }
192
- let t = sub(d, mul(n, dot(d, n)));
193
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
194
- t = norm(t);
195
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
196
- }
197
-
198
- // --- dapples (grey): light rounded spots with a darker network between them
199
- let dapples = null;
200
- if (params.coat === 'grey') {
201
- dapples = poissonFeatures({
202
- pos, nrm, nV, R, cell: 0.08, spacing: 1.25, gap: 0.004,
203
- radius: (v) => (region[v] <= 1 && legness[v] < 0.6 && ventral[v] < 0.7 ? 0.028 : 0),
204
- });
205
- }
206
-
207
- // --- per-vertex fields
208
- const pattern = new Float32Array(nV).fill(1);
209
- const patInt = new Float32Array(nV);
210
- const patCol = new Float32Array(nV * 3);
211
- const markSDF = new Float32Array(nV).fill(1);
212
- const furLen = new Float32Array(nV);
213
- const tint = new Float32Array(nV * 4);
214
- const surf = new Float32Array(nV * 4);
215
- const lipLocal = (h) => h[1] + 0.078 - 0.02 * smoothstep(0.26, 0.4, h[2]);
216
-
217
- for (let v = 0; v < nV; v++) {
218
- const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
219
- let col, mat = MAT.FUR, fl = 0.008, mark = 1, gloss = 0.6;
220
- const wsdf = whiteSDF(v);
221
- const lb = legBone[v];
222
- const lbName = lb >= 0 ? BONES[lb].name : '';
223
- if (r === 0 || r === 1) {
224
- const up = clamp(n[1], -1, 1);
225
- col = mix3(COL.body, COL.dorsal, smoothstep(0.3, 0.95, up) * 0.8);
226
- col = mix3(col, COL.belly, ventral[v]);
227
- fl = mix(0.008, 0.011, ventral[v]);
228
- if (r === 1) fl = 0.008;
229
- // dun: dorsal stripe from the mane to the tail
230
- if (COL.dunFactor && r === 0) mark = Math.min(mark, Math.abs(p[0]) - 0.022 - 0.01 * smoothstep(0.3, -0.6, p[2]) + (1 - smoothstep(0.85, 0.97, up)) * 0.1);
231
- const L = legness[v];
232
- if (L > 0) {
233
- // legs: coat colour above, black points below the knees / hocks (bay, dun, buckskin)
234
- let lc = col;
235
- const knee = /^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(lbName) ? 0.52 : 0.6;
236
- const pts = COL.blackPoints ? smoothstep(knee + 0.18, knee - 0.02, p[1] + (fbm3(p[0] * 20, p[1] * 8, p[2] * 20, 2) - 0.5) * 0.12) : 0;
237
- lc = mix3(lc, COL.points, pts);
238
- if (!COL.blackPoints && params.coat === 'grey') lc = mix3(lc, COL.points, smoothstep(0.6, 0.2, p[1]) * 0.8);
239
- // dun: faint zebra bars on the forearms and gaskins
240
- if (COL.dunFactor && p[1] > 0.5 && p[1] < 0.95) { const bar = Math.sin(p[1] * 70); lc = mix3(lc, COL.points, 0.22 * smoothstep(0.82, 0.95, bar) * smoothstep(0.95, 0.75, p[1])); }
241
- col = mix3(col, lc, L);
242
- fl = mix(fl, mix(0.004, 0.008, smoothstep(0.3, 0.9, p[1])), L);
243
- // fetlock feathers
244
- if (/metacarpus|metatarsus|fpaw|hpaw/.test(lbName) && p[1] < 0.24 && p[1] > 0.1) fl = Math.max(fl, 0.018 * smoothstep(0.0, -0.8, n[2]));
245
- // chestnuts (horny callosities) inside the forearm above the knee and below the hock
246
- const side = p[0] >= 0 ? 1 : -1;
247
- const chestnutY = /radius/.test(lbName) ? 0.62 : /metatarsus/.test(lbName) ? 0.46 : -1;
248
- if (chestnutY > 0 && n[0] * side < -0.6) { const J0 = lbName.startsWith("radius") ? J["wrist" + BONES[lb].side] : J["hock" + BONES[lb].side]; if (Math.hypot((p[1] - chestnutY) / 0.024, (p[2] - J0[2]) / 0.014) < 1) { mat = MAT.KERATIN; col = srgb(0x3a302a); fl = 0; } }
249
- }
250
- // hoof wall below the coronet line (higher at the toe than at the heel)
251
- const Cj = /hoof|paw/.test(lbName) ? J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side] : null;
252
- if (Cj && HOOF_TAGS.has(tag) && p[1] < Cj[1] + 0.012 + 0.45 * (p[2] - Cj[2])) {
253
- mat = MAT.KERATIN; fl = 0; gloss = 0.55;
254
- col = wsdf < 0 ? COL.hoofPale : COL.hoof;
255
- if (tag === 'heelbulb') col = mix3(col, COL.muzzle, 0.5);
256
- }
257
- } else if (r === 3) {
258
- col = COL.mane;
259
- // (the forelock lies flat: its lock is geometry, the shells only add short hair along it)
260
- // (mane: the sheet is geometry; its hair stays under the 25 mm long-hair bevel, so every edge of
261
- // it - the poll, the forelock - tapers at the fur slope limit instead of standing as a wall)
262
- fl = tag === 'manecrest' ? (foal ? 0.03 : 0.022) : tag === 'forelock' ? 0.012 : 0.018;
263
- if (foal) fl = 0.03;
264
- } else if (r === 4) {
265
- col = mix3(COL.body, COL.mane, 0.6 + 0.4 * smoothstep(-0.2, 0.6, n[1]));
266
- fl = 0.035;
267
- } else if (r === 7) {
268
- col = COL.mane;
269
- fl = foal ? 0.04 : 0.055;
270
- } else if (r === 5) {
271
- // ears: coat colour, dark rims / tips on bays, hairy inside
272
- const earP = model.prims.find((q) => q.tag === 'ear' && (q.bone === (p[0] > 0 ? 'earL' : 'earR')));
273
- const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
274
- const front = dot(n, faceDir);
275
- col = COL.head;
276
- if (COL.blackPoints) col = mix3(col, COL.points, 0.5);
277
- fl = 0.005;
278
- if (front > 0.3) { fl = 0.016; col = mix3(col, srgb(0xb0a090), 0.25); }
279
- } else {
280
- // head & jaw
281
- col = COL.head;
282
- fl = 0.005;
283
- const h = HL(p);
284
- // dark soft muzzle skin with whiskers (pink under a white snip / blaze)
285
- // muzzle: fine short hair over dark skin (pink under a white snip)
286
- const muz = smoothstep(0.25, 0.36, h[2]);
287
- if (muz > 0) { col = mix3(col, wsdf < 0 ? COL.pink : COL.muzzle, muz * 0.75); fl = mix(fl, 0.0025, muz); }
288
- // bays and duns have a black-edged muzzle; duns a dark face
289
- if (COL.blackPoints && h[2] > 0.25) col = mix3(col, COL.points, smoothstep(0.25, 0.32, h[2]) * 0.6);
290
- if (h[2] < -0.13) col = mix3(col, COL.body, smoothstep(-0.13, -0.2, h[2]));
291
- // eye rims (dark skin)
292
- for (const e of eyePrims) {
293
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
294
- if (Math.abs(de) < 0.006) mark = Math.min(mark, Math.abs(de) - 0.0035);
295
- if (Math.abs(de) < 0.0024) { mat = MAT.DARK_SKIN; fl = 0; }
296
- else if (Math.abs(de) < 0.006) fl = Math.min(fl, 0.002);
297
- }
298
- // nostrils: dark rims
299
- // (dark, moist lining; SKIN's subsurface scattering read as a pink rim)
300
- for (const e of nostrilPrims) {
301
- const dn = SDFModel.dist(e, p[0], p[1], p[2]);
302
- if (dn < 0.004) { mat = MAT.DARK_SKIN; col = COL.muzzle; fl = 0; mark = Math.min(mark, dn - 0.003); }
303
- }
304
- // lips: head vertices near the jaw surface, jaw vertices near the head surface
305
- // (the closed mouth shows a dark crease where the lips meet; the pink mucosa only on the surfaces
306
- // the other lip hides, which show when the mouth opens)
307
- if (partOf[v] !== 2) {
308
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
309
- if (dj < -0.002 && h[2] > 0.2) { mat = MAT.MOUTH; fl = 0; }
310
- else if (dj < 0.008 && h[2] > 0.2) { mark = Math.min(mark, dj - 0.0045); fl = Math.min(fl, 0.0015); }
311
- } else {
312
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
313
- if (dh < -0.002) { mat = MAT.MOUTH; fl = 0; }
314
- else if (dh < 0.008) { mark = Math.min(mark, dh - 0.0045); fl = Math.min(fl, 0.0015); }
315
- }
316
- void lipLocal;
317
- }
318
- // white markings (not on keratin / mouth / eye skin)
319
- // (the pattern field stays continuous: capped just above zero where there is nothing, so a
320
- // triangle never interpolates across a jump and grows a stray edge line)
321
- const CAP = 0.02;
322
- let pc = COL.white, pi = 1, pd = CAP;
323
- if ((mat === MAT.FUR || mat === MAT.SKIN) && params.coat !== 'grey') pd = Math.min(CAP, wsdf);
324
- if (dapples && (r === 0 || r === 1)) {
325
- const [sd] = featureDistance(dapples, p, n, 1 + 0.25 * (vnoise3(p[0] * 40, p[1] * 40, p[2] * 40) - 0.5));
326
- pd = Math.min(CAP, sd); pi = 0.75 * (1 - legness[v]) * (1 - ventral[v]); pc = COL.dapple;
327
- }
328
- // low-frequency colour variation (and sheen dapples on solid coats)
329
- const cv = fbm3(p[0] * 5, p[1] * 5, p[2] * 5, 3) - 0.5;
330
- if (mat === MAT.FUR) col = [col[0] * (1 + 0.14 * cv), col[1] * (1 + 0.12 * cv), col[2] * (1 + 0.1 * cv)];
331
- // long hair: lock-to-lock shade streaks along the hair
332
- if (r === 3 || r === 7) {
333
- const c = [comb[v * 3], comb[v * 3 + 1], comb[v * 3 + 2]];
334
- const along = dot(p, c), q = sub(p, mul(c, along));
335
- const st = vnoise3(q[0] * 90, q[1] * 90, q[2] * 90 + along * 3), lk = vnoise3(q[0] * 24 + 7.1, q[1] * 24, q[2] * 24 + along * 1.5);
336
- col = mul(col, (0.75 + 0.5 * st) * (0.82 + 0.36 * lk));
337
- }
338
- pattern[v] = pd; patInt[v] = pi;
339
- patCol[v * 3] = pc[0]; patCol[v * 3 + 1] = pc[1]; patCol[v * 3 + 2] = pc[2];
340
- markSDF[v] = mark;
341
- furLen[v] = fl;
342
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
343
- surf[v * 4] = gloss;
344
- }
345
- // foals: fluffy body coat
346
- if (foal) for (let v = 0; v < nV; v++) if (tint[v * 4 + 3] === MAT.FUR && region[v] <= 1) furLen[v] = Math.max(furLen[v], 0.016);
347
- // smooth fur length a little so shells do not step (hair, skin and keratin keep their own)
348
- smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.FUR);
349
- // one coat across the head / neck boundary: no parting in the hair flow along the jowl margin and
350
- // behind the poll (coatSeam.js)
351
- const coatSeam = blendHeadNeckCoat(ctx, { comb, furLen, tint, region });
352
-
353
- const patternColor = new Float32Array(nV * 4);
354
- for (let v = 0; v < nV; v++) { patternColor[v * 4] = patCol[v * 3]; patternColor[v * 4 + 1] = patCol[v * 3 + 1]; patternColor[v * 4 + 2] = patCol[v * 3 + 2]; patternColor[v * 4 + 3] = patInt[v]; }
355
- // the head -> neck hand-over is spread across the throat latch (no fold when the poll flexes), and
356
- // the head and body copies of the throat / upper neck take one set of skin weights across the
357
- // cross-fade band (seamWeights.js; after the coat, which reads each copy's own weights)
358
- const throat = spreadThroatWeights(ctx);
359
- const seam = unifyNeckWeights(ctx);
360
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: { dapples: dapples ? dapples.spots.length : 0, neckWeightsShared: seam.changed, coatSeamBlended: coatSeam.changed, throatWeightsSpread: throat.changed }, region, ventral };
361
- }
@@ -1,122 +0,0 @@
1
- // One coat across the head / neck boundary: hair flow, hair length and colour run smoothly from the
2
- // face onto the throat and the upper neck.
3
- //
4
- // The coat regions (coat.js) give the head (region 2) its own hair flow, combed from the forehead whorl
5
- // down the face toward the muzzle, and the neck (region 1) the flow down the neck from the axial chain;
6
- // the head's short face hair (5 mm) and the neck's 8 mm coat meet on one line, the boundary of region 2:
7
- // across the throat latch and the back of the jowl (where the axial coordinate passes the occiput) and
8
- // up behind the poll (the neck cut, neckS = 0.03). On that line the hair flow turned by up to ~160 deg
9
- // from one vertex to the next: the jowl's hair points to the muzzle, the throat's down the neck, so the
10
- // two coats parted along the line and the dark roots and the shaded base under the shells showed
11
- // through as a thin dark line. It reads most when the jowl margin turns edge-on (the poll flexed in a
12
- // rear, a head looking down to the side): a dark crease across the throat and up behind the eye.
13
- //
14
- // Here the hair direction, length and colour of both coats are blurred over the surface (both mesh
15
- // copies together, splatted into a grid by vertex area and fade, Gaussian sigma SIGMA) and mixed in
16
- // along the boundary only (by the blurred region indicator: full within ~3 cm of the line, nothing
17
- // past ~9 cm), so the face, the poll and the neck keep their own coat and the hair turns from the
18
- // face's flow to the neck's over ~10 cm of skin instead of between two vertices.
19
- import { smoothstep } from '../../core/math/vec.js';
20
- import { MAT } from '../../core/build/coatKit.js';
21
-
22
- const SIGMA = 0.035;
23
-
24
- export function blendHeadNeckCoat(ctx, { comb, furLen, tint, region }, sigma = SIGMA) {
25
- const { pos, nrm, nV, fade } = ctx;
26
- // fur of the head and the neck, both mesh copies (the jaw, the ears, the mane keep their own)
27
- const usable = (v) => (region[v] === 1 || region[v] === 2) && tint[v * 4 + 3] === MAT.FUR;
28
- let mn = [1e9, 1e9, 1e9], mx = [-1e9, -1e9, -1e9], any2 = false;
29
- for (let v = 0; v < nV; v++) {
30
- if (!usable(v) || region[v] !== 2) continue;
31
- any2 = true;
32
- for (let k = 0; k < 3; k++) { mn[k] = Math.min(mn[k], pos[v * 3 + k]); mx[k] = Math.max(mx[k], pos[v * 3 + k]); }
33
- }
34
- if (!any2) return { changed: 0 };
35
- const c = sigma / 2, pad = 4 * sigma;
36
- mn = mn.map((x) => x - pad); mx = mx.map((x) => x + pad);
37
- const G = mn.map((x, k) => Math.ceil((mx[k] - x) / c) + 2);
38
- // channels: comb xyz, hair length, colour rgb, region-2 indicator, weight
39
- const NC = 9, cells = G[0] * G[1] * G[2];
40
- const grid = new Float32Array(cells * NC);
41
- const at = (i, j, k) => (i + G[0] * (j + G[1] * k)) * NC;
42
- const cellOf = (v) => [0, 1, 2].map((k) => (pos[v * 3 + k] - mn[k]) / c);
43
- const inside = (g) => g.every((x, k) => x >= 0 && x <= G[k] - 2);
44
- // vertex areas
45
- const area = new Float32Array(nV);
46
- const { index } = ctx;
47
- for (let i = 0; i < index.length; i += 3) {
48
- const a = index[i], b = index[i + 1], d = index[i + 2];
49
- const ux = pos[b * 3] - pos[a * 3], uy = pos[b * 3 + 1] - pos[a * 3 + 1], uz = pos[b * 3 + 2] - pos[a * 3 + 2];
50
- const wx = pos[d * 3] - pos[a * 3], wy = pos[d * 3 + 1] - pos[a * 3 + 1], wz = pos[d * 3 + 2] - pos[a * 3 + 2];
51
- const A = (0.5 * Math.hypot(uy * wz - uz * wy, uz * wx - ux * wz, ux * wy - uy * wx)) / 3;
52
- area[a] += A; area[b] += A; area[d] += A;
53
- }
54
- const tmp = new Float32Array(NC);
55
- for (let v = 0; v < nV; v++) {
56
- if (!usable(v)) continue;
57
- const g = cellOf(v);
58
- if (!inside(g)) continue;
59
- const a = area[v] * Math.max(0, fade ? fade[v] : 1);
60
- if (!(a > 0)) continue;
61
- tmp[0] = comb[v * 3]; tmp[1] = comb[v * 3 + 1]; tmp[2] = comb[v * 3 + 2];
62
- tmp[3] = furLen[v];
63
- tmp[4] = tint[v * 4]; tmp[5] = tint[v * 4 + 1]; tmp[6] = tint[v * 4 + 2];
64
- tmp[7] = region[v] === 2 ? 1 : 0; tmp[8] = 1;
65
- const i0 = Math.floor(g[0]), j0 = Math.floor(g[1]), k0 = Math.floor(g[2]);
66
- const fx = g[0] - i0, fy = g[1] - j0, fz = g[2] - k0;
67
- for (let dk = 0; dk < 2; dk++) for (let dj = 0; dj < 2; dj++) for (let di = 0; di < 2; di++) {
68
- const t = a * (di ? fx : 1 - fx) * (dj ? fy : 1 - fy) * (dk ? fz : 1 - fz);
69
- const o = at(i0 + di, j0 + dj, k0 + dk);
70
- for (let q = 0; q < NC; q++) grid[o + q] += t * tmp[q];
71
- }
72
- }
73
- // separable Gaussian blur
74
- const R = Math.ceil((3 * sigma) / c), ker = [];
75
- for (let d = -R; d <= R; d++) ker.push(Math.exp(-0.5 * ((d * c) / sigma) ** 2));
76
- const buf = new Float32Array(grid.length);
77
- for (let ax = 0; ax < 3; ax++) {
78
- buf.fill(0);
79
- for (let k = 0; k < G[2]; k++) for (let j = 0; j < G[1]; j++) for (let i = 0; i < G[0]; i++) {
80
- const o = at(i, j, k);
81
- if (grid[o + NC - 1] === 0) continue;
82
- for (let d = -R; d <= R; d++) {
83
- const ii = i + (ax === 0 ? d : 0), jj = j + (ax === 1 ? d : 0), kk = k + (ax === 2 ? d : 0);
84
- if (ii < 0 || jj < 0 || kk < 0 || ii >= G[0] || jj >= G[1] || kk >= G[2]) continue;
85
- const o2 = at(ii, jj, kk), w = ker[d + R];
86
- for (let q = 0; q < NC; q++) buf[o2 + q] += w * grid[o + q];
87
- }
88
- }
89
- grid.set(buf);
90
- }
91
- // sample back; mix in along the boundary
92
- let changed = 0;
93
- for (let v = 0; v < nV; v++) {
94
- if (!usable(v)) continue;
95
- const g = cellOf(v);
96
- if (!inside(g)) continue;
97
- const i0 = Math.floor(g[0]), j0 = Math.floor(g[1]), k0 = Math.floor(g[2]);
98
- const fx = g[0] - i0, fy = g[1] - j0, fz = g[2] - k0;
99
- tmp.fill(0);
100
- for (let dk = 0; dk < 2; dk++) for (let dj = 0; dj < 2; dj++) for (let di = 0; di < 2; di++) {
101
- const t = (di ? fx : 1 - fx) * (dj ? fy : 1 - fy) * (dk ? fz : 1 - fz);
102
- const o = at(i0 + di, j0 + dj, k0 + dk);
103
- for (let q = 0; q < NC; q++) tmp[q] += t * grid[o + q];
104
- }
105
- const W = tmp[NC - 1];
106
- if (!(W > 1e-9)) continue;
107
- const gb = tmp[7] / W;
108
- const m = smoothstep(0.02, 0.25, Math.min(gb, 1 - gb));
109
- if (m <= 0) continue;
110
- // hair direction: the blurred flow, mixed in and laid back into the skin's tangent plane
111
- const n = [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
112
- let d = [0, 1, 2].map((k) => comb[v * 3 + k] * (1 - m) + (tmp[k] / W) * m);
113
- const dn = d[0] * n[0] + d[1] * n[1] + d[2] * n[2];
114
- d = d.map((x, k) => x - dn * n[k]);
115
- const l = Math.hypot(d[0], d[1], d[2]);
116
- if (l > 1e-4) for (let k = 0; k < 3; k++) comb[v * 3 + k] = d[k] / l;
117
- furLen[v] = furLen[v] * (1 - m) + (tmp[3] / W) * m;
118
- for (let k = 0; k < 3; k++) tint[v * 4 + k] = tint[v * 4 + k] * (1 - m) + (tmp[4 + k] / W) * m;
119
- changed++;
120
- }
121
- return { changed };
122
- }
@@ -1,80 +0,0 @@
1
- // Horse (Equus caballus). The first hoofed animal: cannon / pastern / hoof anatomy with a separate
2
- // hoof bone (fetlock sink under load, hoof flip in swing), long mane and tail hair, a rideable back
3
- // with tack attachment points, the four natural gaits and the horse's signature behaviours.
4
- import { horseRig, HEAD_O } from './rig.js';
5
- import { sculptHorse, EYE } from './sculpt.js';
6
- import { horseRegions } from './regions.js';
7
- import { horseCoat, COATS } from './coat.js';
8
- import { srgb } from '../../core/build/coatKit.js';
9
- import { motion } from './motion.js';
10
-
11
- const pick = (R, table) => {
12
- let x = R() * table.reduce((s, [, w]) => s + w, 0);
13
- for (const [k, w] of table) { x -= w; if (x <= 0) return k; }
14
- return table[table.length - 1][0];
15
- };
16
-
17
- export default {
18
- id: 'horse',
19
- name: 'Horse',
20
- latin: 'Equus caballus',
21
- group: 'hooves',
22
- plan: 'quadruped',
23
- covering: 'fur',
24
-
25
- // Seeded individual. Sex: mare, gelding or stallion (crest and sheath); age: adult or foal
26
- // (juvenile: long legs, short body, big head, bristly mane, short fluffy tail); size, proportions
27
- // (sport horse <-> lighter thoroughbred type <-> stockier type); coat colour and white markings.
28
- // o.variant may force a coat colour ('bay', 'chestnut', 'grey', ...).
29
- variation(R, o) {
30
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
31
- const age = o.age || 'adult';
32
- const foal = age === 'juvenile';
33
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
34
- const gelding = sex === 'male' && R() < 0.65;
35
- const type = g(); // - lighter / leggier, + stockier
36
- const size = (sex === 'male' ? 1.02 : 0.98) * (1 + 0.045 * g()) * (foal ? 0.5 : 1);
37
- const coat = COATS[o.variant] ? o.variant : pick(R, [['bay', 30], ['darkbay', 10], ['chestnut', 22], ['black', 8], ['grey', 12], ['palomino', 7], ['dun', 6], ['buckskin', 5]]);
38
- const face = pick(R, [['none', 35], ['star', 20], ['stripe', 10], ['blaze', 20], ['snip', 5], ['starsnip', 10]]);
39
- const sockH = () => pick(R, [[0, 60], [0.085, 8], [0.13, 10], [0.24, 14], [0.45, 8]]);
40
- const socks = [sockH(), sockH(), sockH(), sockH()];
41
- if (R() < 0.3) socks[2] = socks[3] = Math.max(socks[2], socks[3]);
42
- return {
43
- sex, age, size, gelding,
44
- crest: sex === 'male' ? (gelding ? 0.45 : 1) + 0.15 * g() : 0.15 + 0.1 * g(),
45
- coat, face, socks,
46
- flaxen: coat === 'chestnut' && R() < 0.3,
47
- greyLevel: coat === 'grey' ? (foal ? 0.05 : 0.15 + 0.75 * R()) : 0,
48
- maneLen: 0.14 + 0.12 * R(),
49
- maneSide: R() < 0.8 ? -1 : 1,
50
- coatShade: g(), coatLightness: 0.06 * g(),
51
- coatSeed: Math.floor(R() * 1e6),
52
- minThick: 0.006,
53
- warps: [
54
- { type: 'legs', k: (1 + 0.035 * g() - 0.025 * type) * (foal ? 1.55 : 1), top: 0.95 },
55
- { type: 'length', k: (1 + 0.03 * g() - 0.015 * type) * (foal ? 0.86 : 1), z0: -0.62, z1: 0.62 },
56
- { type: 'girth', k: (1 + 0.05 * type) * (sex === 'male' && !gelding ? 1.02 : 1), cy: 1.25, z0: -0.7, z1: 0.75 },
57
- { type: 'scaleAbout', c: HEAD_O, k: (1 + 0.03 * g()) * (foal ? 1.22 : 1), r0: 0.2, r1: 0.4 },
58
- ],
59
- };
60
- },
61
- rig: () => horseRig(),
62
- sculpt: (m, rig, params) => sculptHorse(m, rig, params),
63
- regions: horseRegions,
64
- coat: horseCoat,
65
- eyeSpecs: () => [1, -1].map((side) => ({ side, spec: EYE, headOrigin: HEAD_O, bone: 'head', look: EYE_LOOK })),
66
- motion,
67
- render: { markColor: srgb(0x241f1d), strandDensity: 900, clumpDensity: 1, shellScale: 1, finWidth: 0.45 },
68
- };
69
-
70
- // Horizontal bar pupil with corpora nigra, dark brown iris, sclera hidden, dark lids.
71
- export const EYE_LOOK = {
72
- pupil: 'bar',
73
- lids: 'mammal',
74
- // (dark brown, light enough that the iris and the bar pupil read under the catchlight: near-black
75
- // read as a black button, and as blue-grey glass under a sky reflection)
76
- iris: { inner: srgb(0x2a170c), mid: srgb(0x5c3a20), hi: srgb(0x8a5a2e), outer: srgb(0x1e1109) },
77
- sclera: { color: [0.55, 0.5, 0.45], visible: false },
78
- lidColor: [0.025, 0.02, 0.02],
79
- pupilSize: [0.12, 0.4],
80
- };