@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,137 +0,0 @@
1
- // The eagle sculpted as a signed distance field (the feathered silhouette: contour feathers are part
2
- // of the body shape; flight feathers are cards, see core/build/featherCards.js). Every primitive is
3
- // attached to a bone and a group (axial / legL / wingL / jaw). The torso is laid out in the upright
4
- // body-axis frame (rig.js AX); the head in head-local coordinates (HEAD_O, between the eyes).
5
- import { HEAD_O, NECK_SEGS, AX, U_AX, TILT } from './rig.js';
6
- import { add, sub, mul, lerp, norm, cross, dot } from '../../core/math/vec.js';
7
- import { sculptEyeSocket } from '../../core/sdf/eyeSocket.js';
8
-
9
- // Eye (head-local, left): large, set under the supraorbital shelf, fairly forward (yaw 52 deg: raptor
10
- // binocular overlap), visible iris ~15 mm, round aperture slightly flattened from above.
11
- export const EYE = { c: [0.0242, 0.0, 0.004], r: 0.0118, back: 0.0038, yaw: 0.9, pitch: 0.06, lid: 0.0014, R: 0.0096, d: 0.0012, off: -0.0005, tilt: 0.12, irisZ: 0.0074, irisR: 0.0082 };
12
-
13
- const hl = (v) => [v[0] + HEAD_O[0], v[1] + HEAD_O[1], v[2] + HEAD_O[2]];
14
- const rad = (d) => (d * Math.PI) / 180;
15
- const axisAt = (deg) => [0, Math.sin(rad(deg)), Math.cos(rad(deg))];
16
-
17
- export function sculptEagle(m, rig, params = {}) {
18
- const J = rig.J;
19
- const booted = params.variant === 'golden';
20
- const ellY = (o) => {
21
- const y = norm(o.ydir);
22
- let x = o.xdir || [1, 0, 0];
23
- x = norm(sub(x, mul(y, dot(x, y))));
24
- const z = cross(x, y);
25
- return m.ell({ ...o, axis: z, up: y });
26
- };
27
- const A = { bone: 'chest' }, Pv = { bone: 'pelvis' };
28
- const tilt = U_AX;
29
-
30
- // ---------------------------------------------------------------- TORSO (feathered)
31
- // A streamlined spindle in the body-axis frame (AX: a along the axis, b dorsal): deepest at the breast
32
- // under the wing roots, the belly and flanks tapering into the tail. Depth ~0.19 m and width ~0.16 m
33
- // at the breast (a 4-6 kg bird: ~6 L torso with its feathers; flight photos:
34
- // body depth ~0.18-0.2 of the bill-to-tail length, the back flat, the belly line rising to the vent).
35
- m.ell({ ...A, tag: 'breast', c: AX(0.13, -0.02), r: [0.077, 0.074, 0.135], axis: tilt, k: 0 });
36
- m.ell({ ...A, tag: 'mantle', c: AX(0.08, 0.032), r: [0.076, 0.058, 0.15], axis: tilt, k: 0.04 });
37
- m.ell({ ...Pv, tag: 'belly', c: AX(-0.01, -0.03), r: [0.062, 0.058, 0.13], axis: axisAt(TILT - 8), k: 0.04 });
38
- m.ell({ ...Pv, tag: 'rump', c: AX(-0.07, 0.03), r: [0.052, 0.046, 0.085], axis: axisAt(TILT - 6), k: 0.035 });
39
- const td = norm(sub(J.tailTip, J.tailBase));
40
- m.ell({ bone: 'tail', tag: 'undertail', c: add(J.tailTip, [0, -0.018, 0.01]), r: [0.038, 0.026, 0.1], axis: mul(td, -1), k: 0.03 });
41
- m.ell({ bone: 'tail', tag: 'uppertail', c: add(J.tailTip, [0, 0.012, -0.004]), r: [0.038, 0.018, 0.07], axis: mul(td, -1), k: 0.024 });
42
- for (const s of [1, -1]) {
43
- m.ell({ ...Pv, tag: 'flankfold', c: AX(0.01, -0.036, 0.042 * s), r: [0.032, 0.054, 0.1], axis: tilt, k: 0.04 });
44
- m.ell({ ...A, tag: 'scapular', c: AX(0.13, 0.045, 0.05 * s), r: [0.036, 0.028, 0.11], axis: tilt, k: 0.032 });
45
- }
46
-
47
- // ---------------------------------------------------------------- NECK (thick, hackled)
48
- const nj = ['neckBase', ...Array.from({ length: NECK_SEGS - 1 }, (_, i) => 'neck' + (i + 1)), 'occiput'];
49
- const nr = nj.map((_, i) => { const u = i / NECK_SEGS; return 0.054 - 0.012 * Math.pow(u, 0.8); });
50
- for (let i = 0; i < NECK_SEGS; i++) m.cone({ bone: 'neck' + i, tag: 'neck', a: J[nj[i]], b: J[nj[i + 1]], ra: nr[i], rb: nr[i + 1], k: 0.04 });
51
- // (neck landmarks by fraction of the neck: throat ~40 %, nape ~60 %)
52
- const nAt = (f) => { const x = f * NECK_SEGS, i = Math.min(NECK_SEGS - 1, Math.floor(x)); return { bone: 'neck' + i, p: lerp(J[nj[i]], J[nj[i + 1]], x - i) }; };
53
- const th = nAt(0.4), np = nAt(0.6);
54
- m.ell({ bone: th.bone, tag: 'throat', c: add(th.p, [0, -0.004, 0.024]), r: [0.04, 0.05, 0.034], axis: norm([0, 0.6, 1]), k: 0.04 });
55
- m.ell({ bone: np.bone, tag: 'nape', c: add(np.p, [0, 0.01, -0.026]), r: [0.04, 0.046, 0.038], k: 0.04 });
56
-
57
- // ---------------------------------------------------------------- HEAD (head-local, see HEAD_O)
58
- // landmarks from face1 / face2 (profile): eyes at (+-0.0235, 0, 0.004), crown 0.04 above the eyes,
59
- // occiput (0, -0.026, -0.058), cere 0.03-0.052 ahead of the eye, hook tip (0, -0.036, 0.104)
60
- const H = { bone: 'head' };
61
- m.ell({ ...H, tag: 'cranium', c: hl([0, -0.005, -0.028]), r: [0.035, 0.032, 0.05], k: 0.02 });
62
- m.ell({ ...H, tag: 'crown', c: hl([0, 0.013, -0.006]), r: [0.026, 0.012, 0.042], axis: norm([0, -0.12, 1]), k: 0.016 });
63
- m.ell({ ...H, tag: 'hackles', c: hl([0, -0.008, -0.062]), r: [0.034, 0.03, 0.032], k: 0.02 });
64
- m.ell({ ...H, tag: 'forehead', c: hl([0, 0.014, 0.022]), r: [0.019, 0.015, 0.024], axis: norm([0, -0.35, 1]), k: 0.012 });
65
- m.ell({ ...H, tag: 'chin', c: hl([0, -0.034, -0.012]), r: [0.026, 0.024, 0.044], axis: norm([0, 0.15, 1]), k: 0.02 });
66
- for (const s of [1, -1]) {
67
- m.ell({ ...H, tag: 'cheek', c: hl([0.019 * s, -0.016, -0.008]), r: [0.017, 0.02, 0.032], k: 0.014 });
68
- m.ell({ ...H, tag: 'lore', c: hl([0.013 * s, -0.008, 0.026]), r: [0.011, 0.013, 0.02], axis: norm([0, -0.1, 1]), k: 0.01 });
69
- // supraorbital ridge: a hard shelf over the front of the eye (the fierce frown)
70
- ellY({ ...H, tag: 'brow', c: hl([0.0225 * s, 0.0138, 0.01]), ydir: [0.25 * s, 1, 0.12], xdir: [1, 0, 0], r: [0.0095, 0.0055, 0.023], k: 0.005 });
71
- sculptEyeSocket(m, EYE, HEAD_O, s, { bone: 'head' });
72
- }
73
- // upper mandible: deep at the base, laterally compressed, culmen curving into a strong hook; the
74
- // cere (bare waxy skin with the nostril) covers its base
75
- m.ell({ ...H, tag: 'cere', c: hl([0, 0.003, 0.036]), r: [0.0148, 0.0135, 0.016], axis: norm([0, -0.1, 1]), k: 0.008 });
76
- m.ell({ ...H, tag: 'bill', c: hl([0, -0.0095, 0.055]), r: [0.0128, 0.0205, 0.036], axis: norm([0, -0.1, 1]), k: 0.008 });
77
- m.ell({ ...H, tag: 'bill', c: hl([0, -0.012, 0.082]), r: [0.0095, 0.018, 0.021], axis: norm([0, -0.45, 1]), k: 0.008 });
78
- m.ell({ ...H, tag: 'bill', c: hl([0, -0.02, 0.1]), r: [0.0074, 0.016, 0.012], axis: norm([0, -0.95, 0.3]), k: 0.006 });
79
- m.cone({ ...H, tag: 'bill', a: hl([0, -0.029, 0.1055]), b: hl([0, -0.0465, 0.108]), ra: 0.006, rb: 0.0012, k: 0.004 });
80
- for (const s of [1, -1]) m.ell({ ...H, tag: 'nostril', c: hl([0.0118 * s, 0.003, 0.044]), r: [0.003, 0.0028, 0.005], axis: norm([0, 0.2, 1]), k: 0.002, carve: true });
81
- // lower mandible (own surface so the bill can open), tucked inside the hook
82
- const Jw = { bone: 'jaw', part: 'jaw' };
83
- m.ell({ ...Jw, tag: 'mandible', c: hl([0, -0.035, 0.045]), r: [0.0125, 0.0072, 0.046], axis: norm([0, -0.08, 1]), k: 0.006 });
84
- m.cone({ ...Jw, tag: 'mandible', a: hl([0, -0.036, 0.072]), b: hl([0, -0.0395, 0.095]), ra: 0.007, rb: 0.0018, k: 0.005 });
85
- m.ell({ ...Jw, tag: 'gonys', c: hl([0, -0.039, 0.02]), r: [0.016, 0.008, 0.03], k: 0.008 });
86
-
87
- // ---------------------------------------------------------------- LEGS
88
- for (const s of [1, -1]) {
89
- const S = s > 0 ? 'L' : 'R', g = 'leg' + S;
90
- const j = (n) => J[n + S];
91
- const kn = j('knee'), an = j('ankle'), mt = j('mtp');
92
- // feathered "trousers": long loose tibial feathers hanging over the ankle
93
- ellY({ bone: 'tibia' + S, group: g, tag: 'thigh', c: lerp(kn, an, 0.25), ydir: sub(an, kn), r: [0.036, 0.058, 0.032], k: 0.04 });
94
- m.cone({ bone: 'tibia' + S, group: g, tag: 'trousers', a: lerp(kn, an, 0.3), b: lerp(kn, an, 0.96), ra: 0.029, rb: 0.021, k: 0.02 });
95
- if (booted) {
96
- // golden eagle: feathered to the toes
97
- m.cone({ bone: 'tarsus' + S, group: g, tag: 'boot', a: lerp(an, mt, 0.05), b: lerp(an, mt, 0.66), ra: 0.017, rb: 0.0115, k: 0.006 });
98
- } else {
99
- m.cone({ bone: 'tarsus' + S, group: g, tag: 'cuff', a: an, b: lerp(an, mt, 0.42), ra: 0.02, rb: 0.0135, k: 0.01 });
100
- }
101
- m.cone({ bone: 'tarsus' + S, group: g, tag: 'tarsus', a: an, b: mt, ra: 0.0115, rb: 0.0098, k: 0.006 });
102
- m.sphere({ bone: 'tarsus' + S, group: g, tag: 'pad', c: add(mt, [0, -0.001, 0.003]), rad: 0.0105, k: 0.005 });
103
- for (const t of [1, 2, 3, 4]) {
104
- const a = mt, b = J[`t${t}m${S}`], c = J[`t${t}t${S}`];
105
- const big = t === 1 || t === 2; // hallux and inner toe carry the largest talons
106
- const r0 = t === 1 ? 0.0105 : 0.0092;
107
- m.cone({ bone: `toe${t}a${S}`, group: g, tag: 'toe', a, b, ra: r0, rb: 0.0082, k: 0.005 });
108
- m.cone({ bone: `toe${t}b${S}`, group: g, tag: 'toe', a: b, b: lerp(b, c, 0.5), ra: 0.0082, rb: 0.0068, k: 0.004 });
109
- // talon: thick at the base, strongly curved down to a needle point
110
- const q0 = add(lerp(b, c, 0.42), [0, 0.004, 0]), q1 = add(lerp(b, c, 0.78), [0, 0.0035, 0]), q2 = add(c, [0, -0.0035, 0]);
111
- const tr = big ? 0.0062 : 0.0054;
112
- m.cone({ bone: `toe${t}b${S}`, group: g, tag: 'claw', a: q0, b: q1, ra: tr, rb: tr * 0.62, k: 0.002 });
113
- m.cone({ bone: `toe${t}b${S}`, group: g, tag: 'claw', a: q1, b: q2, ra: tr * 0.62, rb: 0.0008, k: 0.0015 });
114
- }
115
- }
116
-
117
- // ---------------------------------------------------------------- WINGS (arm; flight feathers are cards)
118
- for (const s of [1, -1]) {
119
- const S = s > 0 ? 'L' : 'R', g = 'wing' + S;
120
- const sh = J['shoulder' + S], el = J['elbow' + S], wr = J['wrist' + S], tp = J['handTip' + S];
121
- let n = norm(cross(sub(el, sh), sub(wr, el)));
122
- if (n[1] < 0) n = mul(n, -1);
123
- const seg = (a, b, bone, thick, chord, back, tag, k = 0.016) => {
124
- const d = norm(sub(b, a));
125
- const t = mul(norm(cross(n, d)), s); // trailing side
126
- const c = add(lerp(a, b, 0.5), mul(t, back));
127
- return ellY({ bone, group: g, tag, c, ydir: d, xdir: n, r: [thick, Math.hypot(...sub(b, a)) * 0.58, chord], k });
128
- };
129
- seg(sh, el, 'humerus' + S, 0.019, 0.05, 0.018, 'arm', 0.024);
130
- seg(el, wr, 'ulna' + S, 0.015, 0.052, 0.022, 'arm');
131
- seg(wr, tp, 'hand' + S, 0.012, 0.026, 0.008, 'hand', 0.012);
132
- // leading-edge roll (propatagium) along the forearm: on the ulna only, so it stays on the arm
133
- // when the elbow opens (a web spanning shoulder -> wrist would swing forward as a loop)
134
- seg(lerp(el, wr, 0.08), wr, 'ulna' + S, 0.011, 0.016, -0.02, 'patagium', 0.014);
135
- m.sphere({ bone: 'hand' + S, group: g, tag: 'wrist', c: wr, rad: 0.0135, k: 0.012 });
136
- }
137
- }
@@ -1,271 +0,0 @@
1
- // Fish coat: scales (material 6, with iridescence) on the body, smooth glossy skin on the scaleless
2
- // head, fins as translucent membranes (material 11: ray phase / position along the ray / opacity in
3
- // `surf`), variant colour schemes (trout spots and pink band, goldfish metallic orange and sarasa
4
- // patches, clownfish bands with black edging, bluegill bars, ear flap and breast), mouth and gills.
5
- import { SDFModel } from '../../core/sdf/sdf.js';
6
- import { finRayCoords } from '../../core/rig/swimmer.js';
7
- import { norm, sub, dot, cross, clamp, smoothstep, mix, rng, fbm3 } from '../../core/math/vec.js';
8
- import { srgb, mix3, MAT, poissonFeatures, featureDistance, smoothField } from '../../core/build/coatKit.js';
9
- import { fishGeo } from './rig.js';
10
- import { CELL, opEdgeU } from './sculpt.js';
11
-
12
- const FIN_MAT = MAT.FIN ?? 11;
13
-
14
- export function fishCoat(ctx) {
15
- const { model, pos, nrm, nV, lists, regionOf, regionNames, params, weights } = ctx;
16
- const G = fishGeo(params);
17
- const V = G.V, SL = G.SL, H = V.head;
18
- const key = params.variant;
19
- const R = rng(4049 + (params.coatSeed || 0));
20
- const col = {};
21
- for (const [k, hex] of Object.entries(V.colour)) col[k] = srgb(hex);
22
- const morph = params.morph || '';
23
- const shade = params.coatShade || 0;
24
- const tweak = (c, k = 1) => [c[0] * (1 + shade * 0.12 * k), c[1] * (1 + shade * 0.08 * k), c[2] * (1 + shade * 0.04 * k)];
25
- const cell = SL * CELL * (V.cellK || 1);
26
-
27
- const comb = new Float32Array(nV * 3);
28
- const tint = new Float32Array(nV * 4);
29
- const pattern = new Float32Array(nV).fill(1);
30
- const patternColor = new Float32Array(nV * 4);
31
- const mark = new Float32Array(nV).fill(1);
32
- const furLen = new Float32Array(nV);
33
- const surf = new Float32Array(nV * 4);
34
- const kind = new Uint8Array(nV); // 0 body, 1 head, 2 fin, 5 adipose, 6 jaw, 7 ear flap, 8 buccal cavity, 9 lips' cut faces
35
- const finInfo = new Array(nV);
36
- const jawRegion = regionNames.indexOf('jaw');
37
-
38
- for (let v = 0; v < nV; v++) {
39
- const p = [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
40
- const n = [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
41
- let best = null, bd = 1e9, carveHit = null;
42
- for (const pr of lists[v]) {
43
- const d = SDFModel.dist(pr, p[0], p[1], p[2]);
44
- if (pr.carve) { if (Math.abs(d) < cell * 0.9 && (!carveHit || Math.abs(d) < carveHit.d)) carveHit = { pr, d: Math.abs(d) }; continue; }
45
- if (d < bd) { bd = d; best = pr; }
46
- }
47
- const u = G.u(p[2]);
48
- const u1 = clamp(u, 0, 1), e1 = (p[1] - G.yc(u1)) / Math.max(1e-6, G.hd(u1));
49
- // head (scaleless) in front of the gill cover's free edge, scaled body behind it
50
- let k = u < opEdgeU(H.op, clamp(e1, -1.2, 1.2)) ? 1 : 0;
51
- if (regionOf[v] === jawRegion) k = 6;
52
- if (best && (best.tag === 'fin')) { k = 2; finInfo[v] = { prim: best, rc: finRayCoords(best.fin, p) }; }
53
- if (best && best.tag === 'adipose') k = 5;
54
- if (carveHit) {
55
- const tg = carveHit.pr.tag;
56
- if ((tg === 'mouthcut' && n[1] < -0.25) || (tg === 'jawcut' && n[1] > 0.3)) k = 9;
57
- else if (tg === 'mouth') k = 8;
58
- }
59
- if (best && best.tag === 'earflap') k = 7;
60
- kind[v] = k;
61
- void n;
62
- }
63
-
64
- // ---- spots (trout on the body and on the dorsal / adipose / caudal fins; bluegill dorsal blotch)
65
- let spotsF = null;
66
- if (key === 'trout' || morph === 'sarasa') {
67
- const sarasa = morph === 'sarasa';
68
- spotsF = poissonFeatures({
69
- pos, nrm, nV, R,
70
- radius: (v) => {
71
- const k = kind[v];
72
- const y = pos[v * 3 + 1], u = G.u(pos[v * 3 + 2]);
73
- if (sarasa) {
74
- if (k !== 0 && k !== 1) return 0;
75
- return 0.07 * SL;
76
- }
77
- if (k === 2) {
78
- const fi = finInfo[v];
79
- const nm = fi.prim.fin.name;
80
- if (!(nm.startsWith('dorsal') || nm === 'caudal')) return 0;
81
- return 0.0075 * SL;
82
- }
83
- if (k !== 0 && k !== 5 && k !== 1) return 0;
84
- const e = (y - G.yc(u)) / G.hd(u);
85
- const dens = smoothstep(-0.35, 0.25, e) * (u < 0.1 ? 0 : 1);
86
- return dens > 0.2 ? (0.0042 + 0.003 * dens) * SL * (params.spotScale || 1) : 0;
87
- },
88
- spacing: sarasa ? 1.4 : 1.9, gap: sarasa ? 0.01 * SL : 0.003 * SL, cell: sarasa ? 0.12 * SL : 0.03 * SL,
89
- });
90
- }
91
-
92
- for (let v = 0; v < nV; v++) {
93
- const o3 = v * 3, o4 = v * 4;
94
- const p = [pos[o3], pos[o3 + 1], pos[o3 + 2]];
95
- const n = [nrm[o3], nrm[o3 + 1], nrm[o3 + 2]];
96
- const k = kind[v];
97
- const u = clamp(G.u(p[2]), 0, 1.3);
98
- const uc = Math.min(u, 1);
99
- const e = (p[1] - G.yc(uc)) / Math.max(1e-6, G.hd(uc));
100
- const noise = fbm3(p[0] * 9 / SL, p[1] * 9 / SL, p[2] * 9 / SL, 3) - 0.5;
101
- let c, mat = MAT.SCALES, gl = V.gloss, sz = V.scale * SL, irid = V.irid, sw = 0;
102
- let cm = norm(sub([0, 0, -1], mul(n, -n[2])));
103
- if (k === 2) {
104
- // ---------------- fin membrane
105
- const { prim, rc } = finInfo[v];
106
- const nm = prim.fin.name;
107
- cm = rc.dir;
108
- mat = FIN_MAT;
109
- c = mix3(col.fin, col.finEdge, 0);
110
- let opac = key === 'clownfish' ? 0.86 : key === 'bluegill' ? 0.7 : key === 'goldfish' ? 0.55 : 0.6;
111
- const along = rc.along;
112
- c = mix3(c, mix3(c, col.back, 0.4), smoothstep(0.5, 0.0, along) * 0.5);
113
- if (key === 'trout') {
114
- // white leading edge on the pelvic and anal fins, olive elsewhere
115
- if (nm.startsWith('pelvic') || nm === 'anal') {
116
- const lead = nm === 'anal' ? rc.phase < 1.2 : rc.phase < 1.2;
117
- if (lead) c = mix3(c, col.finEdge, 0.85);
118
- if (nm.startsWith('pelvic') || nm === 'anal') c = mix3(c, srgb(0xb58a6a), 0.3);
119
- }
120
- } else if (key === 'goldfish') {
121
- c = mix3(col.fin, col.finEdge, smoothstep(0.3, 1, along));
122
- } else if (key === 'clownfish') {
123
- // black margins: the outer band of every fin
124
- const rayLen = 1;
125
- void rayLen;
126
- const margin = nm.startsWith('pectoral') ? 0.0 : 0.13 * (params.edgeK ?? 1);
127
- if (margin > 0) mark[v] = (1 - along - margin) * 0.2 * SL;
128
- } else if (key === 'bluegill') {
129
- if (nm === 'dorsal1') {
130
- // dark blotch at the rear of the soft dorsal fin
131
- const ph = rc.phase / Math.max(1, prim.fin.rays.length - 1);
132
- const d = Math.hypot((ph - 0.88) * 0.55, (along - 0.32) * 0.45) - 0.16;
133
- pattern[v] = d * 0.1 * SL;
134
- patternColor.set([...srgb(0x1e1e18), 0.9], o4);
135
- }
136
- if (nm.startsWith('pelvic') || nm === 'anal') c = mix3(c, srgb(0x3c3a2c), 0.5);
137
- }
138
- if (morph === 'black' && key === 'clownfish') c = mix3(c, srgb(0x2a160c), 0.7);
139
- c = tweak(c, 0.5);
140
- sw = opac * (0.92 + 0.16 * noise);
141
- surf[o4] = 0.75; surf[o4 + 1] = rc.phase; surf[o4 + 2] = along; surf[o4 + 3] = sw;
142
- if (spotsF && (nm.startsWith('dorsal') || nm === 'caudal')) {
143
- const [d] = featureDistance(spotsF, p, n);
144
- pattern[v] = d; patternColor.set([...col.spot, 1], o4);
145
- }
146
- tint.set([c[0], c[1], c[2], mat], o4);
147
- comb.set(cm, o3);
148
- continue;
149
- }
150
- if (k === 8) {
151
- // buccal cavity (seen only when the mouth opens): dark, moist red-brown, not a black hole
152
- c = srgb(0x4a2622);
153
- tint.set([c[0], c[1], c[2], MAT.SKIN], o4);
154
- comb.set(cm, o3);
155
- surf.set([0.6, 0, 0, 0], o4);
156
- continue;
157
- }
158
- // ---------------- body, head, jaw, adipose
159
- const up = smoothstep(-0.2, 0.9, e * 0.8 + n[1] * 0.35);
160
- const lowK = smoothstep(0.2, -0.85, e * 0.85 + n[1] * 0.25);
161
- if (key === 'trout') {
162
- c = mix3(col.lower, col.upper, smoothstep(-0.35, 0.3, e));
163
- c = mix3(c, col.back, smoothstep(0.35, 0.95, up));
164
- c = mix3(c, col.belly, smoothstep(0.4, 0.95, lowK));
165
- // the pink lateral band from the gill cover to the tail (and a flush on the cheek)
166
- const band = (1 - smoothstep(0.1, 0.34, Math.abs(e - 0.02))) * smoothstep(0.16, 0.3, u) * (params.bandK ?? 1);
167
- c = mix3(c, col.band, band * 0.55);
168
- if (k === 1 || k === 6) {
169
- const cheek = (1 - smoothstep(0.2, 0.7, Math.abs(e + 0.1))) * smoothstep(0.1, 0.2, u);
170
- c = mix3(mix3(c, col.head, 0.25), col.cheek, cheek * 0.6 * (params.bandK ?? 1));
171
- }
172
- } else if (key === 'goldfish') {
173
- c = mix3(col.lower, col.upper, smoothstep(-0.5, 0.3, e));
174
- c = mix3(c, col.back, smoothstep(0.45, 0.95, up));
175
- c = mix3(c, col.belly, smoothstep(0.35, 0.95, lowK));
176
- if (morph === 'white') c = mix3(srgb(0xf1eee6), srgb(0xf6e7c8), smoothstep(0.3, 0.9, up) * 0.4);
177
- if (morph === 'red') c = mix3(c, srgb(0xd83414), 0.72);
178
- // common goldfish range from orange-gold to red-orange
179
- if (morph === 'common') { const t = (params.hueShift ?? 0.5) - 0.5; c = t < 0 ? mix3(c, srgb(0xf5a834), -t * 1.1) : mix3(c, srgb(0xe85a18), t * 0.8); }
180
- } else if (key === 'clownfish') {
181
- c = mix3(col.lower, col.upper, smoothstep(-0.5, 0.2, e));
182
- c = mix3(c, col.back, smoothstep(0.5, 1.0, up) * 0.6);
183
- c = mix3(c, col.belly, smoothstep(0.5, 1.0, lowK) * 0.5);
184
- if (morph === 'black') c = mix3(c, srgb(0x1c120c), 0.85);
185
- } else {
186
- // bluegill: olive back, brassy flanks, orange / yellow breast, dusky vertical bars, blue cheek
187
- c = mix3(col.lower, col.upper, smoothstep(-0.5, 0.25, e));
188
- c = mix3(c, col.back, smoothstep(0.4, 0.95, up));
189
- const breast = smoothstep(0.1, 0.85, lowK) * (1 - smoothstep(0.35, 0.6, u));
190
- c = mix3(c, params.sex === 'male' ? col.breast : col.belly, breast * 0.85);
191
- c = mix3(c, col.belly, smoothstep(0.55, 1, lowK) * (1 - breast) * 0.5);
192
- const barPh = (u - 0.3) / 0.105;
193
- const bar = u > 0.28 && u < 0.95 ? Math.pow(0.5 + 0.5 * Math.cos(barPh * 2 * Math.PI), 3) * smoothstep(-0.7, 0.2, e) : 0;
194
- c = mix3(c, col.spot, bar * 0.45 * (params.barK ?? 1));
195
- if (k === 1 || k === 6) {
196
- const streak = 0.5 + 0.5 * Math.sin((p[1] - G.axisY) / SL * 90 + u * 30);
197
- // two or three blue-violet streaks across the lower cheek and gill cover
198
- const zone = (1 - smoothstep(0.15, 0.45, Math.abs(e + 0.35))) * smoothstep(0.1, 0.18, u) * (1 - smoothstep(0.28, 0.33, u));
199
- c = mix3(c, col.cheek, zone * smoothstep(0.55, 0.85, streak) * 0.5);
200
- }
201
- }
202
- if (k === 7) c = col.ear;
203
- if (col.ear && (k === 0 || k === 1)) {
204
- // the bluegill's opercular flap: a solid black lobe (~0.08 x 0.05 SL) at the top rear of the gill cover
205
- const fz = (u - (H.op + 0.03)) / 0.058, fy = (p[1] - (G.yc(H.op) + 0.2 * G.hd(H.op))) / (0.034 * SL);
206
- const r = fz * fz + fy * fy;
207
- if (r < 1.25) c = mix3(c, col.ear, smoothstep(1.25, 0.9, r));
208
- }
209
- // the lips' cut faces (the lip line at rest, the inside of the lips when the mouth opens): the
210
- // skin colour a little darker, toward the pale pink of the mouth lining
211
- if (k === 9) c = mix3(c.map((x) => x * 0.85), srgb(0xb88a80), 0.3);
212
- // the gill cover's free edge: a soft shading line (the plate overlaps the body there), the head
213
- // colour darkened ~15 %, fading out where the edge runs under the throat and the nape
214
- const eC = clamp(e, -1.2, 1.2);
215
- const du = u - opEdgeU(H.op, eC);
216
- const edgeLine = (k === 0 || k === 1) ? smoothstep(-0.005, 0.0, du) * (1 - smoothstep(0.0015, 0.008, du)) * (1 - smoothstep(0.62, 0.9, Math.abs(eC + 0.05))) : 0;
217
- c = c.map((x) => x * (1 - 0.15 * edgeLine));
218
- c = mix3(c, c.map((x) => x * 0.8), 0.25 * (noise + 0.5));
219
- c = tweak(c);
220
- if (k === 5) { sz = 0.0008 * SL; }
221
- // the head is scaleless skin (tiny scale size: no scale relief, same sheen); behind the gill
222
- // cover the scales fade in over V.scaleFade of SL (surf.w < 0 lowers their contrast), so large
223
- // scales (goldfish) do not start at a hard line
224
- let sw2 = irid;
225
- if (k === 1 || k === 6 || k === 9) sz = 0.0006 * SL;
226
- else if (k === 0 && V.scaleFade && du < V.scaleFade) sw2 = mix(-1, irid, clamp(du / V.scaleFade, 0, 1));
227
- // spots / patches
228
- if (spotsF && (k === 0 || k === 1 || k === 5 || (k === 6 && morph === 'sarasa'))) {
229
- const [d] = featureDistance(spotsF, p, n);
230
- pattern[v] = d;
231
- patternColor.set([...col.spot, 1], o4);
232
- }
233
- if (key === 'clownfish') {
234
- const bandsSD = clownBands(G, u, e);
235
- pattern[v] = bandsSD;
236
- patternColor.set([...col.spot, 1], o4);
237
- // black edging around the bands
238
- mark[v] = Math.abs(bandsSD) - 0.012 * SL * (params.edgeK ?? 1);
239
- if (morph === 'black') patternColor.set([...col.spot, 1], o4);
240
- }
241
- tint.set([c[0], c[1], c[2], mat], o4);
242
- comb.set(cm, o3);
243
- surf.set([gl, sz, 0, sw2], o4);
244
- }
245
- // far from any feature the SDF is 'infinite': clamp it, or its screen-space derivative (the shader's
246
- // antialiasing width) explodes across the triangles on that border and paints a stepped line
247
- for (let v = 0; v < nV; v++) { if (pattern[v] > 0.03 * SL) pattern[v] = 0.03 * SL; if (mark[v] > 0.03 * SL) mark[v] = 0.03 * SL; }
248
- // soften fin opacity / phase noise across the mesh
249
- const w = new Float32Array(nV);
250
- for (let v = 0; v < nV; v++) w[v] = surf[v * 4 + 3];
251
- smoothField(w, weights.neighbors, 1, (v) => kind[v] !== 2);
252
- for (let v = 0; v < nV; v++) if (kind[v] === 2) surf[v * 4 + 3] = w[v];
253
- return { comb, tint, pattern, mark, furLen, patternColor, surf, stats: { spots: spotsF ? spotsF.spots.length : 0 } };
254
- }
255
-
256
- const mul = (a, s) => [a[0] * s, a[1] * s, a[2] * s];
257
-
258
- // clownfish: three white bands (behind the eye curving forward, mid-body with a forward bulge,
259
- // caudal peduncle), as a signed distance in metres (< 0 inside a band)
260
- function clownBands(G, u, e) {
261
- const SL = G.SL;
262
- // the forehead and chin stand above the outline table (the sections overlap there): without the
263
- // clamp the head band's forward curve ran onto the snout as a white smear
264
- e = clamp(e, -1.1, 1.1);
265
- const band = (c, w) => (Math.abs(u - c) - w) * SL;
266
- const b1 = band(0.235 - 0.05 * e * e + 0.01 * e, 0.045);
267
- const b2 = band(0.53 + 0.07 * Math.exp(-e * e * 4) - 0.03, 0.05 + 0.012 * Math.exp(-e * e * 4));
268
- const b3 = band(0.905, 0.035);
269
- return Math.min(b1, b2, b3);
270
- }
271
- void cross; void dot; void mix;
@@ -1,88 +0,0 @@
1
- // Fish (teleosts): rainbow trout, goldfish, ocellaris clownfish and bluegill as seeded / explicit
2
- // variants (createAnimal('fish', { variant: 'trout' })). Swimmer body plan (core/motion/swimmer.js).
3
- import { fishRig, fishGeo } from './rig.js';
4
- import { sculptFish, CELL } from './sculpt.js';
5
- import { fishRegions } from './regions.js';
6
- import { fishCoat } from './coat.js';
7
- import { motion } from './motion.js';
8
- import { VARIANTS, VARIANT_KEYS } from './variants.js';
9
- import { srgb } from '../../core/build/coatKit.js';
10
- import { rng } from '../../core/math/vec.js';
11
-
12
- const pick = (R, table) => {
13
- let x = R(), acc = 0;
14
- for (const [k, w] of table) { acc += w; if (x < acc) return k; }
15
- return table[table.length - 1][0];
16
- };
17
-
18
- export default {
19
- id: 'fish',
20
- name: 'Fish',
21
- latin: 'Teleostei',
22
- group: 'swimmers',
23
- plan: 'swimmer',
24
- covering: 'scales',
25
- variants: VARIANT_KEYS.map((id) => ({ id, name: VARIANTS[id].name, latin: VARIANTS[id].latin })),
26
-
27
- // Seeded individual. The variant (species) comes from opts.variant or from the seed; within a
28
- // variant: size, body depth, head size, colour morph, sex (bluegill breeding colours, clownfish
29
- // females larger), juveniles smaller with bigger heads and eyes.
30
- variation(R, o) {
31
- const variant = o.variant && VARIANTS[o.variant] ? o.variant : VARIANT_KEYS[Math.floor(R() * VARIANT_KEYS.length) % VARIANT_KEYS.length];
32
- const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
33
- const age = o.age || 'adult';
34
- const juv = age === 'juvenile';
35
- const g = () => (R() + R() + R() - 1.5) / 1.5;
36
- const G = fishGeo({ variant });
37
- let sexK = 1;
38
- if (variant === 'clownfish') sexK = sex === 'female' ? 1.1 : 0.86;
39
- if (variant === 'trout') sexK = sex === 'male' ? 1.04 : 0.97;
40
- const size = sexK * (1 + 0.1 * g()) * (juv ? 0.45 : 1);
41
- // colour morphs from their own hashed stream: consecutive seeds must not correlate
42
- const M = rng(Math.imul((o.seed ?? 1) + 0x632be5ab, 0x9e3779b1) ^ 0x5bd1e995);
43
- for (let i = 0; i < 4; i++) M();
44
- let morph = '';
45
- if (variant === 'trout') morph = pick(M, [['stream', 0.72], ['silver', 0.28]]);
46
- if (variant === 'goldfish') morph = pick(M, [['common', 0.55], ['red', 0.17], ['sarasa', 0.2], ['white', 0.08]]);
47
- if (variant === 'clownfish') morph = pick(M, [['orange', 0.9], ['black', 0.1]]);
48
- const shadeM = M();
49
- const SL = G.SL;
50
- const depthK = 1 + 0.05 * g() + (variant === 'trout' && sex === 'female' ? 0.02 : 0);
51
- return {
52
- variant, sex, age, size, morph,
53
- warps: [
54
- { type: 'girth', k: depthK, cy: G.axisY, z0: G.z(0.95), z1: G.z(0.15), fade: 0.12 * SL },
55
- { type: 'scaleAbout', c: G.headO, k: (1 + 0.035 * g()) * (juv ? 1.14 : 1), r0: 0.12 * SL, r1: 0.3 * SL },
56
- ],
57
- coatShade: 0.6 * g(),
58
- hueShift: variant === 'goldfish' && morph === 'common' ? shadeM : 0.5,
59
- coatSeed: Math.floor(R() * 1e6),
60
- bandK: variant === 'trout' ? (morph === 'silver' ? 0.3 : 0.8 + 0.25 * R()) * (sex === 'male' ? 1.15 : 0.9) : 1,
61
- barK: variant === 'bluegill' ? (sex === 'male' ? 0.8 : 1.1) * (0.8 + 0.4 * R()) : 1,
62
- edgeK: variant === 'clownfish' ? 0.8 + 0.5 * R() : 1,
63
- spotScale: 0.8 + 0.4 * R(),
64
- tailK: (variant === 'goldfish' && morph !== 'common' && R() < 0.5 ? 1.3 : 1) * (1 + 0.06 * g()),
65
- finK: 1 + 0.07 * g() + (juv ? 0.08 : 0),
66
- minThick: SL * CELL * (VARIANTS[variant].cellK || 1) * 1.15,
67
- };
68
- },
69
- rig: (params) => fishRig(params),
70
- sculpt: (m, rig, params) => sculptFish(m, rig, params),
71
- regions: fishRegions,
72
- coat: fishCoat,
73
- eyeSpecs(params) {
74
- const G = fishGeo(params);
75
- const E = G.eye, V = G.V;
76
- const spec = { c: [E.x, 0, 0], r: E.r, back: 0, yaw: 1.28, pitch: 0.1, lid: 0, R: E.r * 1.0, d: 0, off: 0, tilt: 0, irisZ: E.r * 0.42, irisR: E.r * 0.86 };
77
- const iris = V.eye.iris.map((h) => srgb(h));
78
- const look = {
79
- pupil: 'round', lids: 'none', pupilSize: [0.34, 0.42],
80
- iris: { inner: iris[0], mid: iris[1], hi: iris[2], outer: iris[3] },
81
- sclera: { color: [0.05, 0.05, 0.05], visible: false },
82
- };
83
- return [1, -1].map((side) => ({ side, spec, headOrigin: G.headO, bone: 'head', look }));
84
- },
85
- motion,
86
- // scale rim contrast per variant: the goldfish's big scales read as a fine lattice, not outlined cells
87
- render: (params) => ({ markColor: [0.008, 0.008, 0.009], scaleEdge: VARIANTS[params?.variant]?.scaleEdge ?? 0.15 }),
88
- };
@@ -1,92 +0,0 @@
1
- // Fish motion data for the swimmer engine (schema: src/core/motion/swimmer.js). Speeds in body
2
- // lengths per second (BL/s), so one table serves every size. Per-variant overrides in `variants`.
3
- //
4
- // Tail-beat frequency vs speed from Bainbridge (1958, dace / trout / goldfish): U/L = 0.75 f - 1 above
5
- // ~2 BL/s, tail amplitude ~0.2 L peak-to-peak; below that the frequencies and amplitudes are estimates
6
- // from subcarangiform kinematics (Videler 1993). Bluegill: pectoral (labriform) swimming up to 1.2 BL/s,
7
- // fin beat 2.8 Hz at the gait transition (Jones et al. 2007). C-start stage 1 ~ 0.24 s per metre of body
8
- // (Domenici 2023: 16 ms at 5 cm, 60 ms at 25 cm). Wave rows: [v, f Hz, amp p-p / L, lambda / L,
9
- // pectoral activity, pectoral beat Hz, pectoral fold].
10
- export const motion = {
11
- speeds: { scull: 0.4, cruise: 1.5, sprint: 4 },
12
- maxSpeed: 6, burstSpeed: 10, accel: 12, decel: 4, turnRate: 3.2, turnRadius: 0.35,
13
- wave: [
14
- [0, 0.8, 0.03, 1.0, 1.0, 1.5, 0],
15
- [0.5, 1.9, 0.1, 0.95, 0.6, 1.8, 0.3],
16
- [1.5, 3.3, 0.17, 0.9, 0.15, 2.0, 0.75],
17
- [3, 5.3, 0.2, 0.85, 0, 2, 1],
18
- [6, 9.3, 0.2, 0.8, 0, 2, 1],
19
- [10, 14.7, 0.21, 0.75, 0, 2, 1],
20
- ],
21
- envelope: { exp: 3.5 },
22
- flex: [0.15, 0.86], bend: 1.4, cBend: 3.1, bank: 0.3, pitchMax: 0.9, hover: 0.35,
23
- clearance: 0.1, cruiseDepth: 0.6,
24
- pectoral: { rest: [0, 0, 0], flap: 9, row: 14, feather: 22, fold: 24, flare: 45, alternate: 0.25, stiff: 0 },
25
- pelvic: { rest: 0, spread: 10, fold: 12 },
26
- breath: { rate: 1.2, operculum: 9, mouth: 0.07, ram: 2.5 },
27
- jaw: { gape: 30, protrude: 0.02 },
28
- actions: {
29
- burst: { turn: 90 },
30
- jump: { height: 1.4 },
31
- eat: { style: 'suction' },
32
- drink: { reach: 3 },
33
- sleep: { style: 'bottom' },
34
- },
35
- variants: {
36
- trout: {
37
- speeds: { scull: 0.4, cruise: 1.5, sprint: 4 },
38
- jaw: { gape: 34, protrude: 0.012 },
39
- actions: { jump: { height: 1.8 } },
40
- },
41
- goldfish: {
42
- speeds: { scull: 0.35, cruise: 1.3, sprint: 3.5 },
43
- maxSpeed: 5, burstSpeed: 9,
44
- wave: [
45
- [0, 1.0, 0.03, 1.0, 1.0, 2.2, 0],
46
- [0.5, 1.8, 0.09, 0.95, 0.8, 2.4, 0.2],
47
- [1.3, 3.1, 0.17, 0.9, 0.3, 2.4, 0.6],
48
- [3, 5.3, 0.2, 0.85, 0, 2.4, 1],
49
- [6, 9.3, 0.2, 0.8, 0, 2.4, 1],
50
- [10, 14.7, 0.21, 0.75, 0, 2.4, 1],
51
- ],
52
- envelope: { exp: 3.0 }, turnRadius: 0.25,
53
- pectoral: { rest: [0, 0, 0], flap: 11, row: 16, feather: 26, fold: 26, flare: 48, alternate: 0.8 },
54
- jaw: { gape: 36, protrude: 0.03 },
55
- breath: { rate: 1.4 },
56
- actions: { eat: { style: 'bottom' }, jump: { height: 1.0 } },
57
- },
58
- clownfish: {
59
- speeds: { scull: 0.8, cruise: 2.0, sprint: 4 },
60
- maxSpeed: 6, burstSpeed: 9, turnRadius: 0.2, turnRate: 4,
61
- wave: [
62
- [0, 1.0, 0.02, 1.0, 1.0, 3.5, 0],
63
- [0.6, 1.2, 0.03, 1.0, 1.0, 4.5, 0],
64
- [1.4, 2.4, 0.08, 0.95, 0.8, 5.0, 0.15],
65
- [2.2, 4.0, 0.16, 0.9, 0.2, 4, 0.6],
66
- [4, 6.7, 0.2, 0.85, 0, 4, 1],
67
- [8, 12, 0.21, 0.8, 0, 4, 1],
68
- ],
69
- envelope: { exp: 2.8 },
70
- pectoral: { rest: [0, 0, 0], flap: 12, row: 20, feather: 28, fold: 28, flare: 45, alternate: 0.08 },
71
- breath: { rate: 1.8 },
72
- jaw: { gape: 30, protrude: 0.03 },
73
- actions: { sleep: { style: 'side' }, jump: { height: 0.8 } },
74
- },
75
- bluegill: {
76
- speeds: { scull: 0.6, cruise: 1.8, sprint: 4 },
77
- maxSpeed: 6, burstSpeed: 9, turnRadius: 0.2, turnRate: 3.8,
78
- wave: [
79
- [0, 0.8, 0.02, 1.0, 1.0, 1.6, 0],
80
- [0.6, 1.0, 0.03, 1.0, 1.0, 2.2, 0],
81
- [1.2, 2.2, 0.08, 0.95, 0.7, 2.8, 0.2],
82
- [1.8, 3.7, 0.17, 0.9, 0.15, 2.8, 0.7],
83
- [4, 6.7, 0.2, 0.85, 0, 2.8, 1],
84
- [8, 12, 0.21, 0.8, 0, 2.8, 1],
85
- ],
86
- envelope: { exp: 3.0 },
87
- pectoral: { rest: [0, 0, 0], flap: 12, row: 18, feather: 28, fold: 18, flare: 42, alternate: 0.1 },
88
- jaw: { gape: 32, protrude: 0.03 },
89
- actions: { sleep: { style: 'hover' }, jump: { height: 1.0 } },
90
- },
91
- },
92
- };
@@ -1,27 +0,0 @@
1
- // Meshing plan: the whole fish (body + fins) as one surface at SL * CELL (0.8 mm for a 26 cm trout at
2
- // the hero tier), and the lower jaw as its own rigid surface so the mouth can open.
3
- import { fishGeo } from './rig.js';
4
- import { CELL } from './sculpt.js';
5
-
6
- export function fishRegions(rig, params, Q) {
7
- const G = fishGeo(params);
8
- const SL = G.SL, V = G.V, r = Q.res;
9
- const h = SL * CELL * (V.cellK || 1) * r;
10
- // tight box: body outline + the longest dorsal / anal rays + the paired fins
11
- let top = 0, bot = 0, hw = 0;
12
- for (const r of V.prof) { top = Math.max(top, r[1]); bot = Math.max(bot, r[2]); hw = Math.max(hw, r[3]); }
13
- const dl = Math.max(...V.fins.dorsal.map((d) => Math.max(...d.len))), al = Math.max(...V.fins.anal.map((d) => Math.max(...d.len)));
14
- const J = rig.J;
15
- const xMax = Math.max(hw * SL, Math.abs(J.pecTipL[0]), Math.abs(J.pelTipL[0])) + 0.03 * SL;
16
- const topY = G.axisY + (top + dl) * SL + 0.03 * SL;
17
- const botY = Math.min(G.axisY - (bot + al * 0.9) * SL, J.pelTipL[1], J.pecTipL[1]) - 0.03 * SL;
18
- const zTail = G.z(1 + G.tailLen * 1.08 + 0.03);
19
- const g = V.head.mouth.gape;
20
- return {
21
- jobs: [
22
- [{ name: 'body', part: 'body', bmin: [-xMax, botY, zTail], bmax: [xMax, topY, G.z0 + 0.03 * SL], h, F: 6 }],
23
- [{ name: 'jaw', part: 'jaw', bmin: [-0.2 * SL, -0.02 * SL, G.z(g + 0.14)], bmax: [0.2 * SL, G.mouthY + 0.03 * SL, G.z0 + 0.04 * SL], h: h * 0.8, F: 4, rigidBone: 'jaw' }],
24
- ],
25
- fade: () => 1,
26
- };
27
- }