@bitefoundry/retrieviewer 0.0.3 → 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 (254) 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 +6 -1
  6. package/PLUGIN.md +13 -10
  7. package/PREREQUISITES.md +95 -0
  8. package/PUPPY-MEMORY.md +102 -1
  9. package/README.ko.md +110 -0
  10. package/README.md +117 -59
  11. package/RELEASING.md +23 -12
  12. package/THIRD-PARTY-NOTICES.md +5 -3
  13. package/braille.mjs +35 -9
  14. package/examples/live.mjs +2 -2
  15. package/integrations/linux-overlay/overlay.py +2 -2
  16. package/integrations/macos-overlay/overlay.jxa +8 -6
  17. package/integrations/opencode/runtime.mjs +4 -4
  18. package/integrations/opencode/tui.tsx +1 -1
  19. package/integrations/windows-overlay/Overlay.cs +4 -4
  20. package/integrations/windows-overlay/protocol.mjs +11 -7
  21. package/package.json +34 -13
  22. package/puppy-comfort-sprites.mjs +25 -0
  23. package/puppy-companion-comfort-q5.bin.gz +0 -0
  24. package/puppy-companion-comfort.json +77 -0
  25. package/puppy-companion-life.mjs +49 -16
  26. package/puppy-companion-objects.mjs +12 -0
  27. package/puppy-frame-predictive.mjs +15 -9
  28. package/puppy-frame-store.mjs +2 -2
  29. package/puppy-paper-world.mjs +14 -0
  30. package/puppy-paper.mjs +65 -14
  31. package/retrieviewer-plugin.d.mts +1 -1
  32. package/retrieviewer-plugin.mjs +1 -1
  33. package/retrieviewer.d.mts +10 -4
  34. package/retrieviewer.mjs +20 -14
  35. package/locomotion-core.mjs +0 -21
  36. package/locomotion-puppy.animal +0 -0
  37. package/vendor/procedural-animals/package.json +0 -40
  38. package/vendor/procedural-animals/src/animal.js +0 -146
  39. package/vendor/procedural-animals/src/bake.js +0 -51
  40. package/vendor/procedural-animals/src/build.js +0 -81
  41. package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
  42. package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
  43. package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
  44. package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
  45. package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
  46. package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
  47. package/vendor/procedural-animals/src/core/build/seams.js +0 -183
  48. package/vendor/procedural-animals/src/core/build/warp.js +0 -112
  49. package/vendor/procedural-animals/src/core/build/weights.js +0 -393
  50. package/vendor/procedural-animals/src/core/math/vec.js +0 -81
  51. package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
  52. package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
  53. package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
  54. package/vendor/procedural-animals/src/core/motion/common.js +0 -158
  55. package/vendor/procedural-animals/src/core/motion/index.js +0 -22
  56. package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
  57. package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
  58. package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
  59. package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
  60. package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
  61. package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
  62. package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
  63. package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
  64. package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
  65. package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
  66. package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
  67. package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
  68. package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
  69. package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
  70. package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
  71. package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
  72. package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
  73. package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
  74. package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
  75. package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
  76. package/vendor/procedural-animals/src/index.d.ts +0 -98
  77. package/vendor/procedural-animals/src/index.js +0 -29
  78. package/vendor/procedural-animals/src/registry.js +0 -48
  79. package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
  80. package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
  81. package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
  82. package/vendor/procedural-animals/src/species/bear/index.js +0 -139
  83. package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
  84. package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
  85. package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
  86. package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
  87. package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
  88. package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
  89. package/vendor/procedural-animals/src/species/boar/index.js +0 -110
  90. package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
  91. package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
  92. package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
  93. package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
  94. package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
  95. package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
  96. package/vendor/procedural-animals/src/species/cat/index.js +0 -211
  97. package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
  98. package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
  99. package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
  100. package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
  101. package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
  102. package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
  103. package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
  104. package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
  105. package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
  106. package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
  107. package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
  108. package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
  109. package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
  110. package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
  111. package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
  112. package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
  113. package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
  114. package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
  115. package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
  116. package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
  117. package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
  118. package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
  119. package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
  120. package/vendor/procedural-animals/src/species/cow/index.js +0 -118
  121. package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
  122. package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
  123. package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
  124. package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
  125. package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
  126. package/vendor/procedural-animals/src/species/crow/index.js +0 -70
  127. package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
  128. package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
  129. package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
  130. package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
  131. package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
  132. package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
  133. package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
  134. package/vendor/procedural-animals/src/species/deer/index.js +0 -106
  135. package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
  136. package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
  137. package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
  138. package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
  139. package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
  140. package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
  141. package/vendor/procedural-animals/src/species/dog/index.js +0 -144
  142. package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
  143. package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
  144. package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
  145. package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
  146. package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
  147. package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
  148. package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
  149. package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
  150. package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
  151. package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
  152. package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
  153. package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
  154. package/vendor/procedural-animals/src/species/fish/index.js +0 -88
  155. package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
  156. package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
  157. package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
  158. package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
  159. package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
  160. package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
  161. package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
  162. package/vendor/procedural-animals/src/species/fox/index.js +0 -120
  163. package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
  164. package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
  165. package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
  166. package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
  167. package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
  168. package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
  169. package/vendor/procedural-animals/src/species/frog/index.js +0 -97
  170. package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
  171. package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
  172. package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
  173. package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
  174. package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
  175. package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
  176. package/vendor/procedural-animals/src/species/goat/index.js +0 -149
  177. package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
  178. package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
  179. package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
  180. package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
  181. package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
  182. package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
  183. package/vendor/procedural-animals/src/species/horse/index.js +0 -80
  184. package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
  185. package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
  186. package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
  187. package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
  188. package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
  189. package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
  190. package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
  191. package/vendor/procedural-animals/src/species/lion/index.js +0 -111
  192. package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
  193. package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
  194. package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
  195. package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
  196. package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
  197. package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
  198. package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
  199. package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
  200. package/vendor/procedural-animals/src/species/pig/index.js +0 -161
  201. package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
  202. package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
  203. package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
  204. package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
  205. package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
  206. package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
  207. package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
  208. package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
  209. package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
  210. package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
  211. package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
  212. package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
  213. package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
  214. package/vendor/procedural-animals/src/species/rat/index.js +0 -111
  215. package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
  216. package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
  217. package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
  218. package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
  219. package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
  220. package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
  221. package/vendor/procedural-animals/src/species/shark/index.js +0 -104
  222. package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
  223. package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
  224. package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
  225. package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
  226. package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
  227. package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
  228. package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
  229. package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
  230. package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
  231. package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
  232. package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
  233. package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
  234. package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
  235. package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
  236. package/vendor/procedural-animals/src/species/snake/index.js +0 -73
  237. package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
  238. package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
  239. package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
  240. package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
  241. package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
  242. package/vendor/procedural-animals/src/species/spider/index.js +0 -78
  243. package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
  244. package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
  245. package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
  246. package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
  247. package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
  248. package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
  249. package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
  250. package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
  251. package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
  252. package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
  253. package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
  254. 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
- }