@bitefoundry/retrieviewer 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/ASSET-PROVENANCE.md +11 -0
  2. package/ASSETS-LICENSE.md +2 -1
  3. package/ATTRIBUTION.md +6 -1
  4. package/COMFORT-ART.md +95 -0
  5. package/DESKTOP-PLATFORMS.md +6 -1
  6. package/INSTALLING.md +4 -0
  7. package/PLUGIN.md +13 -10
  8. package/PREREQUISITES.md +95 -0
  9. package/PUPPY-MEMORY.md +102 -1
  10. package/README.ko.md +116 -0
  11. package/README.md +121 -59
  12. package/RELEASING.md +33 -13
  13. package/THIRD-PARTY-NOTICES.md +5 -3
  14. package/braille.mjs +35 -9
  15. package/examples/live.mjs +2 -2
  16. package/integrations/linux-overlay/overlay.py +8 -2
  17. package/integrations/macos-overlay/overlay.jxa +9 -7
  18. package/integrations/opencode/runtime.mjs +4 -4
  19. package/integrations/opencode/tui.tsx +1 -1
  20. package/integrations/windows-overlay/Overlay.cs +7 -6
  21. package/integrations/windows-overlay/desktop.mjs +2 -1
  22. package/integrations/windows-overlay/protocol.mjs +13 -7
  23. package/integrations/windows-overlay/worker.mjs +1 -1
  24. package/package.json +34 -13
  25. package/puppy-comfort-sprites.mjs +33 -0
  26. package/puppy-companion-comfort-q5.bin.gz +0 -0
  27. package/puppy-companion-comfort.json +140 -0
  28. package/puppy-companion-life.mjs +58 -18
  29. package/puppy-companion-objects.mjs +12 -0
  30. package/puppy-companion-sprites.mjs +4 -2
  31. package/puppy-frame-predictive.mjs +15 -9
  32. package/puppy-frame-store.mjs +2 -2
  33. package/puppy-paper-actions.mjs +4 -2
  34. package/puppy-paper-behaviors.mjs +1 -1
  35. package/puppy-paper-contact.mjs +4 -2
  36. package/puppy-paper-seat.mjs +4 -2
  37. package/puppy-paper-world.mjs +14 -0
  38. package/puppy-paper.mjs +79 -21
  39. package/retrieviewer-cli.mjs +8 -6
  40. package/retrieviewer-plugin.d.mts +1 -1
  41. package/retrieviewer-plugin.mjs +1 -1
  42. package/retrieviewer.d.mts +12 -4
  43. package/retrieviewer.mjs +20 -14
  44. package/locomotion-core.mjs +0 -21
  45. package/locomotion-puppy.animal +0 -0
  46. package/vendor/procedural-animals/package.json +0 -40
  47. package/vendor/procedural-animals/src/animal.js +0 -146
  48. package/vendor/procedural-animals/src/bake.js +0 -51
  49. package/vendor/procedural-animals/src/build.js +0 -81
  50. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  51. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  52. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  53. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  54. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  55. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  56. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  57. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  58. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  59. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  60. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  61. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  62. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  63. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  64. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  65. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  66. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  67. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  68. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  69. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  70. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  71. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  72. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  73. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  74. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  75. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  76. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  77. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  78. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  79. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  80. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  81. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  82. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  83. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  84. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  85. package/vendor/procedural-animals/src/index.d.ts +0 -98
  86. package/vendor/procedural-animals/src/index.js +0 -29
  87. package/vendor/procedural-animals/src/registry.js +0 -48
  88. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  89. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  90. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  91. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  92. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  93. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  94. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  95. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  96. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  97. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  98. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  99. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  100. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  101. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  102. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  103. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  104. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  105. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  106. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  107. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  108. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  109. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  110. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  111. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  112. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  113. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  114. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  115. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  116. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  117. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  118. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  119. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  120. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  121. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  122. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  123. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  124. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  125. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  126. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  127. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  128. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  129. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  130. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  131. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  132. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  133. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  134. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  135. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  136. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  137. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  138. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  139. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  140. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  141. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  142. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  143. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  144. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  145. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  146. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  147. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  148. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  149. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  150. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  151. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  152. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  153. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  154. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  155. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  156. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  157. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  158. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  159. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  160. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  161. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  162. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  163. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  164. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  165. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  166. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  167. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  168. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  169. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  170. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  171. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  172. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  173. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  174. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  175. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  176. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  177. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  178. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  179. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  180. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  181. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  182. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  183. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  184. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  185. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  186. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  187. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  188. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  189. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  190. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  191. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  192. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  193. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  194. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  195. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  196. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  197. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  198. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  199. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  200. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  201. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  202. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  203. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  204. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  205. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  206. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  207. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  208. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  209. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  210. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  211. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  212. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  213. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  214. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  215. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  216. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  217. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  218. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  219. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  220. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  221. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  222. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  223. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  224. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  225. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  226. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  227. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  228. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  229. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  230. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  231. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  232. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  233. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  234. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  235. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  236. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  237. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  238. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  239. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  240. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  241. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  242. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  243. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  244. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  245. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  246. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  247. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  248. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  249. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  250. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  251. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  252. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  253. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  254. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  255. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  256. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  257. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  258. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  259. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  260. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  261. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  262. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  263. package/vendor/procedural-animals/src/worker.js +0 -29
@@ -1,341 +0,0 @@
1
- // The brown rat's coat: coarse agouti fur (grey base, buff band, black tip; long sparse dark guard
2
- // hairs on the back), countershaded to a grey-buff belly with a fairly sharp flank line; short sleek
3
- // fur on the face; nearly bare pink-brown ears (skin with a haze of fine hair); pink feet (bare skin,
4
- // sparse pale hair on top, keratin claws); the long tail as ring scales (material 6 scutes across the
5
- // tail axis), dark above, paler below; pink nose leather; yellow-orange incisors.
6
- // Colour variants: agouti (wild), dark (urban melanic), albino (white, pink skin), hooded (black hood
7
- // over head and shoulders plus a dorsal stripe on white, grown as a pattern SDF on the surface).
8
- // Works in the reference space of rig.js; colours sampled from the reference photos.
9
- import { HEAD_O, HK } from './rig.js';
10
- import { neckS } from './regions.js';
11
- import { SDFModel } from '../../core/sdf/sdf.js';
12
- import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, fbm3 } from '../../core/math/vec.js';
13
- import { srgb, mix3, MAT } from '../../core/build/coatKit.js';
14
-
15
- // palette per colour variant (sRGB hex, literature + photo samples)
16
- const PALETTES = {
17
- agouti: {
18
- back: 0x615446, flank: 0x5a4f45, low: 0x756b61, belly: 0xa39a8c, head: 0x605244, cheek: 0x6b5e50,
19
- muzzle: 0x716558, earSkin: 0xa07c6e, earIn: 0xb88c80, feet: 0xcfaea2, tailTop: 0x4a3e38, tailUnder: 0x7e6e66,
20
- nose: 0xb9868a, claw: 0xd8c8b4, agouti: 0.95, undercoat: 0.55,
21
- },
22
- dark: {
23
- back: 0x3e3833, flank: 0x3a3430, low: 0x4a433d, belly: 0x7a746c, head: 0x3c3632, cheek: 0x423b36,
24
- muzzle: 0x4a423c, earSkin: 0x6e5a56, earIn: 0x86706a, feet: 0xb89a92, tailTop: 0x36302c, tailUnder: 0x5a504a,
25
- nose: 0x9a7276, claw: 0xc8b8a8, agouti: 0.45, undercoat: 0.35,
26
- },
27
- albino: {
28
- back: 0xf0ece4, flank: 0xf2eee6, low: 0xf2eee8, belly: 0xf5f2ec, head: 0xf2eee6, cheek: 0xf4f0ea,
29
- muzzle: 0xf4efe8, earSkin: 0xe8b9b0, earIn: 0xeab4ac, feet: 0xebbfb6, tailTop: 0xe4b8ae, tailUnder: 0xe8c2b8,
30
- nose: 0xeaafaf, claw: 0xf0e6dc, agouti: 0.0, undercoat: 0.1,
31
- },
32
- hooded: {
33
- // base white; the hood / stripe colour is the pattern colour
34
- back: 0xf0ede6, flank: 0xf0ede6, low: 0xf2efe8, belly: 0xf2efe8, head: 0xf0ede6, cheek: 0xf0ede6,
35
- muzzle: 0xf2eee8, earSkin: 0xa88480, earIn: 0xc49a94, feet: 0xebbfb6, tailTop: 0x8a7872, tailUnder: 0xd8b4aa,
36
- nose: 0xd89a9a, claw: 0xf0e6dc, agouti: 0.0, undercoat: 0.15,
37
- },
38
- };
39
- const HOOD = 0x221e1e;
40
-
41
- function coatColours(p) {
42
- const base = PALETTES[p.colour] || PALETTES.agouti;
43
- const k = p.coatWarmth || 0, l = p.coatLightness || 0;
44
- const fixed = ['belly', 'earSkin', 'earIn', 'feet', 'nose', 'claw', 'tailUnder'];
45
- const tweak = (h) => { const c = srgb(h); return [c[0] * (1 + 0.12 * k + l), c[1] * (1 + 0.03 * k + l), c[2] * (1 - 0.12 * k + l)]; };
46
- const C = {};
47
- for (const [key, v] of Object.entries(base)) {
48
- if (key === 'agouti' || key === 'undercoat') C[key] = v;
49
- else C[key] = p.colour === 'albino' || p.colour === 'hooded' || fixed.includes(key) ? srgb(v) : tweak(v);
50
- }
51
- // juveniles: softer, greyer (less rufous) fur
52
- if (p.juv && p.colour !== 'albino' && p.colour !== 'hooded') for (const key of ['back', 'flank', 'low', 'head', 'cheek']) { const c = C[key], g = (c[0] + c[1] + c[2]) / 3; C[key] = mix3(c, [g, g, g * 1.03], 0.35); }
53
- return C;
54
- }
55
-
56
- export function ratCoat(ctx) {
57
- const { model, rig, pos, nrm, regionOf: partOf, regionNames, weights, nV, params } = ctx;
58
- const { BONES, AXIAL } = rig;
59
- const COL = coatColours(params);
60
- const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
61
- const AX = weights.axialLengths;
62
- const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
63
- const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
64
- const sOcc = AX[1], sNeckBase = AX[3], sTailBase = AX[8];
65
- const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|femur|tibia|metatarsus|hpaw)/.test(b.name));
66
- const isFoot = BONES.map((b) => /^(fpaw|hpaw|metacarpus|metatarsus)/.test(b.name));
67
- const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
68
- const nosePrim = model.prims.find((p) => p.tag === 'nose');
69
- const cleftPrim = model.prims.find((p) => p.tag === 'lipcleft');
70
- const earPrims = model.prims.filter((p) => p.tag === 'ear');
71
- const clawPrims = model.prims.filter((p) => p.tag === 'claw');
72
- const jawPrims = model.forPart('jaw');
73
- const headPrims = model.forPart('body').filter((p) => (p.bone === 'head' || p.bone === 'snout') && !p.carve);
74
- const hooded = params.colour === 'hooded';
75
- const albino = params.colour === 'albino';
76
- const teethPart = regionNames.map((n) => n === 'uteeth' || n === 'lteeth');
77
- const jawPart = regionNames.map((n) => n === 'jaw');
78
- const headPart = regionNames.map((n) => n === 'head');
79
-
80
- const region = new Uint8Array(nV); // 0 torso, 1 neck, 2 head, 3 teeth, 4 tail, 5 ear, 6 jaw, 7 snout
81
- const legness = new Float32Array(nV);
82
- const legBone = new Int16Array(nV).fill(-1);
83
- for (let v = 0; v < nV; v++) {
84
- const bn = BONES[dominant[v]].name;
85
- const p = P(v);
86
- let lb = -1, lw = 0;
87
- for (let k = 0; k < 4; k++) {
88
- const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
89
- if (isLimb[b] && w > lw) { lw = w; lb = b; }
90
- }
91
- legBone[v] = lb;
92
- legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
93
- if (teethPart[partOf[v]]) region[v] = 3;
94
- else if (jawPart[partOf[v]]) region[v] = 6;
95
- else if (bn.startsWith('ear')) region[v] = 5;
96
- else if (bn === 'snout') region[v] = 7;
97
- else if (axialS[v] > sTailBase + 0.003 && bn.startsWith('tail')) region[v] = 4;
98
- else if (axialS[v] < sOcc + 0.002 || (headPart[partOf[v]] && neckS(p[0], p[1], p[2]) > 0.006)) region[v] = 2;
99
- else if (axialS[v] < sNeckBase) region[v] = 1;
100
- else region[v] = 0;
101
- if (region[v] === 2 || region[v] >= 3) legness[v] = 0;
102
- }
103
-
104
- // hooded pattern: signed distance (m, < 0 = coloured) of the hood (head, neck, shoulders and chest
105
- // down to the elbows) and the dorsal stripe to the tail root
106
- const hoodSDF = (p, n, r, v) => {
107
- if (r === 2 || r === 5 || r === 6 || r === 7) {
108
- // an optional blaze: a white wedge up the face from the nose (the berkshire / blazed fancy)
109
- if (params.blaze) {
110
- const h = mul(sub(p, HEAD_O), 1 / HK);
111
- const b = Math.abs(h[0]) - (0.0012 + 0.16 * Math.max(0, h[2] + 0.012)) * params.blaze;
112
- if (h[1] > -0.02) return clamp(-b, -0.004, 0.004) * (h[2] > -0.014 ? 1 : -1) - (h[2] > -0.014 ? 0 : 0.004);
113
- }
114
- return -0.006;
115
- }
116
- if (r === 4) return 0.006;
117
- // hood: behind the ears to the shoulders, slanting back over the withers
118
- const zc = 0.004 + 0.3 * (p[1] - 0.04) + 0.0012 * Math.sin(p[0] * 700);
119
- let d = zc - p[2];
120
- // the forelegs below the elbow stay white
121
- if (legness[v] > 0.5 && legBone[v] >= 0 && /radius|metacarpus|fpaw/.test(BONES[legBone[v]].name)) d = Math.max(d, 0.003);
122
- // the belly behind the chest stays white
123
- if (n[1] < -0.3) d = Math.max(d, -0.002 + (0.03 - p[1]) * 0.4);
124
- // dorsal stripe: ~16 mm wide down the back to the tail root, a little ragged
125
- const w = 0.0085 * (1 + 0.15 * Math.sin(p[2] * 180)) * smoothstep(-0.095, -0.075, p[2]) + 0.002;
126
- const up = smoothstep(0.1, 0.5, n[1]);
127
- const stripe = up > 0 ? Math.abs(p[0]) - w * up : 0.01;
128
- d = Math.min(d, stripe);
129
- return clamp(d, -0.006, 0.006);
130
- };
131
-
132
- const comb = new Float32Array(nV * 3);
133
- const tint = new Float32Array(nV * 4);
134
- const furLen = new Float32Array(nV);
135
- const markSDF = new Float32Array(nV).fill(1);
136
- const pattern = new Float32Array(nV).fill(1);
137
- const patternColor = new Float32Array(nV * 4);
138
- const surf = new Float32Array(nV * 4);
139
- const hoodC = srgb(params.hoodColour || HOOD);
140
- const tooth = srgb(0xd4a050), toothTip = srgb(0xeed8a8);
141
- const juvK = params.juv ? 0.75 : 1;
142
-
143
- for (let v = 0; v < nV; v++) {
144
- const p = P(v), n = N(v), r = region[v];
145
- const h = mul(sub(p, HEAD_O), 1 / HK);
146
- const bone = BONES[dominant[v]];
147
- const L = legness[v];
148
- let col, fl, mat = MAT.FUR, mark = 1, ag = COL.agouti, uc = COL.undercoat, gloss = 0, feat = 0, sz = 0;
149
-
150
- // ---- comb
151
- let d;
152
- if (r === 5) d = norm(sub(bone.tail, bone.head));
153
- else if (r === 2 || r === 6 || r === 7 || r === 3) d = norm(add(norm(sub(h, [0, -0.008, 0.026])), [0, -0.1, -0.5]));
154
- else if (r === 4) d = norm(sub(bone.tail, bone.head));
155
- else {
156
- const seg = weights.segT[v * 2] | 0;
157
- const a = AXIAL.points[seg], b = AXIAL.points[Math.min(seg + 1, AXIAL.points.length - 1)];
158
- d = norm(add(norm(sub(b, a)), [0, -0.5 * Math.abs(n[0]), 0]));
159
- if (L > 0 && legBone[v] >= 0) {
160
- const lbn = BONES[legBone[v]];
161
- let dl = norm(sub(lbn.tail, lbn.head));
162
- if (isFoot[legBone[v]]) dl = norm(add(dl, [0, 0, 0.4]));
163
- else dl = norm(add(dl, [0, -0.3, -0.2]));
164
- d = norm(add(mul(d, 1 - L), mul(dl, L)));
165
- }
166
- }
167
- let t = sub(d, mul(n, dot(d, n)));
168
- if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
169
- t = norm(t);
170
- comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
171
-
172
- // ---- colour, fur length, marks
173
- if (r === 3) {
174
- // incisors: orange-yellow enamel on the front face, paler toward the chisel tip
175
- mat = MAT.KERATIN; fl = 0; gloss = 0.8;
176
- const tipK = partOf[v] >= 0 && regionNames[partOf[v]] === 'uteeth' ? smoothstep(-0.016, -0.019, h[1]) : smoothstep(-0.019, -0.0165, h[1]);
177
- col = albino ? mix3(tooth, toothTip, 0.5) : mix3(tooth, toothTip, tipK * 0.6);
178
- } else if (r === 0 || r === 1) {
179
- const up = clamp(n[1], -1, 1);
180
- col = mix3(COL.low, COL.flank, smoothstep(-0.35, 0.2, up));
181
- col = mix3(col, COL.back, smoothstep(0.25, 0.85, up));
182
- // belly: grey-buff, a fairly sharp flank line (blended by normal and height)
183
- const ventral = smoothstep(-0.2, -0.55, n[1]) * smoothstep(0.045, 0.02, p[1]);
184
- const chest = smoothstep(0.0, 0.03, p[2]) * smoothstep(0.2, 0.6, n[2]) * smoothstep(0.045, 0.02, p[1]);
185
- col = mix3(col, COL.belly, clamp(Math.max(ventral, chest * 0.8, smoothstep(0.4, 0.8, -n[1])), 0, 1));
186
- // coarse coat: long dorsal guard hairs, shorter on the flanks, dense short belly fur
187
- fl = mix(0.0068, 0.0095, smoothstep(-0.4, 0.3, n[1]));
188
- fl = Math.max(fl, 0.0115 * smoothstep(0.5, 0.95, n[1]) * smoothstep(0.02, -0.02, p[2]) + 0.0098 * smoothstep(0.5, 0.95, n[1]) * smoothstep(-0.02, 0.02, p[2])); // longest over the loins
189
- fl = mix(fl, 0.0055, smoothstep(-0.3, -0.8, n[1]));
190
- ag *= 1 - smoothstep(0.1, 0.6, -n[1]);
191
- if (L > 0) {
192
- const lb = legBone[v] >= 0 ? BONES[legBone[v]].name : '';
193
- let lc = mix3(COL.flank, COL.belly, smoothstep(0.0, -0.8, n[0] * Math.sign(p[0] || 1)) * 0.6), lf = mix(0.003, 0.006, smoothstep(0.01, 0.04, p[1]));
194
- if (isFoot[legBone[v]] && p[1] < 0.013) {
195
- // pink feet: bare skin, sparse short pale hair on the top of the foot
196
- lc = COL.feet; lf = 0; mat = MAT.SKIN; gloss = 0.25;
197
- } else if (/radius|tibia/.test(lb)) {
198
- // lower legs: short fur, pinkish skin showing near the wrist and the heel
199
- const sk = smoothstep(0.016, 0.006, p[1]);
200
- lc = mix3(mix3(COL.flank, COL.belly, 0.35), COL.feet, sk * 0.6); lf = mix(0.0035, 0.0016, sk);
201
- }
202
- if (/femur/.test(lb)) { lc = mix3(col, lc, smoothstep(0.035, 0.015, p[1])); lf = mix(fl, lf, 0.4); }
203
- col = mix3(col, lc, L);
204
- fl = mix(fl, lf, L);
205
- if (mat === MAT.SKIN) { col = COL.feet; fl = lf; }
206
- ag *= 1 - 0.8 * L * (/radius|tibia|metacarpus|fpaw|metatarsus|hpaw/.test(lb) ? 1 : 0);
207
- // claws: pale keratin
208
- for (const c of clawPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.00025) { mat = MAT.KERATIN; col = COL.claw; fl = 0; gloss = 0.7; }
209
- // soles: the bare pads, a little darker and glossier
210
- if (mat === MAT.SKIN && n[1] < -0.5) { col = mix3(col, [col[0] * 0.85, col[1] * 0.78, col[2] * 0.78], 0.6); fl = 0; gloss = 0.4; }
211
- }
212
- // the anus / scrotum region: short sparse hair
213
- if (p[2] < -0.078 && p[1] < 0.045 && n[2] < -0.3) fl = Math.min(fl, 0.004);
214
- } else if (r === 4) {
215
- // tail: ring scales (scutes across the tail axis, ~1 mm), dark above, paler below, a little
216
- // paler toward the tip; sparse short bristles are the scute relief
217
- const bb = BONES[dominant[v]];
218
- const ax = norm(sub(bb.tail, bb.head));
219
- const dorsal = norm(cross([1, 0, 0], ax));
220
- const top = dot(n, dorsal);
221
- const along = clamp((axialS[v] - sTailBase) / Math.max(1e-4, AX[AX.length - 1] - sTailBase), 0, 1);
222
- col = mix3(COL.tailUnder, COL.tailTop, smoothstep(-0.45, 0.35, top));
223
- col = mix3(col, COL.tailUnder, 0.25 * along);
224
- mat = MAT.SCALES; fl = 0; gloss = 0.3;
225
- sz = 0.00095 * (1 - 0.35 * along) * juvK;
226
- feat = -1; // scutes: rings across the comb
227
- // the fur of the rump runs a little way onto the tail root
228
- if (axialS[v] < sTailBase + 0.009) { mat = MAT.FUR; col = mix3(COL.flank, COL.tailTop, 0.4); fl = 0.004; sz = 0; feat = 0; }
229
- } else if (r === 5) {
230
- // ears: thin, nearly bare skin (fine short hair haze), pinker in the cup, a darker rim
231
- const side = p[0] >= 0 ? 0 : 1;
232
- const ep = earPrims[side];
233
- const face = [ep.P[9], ep.P[10], ep.P[11]];
234
- const upE = [ep.P[6], ep.P[7], ep.P[8]], latE = [ep.P[3], ep.P[4], ep.P[5]];
235
- const c = [ep.P[0], ep.P[1], ep.P[2]];
236
- const q = sub(p, c);
237
- const along = dot(q, upE) / ep.P[13], across = dot(q, latE) / ep.P[12];
238
- const rim = 1 - Math.hypot(along, across);
239
- const inner = smoothstep(0.0, 0.4, dot(n, face)) * smoothstep(0.02, 0.12, rim);
240
- col = mix3(COL.earSkin, COL.earIn, inner);
241
- mat = MAT.FUR; gloss = 0;
242
- fl = 0.0004;
243
- // the ear root is furred like the head
244
- const root = smoothstep(-0.2, -0.75, along);
245
- if (root > 0) { col = mix3(col, COL.head, root); fl = mix(fl, 0.003, root); }
246
- ag = 0; uc = 0.6;
247
- } else {
248
- // head, snout and jaw: short sleek fur, paler on the lips and chin
249
- col = COL.head;
250
- const g3 = (cc, rr) => Math.exp(-(((Math.abs(h[0]) - cc[0]) / rr[0]) ** 2 + ((h[1] - cc[1]) / rr[1]) ** 2 + ((h[2] - cc[2]) / rr[2]) ** 2));
251
- col = mix3(col, COL.cheek, g3([0.009, -0.01, -0.006], [0.005, 0.006, 0.01]));
252
- col = mix3(col, COL.muzzle, smoothstep(0.004, 0.018, h[2]) * 0.8);
253
- const chinW = r === 6 ? 0.7 : smoothstep(-0.015, -0.019, h[1]) * smoothstep(-0.2, -0.7, n[1]);
254
- col = mix3(col, COL.belly, clamp(chinW, 0, 1) * 0.8);
255
- fl = mix(0.0045, 0.0018, smoothstep(-0.01, 0.02, h[2]));
256
- if (Math.abs(h[0]) > 0.008 && h[1] < -0.004 && h[2] < 0.004) fl = Math.max(fl, 0.0048); // cheek fur
257
- if (h[2] < -0.02) fl = mix(fl, 0.0068, smoothstep(-0.02, -0.03, h[2])); // nape
258
- ag *= 0.75;
259
- // eye rims: dark bare lid margin
260
- for (const e of eyePrims) {
261
- const de = SDFModel.dist(e, p[0], p[1], p[2]);
262
- if (Math.abs(de) < 0.0008) mark = Math.min(mark, Math.abs(de) - 0.0003);
263
- if (Math.abs(de) < 0.0003) { mat = MAT.DARK_SKIN; fl = 0; }
264
- else fl = Math.min(fl, mix(0.0004, fl, smoothstep(0.0009, 0.0045, Math.abs(de))));
265
- }
266
- // nose leather (pink rhinarium) and the split upper lip
267
- if (nosePrim && SDFModel.dist(nosePrim, p[0], p[1], p[2]) < 0.00035 && h[2] > 0.0205 && h[1] > -0.0145) { mat = MAT.SKIN; col = COL.nose; fl = 0; gloss = 0.65; }
268
- if (cleftPrim) {
269
- const dc = SDFModel.dist(cleftPrim, p[0], p[1], p[2]);
270
- if (dc < 0.0005) { mark = Math.min(mark, dc - 0.0003); fl = Math.min(fl, 0.0004); }
271
- }
272
- if (r === 7) fl = Math.min(fl, 0.0022); // whisker pads: short dense fur
273
- // lips / mouth lining: head vertices touching the jaw surface, jaw vertices inside the head
274
- if (partOf[v] >= 0 && !jawPart[partOf[v]]) {
275
- const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
276
- if (dj < 0.00025) { mat = MAT.MOUTH; col = [0.3, 0.12, 0.12]; fl = 0; }
277
- } else {
278
- const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
279
- if (dh < 0.0) { mat = MAT.MOUTH; col = [0.3, 0.12, 0.12]; fl = 0; }
280
- }
281
- }
282
- if (params.juv && mat === MAT.FUR) fl *= 0.8;
283
-
284
- // ---- hooded: hood and dorsal stripe (pattern SDF, crisp but hair-ragged edges)
285
- if (hooded && (mat === MAT.FUR || mat === MAT.DARK_SKIN)) pattern[v] = hoodSDF(p, n, r, v);
286
-
287
- // low-frequency colour variation
288
- const cv = fbm3(p[0] * 90, p[1] * 90, p[2] * 90, 3) - 0.5;
289
- if (mat === MAT.FUR) col = [col[0] * (1 + 0.16 * cv), col[1] * (1 + 0.13 * cv), col[2] * (1 + 0.1 * cv)];
290
-
291
- tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
292
- furLen[v] = mat === MAT.FUR || mat === MAT.SKIN ? fl : 0;
293
- markSDF[v] = mark;
294
- patternColor[v * 4] = hoodC[0]; patternColor[v * 4 + 1] = hoodC[1]; patternColor[v * 4 + 2] = hoodC[2]; patternColor[v * 4 + 3] = hooded ? 1 : 0;
295
- if (mat === MAT.SCALES) { surf[v * 4] = gloss; surf[v * 4 + 1] = sz; surf[v * 4 + 2] = feat; surf[v * 4 + 3] = -0.4; }
296
- else { surf[v * 4] = gloss; surf[v * 4 + 1] = 0; surf[v * 4 + 2] = mat === MAT.FUR ? clamp(ag, 0, 1) : 0; surf[v * 4 + 3] = mat === MAT.FUR ? uc : 0; }
297
- }
298
-
299
- // smooth fur length, agouti banding and colour across the fur so region boundaries (head / neck,
300
- // body / legs) never show as a line in the coat
301
- const nb = weights.neighbors;
302
- const tmp = new Float32Array(nV), tmpA = new Float32Array(nV), tmpC = new Float32Array(nV * 3);
303
- for (let it = 0; it < 8; it++) {
304
- for (let v = 0; v < nV; v++) {
305
- const ns = nb[v];
306
- tmp[v] = furLen[v]; tmpA[v] = surf[v * 4 + 2];
307
- tmpC[v * 3] = tint[v * 4]; tmpC[v * 3 + 1] = tint[v * 4 + 1]; tmpC[v * 3 + 2] = tint[v * 4 + 2];
308
- if (!ns || !ns.length || tint[v * 4 + 3] !== MAT.FUR) continue;
309
- let a = 0, g = 0, n = 0, c0 = 0, c1 = 0, c2 = 0;
310
- for (const q of ns) {
311
- if (tint[q * 4 + 3] !== MAT.FUR) continue;
312
- a += furLen[q]; g += surf[q * 4 + 2]; c0 += tint[q * 4]; c1 += tint[q * 4 + 1]; c2 += tint[q * 4 + 2]; n++;
313
- }
314
- if (!n) continue;
315
- tmp[v] = 0.5 * furLen[v] + (0.5 * a) / n;
316
- tmpA[v] = 0.5 * surf[v * 4 + 2] + (0.5 * g) / n;
317
- if (it < 3) { tmpC[v * 3] = 0.5 * tint[v * 4] + 0.5 * c0 / n; tmpC[v * 3 + 1] = 0.5 * tint[v * 4 + 1] + 0.5 * c1 / n; tmpC[v * 3 + 2] = 0.5 * tint[v * 4 + 2] + 0.5 * c2 / n; }
318
- }
319
- furLen.set(tmp);
320
- for (let v = 0; v < nV; v++) { surf[v * 4 + 2] = tmpA[v]; tint[v * 4] = tmpC[v * 3]; tint[v * 4 + 1] = tmpC[v * 3 + 1]; tint[v * 4 + 2] = tmpC[v * 3 + 2]; }
321
- }
322
- // smooth the hair flow (the axial segment directions change at the joints: no seams in the coat)
323
- const cb = new Float32Array(nV * 3);
324
- for (let it = 0; it < 6; it++) {
325
- for (let v = 0; v < nV; v++) {
326
- const ns = nb[v];
327
- let x = comb[v * 3], y = comb[v * 3 + 1], z = comb[v * 3 + 2];
328
- if (ns && ns.length && tint[v * 4 + 3] === MAT.FUR && region[v] !== 5) {
329
- for (const q of ns) { if ((region[q] <= 1 ? 0 : region[q]) !== (region[v] <= 1 ? 0 : region[v])) continue; x += comb[q * 3]; y += comb[q * 3 + 1]; z += comb[q * 3 + 2]; }
330
- const nx = nrm[v * 3], ny = nrm[v * 3 + 1], nz = nrm[v * 3 + 2];
331
- const dn = x * nx + y * ny + z * nz;
332
- x -= dn * nx; y -= dn * ny; z -= dn * nz;
333
- const l = Math.hypot(x, y, z) || 1;
334
- x /= l; y /= l; z /= l;
335
- }
336
- cb[v * 3] = x; cb[v * 3 + 1] = y; cb[v * 3 + 2] = z;
337
- }
338
- comb.set(cb);
339
- }
340
- return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, region };
341
- }
@@ -1,111 +0,0 @@
1
- // Brown rat (Rattus norvegicus). A small plantigrade quadruped on the rabbit / bear templates: a
2
- // hunched pear-shaped body, pointed snout with whiskers (real geometry) and yellow incisors, dark
3
- // bulging eyes, thin rounded ears, pink hands and feet with separate toes, and a long scaly tail.
4
- // Colours are variants (createAnimal('rat', { variant: 'hooded' })): 'wild' (default: agouti, ~15 %
5
- // of seeds the dark urban form), 'agouti', 'dark', 'albino' (white, pink skin, red eyes), 'hooded'
6
- // (fancy: black hood and dorsal stripe on white, sometimes a blaze); pups with { age: 'juvenile' }.
7
- import { ratRig, HEAD_O, hl } from './rig.js';
8
- import { sculptRat, eyeOf } from './sculpt.js';
9
- import { ratRegions } from './regions.js';
10
- import { ratCoat } from './coat.js';
11
- import { ratWhiskers } from './whiskers.js';
12
- import { motion } from './motion.js';
13
- import { rng } from '../../core/math/vec.js';
14
-
15
- const VARIANTS = [
16
- ['wild', 'Wild brown rat'],
17
- ['agouti', 'Agouti'],
18
- ['dark', 'Dark (urban)'],
19
- ['albino', 'Albino (PEW)'],
20
- ['hooded', 'Hooded (fancy)'],
21
- ];
22
-
23
- // Seeded individual. Size: males 350-500 g, females 250-350 g (length ~ mass^1/3 against the
24
- // ~330 g reference); juveniles (~4-5 weeks, ~70 g): 0.6 x, big head, eyes, ears and feet, shorter
25
- // tail. Proportions per seed: legs, body length, girth, head width, muzzle, ears, tail length.
26
- function variation(R, o) {
27
- const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
28
- // the coat from its own hashed stream so consecutive seeds do not correlate
29
- const M = rng(Math.imul((o.seed ?? 1) + 0x5bd1e995, 0x297a2d39) ^ 0x1b873593);
30
- for (let i = 0; i < 3; i++) M();
31
- let variant = o.variant && VARIANTS.some((v) => v[0] === o.variant) ? o.variant : 'wild';
32
- const colour = variant === 'wild' ? (M() < 0.15 ? 'dark' : 'agouti') : variant;
33
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
34
- const age = o.age || 'adult';
35
- const juv = age === 'juvenile' ? 1 : 0;
36
- const male = sex === 'male' ? 1 : 0;
37
- const fancy = colour === 'albino' || colour === 'hooded';
38
- const size = (male ? 1.07 : 0.965) * (1 + 0.045 * g()) * (juv ? 0.6 : 1);
39
- const head = (male ? 1.03 : 0.99) * (1 + 0.025 * g()) * (juv ? 1.22 : 1);
40
- const params = {
41
- sex, age, size, variant, colour, juv,
42
- tail: (1 + 0.05 * g()) * (juv ? 0.86 : 1),
43
- tailThick: (1 + 0.06 * g()) * (male ? 1.05 : 1) * (juv ? 0.9 : 1),
44
- ear: (1 + 0.06 * g()) * (juv ? 1.14 : 1) * (fancy ? 1.04 : 1),
45
- earWidth: 1 + 0.05 * g(),
46
- headWidth: (male ? 1.05 : 1) * (1 + 0.03 * g()),
47
- muzzle: (1 + 0.04 * g()) * (juv ? 0.86 : 1) * (fancy ? 0.97 : 1),
48
- whisker: (1 + 0.06 * g()) * (juv ? 0.8 : 1),
49
- blaze: colour === 'hooded' && M() < 0.35 ? 0.7 + 0.5 * M() : 0,
50
- coatWarmth: colour === 'agouti' ? 0.9 * g() : 0.3 * g(),
51
- coatLightness: 0.09 * g(),
52
- coatSeed: Math.floor(R() * 1e6),
53
- minThick: 0.0005,
54
- warps: [
55
- { type: 'legs', k: 1 + 0.035 * g() - 0.04 * juv, top: 0.03 },
56
- { type: 'length', k: 1 + 0.03 * g() - 0.07 * juv + 0.01 * male, z0: -0.07, z1: 0.03 },
57
- { type: 'girth', k: 1 + 0.06 * g() + 0.03 * male - 0.04 * juv + (fancy ? 0.02 : 0), cy: 0.05, z0: -0.085, z1: 0.04, fade: 0.012 },
58
- { type: 'scaleAbout', c: HEAD_O, k: head, r0: 0.016, r1: 0.034 },
59
- // pups: big feet
60
- ...(juv ? [[0.019, 0.037], [-0.019, 0.037], [0.024, -0.028], [-0.024, -0.028]].map(([x, z]) => ({ type: 'scaleAbout', c: [x, 0.003, z], k: 1.15, r0: 0.006, r1: 0.014 })) : []),
61
- ],
62
- };
63
- return params;
64
- }
65
-
66
- export default {
67
- id: 'rat',
68
- name: 'Brown rat',
69
- latin: 'Rattus norvegicus',
70
- group: 'paws',
71
- plan: 'quadruped',
72
- covering: 'fur',
73
- variants: VARIANTS.map(([id, name]) => ({ id, name, latin: 'Rattus norvegicus' })),
74
- variation,
75
- rig: (params) => ratRig(params),
76
- sculpt: (m, rig, params) => sculptRat(m, rig, params),
77
- regions: ratRegions,
78
- coat: ratCoat,
79
- surfaces: ratWhiskers,
80
- eyeSpecs: (params) => [1, -1].map((side) => ({ side, spec: eyeOf(params), headOrigin: HEAD_O, bone: 'head', look: params.colour === 'albino' ? EYE_ALBINO : EYE_LOOK })),
81
- motion,
82
- render: (params = {}) => ({
83
- markColor: params.colour === 'albino' ? [0.5, 0.25, 0.25] : [0.02, 0.015, 0.013],
84
- strandDensity: 2600,
85
- clumpDensity: 2.2,
86
- shellScale: 1,
87
- // (no silhouette fins: on this short coat their inset edge drew a dark line along the outline)
88
- fins: false,
89
- raiseLen: [0.004, 0.009],
90
- }),
91
- };
92
-
93
- // Wild type: a glossy near-black bead (very dark brown iris, the round pupil barely distinguishable)
94
- export const EYE_LOOK = {
95
- pupil: 'round',
96
- lids: 'mammal',
97
- iris: { inner: [0.006, 0.003, 0.0025], mid: [0.011, 0.006, 0.005], hi: [0.03, 0.017, 0.012], outer: [0.004, 0.0025, 0.002] },
98
- sclera: { color: [0.01, 0.007, 0.006], visible: false },
99
- lidColor: [0.03, 0.02, 0.018],
100
- pupilSize: [0.55, 0.7],
101
- };
102
- // Albino: red (the fundus through an unpigmented iris), a pale pink tinge at the rim
103
- export const EYE_ALBINO = {
104
- pupil: 'round',
105
- lids: 'mammal',
106
- iris: { inner: [0.35, 0.02, 0.03], mid: [0.45, 0.04, 0.05], hi: [0.7, 0.3, 0.3], outer: [0.3, 0.05, 0.06] },
107
- sclera: { color: [0.4, 0.1, 0.1], visible: false },
108
- lidColor: [0.7, 0.42, 0.4],
109
- pupilSize: [0.45, 0.6],
110
- };
111
- void hl;
@@ -1,90 +0,0 @@
1
- // Brown rat motion data for the quadruped engine (schema: src/core/motion/quadruped.js).
2
- // Numbers are for the reference individual (params.size = 1, ~330 g, hip joint ~56 mm); the engine
3
- // scales them by dynamic similarity for pups and big males.
4
- //
5
- // Gaits (overground runway data for intact rats, Górska et al. / Koopmans et al.;
6
- // hindlimb kinematics, Sci. Rep. 2022): an exploratory walk (lateral-sequence "alternate" pattern,
7
- // LF ~0.3 after LH, duty 0.64-0.75, 1.5-2.5 Hz), a fast walk (4 Hz, duty 0.52), the trot (4.5 Hz,
8
- // duty 0.43, diagonal pairs), a transverse gallop (5.1 Hz), the half-bound (5.6 Hz: the hind feet
9
- // land almost together, the forefeet staggered) and the bound (5.7 Hz, pairs synchronous), mean duty
10
- // 0.40 when running; bursts to ~3 m/s. Offsets [FL, FR, HL, HR] relative to HL touchdown.
11
- import { hooks } from './behaviour.js';
12
-
13
- const G = (name, v, f, D, off, o) => ({ name, v, f, D, off, ...o });
14
- const WALK = [0.3, 0.8, 0.0, 0.5];
15
- const TROT = [0.5, 0.02, 0.0, 0.5];
16
- const GALLOP = [0.46, 0.58, 0.0, 0.12];
17
- const HALFB = [0.6, 0.48, 0.0, 0.06];
18
- const BOUND = [0.5, 0.52, 0.0, 0.02];
19
-
20
- export const motion = {
21
- maxSpeed: 3.0,
22
- accel: [7, 12],
23
- decel: 6,
24
- turnRate: 5,
25
- latAccelMax: 6,
26
- gears: { walk: 0.3, trot: 0.95, gallop: 1.25, halfbound: 1.45, bound: 1.75 },
27
- gallopBlend: [1.1, 1.8],
28
- flexBlend: [1.0, 1.7],
29
- hooks,
30
- gaits: [
31
- G('walk', 0.0, 1.6, 0.74, WALK, { liftF: 0.006, liftH: 0.007, bobF: 0.0012, bobH: 0.0015, flex: 0.0, lat: 0.07, fold: 0.5, heel: 25, tail: 0, drop: 0 }),
32
- G('walk', 0.3, 2.5, 0.64, WALK, { liftF: 0.007, liftH: 0.008, bobF: 0.0015, bobH: 0.002, flex: 0.0, lat: 0.08, fold: 0.55, heel: 35, tail: 0.1, drop: 0 }),
33
- G('walk', 0.7, 4.0, 0.52, WALK, { liftF: 0.008, liftH: 0.009, bobF: 0.002, bobH: 0.0025, flex: 0.02, lat: 0.06, fold: 0.6, heel: 45, tail: 0.2, drop: 0.001 }),
34
- G('trot', 0.9, 4.5, 0.43, TROT, { liftF: 0.009, liftH: 0.01, bobF: 0.0025, bobH: 0.003, flex: 0.03, lat: 0.03, fold: 0.7, heel: 55, tail: 0.3, drop: 0.002 }),
35
- G('trot', 1.05, 4.6, 0.43, TROT, { liftF: 0.009, liftH: 0.01, bobF: 0.0025, bobH: 0.003, flex: 0.04, lat: 0.03, fold: 0.7, heel: 55, tail: 0.3, drop: 0.002 }),
36
- G('gallop', 1.15, 5.0, 0.4, GALLOP, { liftF: 0.011, liftH: 0.012, bobF: 0.003, bobH: 0.004, flex: 0.14, lat: 0, fold: 0.8, heel: 60, tail: 0.5, drop: 0.003 }),
37
- G('gallop', 1.3, 5.2, 0.39, GALLOP, { liftF: 0.011, liftH: 0.012, bobF: 0.003, bobH: 0.004, flex: 0.16, lat: 0, fold: 0.8, heel: 60, tail: 0.5, drop: 0.003 }),
38
- G('halfbound', 1.4, 5.6, 0.38, HALFB, { liftF: 0.012, liftH: 0.013, bobF: 0.0035, bobH: 0.0045, flex: 0.2, lat: 0, fold: 0.85, heel: 62, tail: 0.6, drop: 0.003 }),
39
- G('halfbound', 1.6, 5.65, 0.36, HALFB, { liftF: 0.012, liftH: 0.013, bobF: 0.0035, bobH: 0.0045, flex: 0.22, lat: 0, fold: 0.85, heel: 62, tail: 0.6, drop: 0.003 }),
40
- G('bound', 1.75, 5.7, 0.34, BOUND, { liftF: 0.013, liftH: 0.014, bobF: 0.004, bobH: 0.005, flex: 0.25, lat: 0, fold: 0.72, heel: 64, tail: 0.7, drop: 0.004 }),
41
- G('bound', 3.0, 6.2, 0.28, BOUND, { liftF: 0.014, liftH: 0.015, bobF: 0.0045, bobH: 0.0055, flex: 0.28, lat: 0, fold: 0.75, heel: 66, tail: 0.8, drop: 0.004 }),
42
- ],
43
- feet: {
44
- // plantigrade: the palm and the whole hind sole flat in stance, the heel lifting first
45
- front: { type: 'plantigrade', curl: 20, heelStrike: 0, flex: 6, couple: 0.6, toeOut: 0.12, elbowDown: 0, retract: 0.5, liftBehind: 0.4 },
46
- hind: { type: 'plantigrade', curl: 35, heelStrike: 0, lead: 0.35, toeOut: 0.22, retract: 0.35, liftBehind: 0.3 },
47
- },
48
- stance: { width: 1, gallopWidth: 0.85, sprawl: 0, crouch: 0, scuff: [2, 3, 0] },
49
- body: {
50
- // sections of the bind mesh at the girdles, fur included (measured on the bind mesh: chest bottom
51
- // ~25 mm, belly ~22 mm (the sections reach a little lower, to 19-22 mm: with the measured 23 mm a
52
- // curled sleeping rat pushed a forearm into the ground), the rump over the tail root ~21 mm above the ground; the back
53
- // (long and low) ~91 mm over the withers, ~96 mm over the loins, ~85 mm over the tail root)
54
- chest: { y: 0.0565, hh: 0.0345, hw: 0.033 },
55
- pelvis: { y: 0.059, hh: 0.037, hw: 0.047 },
56
- mid: { y: 0.0575, hh: 0.0385, hw: 0.048 },
57
- rump: { z: -0.08, y: 0.0545, hh: 0.038, hw: 0.029 },
58
- back: 0.024,
59
- },
60
- head: {
61
- // [speed, raise (m), reach (m), pitch (rad, + = nose down)]: nose low, sniffing the ground
62
- carriage: [[0, -0.002, 0.002, 0.18], [0.3, -0.004, 0.004, 0.2], [1.0, -0.004, 0.006, 0.12], [2, -0.004, 0.008, 0.08]],
63
- stab: [[0, 0], [0.3, 0.15], [1, 0.25], [2, 0.35]],
64
- neckBend: 35, neckPivot: 0.5, yaw: 0.9, pitchUp: 0.6, pitchDown: 0.6,
65
- },
66
- tail: {
67
- // slopes down from the rump and trails along the ground at rest; carried straight out behind,
68
- // just off the ground, when running
69
- carriage: [
70
- [0.0, [-38, -30, -20, -12, -6, -3, -1, 0, 0, 0, 0, 0]],
71
- [0.5, [-30, -22, -14, -8, -4, -2, 0, 0, 0, 0, 0, 0]],
72
- [1.3, [-18, -10, -5, -2, 0, 0, 1, 1, 1, 1, 1, 1]],
73
- [3.0, [-12, -5, -1, 1, 2, 2, 2, 2, 2, 2, 2, 2]],
74
- ],
75
- stiffness: 1.1, sway: 6, flick: 0, wag: 0, swish: 0, curl: 0, balance: 0.4,
76
- radius: [0.0065, 0.0014],
77
- },
78
- ears: { mobility: 0.5, speedFlatten: 0.5, flattenSpeeds: [1.2, 3], twitch: 1.2, prick: 0.3 },
79
- breath: { rate: 1.4, amp: 0.02, pant: false, heatSpeed: 3 },
80
- actions: {
81
- attack: { style: 'bite-lunge' },
82
- eat: { style: 'graze', dropF: 0.1, dropH: 0.0 },
83
- drink: { dropF: 0.12, dropH: 0.0 },
84
- jump: { height: 0.25, distance: 0.45 },
85
- idle: { shift: 0.5 },
86
- sit: {},
87
- lie: {},
88
- sleep: { style: 'curl' },
89
- },
90
- };
@@ -1,92 +0,0 @@
1
- // Meshing plan: body at 1 mm, the thin tail at 0.7 mm, head (with the thin ears) at 0.6 mm overlapping across the nape and
2
- // cross-faded, the four feet (separate toes, ~1.7 mm thick) at 0.5 mm cross-faded above the wrist and
3
- // the heel, eyelid patches at 0.2 mm, the lower jaw as its own rigid surface, and the incisors as
4
- // rigid parts (hero .. medium; sub-pixel below).
5
- import { ratJoints, HEAD_O } from './rig.js';
6
- import { EYE } from './sculpt.js';
7
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
8
- import { lerp, norm, sub, smoothstep } from '../../core/math/vec.js';
9
-
10
- const J0 = ratJoints();
11
- export const NECK_CUT = (() => {
12
- const d = norm(sub(J0.occiput, J0.neckBase));
13
- const c = lerp(J0.neckMid, J0.occiput, 0.5);
14
- return { c, d, band: 0.003 };
15
- })();
16
- export const neckS = (x, y, z) => (x - NECK_CUT.c[0]) * NECK_CUT.d[0] + (y - NECK_CUT.c[1]) * NECK_CUT.d[1] + (z - NECK_CUT.c[2]) * NECK_CUT.d[2];
17
-
18
- // feet: below this height (and inside the foot boxes) the fine foot mesh takes over
19
- // tail: behind this plane the tail region takes over
20
- export const TAIL_Z = -0.104, TAIL_B = 0.002;
21
- export const FOOT_Y = 0.0105, FOOT_B = 0.0018;
22
- const FOOT_BOX = {
23
- F: { x0: 0.006, x1: 0.036, z0: 0.012, z1: 0.058 },
24
- H: { x0: 0.006, x1: 0.04, z0: -0.058, z1: 0.01 },
25
- };
26
- export const inFoot = (x, z) => {
27
- const ax = Math.abs(x);
28
- for (const b of Object.values(FOOT_BOX)) if (ax > b.x0 && ax < b.x1 && z > b.z0 && z < b.z1) return true;
29
- return false;
30
- };
31
- // > 0 inside the foot region (signed height below the cut)
32
- const footS = (x, y, z) => (inFoot(x, z) ? FOOT_Y - y : -1);
33
-
34
- export const EYE_PATCH = { R: 0.0056, B: 0.0008, h: 0.0002, centres: [1, -1].map((c) => eyeFrameOf(EYE, HEAD_O, c).c) };
35
- export function eyePatchWeight(x, y, z) {
36
- let dmin = 1e9;
37
- for (const c of EYE_PATCH.centres) dmin = Math.min(dmin, Math.hypot(x - c[0], y - c[1], z - c[2]));
38
- return 1 - smoothstep(EYE_PATCH.R - EYE_PATCH.B, EYE_PATCH.R + EYE_PATCH.B, dmin);
39
- }
40
-
41
- export function ratRegions(rig, params, Q) {
42
- // (the crowd tier's ~4 mm body cells need a wider overlap across the nape, or a slit opens there)
43
- const r = Q.res, B = NECK_CUT.band * Math.max(1, r / 2.6);
44
- const patches = Q.eyePatches ? EYE_PATCH.centres.map((c) => ({ c, R: EYE_PATCH.R, B: EYE_PATCH.B, h: EYE_PATCH.h * Math.max(1, r) })) : [];
45
- const feet = r < 2.5; // low / crowd: the feet stay in the body mesh
46
- const fine = r < 2.5; // the thin tail in its own finer region (hero .. medium)
47
- const jobs = [
48
- [{ name: 'body', part: 'body', bmin: [-0.055, -0.006, -0.3], bmax: [0.055, 0.15, 0.08], h: 0.00103 * r, F: 4, clip: (x, y, z) => neckS(x, y, z) < B && (!feet || footS(x, y, z) < FOOT_B) && (!fine || z > TAIL_Z - TAIL_B) }],
49
- [
50
- { name: 'head', part: 'body', bmin: [-0.04, 0.026, 0.035], bmax: [0.04, 0.108, 0.114], h: 0.00062 * r, F: 6, clip: (x, y, z) => neckS(x, y, z) > -B, patches },
51
- { name: 'jaw', part: 'jaw', bmin: [-0.013, 0.032, 0.053], bmax: [0.013, 0.062, 0.106], h: 0.00055 * r, F: 6, rigidBone: 'jaw' },
52
- ],
53
- ];
54
- if (feet) {
55
- const fj = [];
56
- for (const [k, b] of Object.entries(FOOT_BOX)) {
57
- for (const s of [1, -1]) {
58
- const x0 = s > 0 ? b.x0 : -b.x1, x1 = s > 0 ? b.x1 : -b.x0;
59
- fj.push({ name: 'foot' + k + (s > 0 ? 'L' : 'R'), part: 'body', bmin: [x0, -0.004, b.z0], bmax: [x1, FOOT_Y + 0.004, b.z1], h: 0.0005 * r, F: 6, clip: (x, y, z) => footS(x, y, z) > -FOOT_B });
60
- }
61
- }
62
- jobs.push(fj);
63
- }
64
- if (fine) jobs[0].push({ name: 'tail', part: 'body', bmin: [-0.02, -0.004, -0.32], bmax: [0.02, 0.05, TAIL_Z + 0.004], h: 0.0007 * r, F: 4, clip: (x, y, z) => z < TAIL_Z + TAIL_B });
65
- if (r < 2.5) {
66
- const ht = 0.00022 * Math.min(r, 1.6);
67
- jobs.push([
68
- { name: 'uteeth', part: 'uteeth', bmin: [-0.004, 0.042, 0.09], bmax: [0.004, 0.056, 0.106], h: ht, F: 4, rigidBone: 'head' },
69
- { name: 'lteeth', part: 'lteeth', bmin: [-0.004, 0.04, 0.088], bmax: [0.004, 0.052, 0.104], h: ht, F: 4, rigidBone: 'jaw' },
70
- ]);
71
- }
72
- return {
73
- jobs,
74
- fade(x, y, z, name, patch) {
75
- const sN = neckS(x, y, z);
76
- if (name === 'body') {
77
- let f = 1 - smoothstep(-B, B, sN);
78
- if (feet && inFoot(x, z)) f *= 1 - smoothstep(-FOOT_B, FOOT_B, FOOT_Y - y);
79
- if (fine) f *= smoothstep(TAIL_Z - TAIL_B, TAIL_Z + TAIL_B, z);
80
- return f;
81
- }
82
- if (name === 'head') {
83
- let f = smoothstep(-B, B, sN);
84
- if (patches.length) { const ew = eyePatchWeight(x, y, z); f *= patch ? ew : 1 - ew; }
85
- return f;
86
- }
87
- if (name === 'tail') return 1 - smoothstep(TAIL_Z - TAIL_B, TAIL_Z + TAIL_B, z);
88
- if (name.startsWith('foot')) return smoothstep(-FOOT_B, FOOT_B, FOOT_Y - y);
89
- return 1;
90
- },
91
- };
92
- }