@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,384 +0,0 @@
1
- // The lion, sculpted as a signed distance field.
2
- // Every primitive is attached to a bone (skinning) and a group (axial / limb / ear / jaw).
3
- // Heavy build: a broad, deep chest, thick forearms and big paws, a long level back, a massive head
4
- // with a broad straight muzzle. The male's mane is sculpted volume (the shells add the hair on top
5
- // of it), sized by params.mane (0 lioness / cub .. 1 full black-fringed mane).
6
- import { HEAD_O, TAIL_SEGS, HS, hl, fe, FACE } from './rig.js';
7
- import { add, sub, mul, lerp, norm, cross, rng, len } from '../../core/math/vec.js';
8
- import { SDFModel } from '../../core/sdf/sdf.js';
9
- import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
10
-
11
- // Eye geometry shared by the sculpt (lids + aperture) and the eyeball shader (head-local, left eye).
12
- // Measured on face_female1 / face_male1 in units of the eye separation E (0.129 m): the opening inside the
13
- // black lid margins 0.25-0.27 E wide and 0.17-0.19 E tall (36 x 21 mm), the iris 0.15-0.19 E across (23 mm),
14
- // the upper lid over the top of the iris (the opening set 3 mm low over a 26 mm iris: the lids cut its top ~20 %, face_female1 /
15
- // face_male1; a 23 mm iris in a round opening read as a staring cartoon eye), the inner corner low (tilt 14 deg);
16
- // eyes set forward (yaw 0.15 rad).
17
- // (A 46 x 22 mm slit over a 39 mm iris: the lids always cut the iris, a sleepy look.)
18
- export const EYE = { c: [0.05 * 1.12 * 1.15, 0.047 * 1.15, 0.05 * 1.15], r: 0.0235, back: 0.0133, yaw: 0.15, pitch: 0.03, lid: 0.002, R: 0.0225, d: 0.0135, off: -0.0035, tilt: (14 * Math.PI) / 180, irisZ: 0.019, irisR: 0.0128 };
19
-
20
- const X = [1, 0, 0];
21
- const smoothstep01 = (t) => Math.min(1, Math.max(0, t / 0.45)); // (0 at the ear's base .. 1 from its middle up)
22
-
23
- export function sculptLion(m, rig, params = {}) {
24
- const J = rig.J;
25
- const mane = params.mane || 0; // 0..1 mane volume
26
- const male = params.sex === 'male' && !params.juv ? 1 : 0;
27
- const juv = params.juv || 0;
28
- const eyeFrame = (s) => eyeFrameOf(EYE, HEAD_O, s);
29
-
30
- // Ellipsoid whose local Y follows `ydir`; radii = [lateral, along ydir, third axis]
31
- const ellY = (o) => {
32
- const y = norm(o.ydir);
33
- const lat = o.lateral || X;
34
- const z = norm(cross(lat, y));
35
- return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
36
- };
37
-
38
- // ---------------------------------------------------------------- TORSO
39
- // broad, deep chest (brisket ~0.45 m above the ground), level back, low belly line with a loose
40
- // skin fold ("primordial pouch") in front of the hind legs
41
- m.ell({ tag: 'ribcage', bone: 'spine3', c: [0, 0.745, 0.12], r: [0.145, 0.245, 0.33], axis: norm([0, 0.12, -1]), k: 0 });
42
- m.ell({ tag: 'brisket', bone: 'chest', c: [0, 0.59, 0.3], r: [0.1, 0.12, 0.14], axis: norm([0, -0.35, 1]), k: 0.08 });
43
- m.ell({ tag: 'pectoral', bone: 'chest', c: [0, 0.625, 0.41], r: [0.11, 0.13, 0.1], k: 0.07 });
44
- m.ell({ tag: 'withers', bone: 'chest', c: [0, 0.955, 0.3], r: [0.085, 0.08, 0.2], axis: norm([0, -0.08, 1]), k: 0.08 });
45
- m.ell({ tag: 'back', bone: 'spine3', c: [0, 0.965, 0.0], r: [0.11, 0.07, 0.25], k: 0.08 });
46
- // (a level underline from the brisket to the flank fold, no dog's tuck: side_walk_male3 / male2)
47
- m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.735, -0.24], r: [0.135, 0.215, 0.29], axis: norm([0, 0.18, -1]), k: 0.09 });
48
- m.ell({ tag: 'loin', bone: 'spine1', c: [0, 0.955, -0.34], r: [0.105, 0.075, 0.25], k: 0.07 });
49
- m.ell({ tag: 'flank', bone: 'spine1', c: [0, 0.79, -0.45], r: [0.12, 0.12, 0.14], k: 0.07 });
50
- const bk = params.belly ?? 1; // (individual: small .. heavy belly fold)
51
- m.ell({ tag: 'pouch', bone: 'spine1', c: [0, 0.6 + 0.02 * (1 - male) + 0.015 * (1 - bk), -0.36], r: [0.085, (0.07 - 0.015 * (1 - male)) * (0.75 + 0.25 * bk), 0.16], axis: norm([0, 0.2, -1]), k: 0.09 });
52
- m.ell({ tag: 'pelvis', bone: 'pelvis', c: [0, 0.885, -0.64], r: [0.125, 0.14, 0.19], k: 0.08 });
53
- m.ell({ tag: 'croup', bone: 'pelvis', c: [0, 0.975, -0.63], r: [0.09, 0.06, 0.17], k: 0.06 });
54
- // a faint ridge along the spine (the dorsal processes; a lion asleep on its side read as a smooth
55
- // sausage from its back)
56
- const SPN = [[[0, 1.0, 0.3], 'chest'], [[0, 1.018, 0.08], 'spine3'], [[0, 1.012, -0.2], 'spine2'], [[0, 1.008, -0.42], 'spine1'], [[0, 1.018, -0.6], 'pelvis']];
57
- for (let i = 0; i < SPN.length - 1; i++) m.cone({ tag: 'spine', bone: SPN[i][1], a: SPN[i][0], b: SPN[i + 1][0], ra: 0.024, rb: 0.024, k: 0.03 });
58
- for (const s of [1, -1]) {
59
- // the wings of the ilium either side of the loin
60
- // (stronger: a lion asleep on its side still read as a smooth tube from behind)
61
- m.ell({ tag: 'ilium', bone: 'pelvis', c: [0.08 * s, 0.997, -0.53], r: [0.052, 0.048, 0.08], k: 0.04 }); // (still a log at medium)
62
- // (the buttocks below and in front of the tail root, not a square rear face behind it)
63
- m.ell({ tag: 'rump', bone: 'pelvis', c: [0.07 * s, 0.81, -0.755], r: [0.066, 0.11, 0.063], k: 0.06 });
64
- }
65
-
66
- // ---------------------------------------------------------------- NECK (short and thick; males thicker)
67
- const nk = 1 + 0.12 * male;
68
- m.cone({ tag: 'neck', bone: 'neck1', a: [0, 0.8, 0.43], b: [0, 0.9, 0.585], ra: 0.14 * nk, rb: 0.115 * nk, k: 0.08 });
69
- m.cone({ tag: 'neck', bone: 'neck2', a: [0, 0.9, 0.585], b: hl([0, 0.0, -0.1]), ra: 0.115 * nk, rb: 0.1 * nk, k: 0.06 });
70
- m.ell({ tag: 'nape', bone: 'neck1', c: [0, 0.95, 0.54], r: [0.07, 0.05, 0.15], axis: norm([0, 0.35, 1]), k: 0.06 });
71
- m.ell({ tag: 'throat', bone: 'neck1', c: [0, 0.73, 0.57], r: [0.085, 0.1, 0.12], axis: norm([0, 0.8, 0.6]), k: 0.07 });
72
- m.ell({ tag: 'throat', bone: 'neck2', c: hl([0, -0.095, -0.075]), r: [0.07, 0.055, 0.09], k: 0.05 });
73
-
74
- // ---------------------------------------------------------------- MANE (sculpted coat volume)
75
- if (mane > 0) {
76
- const g = mane;
77
- // collar round the whole neck, fullest at the sides and throat, hanging down the chest
78
- m.ell({ tag: 'mane', bone: 'neck1', c: [0, 0.87, 0.53], r: [0.17 + 0.11 * g, 0.2 + 0.1 * g, 0.18 + 0.07 * g], axis: norm([0, 0.55, 1]), k: 0.13 });
79
- // crest: long hair standing over the nape and between the shoulders
80
- m.ell({ tag: 'mane', bone: 'neck2', c: [0, 1.0 + 0.07 * g, 0.63], r: [0.11 + 0.08 * g, 0.08 + 0.07 * g, 0.17 + 0.05 * g], axis: norm([0, 0.3, 1]), k: 0.12 });
81
- m.ell({ tag: 'mane', bone: 'chest', c: [0, 0.96 + 0.025 * g, 0.38], r: [0.13 + 0.08 * g, 0.075 + 0.035 * g, 0.16 + 0.08 * g], axis: norm([0, 0.2, 1]), k: 0.13 });
82
- // bib under the throat and down the chest between the forelegs
83
- m.ell({ tag: 'mane', bone: 'neck1', c: [0, 0.7 - 0.02 * g, 0.585], r: [0.13 + 0.045 * g, 0.13 + 0.06 * g, 0.1 + 0.05 * g], axis: norm([0, 0.8, 0.35]), k: 0.09 });
84
- m.ell({ tag: 'mane', bone: 'chest', c: [0, 0.63 - 0.03 * g, 0.49], r: [0.1 + 0.035 * g, 0.1 + 0.05 * g, 0.075 + 0.03 * g], k: 0.08 });
85
- // face ruff: frames the face from the cheeks round behind the ears; crown over the forehead
86
- for (const s of [1, -1]) {
87
- // (behind the cheeks, not round the face: a ruff volume reaching forward to the eyes read as a hood with
88
- // a groove round the face; the hair grows out of the cheeks in front of it, coat.js)
89
- m.ell({ tag: 'mane', bone: 'head', c: hl([0.09 * s, -0.06, -0.175]), r: [0.05 + 0.055 * g, 0.1 + 0.07 * g, 0.065 + 0.04 * g], axis: norm([0.25 * s, 0, 1]), k: 0.09 });
90
- }
91
- // (behind the ears, which stand out of the mane: over their bases it buried the lower half of a male's pinnae, leaving
92
- // small grey plates on top of the mane)
93
- m.ell({ tag: 'mane', bone: 'head', c: hl([0, 0.08 + 0.03 * g, -0.215]), r: [0.09 + 0.05 * g, 0.05 + 0.05 * g, 0.07 + 0.04 * g], k: 0.09 });
94
- m.ell({ tag: 'mane', bone: 'head', c: hl([0, -0.14 - 0.03 * g, -0.08]), r: [0.08 + 0.045 * g, 0.05 + 0.05 * g, 0.08], k: 0.07 });
95
- // locks: the surface breaks up into layered locks and tufts, the outline and the rear edge over the
96
- // shoulders into ragged points (a smooth volume read as a helmet with a hard rim).
97
- // Seeded per individual; each lock lies along the hair's fall (radiating on the face ruff, back and
98
- // down over the neck), partly sunk into the volume, and rides on the bone of the part it grows from.
99
- const base = m.prims.filter((p) => p.tag === 'mane');
100
- const RL = rng(4127 + (params.coatSeed || 0));
101
- const A0 = [0, 0.92, 0.36], A1 = hl([0, 0.0, -0.07]); // the neck's axis: withers -> behind the skull
102
- const FC = hl([0, -0.03, 0.02]);
103
- const nLocks = Math.round(14 + 20 * g);
104
- for (let i = 0; i < nLocks; i++) {
105
- const u = 0.62 * Math.pow(RL(), 0.8); // (the rear two thirds: withers to mid-neck, clear of the skull's skin blend)
106
- const th = (RL() * 2 - 1) * Math.PI * 0.9; // round the neck, 0 = the crest
107
- const ax = lerp(A0, A1, u);
108
- const dir = norm([Math.sin(th), Math.cos(th), 0.25 * (u - 0.4)]);
109
- let lo = 0, hi = 0.8;
110
- for (let k = 0; k < 22; k++) { const mid = (lo + hi) / 2; if (SDFModel.evalList(base, ...add(ax, mul(dir, mid))) < 0) lo = mid; else hi = mid; }
111
- const P = add(ax, mul(dir, lo));
112
- const e = 0.002, f0 = SDFModel.evalList(base, ...P);
113
- const n = norm([SDFModel.evalList(base, P[0] + e, P[1], P[2]) - f0, SDFModel.evalList(base, P[0], P[1] + e, P[2]) - f0, SDFModel.evalList(base, P[0], P[1], P[2] + e) - f0]);
114
- const ruff = Math.max(0, Math.min(1, (P[2] - 0.62) / 0.1));
115
- let fl = add(mul(norm([0, -0.9, -0.55]), 1 - ruff), mul(norm(add(norm(sub(P, FC)), [0, -0.3, -0.4])), ruff));
116
- fl = sub(fl, mul(n, fl[0] * n[0] + fl[1] * n[1] + fl[2] * n[2]));
117
- if (Math.hypot(...fl) < 1e-3) continue;
118
- fl = norm(fl);
119
- const rt = (0.022 + 0.016 * RL()) * (0.75 + 0.45 * g), rl = rt * (1.8 + 0.9 * RL());
120
- let bone = 'neck1', bd = 1e9;
121
- for (const q of base) { const dq = SDFModel.dist(q, ...P); if (dq < bd) { bd = dq; bone = q.bone; } }
122
- // (none on the face ruff, which rides on the skull: a lock there moved with the head against the
123
- // neck's skin, and lay 15 cm in the ground when a sleeping lion rested its head on its side)
124
- if (bone === 'head') continue;
125
- ellY({ tag: 'mane', bone, c: add(add(P, mul(n, -0.05 * rt)), mul(fl, 0.4 * rl)), ydir: fl, lateral: norm(cross(fl, n)), r: [rt, rl, 0.48 * rt], k: 0.03 });
126
- }
127
- }
128
-
129
- // ---------------------------------------------------------------- HEAD (face units, rig.js FACE)
130
- // Landmarks in units of the eye separation E from the eye midpoint (head frame, gaze +Z), measured on
131
- // face_female1 / face_male1 (the lion looks into the lens, so the camera is on the gaze axis): nose
132
- // leather top edge 0.8 E below the eye line and +-0.3 E wide, its lower point 1.07 E down, whisker
133
- // pads flanking it +-0.52 E wide down to the lip line at 1.3 E, cheeks +-1.15 E; and on the profile
134
- // lioness of front_female1: a long straight bridge falling ~50 deg from the stop to the nose, the nose
135
- // front ~1.2 E ahead of the eyes (0.57 of the way from the ear base to the nose), a deep square
136
- // muzzle, the chin set back under the upper lip.
137
- const H = { bone: 'head' };
138
- const hw = 1.12 + 0.02 * male - 0.04 * juv;
139
- const hr = (r) => [r[0] * hw * HS, r[1] * HS, r[2] * HS];
140
- const F = (X, Y, Z) => fe(X, Y, Z, hw);
141
- const FR = (rx, ry, rz) => hr([rx * FACE.X, ry * FACE.E, rz * FACE.E]);
142
- const FS = (r) => r * FACE.X * hw * HS; // a radius across the face
143
- // (the face still read as a smooth swollen dome, a plush toy, worst on the lioness and the white lion:
144
- // the head is rebuilt as planes with edges after the photos (ffront_*, fprof_*, mprof_*, white_Flickr_bslmmrs_White_Lion): a
145
- // flat broad skull whose profile runs straight from the nose over the forehead to the crown (the forehead blob rose 0.23 E
146
- // above that line at the eyes: a dome), a dished forehead between two brow ridges the eyes look out from under, a broad
147
- // flat-topped bridge with crisp edges falling to the sides of the muzzle, a broad square muzzle of swollen whisker pads, the
148
- // cheekbones the widest point of the bare head (the head was widest 0.6-0.9 E below the eyes, a pear with hamster cheeks),
149
- // flat cheeks and heavy jowls under them, a strong chin)
150
- // braincase: broad and flat-topped, the crown ~0.9 E above the eye line between the ears
151
- m.ell({ ...H, tag: 'cranium', c: F(0, 0.1 + 0.07 * juv, -0.98), r: FR(0.96 - 0.05 * juv, 0.8 + 0.17 * juv, 1.0), k: 0.05 }); // (cubs: a domed forehead)
152
- // the forehead: a broad flat plane rising straight back from the stop to the crown at ~45 deg (fprof_*: one straight line from
153
- // the nose to the crown), its middle set back between the brow ridges (the dished forehead)
154
- const fhN = norm([0, 0.72 - 0.25 * juv, 1]);
155
- m.ell({ ...H, tag: 'forehead', c: F(0, 0.42 + 0.06 * juv, -0.5), r: FR(0.64, 0.62, 0.3 + 0.06 * juv), axis: fhN, up: norm([0, 1, -(0.72 - 0.25 * juv)]), k: 0.055 }); // (k 0.055: at 0.045 its seam with the cranium drew a crease, a dark band across the forehead from the front)
156
- // the temporal muscles fill the head's sides above the cheekbones up to the ear bases: flat sides
157
- for (const s of [1, -1]) m.ell({ ...H, tag: 'temporal', c: F((0.62 - 0.04 * juv) * s, 0.36 - 0.08 * juv, -0.92), r: FR(0.38, 0.42, 0.6), axis: norm([-0.3 * s, 0, 1]), k: 0.05 });
158
- // the facial mask round the eyes, flush with the eyeballs' fronts and wrapping their outer sides (set back: it made the face in
159
- // front of the eyes a dome)
160
- m.ell({ ...H, tag: 'facemask', c: F(0, -0.12, -0.36), r: FR(0.76, 0.5, 0.36), k: 0.04 });
161
- for (const s of [1, -1]) {
162
- // the brow ridge: a bar over each eye from above its inner corner out and back to the temple, standing ~0.15 E in front of the
163
- // forehead's middle (the dish between them) and overhanging the upper lid, its inner end a little lower (fprof_*: the bump over
164
- // the eye; ffront_*: a level brow, the eyes looking out from under it); a cub's soft
165
- m.ell({ ...H, tag: 'brow', c: F(0.4 * s, 0.28, 0.0 - 0.12 * juv), r: FR(0.3, 0.12, 0.2), axis: norm([0.45 * s, 0, 1]), up: norm([0, 1, 0.3]), k: 0.026 }); // (level, its inner end no lower: in the clay the bars met over the bridge in a frown)
166
- // zygomatic arch: the cheekbone from under the eye's outer corner out and back toward the ear: the widest point of the bare head
167
- m.ell({ ...H, tag: 'zygomatic', c: F(0.92 * s, -0.14, -0.66), r: FR(0.22, 0.2, 0.6), axis: norm([-0.42 * s, 0, 1]), k: 0.06 }); // (further out, the head's widest point at the eye line behind the eyes: the face read as a column)
168
- // cheek: the flat side of the face below the cheekbone, down to the jowl (set in from the arch: a ball here made hamster cheeks)
169
- m.ell({ ...H, tag: 'cheek', c: F(0.72 * s, -0.72, -0.66), r: FR(0.14, 0.5, 0.62), axis: norm([-0.3 * s, -0.2, 1]), k: 0.035 });
170
- // upper lip: hangs over the lower jaw from the whisker pad back to the corner of the mouth
171
- m.ell({ ...H, tag: 'lip', c: F(0.38 * s, -1.22, 0.26), r: FR(0.22, 0.24, 0.56), axis: norm([-0.35 * s, -0.1, 1]), k: 0.03 });
172
- // the back of the cheeks behind the jaw's angle (the mastoid and the thick neck's top), flatter: the head narrows below the
173
- // cheekbones to the jowls (it was widest here, a pear)
174
- m.ell({ ...H, tag: 'mastoid', c: F(0.54 * s, -0.98, -1.32), r: FR(0.44, 0.5, 0.46), k: 0.035 });
175
- // masseter: the chewing muscle under the cheekbone, one flat plane from the arch down to the jaw's angle
176
- m.ell({ ...H, tag: 'masseter', c: F(0.78 * s, -0.72, -1.04), r: FR(0.18, 0.54, 0.56), axis: norm([-0.25 * s, 0, 1]), k: 0.045 });
177
- // jowl: heavy skin wrapping the back of the mandible down to the corner of the mouth (lower and fuller: the heavy jowls of
178
- // mfront_* / white_Flickr_*, under the flat cheek)
179
- m.ell({ ...H, tag: 'jowl', c: F(0.56 * s, -1.22, -0.78), r: FR(0.26, 0.36, 0.54), k: 0.03 });
180
- // the side of the muzzle: a steep plane from the bridge's edge down to the whisker pad (set back under the eye: it bulged 0.46 E
181
- // in front of the eyeball there, a pillow under the eye)
182
- m.ell({ ...H, tag: 'padside', c: F(0.41 * s, -0.76, 0.3), r: FR(0.17, 0.4, 0.6), axis: norm([-0.22 * s, -0.12, 1]), k: 0.05 });
183
- // the infraorbital slope under the eye: the muzzle's side runs back into the cheek here as one surface facing forward and out (the
184
- // bridge stood out of the face as a box with vertical sides, the cheeks a shelf behind it: a dog's snout)
185
- m.ell({ ...H, tag: 'infraorbital', c: F(0.5 * s, -0.58, -0.12), r: FR(0.18, 0.24, 0.22), axis: norm([0.35 * s, -0.3, 1]), k: 0.04 });
186
- // whisker pad: a big swollen lobe each side of the philtrum, under and beside the nose leather, reaching back along the lip
187
- m.ell({ ...H, tag: 'whisker', c: F(0.28 * s, -1.16, 0.58), r: FR(0.3, 0.36, 0.48), axis: norm([0.15 * s, -0.08, 1]), k: 0.025 });
188
- }
189
- // the front of the muzzle squared off: a broad, nearly flat plate under the nose carrying the whisker pads, a corner where it
190
- // meets the side of the muzzle
191
- m.ell({ ...H, tag: 'whisker', c: F(0, -1.08, 0.82), r: FR(0.42, 0.26, 0.18), axis: norm([0, -0.1, 1]), k: 0.025 });
192
- // maxilla: the broad bony face under the eyes, tapering forward to the nose under the bridge
193
- m.ell({ ...H, tag: 'maxilla', c: F(0, -0.68, -0.25), r: FR(0.5, 0.46, 1.0), axis: norm([0, -0.2, 1]), k: 0.025 });
194
- // muzzle: the deep, square upper jaw under the bridge
195
- m.ell({ ...H, tag: 'muzzle', c: F(0, -0.82, 0.4), r: FR(0.4, 0.38, 0.54), axis: norm([0, -0.15, 1]), k: 0.04 }); // (smaller, under the bridge: as big as the pads it made the front of the muzzle one round snout, a bear's, the pads lost in it)
196
- // nasal bridge: long, broad and slightly convex, from the stop (between the brows) straight to the top edge of the nose leather, its
197
- // shoulders where the tear lines run down (ffront_*, white_Flickr_*: the bridge as wide as the gap between the eyes' inner corners
198
- // all the way down; two rounded bands read as a round-topped dome of a snout, a flat plate as a horse's plank from above)
199
- // (the leather stood on the end of a tapering bridge as a disc facing forward, a pig's or a bear's snout in the
200
- // clay, mfront_Lion_Face / f34_*: the bridge stays broad to the nose and the leather is its end, its top flush with the bridge's,
201
- // sloping on down to a rounded front over the stem; the nostrils open forward and out at the bar's lower outer corners)
202
- const top0 = F(0, 0.24 - 0.08 * juv, 0.18), top1 = F(0, -0.68, 0.98); // the bridge's top line at the stop and over the leather
203
- const bd = norm(sub(top1, top0)), bu = norm(cross(bd, [1, 0, 0])); // along the bridge (down-forward), its top's normal
204
- const EH = FACE.E * HS; // metres per face unit
205
- m.ell({ ...H, tag: 'nasal', c: add(lerp(top0, top1, 0.55), mul(bu, -0.24 * EH)), r: FR(0.42, 0.24, 0.82), axis: bd, up: bu, k: 0.026 });
206
- // the bridge's broad end (an ellipsoid along it tapers to a ridge at the nose)
207
- m.ell({ ...H, tag: 'nasal', c: add(lerp(top0, top1, 0.86), mul(bu, -0.17 * EH)), r: FR(0.39, 0.17, 0.42), axis: bd, up: bu, k: 0.03 });
208
- // nose leather: the bridge's end, broad and flat on top, rounding down at its front to the stem between the pads
209
- m.ell({ ...H, tag: 'nose', c: add(add(top1, mul(bd, 0.06 * EH)), mul(bu, -0.105 * EH)), r: FR(0.39, 0.115, 0.17), axis: bd, up: bu, k: 0.022 }); // (+-0.33 E with its rim: face_female1, face_male1, ffront_*)
210
- m.ell({ ...H, tag: 'nose', c: F(0, -1.03, 1.06), r: FR(0.15, 0.12, 0.08), k: 0.014 }); // (the stem)
211
- for (const s of [1, -1]) {
212
- const ef = eyeFrame(s);
213
- const at = (u, v, w) => add(add(add(ef.c, mul(ef.x, u)), mul(ef.y, v)), mul(ef.z, w));
214
- // orbit: a soft hollow so brow, cheek and bridge fall away to the lids
215
- ellY({ ...H, tag: 'orbit', c: at(-0.003 * s, -0.001, 0.029), ydir: ef.y, lateral: ef.x, r: [0.025, 0.018, 0.012], k: 0.012, carve: true });
216
- m.sphere({ ...H, tag: 'eyelid', c: ef.c, rad: EYE.r + EYE.lid, k: 0.008 });
217
- m.lens({ ...H, tag: 'eyesocket', c: add(ef.c, mul(ef.y, EYE.off)), x: ef.x, y: ef.y, z: ef.z, R: EYE.R, d: EYE.d, zMin: -0.003, zMax: 0.036, k: 0.0033, carve: true });
218
- // nostril: a slit curving up and out from the lower corner of the leather
219
- m.ell({ ...H, tag: 'nostril', c: F(0.22 * s, -0.94, 1.08), r: FR(0.12, 0.045, 0.08), axis: norm([0.55 * s, -0.2, 1]), up: [-Math.sin(0.4), s * Math.cos(0.4), 0], k: 0.005, carve: true }); // (slits at the leather's lower corners, not two holes in its front: a pig's snout in 3/4)
220
- // upper canine (~6 cm): its own rigid surface (regions.js 'canine'), hidden inside the upper lip and the closed jaw,
221
- // bared when the mouth opens and the lip draws back (behaviour.js); merged into the head's surface, only the tip that
222
- // stood below the lip existed as skin, and the lip carried it up with it
223
- // (only the part that can show: its root ends inside the muzzle above the highest the lip is drawn back)
224
- m.cone({ bone: 'head', part: 'canine', tag: 'canine', a: F(0.27 * s, -1.13, 0.645), b: F(0.27 * s, -1.45, 0.61), ra: 0.0076 * HS, rb: 0.0022 * HS, k: 0, thin: true });
225
- }
226
- // the upper lip's edge from in front of the canine back to the corner of the mouth, deep inside the lip (it adds no
227
- // shape): the skin within a few cm of it rides on the lip bone, which draws the lip back and up with the gape
228
- for (const s of [1, -1]) {
229
- const S = s > 0 ? 'L' : 'R';
230
- m.ell({ ...H, bone: 'lip' + S, group: 'lip' + S, tag: 'lipedge', c: F(0.43 * s, -1.36, 0.28), r: FR(0.05, 0.045, 0.42), axis: norm([-0.26 * s, 0, 1]), k: 0.004 });
231
- }
232
- // philtrum: the groove from the nose down to the lip line, between the whisker pads
233
- m.ell({ ...H, tag: 'philtrum', c: F(0, -1.3, 1.1), r: FR(0.03, 0.16, 0.05), k: 0.01, carve: true });
234
-
235
- // ---------------------------------------------------------------- JAW (separate surface so the mouth can open)
236
- // A lower jaw that opens into a mouth (a convex jaw read as a tongue slab in the roar, its
237
- // outside fur showed inside the gape): the two rami carry the lower lip's rim, joined by the chin and the
238
- // floor under the tongue; a trough between them holds the tongue below the rim; the lower canines stand at
239
- // the front of the rim. Closed, the rim and the canines lie inside the upper lip, and the chin's white fur
240
- // runs up to the one lip line.
241
- const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
242
- const ER = (r) => r * FACE.E * HS; // a radius in E (metres)
243
- // (the chin +-0.3 E wide, face_male1: +-0.45 E read as a white bar under the mouth)
244
- // (a U from the front and a V from below: the rami carry the lower lip's rim and the canines at the top front, the chin
245
- // bulges below between them, and the floor lies above the rami's lower edges; a flat floor as low as the rami made the
246
- // jaw a flat shield with a rim, the "pacifier")
247
- // (the front narrow enough to tuck under the hanging upper lips: wider, its sides stood out below them as a lens)
248
- for (const s of [1, -1]) m.cone({ ...JW, tag: 'mandible', a: F(0.4 * s, -1.14, -0.78), b: F(0.165 * s, -1.28, 0.55), ra: ER(0.18), rb: ER(0.125), k: 0.03 });
249
- // (tucked back and up under the upper lip and blended softly into the mandible: a distinct ball jutting under the lip
250
- // read as a pale pacifier with a crease under it)
251
- // (tucked back and up under the upper lip, small, blended softly into the mandible (a distinct ball
252
- // jutting under the lip read as a pale pacifier with a crease under it); the chin's depth in the photos is its white
253
- // tuft of hair (coat.js), which runs on into the throat's)
254
- m.ell({ ...JW, tag: 'chin', c: F(0, -1.48, 0.47), r: FR(0.2, 0.2, 0.19), k: 0.06 }); // (a stronger chin, its front facing forward under the lip line: face_male1, ffront_*)
255
- // (the floor under the tongue: its underside runs on level from the chin to the throat, so the lower outline is one line
256
- // from the chin back (raised behind the chin, it left a dip there and the chin stood out as a lump, a pacifier; level and low at the back, it jutted below the throat line as a slab)
257
- m.ell({ ...JW, tag: 'mandible', c: F(0, -1.46, -0.12), r: FR(0.22, 0.15, 0.62), axis: norm([0, 0.12, 1]), k: 0.04 });
258
- m.ell({ ...JW, tag: 'mouth', c: F(0, -1.13, -0.02), r: FR(0.24, 0.16, 0.7), k: 0.015, carve: true });
259
- m.ell({ ...JW, tag: 'tongue', c: F(0, -1.28, 0.08), r: FR(0.21, 0.07, 0.56), k: 0.02 });
260
- for (const s of [1, -1]) m.cone({ ...JW, tag: 'canine', a: F(0.2 * s, -1.23, 0.64), b: F(0.215 * s, -0.99, 0.68), ra: 0.0072 * HS, rb: 0.0022 * HS, k: 0.003 });
261
-
262
- // ---------------------------------------------------------------- EARS
263
- // short, rounded, cupped forward; set on the sides of the crown
264
- for (const S of ['L', 'R']) {
265
- const s = S === 'L' ? 1 : -1;
266
- const base = J['earBase' + S], tip = J['earTip' + S];
267
- const up = norm(sub(tip, base));
268
- const facing = norm([0.7 * s, 0.05, 1]);
269
- const lat = norm(cross(up, facing));
270
- const ear = { bone: 'ear' + S, group: 'ear' + S, thin: true };
271
- const at = (t) => lerp(base, tip, t);
272
- const ek = (1 + 0.45 * juv) * (1 + 0.15 * male); // (cubs: big ears, research 1; males' stand clear of the mane)
273
- // the upper pinna's width: broad and round-topped (face_female1 0.77 E, face_male1 0.86 E, cub_lying1 ~0.9 E of their
274
- // eye separations; pointed, 0.63 / 0.57 / 0.5 E)
275
- // (cubs: ~1 E wide and round, cub_lying1; at 0.5 E they read too small)
276
- // (a little lower than wide: standing 0.5 E above the crown's side the pinnae read as a mouse's)
277
- // (a male's 0.85-0.9 E wide, face_male1: at 1.1 + 0.3 they were 0.53 E, small grey plates in the mane)
278
- const ew = (1 + 1.0 * juv) * (1.1 + 0.6 * male), eh = (1 + 0.55 * juv) * (0.94 + 0.22 * male);
279
- // a rounded triangle as broad at the base as it is tall (face_female1: 0.55-0.6 E wide, tip ~1.2 E above the eye line
280
- // and ~1.15 E out), a thin pinna with a shallow bowl in front, its broad base merging into the side of the crown;
281
- // the stack of discs leans ~9 deg toward the midline of the joints' axis (the tip stood 0.15 E too far out: a mouse's
282
- // ear on the corner of a ball) and narrows toward a rounded tip (one broad disc on top read as a round
283
- // teddy bear's ear)
284
- // (the base disc no lower or wider than this: a head lying on its side rests on it, and 4 mm more reach put a dead
285
- // lioness's ear base 1 % S in the ground)
286
- const ca = Math.cos(0.16), sa = Math.sin(0.16);
287
- const U2 = norm(add(mul(up, ca), mul(lat, -sa * s))), L2 = norm(cross(U2, facing)); // (s: lat points out on the left ear, in on the right)
288
- const L = len(sub(tip, base));
289
- // (l outward on both sides: L2 points out on the left ear and in on the right, and the offsets of the upper discs put the
290
- // right pinna ~0.2 E further in than the left, as measured: ear tips -1.2 / +0.98 E)
291
- // (a male's pinna a little further out and up: it stands out of the mane, face_male1)
292
- const pe = (t, l, n = 0) => add(add(add(base, mul(U2, t * L + 0.008 * male * smoothstep01(t))), mul(L2, s * (l + 0.012 * male * smoothstep01(t)))), mul(facing, n));
293
- ellY({ ...ear, tag: 'ear', c: pe(0.26, 0), ydir: U2, lateral: L2, r: [0.046 * ek, 0.042 * ek, 0.013], k: 0.022 });
294
- ellY({ ...ear, tag: 'ear', c: pe(0.45, 0.009 * ew), ydir: U2, lateral: L2, r: [0.046 * ew, 0.046 * eh, 0.0085], k: 0.02 });
295
- ellY({ ...ear, tag: 'ear', c: pe(0.64, 0.004 * ew), ydir: U2, lateral: L2, r: [0.036 * ew, 0.042 * eh, 0.0075], k: 0.018 });
296
- // (a rounder tip, the lioness's ears read as a fox's or a capybara's pointed ones in the showcase)
297
- ellY({ ...ear, tag: 'ear', c: pe(0.77, 0.0), ydir: U2, lateral: L2, r: [0.025 * ew, 0.03 * eh, 0.0065], k: 0.016 });
298
- // (the bowl inside the rim: narrower than the upper pinna, so the rim keeps some flesh; clear of the base by ~3 cm:
299
- // reaching down to it, it left a sliver where the pinna joins the skull that folded over at the medium tier; a soft
300
- // carve: k 0.006 drew its edge as dark crack lines at the medium tier)
301
- ellY({ ...ear, tag: 'earinner', c: add(pe(0.54, 0.002 * ew), mul(facing, 0.0102)), ydir: U2, lateral: L2, r: [0.028 * ew, 0.04 * eh, 0.005], k: 0.01, carve: true });
302
- }
303
-
304
- // ---------------------------------------------------------------- LEGS
305
- // thick, muscular forelegs (grappling) with a massive forearm; big round forepaws (the legs read thin
306
- // against side_male1: +10 % on the arm, forearm, thigh and shin)
307
- // (cubs: big paws, longer and deeper but no wider than an adult's for their size: a paw lying on its side rests on its
308
- // outer toes ~3 cm x size above the ground (the core's side pose), and broader cub paws (1.1 x 1.1 warped) lay 2 % S in
309
- // the ground in death)
310
- const pawK = 1 + 0.1 * juv, pawL = 1 + 0.25 * juv;
311
- for (const S of ['L', 'R']) {
312
- const s = S === 'L' ? 1 : -1;
313
- const lat = [s, 0, 0];
314
- const sx = (v) => [v[0] * s, v[1], v[2]];
315
- const g = 'F' + S;
316
- const Sc = J['scapTop' + S], Sh = J['shoulder' + S], E = J['elbow' + S], W = J['wrist' + S], M = J['mcp' + S], T = J['ftoe' + S];
317
- // the top of the shoulder blade: it rolls above the back line at every step (research 3) and gives a
318
- // lying lion's back some relief (a featureless cylinder from behind)
319
- ellY({ tag: 'scaptop', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.1), sx([0.016, 0.03, 0])), ydir: sub(Sh, Sc), lateral: lat, r: [0.045, 0.08, 0.065], k: 0.04 }); // (more relief: a sleeping lion's back read as a smooth log at medium)
320
- ellY({ tag: 'scapmuscle', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.5), sx([0.012, 0, 0])), ydir: sub(Sh, Sc), lateral: lat, r: [0.042, 0.16, 0.1], k: 0.09, bias: 0.006 });
321
- m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: Sh, b: E, ra: 0.076, rb: 0.063, k: 0.09 });
322
- ellY({ tag: 'triceps', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.55), [0, 0, -0.05]), ydir: sub(E, Sh), lateral: lat, r: [0.061, 0.13, 0.07], k: 0.07 });
323
- m.sphere({ tag: 'olecranon', bone: 'radius' + S, group: g, c: add(E, [0, 0.016, -0.045]), rad: 0.034, k: 0.03 });
324
- m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: E, b: W, ra: 0.075, rb: 0.047, k: 0.04 });
325
- ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.28), sx([0.006, 0, 0.008])), ydir: sub(W, E), lateral: lat, r: [0.067, 0.13, 0.07], k: 0.045 });
326
- if (male) ellY({ tag: 'elbowtuft', bone: 'radius' + S, group: g, c: add(E, [0, -0.02, -0.06]), ydir: [0, -1, -0.3], lateral: lat, r: [0.03, 0.06, 0.03], k: 0.04 });
327
- m.sphere({ tag: 'wrist', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0, -0.006]), rad: 0.036, k: 0.02 });
328
- m.sphere({ tag: 'carpalpad', bone: 'metacarpus' + S, group: g, c: add(W, [0, -0.018, -0.034]), rad: 0.015, k: 0.015 });
329
- m.cone({ tag: 'pastern', bone: 'metacarpus' + S, group: g, a: W, b: M, ra: 0.036, rb: 0.036 * pawK, k: 0.02 });
330
- m.sphere({ tag: 'dewclaw', bone: 'metacarpus' + S, group: g, c: add(lerp(W, M, 0.35), sx([-0.03, 0, 0.006])), rad: 0.011, k: 0.01 });
331
- const dp = norm(sub(T, M));
332
- ellY({ tag: 'paw', bone: 'fpaw' + S, group: g, c: add(add(M, mul(dp, 0.03)), [0, -0.008 + 0.005 * juv, 0]), ydir: dp, lateral: lat, r: [0.051, 0.066 * pawL, 0.032], k: 0.022 });
333
- m.sphere({ tag: 'pad', bone: 'fpaw' + S, group: g, c: add(M, [0, -0.025 + 0.004 * juv, 0.02]), rad: 0.02 * pawK, k: 0.015 });
334
- // (the outer toes within the paw's width: a paw lying on its side (death, sleep) rests 3 cm above the ground
335
- // on its wrist's skin radius, and outer toes 57 mm out lay 10 mm in the ground)
336
- // (toes and pads 3 mm higher: the smooth union's blend bulged the sole ~5 mm below the ground plane, so a standing paw
337
- // sat 0.5 % S in the ground and a cub's lying paw 2 % S)
338
- const toeX = [-0.033, -0.011, 0.011, 0.033], toeZ = [-0.018, 0, 0, -0.018];
339
- for (let i = 0; i < 4; i++) m.sphere({ tag: 'toe', bone: 'fpaw' + S, group: g, c: [T[0] + toeX[i] * s, 0.024 + 0.004 * juv, T[2] - 0.016 * pawL + toeZ[i] * pawL], rad: 0.02 * pawK, k: 0.012 });
340
-
341
- const h = 'H' + S;
342
- const Hp = J['hip' + S], K = J['knee' + S], Hk = J['hock' + S], Mt = J['mtp' + S], Tt = J['htoe' + S];
343
- // a broad, heavy thigh rising into the flank and croup; the flank fold webs stifle and belly
344
- ellY({ tag: 'thigh', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.4), sx([0.008, 0, -0.055])), ydir: sub(K, Hp), lateral: lat, r: [0.076, 0.25, 0.168], k: 0.08, bias: 0.006 });
345
- m.cone({ tag: 'thighfront', bone: 'femur' + S, group: h, a: sx([0.072, 0.87, -0.47]), b: add(K, sx([-0.006, 0.08, 0.0])), ra: 0.068, rb: 0.042, k: 0.09 });
346
- m.cone({ tag: 'hamstring', bone: 'femur' + S, group: h, a: sx([0.075, 0.88, -0.8]), b: add(lerp(K, Hk, 0.28), [0, 0, -0.045]), ra: 0.08, rb: 0.05, k: 0.06 });
347
- // (smaller and higher: skinned to the thigh, a bigger fold swung forward and down under the belly as a
348
- // lump whenever the hind leg reached forward at the gallop)
349
- // (and reaching down the thigh's front toward the knee, so a knee swung forward at the sprint stays webbed to the belly
350
- // rather than standing under it as a round lump)
351
- ellY({ tag: 'flankfold', bone: 'femur' + S, group: h, c: sx([0.092, 0.62, -0.435]), ydir: [-0.03, 0.16, 0.06], lateral: lat, r: [0.03, 0.13, 0.05], k: 0.09 });
352
- // (a slim knee: a big, widely blended stifle ball read as a round lump under the belly whenever the hind leg
353
- // reached forward at the gallop)
354
- m.sphere({ tag: 'stifle', bone: 'tibia' + S, group: h, c: add(K, sx([0.004, 0.014, 0.004])), rad: 0.026, k: 0.04 });
355
- m.cone({ tag: 'shin', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.06), sx([0.0, 0.0, 0.006])), b: Hk, ra: 0.046, rb: 0.033, k: 0.045 });
356
- ellY({ tag: 'calf', bone: 'tibia' + S, group: h, c: add(lerp(K, Hk, 0.3), sx([0.004, 0.015, -0.045])), ydir: sub(Hk, K), lateral: lat, r: [0.05, 0.11, 0.055], k: 0.05 });
357
- m.cone({ tag: 'achilles', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.5), [0, 0.015, -0.055]), b: add(Hk, [0, 0.02, -0.05]), ra: 0.02, rb: 0.019, k: 0.022 });
358
- m.sphere({ tag: 'calcaneus', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.026, -0.038]), rad: 0.02, k: 0.018 });
359
- m.sphere({ tag: 'hock', bone: 'metatarsus' + S, group: h, c: Hk, rad: 0.031, k: 0.018 });
360
- m.cone({ tag: 'metatarsus', bone: 'metatarsus' + S, group: h, a: Hk, b: Mt, ra: 0.034, rb: 0.033 * pawK, k: 0.018 });
361
- const dh = norm(sub(Tt, Mt));
362
- ellY({ tag: 'paw', bone: 'hpaw' + S, group: h, c: add(add(Mt, mul(dh, 0.03)), [0, -0.008 + 0.005 * juv, 0]), ydir: dh, lateral: lat, r: [0.048, 0.062 * pawL, 0.03], k: 0.022 });
363
- m.sphere({ tag: 'pad', bone: 'hpaw' + S, group: h, c: add(Mt, [0, -0.027 + 0.004 * juv, 0.016]), rad: 0.018 * pawK, k: 0.015 });
364
- for (let i = 0; i < 4; i++) m.sphere({ tag: 'toe', bone: 'hpaw' + S, group: h, c: [Tt[0] + toeX[i] * 0.9 * s, 0.023 + 0.004 * juv, Tt[2] - 0.016 * pawL + toeZ[i] * pawL], rad: 0.0185 * pawK, k: 0.012 });
365
- }
366
-
367
- // ---------------------------------------------------------------- TAIL
368
- // a smooth round tail tapering a little, ending in the black tuft (a teardrop of long hair)
369
- const tailR = (t) => (t < 0.3 ? 0.05 + (0.034 - 0.05) * (t / 0.3) : 0.034 - 0.012 * ((t - 0.3) / 0.7));
370
- m.cone({ tag: 'tailroot', bone: 'tail0', a: [0, 0.935, -0.72], b: J.tail1, ra: 0.06, rb: tailR(0.1), k: 0.06 });
371
- for (let i = 0; i < TAIL_SEGS; i++) {
372
- // (a hard union: consecutive round cones share the sphere at their joint, so the tube runs on smoothly; a
373
- // smooth union swelled every joint by ~4 mm, which shaded into rings)
374
- m.cone({ tag: 'tail', bone: 'tail' + i, a: J['tail' + i], b: J['tail' + (i + 1)], ra: tailR(i / TAIL_SEGS), rb: tailR((i + 1) / TAIL_SEGS), k: i === 0 ? 0.015 : 0, thin: i >= TAIL_SEGS - 2 });
375
- }
376
- if (!juv || juv < 0.8) {
377
- const a = J['tail' + (TAIL_SEGS - 1)], b = J['tail' + TAIL_SEGS];
378
- const d = norm(sub(b, a));
379
- const tk = 1 - 0.6 * juv;
380
- ellY({ tag: 'tuft', bone: 'tail' + (TAIL_SEGS - 1), c: add(b, mul(d, -0.012)), ydir: d, lateral: norm(cross(d, [0, 1, 0])), r: [0.038 * tk, 0.075 * tk, 0.038 * tk], k: 0.03, thin: true });
381
- }
382
-
383
- return m;
384
- }
@@ -1,132 +0,0 @@
1
- // Whiskers: the mystacial vibrissae as real geometry (species.surfaces, appended to the base mesh), after
2
- // the cat's (src/species/cat/whiskers.js).
3
- //
4
- // The whisker pads carry four slightly curved rows of black follicle dots (face_female1: 0.93-1.19 E below
5
- // the eye line, from beside the nose out to ~0.45 E; the coat paints them from whiskerFollicles()); the
6
- // bigger follicles of the three upper rows carry the whiskers: white keratin strands 7-14 cm long
7
- // (the upper rows longest), 0.4 mm at the root tapering to 0.07 mm, fanning out sideways, a little
8
- // forward and down and curving down. They ride on a bone per side (whiskerL / whiskerR, children of the
9
- // head), so the behaviour hook can sweep the fan back and lift it clear of the ground when the head lies
10
- // on it. Not built for the crowd tier.
11
- import { HEAD_O, HS, FACE } from './rig.js';
12
- import { MAT, srgb } from '../../core/build/coatKit.js';
13
- import { SDFModel } from '../../core/sdf/sdf.js';
14
- import { norm, add, mul, cross, rng } from '../../core/math/vec.js';
15
-
16
- // the bone of each fan (left): from the middle of the pad out to the side (face units)
17
- export const WHISKER_BASE_F = [0.34, -1.11, 0.78];
18
- export const WHISKER_TIP_F = [1.3, -1.25, 1.15];
19
-
20
- // face units -> reference metres (hw: head width factor, rig.js FACE)
21
- const faceToRef = (X, Y, Z, hw) => [HEAD_O[0] + X * FACE.X * hw * HS, HEAD_O[1] + (FACE.EY + Y * FACE.E) * HS, HEAD_O[2] + (FACE.EZ + Z * FACE.E) * HS];
22
-
23
- // follicles (both sides, jittered per individual): { s, row, i, X, Y } in face units (X >= 0 for the left)
24
- export function whiskerFollicles(coatSeed = 0) {
25
- const RD = rng(517 + coatSeed);
26
- const out = [];
27
- for (const s of [1, -1]) {
28
- for (let row = 0; row < 4; row++) {
29
- const nDots = [4, 5, 6, 5][row];
30
- for (let i = 0; i < nDots; i++) {
31
- const X = 0.12 + 0.022 * row + 0.058 * i + 0.012 * (RD() - 0.5);
32
- const Y = -0.98 - 0.086 * row - 0.1 * (X - 0.13) + 0.012 * (RD() - 0.5);
33
- out.push({ s, row, i, X, Y, r: 0.0019 + 0.0006 * RD() });
34
- }
35
- }
36
- }
37
- return out;
38
- }
39
-
40
- // the follicle's point on the pad (march in along -Z from in front of the face onto the head surface)
41
- export function follicleRoot(headPrims, f, hw) {
42
- let Z = 1.4;
43
- for (let k = 0; k < 300 && SDFModel.evalList(headPrims, ...faceToRef(f.X * f.s, f.Y, Z, hw)) > 0; k++) Z -= 0.005;
44
- return faceToRef(f.X * f.s, f.Y, Z, hw);
45
- }
46
-
47
- // the strand of one follicle (reference space): root, a direction and a length
48
- function strandOf(f, root, lenK) {
49
- const s = f.s;
50
- const d0 = norm([0.86 * s, -0.08 - 0.13 * f.row, 0.42 + 0.06 * f.i]);
51
- const bow = norm([0.1 * s, -0.6, 0.3]);
52
- const L = (0.14 - 0.022 * f.row - 0.006 * Math.abs(f.i - 2)) * lenK;
53
- return { root, d0, bow, L };
54
- }
55
-
56
- // sample points of the left fan in reference space (mid-points and tips of the strands) for the ground
57
- // clearance in the behaviour hook, with the head width of a lioness (the hook scales them to the bone)
58
- export function whiskerSamples(params = {}) {
59
- const hw = 1.12 + 0.02 * (params.sex === 'male' && !params.juv ? 1 : 0) - 0.04 * (params.juv || 0);
60
- const out = [];
61
- for (const f of whiskerFollicles(params.coatSeed || 0)) {
62
- if (f.s < 0 || f.row > 2) continue;
63
- // (the root on the pad lies between Z 0.9 and 1.1 E: both bracket it, so the samples cover the strand)
64
- for (const Z of [0.9, 1.1]) {
65
- const root = faceToRef(f.X, f.Y, Z, hw);
66
- const st = strandOf(f, root, 1);
67
- // (from near the root to the tip: with the pad on the ground the parts near the roots dip first)
68
- for (const t of [0.15, 0.35, 0.6, 1]) out.push(add(add(root, mul(st.d0, st.L * t)), mul(st.bow, st.L * 0.14 * t * t)));
69
- }
70
- }
71
- return out;
72
- }
73
-
74
- export function lionWhiskers({ rig, params, Q, model }) {
75
- if (!Q || Q.shells <= 0) return null;
76
- const juv = params.juv || 0, male = params.sex === 'male' && !juv ? 1 : 0;
77
- const hw = 1.12 + 0.02 * male - 0.04 * juv;
78
- const headPrims = model.prims.filter((p) => p.bone === 'head' && !p.carve && p.part === 'body' && p.tag !== 'mane' && p.tag !== 'canine');
79
- const col = params.variant === 'white' ? srgb(0xf4f0e8) : srgb(0xece8e0);
80
- const SEG = Q.res <= 1.35 ? 7 : 4, SIDES = 3;
81
- const lenK = juv ? 0.7 : 1;
82
- const pos = [], nrm = [], idx = [], comb = [], skinIndex = [];
83
- for (const f of whiskerFollicles(params.coatSeed || 0)) {
84
- if (f.row > 2) continue; // (the lowest row: short bristles, painted dots only)
85
- const bi = rig.BONE[f.s > 0 ? 'whiskerL' : 'whiskerR'].index;
86
- const st = strandOf(f, follicleRoot(headPrims, f, hw), lenK);
87
- const pts = [], tans = [];
88
- for (let k = 0; k <= SEG; k++) {
89
- const t = k / SEG;
90
- pts.push(add(add(st.root, mul(st.d0, st.L * t)), mul(st.bow, st.L * 0.14 * t * t)));
91
- tans.push(norm(add(st.d0, mul(st.bow, 0.28 * t))));
92
- }
93
- const v0 = pos.length / 3;
94
- for (let k = 0; k <= SEG; k++) {
95
- const t = k / SEG, T = tans[k];
96
- const r = 0.0004 * (1 - t) + 0.00007 * t;
97
- let a = cross(T, [0, 1, 0]);
98
- if (Math.hypot(...a) < 1e-4) a = cross(T, [1, 0, 0]);
99
- a = norm(a);
100
- const b = cross(T, a);
101
- for (let q = 0; q < SIDES; q++) {
102
- const ang = (q / SIDES) * Math.PI * 2;
103
- const n = add(mul(a, Math.cos(ang)), mul(b, Math.sin(ang)));
104
- pos.push(...add(pts[k], mul(n, r)));
105
- nrm.push(...n);
106
- comb.push(...T);
107
- skinIndex.push(bi, 0, 0, 0);
108
- }
109
- }
110
- for (let k = 0; k < SEG; k++) {
111
- for (let q = 0; q < SIDES; q++) {
112
- const a = v0 + k * SIDES + q, b = v0 + k * SIDES + ((q + 1) % SIDES);
113
- const c = a + SIDES, d = b + SIDES;
114
- idx.push(a, b, c, b, d, c);
115
- }
116
- }
117
- }
118
- const nV = pos.length / 3;
119
- const tint = new Float32Array(nV * 4), surf = new Float32Array(nV * 4), patternColor = new Float32Array(nV * 4);
120
- for (let v = 0; v < nV; v++) {
121
- tint.set([col[0], col[1], col[2], MAT.KERATIN], v * 4);
122
- surf.set([0.75, 0, 0, 0], v * 4);
123
- }
124
- const skinWeight = new Float32Array(nV * 4);
125
- for (let v = 0; v < nV; v++) skinWeight[v * 4] = 1;
126
- return {
127
- nV, pos: new Float32Array(pos), nrm: new Float32Array(nrm), index: new Uint32Array(idx),
128
- skinIndex: new Uint16Array(skinIndex), skinWeight, comb: new Float32Array(comb), tint,
129
- pattern: new Float32Array(nV).fill(0.05), mark: new Float32Array(nV).fill(0.03), furLen: new Float32Array(nV),
130
- patternColor, surf,
131
- };
132
- }