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