@bitefoundry/retrieviewer 0.0.3 → 0.0.5

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