@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.
- package/ASSET-PROVENANCE.md +6 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +65 -0
- package/DESKTOP-PLATFORMS.md +6 -1
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +110 -0
- package/README.md +117 -59
- package/RELEASING.md +23 -12
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/linux-overlay/overlay.py +2 -2
- package/integrations/macos-overlay/overlay.jxa +8 -6
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +4 -4
- package/integrations/windows-overlay/protocol.mjs +11 -7
- package/package.json +34 -13
- package/puppy-comfort-sprites.mjs +25 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +77 -0
- package/puppy-companion-life.mjs +49 -16
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +65 -14
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +10 -4
- package/retrieviewer.mjs +20 -14
- package/locomotion-core.mjs +0 -21
- package/locomotion-puppy.animal +0 -0
- package/vendor/procedural-animals/package.json +0 -40
- package/vendor/procedural-animals/src/animal.js +0 -146
- package/vendor/procedural-animals/src/bake.js +0 -51
- package/vendor/procedural-animals/src/build.js +0 -81
- package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
- package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
- package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
- package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
- package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
- package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
- package/vendor/procedural-animals/src/core/build/seams.js +0 -183
- package/vendor/procedural-animals/src/core/build/warp.js +0 -112
- package/vendor/procedural-animals/src/core/build/weights.js +0 -393
- package/vendor/procedural-animals/src/core/math/vec.js +0 -81
- package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
- package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
- package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
- package/vendor/procedural-animals/src/core/motion/common.js +0 -158
- package/vendor/procedural-animals/src/core/motion/index.js +0 -22
- package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
- package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
- package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
- package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
- package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
- package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
- package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
- package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
- package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
- package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
- package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
- package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
- package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
- package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
- package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
- package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
- package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
- package/vendor/procedural-animals/src/index.d.ts +0 -98
- package/vendor/procedural-animals/src/index.js +0 -29
- package/vendor/procedural-animals/src/registry.js +0 -48
- package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
- package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
- package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
- package/vendor/procedural-animals/src/species/bear/index.js +0 -139
- package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
- package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
- package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
- package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
- package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
- package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
- package/vendor/procedural-animals/src/species/boar/index.js +0 -110
- package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
- package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
- package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
- package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
- package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
- package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
- package/vendor/procedural-animals/src/species/cat/index.js +0 -211
- package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
- package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
- package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
- package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
- package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
- package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
- package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
- package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
- package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
- package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
- package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
- package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
- package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
- package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
- package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
- package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
- package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
- package/vendor/procedural-animals/src/species/cow/index.js +0 -118
- package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
- package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
- package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
- package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
- package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
- package/vendor/procedural-animals/src/species/crow/index.js +0 -70
- package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
- package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
- package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
- package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
- package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
- package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
- package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
- package/vendor/procedural-animals/src/species/deer/index.js +0 -106
- package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
- package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
- package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
- package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
- package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
- package/vendor/procedural-animals/src/species/dog/index.js +0 -144
- package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
- package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
- package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
- package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
- package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
- package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
- package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
- package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
- package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
- package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
- package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
- package/vendor/procedural-animals/src/species/fish/index.js +0 -88
- package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
- package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
- package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
- package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
- package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
- package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
- package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
- package/vendor/procedural-animals/src/species/fox/index.js +0 -120
- package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
- package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
- package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
- package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
- package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
- package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
- package/vendor/procedural-animals/src/species/frog/index.js +0 -97
- package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
- package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
- package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
- package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
- package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
- package/vendor/procedural-animals/src/species/goat/index.js +0 -149
- package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
- package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
- package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
- package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
- package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
- package/vendor/procedural-animals/src/species/horse/index.js +0 -80
- package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
- package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
- package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
- package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
- package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
- package/vendor/procedural-animals/src/species/lion/index.js +0 -111
- package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
- package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
- package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
- package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
- package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
- package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
- package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
- package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
- package/vendor/procedural-animals/src/species/pig/index.js +0 -161
- package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
- package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
- package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
- package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
- package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
- package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
- package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
- package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
- package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
- package/vendor/procedural-animals/src/species/rat/index.js +0 -111
- package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
- package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
- package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
- package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
- package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
- package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
- package/vendor/procedural-animals/src/species/shark/index.js +0 -104
- package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
- package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
- package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
- package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
- package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
- package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
- package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
- package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
- package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
- package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
- package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
- package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
- package/vendor/procedural-animals/src/species/snake/index.js +0 -73
- package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
- package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
- package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
- package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
- package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
- package/vendor/procedural-animals/src/species/spider/index.js +0 -78
- package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
- package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
- package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
- package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
- package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
- package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
- package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
- package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
- package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
- 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
|
-
}
|