@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,529 +0,0 @@
|
|
|
1
|
-
// The sheep's coat: the fleece (crimped wool staples clumped into locks: bright weathered tips on the
|
|
2
|
-
// lock crests, dark crevices between the locks, dusty backline, stained britch and belly, matte),
|
|
3
|
-
// the clean short-haired face, ears and legs (white, or black in the Suffolk and the black morph),
|
|
4
|
-
// pink skin under white hair (nose, lips, inner ears), keratin horns with transverse growth ridges and
|
|
5
|
-
// cloven hooves, hair flow following the hair tracts.
|
|
6
|
-
// Works in the reference space (see rig.js); seeded variation picks the breed colours.
|
|
7
|
-
import { HEAD_O, HZ, HY } from './rig.js';
|
|
8
|
-
import { neckS, EYE_PATCH } from './regions.js';
|
|
9
|
-
import { hornPath, WOOL_TAGS, stapleFall, lockRadius } from './sculpt.js';
|
|
10
|
-
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
11
|
-
import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
|
|
12
|
-
import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
|
|
13
|
-
|
|
14
|
-
// sRGB swatches sampled from the reference photos.
|
|
15
|
-
// tip = weathered lock crests, clean = the fleece's inside / fresh wool, crevice = the gaps between
|
|
16
|
-
// locks, dust = backline dirt, stain = belly / britch, hair = face and legs, ear = inner ear skin,
|
|
17
|
-
// nose = nose leather, hoof.
|
|
18
|
-
export const COATS = {
|
|
19
|
-
white: { tip: 0xdccdae, clean: 0xeee7d5, crevice: 0xa39886, dust: 0xcbc1ad, stain: 0xd3c6a6, hair: 0xf0efec, hairShade: 0xdedbd5, muzzle: 0xdccfca, skin: 0xcfb6b0, nose: 0xa88c8a, hoof: 0x9c8a74, earIn: 0xdcaca2, lid: 0xd8c4bf },
|
|
20
|
-
suffolk: { tip: 0xe9e0cb, clean: 0xf5f0e4, crevice: 0xa29479, dust: 0xcdbfa5, stain: 0xd5c39e, hair: 0x4e4a4b, hairShade: 0x3c3839, muzzle: 0x534d4c, skin: 0x2a2626, nose: 0x1a1818, hoof: 0x2a2725, earIn: 0x2e2a2a, lid: 0x4a4542, black: 1 },
|
|
21
|
-
merino: { tip: 0xd8cbad, clean: 0xf0e9d7, crevice: 0x9c8f7a, dust: 0xc4b89f, stain: 0xcdbd98, hair: 0xebe8e2, hairShade: 0xd6d1c8, muzzle: 0xe6d2cc, skin: 0xe8b4a8, nose: 0xc99a92, hoof: 0xc0ad92, earIn: 0xe8b0a4, lid: 0xe0c8c0 },
|
|
22
|
-
black: { tip: 0x5e4c3e, clean: 0x2f2723, crevice: 0x211a16, dust: 0x5e4c3e, stain: 0x4a3c31, hair: 0x463c3a, hairShade: 0x332b29, muzzle: 0x4e4440, skin: 0x2a2424, nose: 0x1c1817, hoof: 0x2a2624, earIn: 0x3a302c, lid: 0x3e3532, black: 1 },
|
|
23
|
-
brown: { tip: 0x7e654c, clean: 0x4a3a2e, crevice: 0x30251d, dust: 0x7a624a, stain: 0x5c4838, hair: 0x4a3a30, hairShade: 0x3a2e26, muzzle: 0x55463c, skin: 0x3a3030, nose: 0x2a2222, hoof: 0x302a26, earIn: 0x4a3a34, lid: 0x5a4a40, black: 1 },
|
|
24
|
-
texel: { tip: 0xc9ab80, clean: 0xe7dbc4, crevice: 0x8f7352, dust: 0xa88a64, stain: 0xa98a60, hair: 0xe8e6e2, hairShade: 0xd2cec6, muzzle: 0xdcd0cc, skin: 0xe0a698, nose: 0x2a2424, hoof: 0x9c8a74, earIn: 0xe0a698, lid: 0xd8c4bf },
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function palette(p) {
|
|
28
|
-
let C = COATS[p.coat] || COATS.white;
|
|
29
|
-
// most white-faced sheep have a dark, pigmented nose (threequarter1, face2, side2, extra/Lundy); some a
|
|
30
|
-
// pale pink-grey one (front1): three in four dark, by the individual's coat seed (no extra draw from the
|
|
31
|
-
// variation stream, which would reshuffle every seed)
|
|
32
|
-
if (C === COATS.white && (Math.imul(p.coatSeed || 0, 0x9e3779b1) >>> 28) % 4 !== 0) C = { ...C, nose: 0x5a5251, muzzle: 0xd2cac6 };
|
|
33
|
-
const k = p.coatShade || 0, l = p.coatLightness || 0;
|
|
34
|
-
const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.05 * k + l), c[1] * (1 + l), c[2] * (1 - 0.06 * k + l)]; };
|
|
35
|
-
const out = {};
|
|
36
|
-
for (const [key, v] of Object.entries(C)) out[key] = typeof v === 'number' && key !== 'black' ? tw(v) : v;
|
|
37
|
-
out.horn = srgb(p.hornColor || 0x9c8462); out.hornDark = srgb(0x6e5a42);
|
|
38
|
-
return out;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const HOOF_TAGS = new Set(['hoof', 'heelbulb', 'dewclaw']);
|
|
42
|
-
|
|
43
|
-
export function sheepCoat(ctx) {
|
|
44
|
-
const { model, rig, pos, nrm, regionOf, regionNames, weights, nV, params, lists } = ctx;
|
|
45
|
-
const { BONES } = rig;
|
|
46
|
-
const J = rig.J;
|
|
47
|
-
const R = rng(7331 + (params.coatSeed || 0));
|
|
48
|
-
const COL = palette(params);
|
|
49
|
-
const lamb = params.age === 'juvenile';
|
|
50
|
-
const merino = params.variant === 'merino';
|
|
51
|
-
const staple = params.staple ?? 0.014; // shell length on the fleece (crimped staple tips)
|
|
52
|
-
const woolD = params.wool ?? 0.07; // sculpted wool depth
|
|
53
|
-
const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
|
|
54
|
-
const AX_LEN = weights.axialLengths;
|
|
55
|
-
const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
56
|
-
const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
|
|
57
|
-
const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
|
|
58
|
-
const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
|
|
59
|
-
const jawPrims = model.forPart('jaw');
|
|
60
|
-
const headPrims = model.forPart('body').filter((p) => p.bone === 'head' && !p.carve);
|
|
61
|
-
const preorb = model.prims.filter((q) => q.tag === 'preorbital');
|
|
62
|
-
const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril' || p.tag === 'philtrum');
|
|
63
|
-
const fleeceBase = model.prims.filter((p) => WOOL_TAGS.has(p.tag) && p.tag !== 'lock' && p.tag !== 'lockleg' && !p.carve);
|
|
64
|
-
const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
|
|
65
|
-
const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(b.name));
|
|
66
|
-
const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
|
|
67
|
-
const partName = (v) => regionNames[regionOf[v]];
|
|
68
|
-
const horns = [hornPath(params, 1), hornPath(params, -1)];
|
|
69
|
-
const rl = lockRadius(woolD, merino); // lock radius (sculpt.js)
|
|
70
|
-
|
|
71
|
-
// --- nearest primitive tag per vertex
|
|
72
|
-
const tagOf = new Array(nV);
|
|
73
|
-
for (let v = 0; v < nV; v++) {
|
|
74
|
-
const L = lists[v], p = P(v);
|
|
75
|
-
let best = 1e9, t = '';
|
|
76
|
-
for (const pr of L) {
|
|
77
|
-
if (pr.carve) continue;
|
|
78
|
-
const d = SDFModel.dist(pr, p[0], p[1], p[2]);
|
|
79
|
-
if (d < best) { best = d; t = pr.tag; }
|
|
80
|
-
}
|
|
81
|
-
tagOf[v] = t;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// --- regions: 0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 horn
|
|
85
|
-
const region = new Uint8Array(nV);
|
|
86
|
-
const legness = new Float32Array(nV);
|
|
87
|
-
const legBone = new Int16Array(nV).fill(-1);
|
|
88
|
-
for (let v = 0; v < nV; v++) {
|
|
89
|
-
const bn = BONES[dominant[v]].name, p = P(v), pn = partName(v);
|
|
90
|
-
let lb = -1, lw = 0;
|
|
91
|
-
for (let k = 0; k < 4; k++) {
|
|
92
|
-
const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
|
|
93
|
-
if (isLimb[b] && w > lw) { lw = w; lb = b; }
|
|
94
|
-
}
|
|
95
|
-
legBone[v] = lb;
|
|
96
|
-
legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
|
|
97
|
-
if (pn === 'horn') region[v] = 7;
|
|
98
|
-
else if (pn === 'jaw') region[v] = 6;
|
|
99
|
-
else if (bn.startsWith('ear')) region[v] = 5;
|
|
100
|
-
else if (bn.startsWith('tail') && axialS[v] > sTailBase + 0.01) region[v] = 4;
|
|
101
|
-
else if (axialS[v] < sOcc + 0.01 || (pn === 'head' && neckS(p[0], p[1], p[2]) > 0.02)) region[v] = 2;
|
|
102
|
-
else if (axialS[v] < sNeckBase) region[v] = 1;
|
|
103
|
-
else region[v] = 0;
|
|
104
|
-
if (region[v] >= 2) legness[v] = 0;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// --- wool: which vertices grow fleece, and where they sit on a lock (crest 1 .. crevice 0)
|
|
108
|
-
const wool = new Float32Array(nV);
|
|
109
|
-
const crest = new Float32Array(nV);
|
|
110
|
-
const fleeceOn = fleeceBase.length > 0;
|
|
111
|
-
// away from any lock (a smooth stretch of fleece) the surface is a crest, not a crevice
|
|
112
|
-
const lockPrims = model.prims.filter((q) => q.tag === 'lock' || q.tag === 'lockleg');
|
|
113
|
-
const lockNear = (p) => {
|
|
114
|
-
let dm = 1e9;
|
|
115
|
-
for (const q of lockPrims) { const dx = p[0] - q.P[0], dy = p[1] - q.P[1], dz = p[2] - q.P[2]; const d = dx * dx + dy * dy + dz * dz; if (d < dm) dm = d; }
|
|
116
|
-
return smoothstep(1.4 * rl, 2.4 * rl, Math.sqrt(dm)) * 4;
|
|
117
|
-
};
|
|
118
|
-
for (let v = 0; v < nV; v++) {
|
|
119
|
-
const t = tagOf[v];
|
|
120
|
-
const r = region[v];
|
|
121
|
-
if (r === 7 || r === 5 || r === 6) continue;
|
|
122
|
-
if (fleeceOn) {
|
|
123
|
-
const p = P(v);
|
|
124
|
-
const d = SDFModel.evalList(fleeceBase, p[0], p[1], p[2]);
|
|
125
|
-
// (skin that pokes through a thin fleece between wool primitives is wool too)
|
|
126
|
-
if (!WOOL_TAGS.has(t) && !(r <= 1 && d < 0.012 && legness[v] < 0.5)) continue;
|
|
127
|
-
wool[v] = 1;
|
|
128
|
-
crest[v] = smoothstep(-0.08 * rl, 0.3 * rl, d + 0.09 * rl * lockNear(p));
|
|
129
|
-
} else if (r <= 1 || r === 4) {
|
|
130
|
-
// shorn / lamb: the whole body skin is the (short) fleece, except the lower legs
|
|
131
|
-
const p = P(v), L = legness[v];
|
|
132
|
-
const lb = legBone[v];
|
|
133
|
-
const kneeY = lb >= 0 && isFront[lb] ? 0.3 : 0.26;
|
|
134
|
-
wool[v] = 1 - L * smoothstep(kneeY + 0.03, kneeY - 0.03, p[1]);
|
|
135
|
-
crest[v] = 0.5 + 0.5 * (fbm3(p[0] * 40, p[1] * 40, p[2] * 40, 2) - 0.5) * 2;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// the face's edge: the wool starts on a line across the head's axis just behind the ears and
|
|
139
|
-
// the angle of the jaw (side2, side3_texel, threequarter1); the poll and the throat behind it are wool, the face
|
|
140
|
-
// in front of it short white hair. (The throat and the back of the poll carried the face's hair back into the
|
|
141
|
-
// collar: the white face ran on into the neck with no edge.)
|
|
142
|
-
// (not on a black face: the Suffolk's black runs back over a clean poll and down the throat, its own edge)
|
|
143
|
-
for (let v = 0; v < (COL.black ? 0 : nV); v++) {
|
|
144
|
-
const r = region[v];
|
|
145
|
-
if (r !== 1 && r !== 2) continue;
|
|
146
|
-
const p = P(v), h = HL(p);
|
|
147
|
-
const rag = 0.006 * (vnoise3(p[0] * 120, p[1] * 120, p[2] * 120) - 0.5);
|
|
148
|
-
// (and below the lower edge of the mandible: the throat runs down and forward in the head's frame)
|
|
149
|
-
const yJaw = -0.14 + 0.387 * (h[2] + 0.03);
|
|
150
|
-
const wl = Math.max(smoothstep(-0.061, -0.071, h[2] + rag), smoothstep(-0.006, -0.016, h[1] - yJaw + rag));
|
|
151
|
-
if (wl <= wool[v]) continue;
|
|
152
|
-
if (wool[v] <= 0) crest[v] = 0.5 + 0.5 * (fbm3(p[0] * 40, p[1] * 40, p[2] * 40, 2) - 0.5) * 2;
|
|
153
|
-
wool[v] = wl;
|
|
154
|
-
}
|
|
155
|
-
// the wool line is a little ragged
|
|
156
|
-
smoothField(wool, weights.neighbors, 1);
|
|
157
|
-
// (and on the head, where the poll's topknot meets the face, a soft edge rather than a line)
|
|
158
|
-
// (only round the topknot: smoothed at the collar it lit white tufts at the ear roots of a Suffolk)
|
|
159
|
-
smoothField(wool, weights.neighbors, 8, (v) => region[v] !== 2 || !lists[v].some((q) => q.tag === 'woolpoll'));
|
|
160
|
-
|
|
161
|
-
// --- comb (hair flow). Fleece: the staples fall down the sides and back along the top, each lock
|
|
162
|
-
// crimped its own way (a disordered flow at the lock scale); hair: nose -> tail, down the legs,
|
|
163
|
-
// outward from the forehead whorl on the face
|
|
164
|
-
const comb = new Float32Array(nV * 3);
|
|
165
|
-
const whorl = [0, 0.03, -0.02]; // forehead whorl, head-local
|
|
166
|
-
// the face's hair flow before its fine jitter: out from the forehead whorl, down the face to the nose
|
|
167
|
-
const faceFlow = (p) => {
|
|
168
|
-
const h = HL(p);
|
|
169
|
-
const rel = [h[0] - whorl[0], (h[1] - whorl[1]) * 0.4, h[2] - whorl[2] + 0.05];
|
|
170
|
-
let d = norm(add(mul(HY, rel[1] - 0.03), add(mul(HZ, rel[2]), [rel[0], 0, 0])));
|
|
171
|
-
if (h[2] < -0.05) d = norm(add(d, [0, -0.6, -0.8]));
|
|
172
|
-
return d;
|
|
173
|
-
};
|
|
174
|
-
for (let v = 0; v < nV; v++) {
|
|
175
|
-
const p = P(v), n = N(v), r = region[v];
|
|
176
|
-
const bone = BONES[dominant[v]];
|
|
177
|
-
let d;
|
|
178
|
-
if (r === 5) d = norm(sub(bone.tail, bone.head));
|
|
179
|
-
else if (r === 7) d = [0, 1, 0];
|
|
180
|
-
else if (r === 2 || r === 6) {
|
|
181
|
-
d = faceFlow(p);
|
|
182
|
-
// (the hairs lie a little unevenly: breaks the sheen of a short coat into hair texture)
|
|
183
|
-
const f = 220;
|
|
184
|
-
d = norm(add(d, [vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 7, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 3, p[1] * f + 11, p[2] * f + 5) - 0.5].map((x) => x * 0.9)));
|
|
185
|
-
} else if (r === 4) {
|
|
186
|
-
d = norm(sub(bone.tail, bone.head));
|
|
187
|
-
} else {
|
|
188
|
-
const seg = weights.segT[v * 2] | 0;
|
|
189
|
-
const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
|
|
190
|
-
d = norm(sub(b, a));
|
|
191
|
-
if (r === 0) d = norm(add(d, [0, -0.8 * Math.abs(n[0]), 0]));
|
|
192
|
-
if (r === 1) d = norm(add(d, [0, -0.6, 0.1]));
|
|
193
|
-
const L = legness[v];
|
|
194
|
-
if (L > 0 && legBone[v] >= 0) {
|
|
195
|
-
const lbn = BONES[legBone[v]];
|
|
196
|
-
let dl = norm(sub(lbn.tail, lbn.head));
|
|
197
|
-
if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
|
|
198
|
-
else dl = norm(add(dl, [0, -0.3, 0]));
|
|
199
|
-
d = norm(add(mul(d, 1 - L), mul(dl, L)));
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (wool[v] <= 0.5 && legness[v] > 0.3) {
|
|
203
|
-
// short leg hair lies a little unevenly too (matte, not a sheen)
|
|
204
|
-
const f = 220;
|
|
205
|
-
d = norm(add(d, [vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 7, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 3, p[1] * f + 11, p[2] * f + 5) - 0.5].map((x) => x * 0.8)));
|
|
206
|
-
}
|
|
207
|
-
if (wool[v] > 0.5) {
|
|
208
|
-
// the staples' fibres fall together: one continuous field over the whole fleece (sculpt.js
|
|
209
|
-
// stapleFall: away from the head and down). (An earlier field turned it a little lock by lock: the fur
|
|
210
|
-
// shading follows the hair direction, and under a low sun the fleece lit in grey blotches the size of
|
|
211
|
-
// a few locks; the locks now show as the crevices between them, below.) The fine crimp is added after
|
|
212
|
-
// smoothing.
|
|
213
|
-
d = stapleFall(n, p, J);
|
|
214
|
-
}
|
|
215
|
-
let t = sub(d, mul(n, dot(d, n)));
|
|
216
|
-
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
217
|
-
t = norm(t);
|
|
218
|
-
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// fleece flow: smoothed, then the fine crimp (the fibres wave every few millimetres)
|
|
222
|
-
{
|
|
223
|
-
const cx = new Float32Array(nV), cy = new Float32Array(nV), cz = new Float32Array(nV);
|
|
224
|
-
for (let v = 0; v < nV; v++) { cx[v] = comb[v * 3]; cy[v] = comb[v * 3 + 1]; cz[v] = comb[v * 3 + 2]; }
|
|
225
|
-
const keep = (v) => wool[v] <= 0.5;
|
|
226
|
-
for (const f of [cx, cy, cz]) smoothField(f, weights.neighbors, 3, keep);
|
|
227
|
-
for (let v = 0; v < nV; v++) {
|
|
228
|
-
if (wool[v] <= 0.5) continue;
|
|
229
|
-
const p = P(v), n = N(v), f = 140;
|
|
230
|
-
let d = add([cx[v], cy[v], cz[v]], mul([vnoise3(p[0] * f, p[1] * f, p[2] * f) - 0.5, vnoise3(p[0] * f + 17, p[1] * f + 3, p[2] * f) - 0.5, vnoise3(p[0] * f + 5, p[1] * f + 31, p[2] * f + 9) - 0.5], 0.3));
|
|
231
|
-
let t = sub(d, mul(n, dot(d, n)));
|
|
232
|
-
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
233
|
-
t = norm(t);
|
|
234
|
-
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// --- per-vertex fields
|
|
239
|
-
const pattern = new Float32Array(nV).fill(0.02);
|
|
240
|
-
const patternColor = new Float32Array(nV * 4);
|
|
241
|
-
const markSDF = new Float32Array(nV).fill(1);
|
|
242
|
-
const furLen = new Float32Array(nV);
|
|
243
|
-
const tint = new Float32Array(nV * 4);
|
|
244
|
-
const surf = new Float32Array(nV * 4);
|
|
245
|
-
const hairLen = lamb ? 0.007 : 0.0045;
|
|
246
|
-
|
|
247
|
-
for (let v = 0; v < nV; v++) {
|
|
248
|
-
const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
|
|
249
|
-
// (short hair: matte; 0.45 gave the black face a plastic sheen)
|
|
250
|
-
let col, mat = MAT.FUR, fl = hairLen, mark = 1, gloss = 0.22, undercoat = 0;
|
|
251
|
-
const lb = legBone[v];
|
|
252
|
-
const lbName = lb >= 0 ? BONES[lb].name : '';
|
|
253
|
-
const cv = fbm3(p[0] * 6, p[1] * 6, p[2] * 6, 3) - 0.5;
|
|
254
|
-
if (r === 0 || r === 1 || r === 4) {
|
|
255
|
-
// clean hair (legs below the wool, and wherever the fleece stops)
|
|
256
|
-
col = mix3(COL.hair, COL.hairShade, smoothstep(0.2, -0.6, n[1]) * 0.6);
|
|
257
|
-
const L = legness[v];
|
|
258
|
-
// (neck skin showing between the wool primitives behind the head is the fleece's colour, not the
|
|
259
|
-
// face's: on a Suffolk it showed as black tufts in the white throat wool)
|
|
260
|
-
if (fleeceOn && r === 1 && L < 0.3) col = mix3(col, COL.clean, 1 - L / 0.3);
|
|
261
|
-
fl = mix(0.006, 0.004, L);
|
|
262
|
-
if (tag === 'udder' || tag === 'teat') {
|
|
263
|
-
mat = MAT.SKIN; fl = tag === 'teat' ? 0 : 0.002; gloss = 0.35;
|
|
264
|
-
col = COL.black ? mix3(COL.skin, [0.1, 0.08, 0.08], 0.3) : COL.skin;
|
|
265
|
-
}
|
|
266
|
-
if (tag === 'scrotum') { fl = 0.005; col = mix3(col, COL.skin, 0.25); }
|
|
267
|
-
if (/hoof|paw/.test(lbName) && HOOF_TAGS.has(tag)) {
|
|
268
|
-
const Cj = J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side];
|
|
269
|
-
if (tag === 'dewclaw' || p[1] < Cj[1] + 0.006 + 0.35 * (p[2] - Cj[2])) {
|
|
270
|
-
mat = MAT.KERATIN; fl = 0; gloss = 0.45;
|
|
271
|
-
col = COL.hoof;
|
|
272
|
-
// unpigmented hooves are often striped (a dark vertical band in a pale wall)
|
|
273
|
-
if (!COL.black) col = mix3(col, [0.06, 0.05, 0.045], smoothstep(0.3, 0.7, vnoise3(p[0] * 300, 3.1, p[2] * 90)) * 0.7 * (params.hoofStripe ?? 0.5));
|
|
274
|
-
if (tag === 'heelbulb') col = mix3(col, [0.1, 0.08, 0.07], 0.4);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
} else if (r === 5) {
|
|
278
|
-
// ears: short hair outside, sparse inside with the skin showing
|
|
279
|
-
const earP = model.prims.find((q) => q.tag === 'ear' && (q.bone === (p[0] > 0 ? 'earL' : 'earR')));
|
|
280
|
-
const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
|
|
281
|
-
const front = dot(n, faceDir);
|
|
282
|
-
col = COL.hair;
|
|
283
|
-
fl = 0.0035;
|
|
284
|
-
// (the inside of the cup: on the carved hollow, whichever way its walls face)
|
|
285
|
-
const cup = model.prims.find((q) => q.tag === 'earinner' && q.bone === earP.bone);
|
|
286
|
-
const dc = cup ? Math.abs(SDFModel.dist(cup, p[0], p[1], p[2])) : 1;
|
|
287
|
-
// (pink skin inside the cup; the face of the leaf round it and its rim keep their white hair, so the
|
|
288
|
-
// ear seen from the side or above reads as a white leaf, not a pink saucer)
|
|
289
|
-
const inner = Math.max(0.2 * smoothstep(0.35, 0.8, front), smoothstep(0.0025, 0.0005, dc) * smoothstep(-0.3, 0.0, front));
|
|
290
|
-
fl = mix(fl, 0.002, inner); col = mix3(col, COL.earIn, 0.55 * inner);
|
|
291
|
-
} else if (r === 7) {
|
|
292
|
-
// horn: pale ridged keratin, darker toward the base, transverse growth ridges (the "corrugations")
|
|
293
|
-
const S = p[0] >= 0 ? 0 : 1, Hh = horns[S];
|
|
294
|
-
let bt = 0, bd = 1e9;
|
|
295
|
-
for (let i = 0; i + 1 < Hh.length; i++) {
|
|
296
|
-
const a = Hh[i].p, b = Hh[i + 1].p, ab = sub(b, a);
|
|
297
|
-
const u = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1);
|
|
298
|
-
const q = add(a, mul(ab, u)), dd = len(sub(p, q));
|
|
299
|
-
if (dd < bd) { bd = dd; bt = Hh[i].t + (Hh[i + 1].t - Hh[i].t) * u; }
|
|
300
|
-
}
|
|
301
|
-
mat = MAT.KERATIN; fl = 0; gloss = 0.3;
|
|
302
|
-
col = mix3(COL.hornDark, COL.horn, smoothstep(0.0, 0.35, bt));
|
|
303
|
-
col = mix3(col, mul(COL.horn, 1.12), smoothstep(0.75, 1, bt) * 0.5);
|
|
304
|
-
const hlen = (params.horns?.turns || 1) * 0.5 * bt;
|
|
305
|
-
const ring = 0.5 + 0.5 * Math.sin(hlen * 2 * Math.PI / 0.012);
|
|
306
|
-
col = mul(col, 1 - 0.14 * ring * (1 - smoothstep(0.8, 1, bt)));
|
|
307
|
-
col = mul(col, 0.9 + 0.2 * vnoise3(p[0] * 300, p[1] * 60, p[2] * 300));
|
|
308
|
-
} else {
|
|
309
|
-
// head & jaw: short, fine, matte hair (5-7 mm on the skull and cheeks, 3 mm on the muzzle and round
|
|
310
|
-
// the eyes); a black face is charcoal rather than ink, greyer round the muzzle and the eyes (face1,
|
|
311
|
-
// extra/Tete_de_brebie_Suffolk), with a fine, uneven tone that keeps it from reading as plastic
|
|
312
|
-
const h = HL(p);
|
|
313
|
-
col = mix3(COL.hair, COL.hairShade, smoothstep(0.1, -0.7, dot(n, HY)) * 0.5);
|
|
314
|
-
fl = (lamb ? 0.0065 : 0.0055) * (1 + 0.25 * smoothstep(0.02, -0.06, h[2]));
|
|
315
|
-
const muz = smoothstep(0.12, 0.175, h[2]);
|
|
316
|
-
col = mix3(col, COL.muzzle, muz * 0.7);
|
|
317
|
-
fl = mix(fl, 0.0018, muz);
|
|
318
|
-
const eyeD = EYE_PATCH.centres.reduce((a, c) => Math.min(a, Math.hypot(p[0] - c[0], p[1] - c[1], p[2] - c[2])), 1);
|
|
319
|
-
col = mix3(col, COL.muzzle, smoothstep(0.024, 0.012, eyeD) * 0.2);
|
|
320
|
-
fl = Math.min(fl, mix(0.003, fl, smoothstep(0.012, 0.022, eyeD)));
|
|
321
|
-
const tn = vnoise3(p[0] * 160, p[1] * 160, p[2] * 160) - 0.5;
|
|
322
|
-
col = mul(col, 1 + 0.16 * tn + 0.08 * (fbm3(p[0] * 30, p[1] * 30, p[2] * 30, 2) - 0.5));
|
|
323
|
-
// the short coat's texture: streaks drawn out along the hair flow (partings and tufts lying together a
|
|
324
|
-
// few millimetres wide, a centimetre or two long), so the face reads as short hair, not a smooth skin
|
|
325
|
-
// (without it: smooth white or black rubber, hard to tell from the fleece)
|
|
326
|
-
{
|
|
327
|
-
const c = faceFlow(p), al = dot(p, c);
|
|
328
|
-
const q = [p[0] - c[0] * al * 0.85, p[1] - c[1] * al * 0.85, p[2] - c[2] * al * 0.85];
|
|
329
|
-
const st = vnoise3(q[0] * 230 + 5, q[1] * 230, q[2] * 230 + 9) - 0.5;
|
|
330
|
-
col = mul(col, 1 + (COL.black ? 0.5 : 0.22) * st * smoothstep(0.0025, 0.004, fl));
|
|
331
|
-
}
|
|
332
|
-
// a white face is a little mottled grey where the skin shows through the short hair (threequarter1)
|
|
333
|
-
if (!COL.black) col = mul(col, 1 - 0.1 * smoothstep(0.55, 0.8, fbm3(p[0] * 22 + 3, p[1] * 22, p[2] * 22 + 1, 2)));
|
|
334
|
-
const nearEye = eyeD < 0.019;
|
|
335
|
-
// the lids: a thin dark margin (the lash line) and round it a few millimetres of pale, almost bare lid
|
|
336
|
-
// skin (threequarter1, face2, extra/Lundy: the eye reads as an almond in a pale rim, not a black bead)
|
|
337
|
-
// (only on the lids themselves, the skin over the globe (r + lid 16.2 mm from its centre): the almond's cut
|
|
338
|
-
// runs on outward and passes close to the brow, and the margin drawn there was a dark frown line over the eye)
|
|
339
|
-
const onLid = smoothstep(0.0205, 0.0178, eyeD);
|
|
340
|
-
for (const e of nearEye ? eyePrims : []) {
|
|
341
|
-
const de = Math.abs(SDFModel.dist(e, p[0], p[1], p[2]));
|
|
342
|
-
if (de < 0.0026 && eyeD < 0.0178) mark = Math.min(mark, de - 0.0011);
|
|
343
|
-
if (de < 0.001 && eyeD < 0.0178) { mat = MAT.DARK_SKIN; fl = 0; }
|
|
344
|
-
else if (de < 0.005) {
|
|
345
|
-
const lid = smoothstep(0.005, 0.0025, de) * onLid;
|
|
346
|
-
col = mix3(col, COL.lid, lid);
|
|
347
|
-
fl = Math.min(fl, mix(0.0018, 0.0006, lid));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
// nostrils: comma slits in a deeper shade of the nose, soft-edged; the philtrum a fine groove (front1: on a
|
|
351
|
-
// pink nose the slits are a deeper pink-mauve, the philtrum a pink line). The front of the nose is meshed at
|
|
352
|
-
// ~1.2 mm (regions.js NOSE_PATCH), so the carved slits come out clean. (the dark leather
|
|
353
|
-
// material and a black mark inside them, on the 4-5 mm face cells, read as ragged black blotches with a
|
|
354
|
-
// glint, and the philtrum as a black T)
|
|
355
|
-
const paleNose = COL.nose[0] + COL.nose[1] + COL.nose[2] > 0.45;
|
|
356
|
-
for (const e of nostrilPrims) {
|
|
357
|
-
const dn = SDFModel.dist(e, p[0], p[1], p[2]);
|
|
358
|
-
if (e.tag === 'philtrum') continue;
|
|
359
|
-
if (dn < 0.005) {
|
|
360
|
-
// (how deep the skin here lies inside the nose's uncarved form: the slit's walls; the carve's own distance
|
|
361
|
-
// is loose for so thin an ellipsoid)
|
|
362
|
-
const depth = -SDFModel.evalList(headPrims, p[0], p[1], p[2]), inside = smoothstep(0.0002, 0.0012, depth);
|
|
363
|
-
const ins = Math.max(inside, 0.5 * smoothstep(0.002, 0, dn));
|
|
364
|
-
col = mix3(col, paleNose ? [COL.nose[0] * 0.36, COL.nose[1] * 0.22, COL.nose[2] * 0.26] : mul(COL.nose, COL.black ? 0.4 : 0.28), ins);
|
|
365
|
-
// (bare inside the slit: the 1 mm hair's lit tips drew pale streaks there; the fine hair runs on to its
|
|
366
|
-
// edge: a bare band round it showed the fur base, shaded as if under the coat, as a ragged dark ring)
|
|
367
|
-
fl = Math.min(fl, 0.0012 * (1 - smoothstep(0.25, 0.6, inside)));
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// the philtrum: a fine line from between the nostrils down to the lip (front1, face2, threequarter1)
|
|
371
|
-
{
|
|
372
|
-
const pl = smoothstep(0.0012, 0.0004, Math.abs(h[0])) * smoothstep(-0.026, -0.03, h[1]) * smoothstep(0.19, 0.196, h[2]) * smoothstep(0.1, 0.4, dot(n, HZ));
|
|
373
|
-
if (pl > 0) { col = mix3(col, mul(COL.nose, paleNose ? 0.75 : 0.7), pl * 0.85); fl = Math.min(fl, mix(fl, 0.0004, pl)); }
|
|
374
|
-
}
|
|
375
|
-
// the small bare nose pad between the slit nostrils (soft-edged; covered in very fine hair: only
|
|
376
|
-
// the nostrils are bare)
|
|
377
|
-
// (a rounded pad over the nose front between the nostrils, running down the philtrum, its edge soft and
|
|
378
|
-
// mottled into the muzzle hair: a square block of colour read as a rubber cap)
|
|
379
|
-
const fwdN = smoothstep(0.15, 0.5, dot(n, HZ)) * smoothstep(0.17, 0.182, h[2]);
|
|
380
|
-
const dpad = Math.hypot(h[0] / 0.021, (h[1] + 0.017) / 0.014);
|
|
381
|
-
const phil = smoothstep(0.006, 0.003, Math.abs(h[0])) * smoothstep(-0.04, -0.033, h[1]) * smoothstep(-0.012, -0.02, h[1]);
|
|
382
|
-
const mott = vnoise3(p[0] * 260, p[1] * 260, p[2] * 260);
|
|
383
|
-
const pad = fwdN * Math.max(smoothstep(1.2 + 0.25 * mott, 0.7, dpad), phil);
|
|
384
|
-
if (pad > 0) { col = mix3(col, COL.nose, pad); fl = mix(fl, Math.min(fl, 0.0012), pad); }
|
|
385
|
-
// (a dark nose dusts the muzzle round it grey)
|
|
386
|
-
if (!COL.black) col = mix3(col, mix3(COL.nose, col, 0.5), muz * smoothstep(1.9, 1.1, dpad) * smoothstep(0.35, 0.75, mott) * 0.5 * (1 - pad));
|
|
387
|
-
// the lips meet in a dark line; the pink inside of the mouth only where it faces into the mouth
|
|
388
|
-
const into = dot(n, HY);
|
|
389
|
-
// (the line only along the front of the mouth, fading out 4 cm back from the nose: drawn round the rear
|
|
390
|
-
// of the lower jaw it turned down at the corner, a sharply down-turned mouth (side2: the mouth is short,
|
|
391
|
-
// its corner level or a little up))
|
|
392
|
-
const lipF = smoothstep(0.15, 0.165, h[2]);
|
|
393
|
-
// (the lips overlap: what one lip hides is lip skin, so a chewing jaw that slides aside shows lip,
|
|
394
|
-
// not a hole; the line where they meet is a dark mark)
|
|
395
|
-
// (a dark line where the lips meet, as in the photos; not a pink gash)
|
|
396
|
-
// (on a white face the hidden lip skin is a pale pink-grey: a dark lip surface showed as a dark gap wherever
|
|
397
|
-
// chewing parted the lips)
|
|
398
|
-
const lipCol = COL.black ? mul(COL.muzzle, 0.7) : mix3(COL.muzzle, mix3(COL.skin, COL.nose, 0.3), 0.3);
|
|
399
|
-
if (regionNames[regionOf[v]] !== 'jaw') {
|
|
400
|
-
const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
|
|
401
|
-
if (h[2] > 0.135 && dj < 0.004) {
|
|
402
|
-
mark = Math.min(mark, Math.abs(dj) - 0.0014 * lipF);
|
|
403
|
-
fl = Math.min(fl, 0.0012);
|
|
404
|
-
if (dj < 0) { col = lipCol; fl = 0; if (dj < -0.004 && into < -0.6) mat = MAT.MOUTH; }
|
|
405
|
-
}
|
|
406
|
-
} else {
|
|
407
|
-
const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
|
|
408
|
-
if (dh < 0.0035) {
|
|
409
|
-
mark = Math.min(mark, Math.abs(dh) - 0.0014 * lipF);
|
|
410
|
-
fl = Math.min(fl, 0.0012);
|
|
411
|
-
if (dh < 0) { col = lipCol; fl = 0; if (dh < -0.004 && into > 0.6) mat = MAT.MOUTH; }
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
// the preorbital gland: a bare dark slit
|
|
415
|
-
const dp = preorb.reduce((a, q) => Math.min(a, SDFModel.dist(q, p[0], p[1], p[2])), 1);
|
|
416
|
-
if (dp < 0.003) { col = mul(col, COL.black ? 0.8 : 0.75); fl = Math.min(fl, 0.002); } // (0.55 drew a dark stroke like a frowning brow)
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// ---- fleece
|
|
420
|
-
const w = wool[v];
|
|
421
|
-
if (w > 0.01 && mat === MAT.FUR) {
|
|
422
|
-
const c = crest[v];
|
|
423
|
-
const up = clamp(n[1], -1, 1);
|
|
424
|
-
// clean wool inside, weathered tips on the lock crests, the crevices between the locks only a little
|
|
425
|
-
// darker (the shells' own depth shading darkens them further): an even, creamy fleece
|
|
426
|
-
let wc = mix3(mix3(COL.crevice, COL.clean, 0.62), COL.clean, smoothstep(0.0, 0.45, c));
|
|
427
|
-
wc = mix3(wc, COL.tip, smoothstep(0.3, 1, c) * (COL.black ? 0.35 : 0.55));
|
|
428
|
-
// a faint dusting along the top line, light stains on the belly and the britch
|
|
429
|
-
wc = mix3(wc, COL.dust, smoothstep(0.6, 0.95, up) * 0.22 * smoothstep(0.2, 0.8, c));
|
|
430
|
-
const low = smoothstep(0.34, 0.2, p[1]) * (1 - legness[v] * 0.5);
|
|
431
|
-
const britch = smoothstep(-0.35, -0.5, p[2]) * smoothstep(0.55, 0.35, p[1]);
|
|
432
|
-
wc = mix3(wc, COL.stain, Math.max(low, britch) * 0.3);
|
|
433
|
-
// (no large-scale grey blotches: the tone varies a little from staple to staple only)
|
|
434
|
-
const sn = vnoise3(p[0] * 45, p[1] * 45, p[2] * 45) - 0.5;
|
|
435
|
-
wc = [wc[0] * (1 + 0.035 * cv + 0.05 * sn), wc[1] * (1 + 0.03 * cv + 0.05 * sn), wc[2] * (1 + 0.025 * cv + 0.045 * sn)];
|
|
436
|
-
col = mix3(col, wc, w);
|
|
437
|
-
// staple tips: crests longer, crevices short (the gaps open between the locks)
|
|
438
|
-
// (shorter close behind the head, where the poll and the throat fold the wool in every pose)
|
|
439
|
-
const nearHead = smoothstep(-0.09, -0.01, neckS(p[0], p[1], p[2]));
|
|
440
|
-
const flW = staple * mix(0.75, 1.1, c) * (1 - 0.3 * nearHead);
|
|
441
|
-
fl = mix(fl, flW, w);
|
|
442
|
-
gloss = mix(gloss, merino ? 0.22 : 0.15, w);
|
|
443
|
-
undercoat = w * 0.08; // (greyer roots greyed the cream)
|
|
444
|
-
} else if (mat === MAT.FUR) {
|
|
445
|
-
const tn = r <= 1 ? vnoise3(p[0] * 160, p[1] * 160, p[2] * 160) - 0.5 : 0;
|
|
446
|
-
col = [col[0] * (1 + 0.08 * cv + 0.14 * tn), col[1] * (1 + 0.07 * cv + 0.14 * tn), col[2] * (1 + 0.06 * cv + 0.14 * tn)];
|
|
447
|
-
}
|
|
448
|
-
markSDF[v] = mark;
|
|
449
|
-
furLen[v] = fl;
|
|
450
|
-
tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
|
|
451
|
-
surf[v * 4] = gloss;
|
|
452
|
-
surf[v * 4 + 3] = undercoat;
|
|
453
|
-
}
|
|
454
|
-
smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.FUR);
|
|
455
|
-
|
|
456
|
-
// --- the head's form in the coat: the hollows of the face (round the eye, the groove
|
|
457
|
-
// between cheek and muzzle, under the jaw, the throat behind it and the face's edge where the wool collar
|
|
458
|
-
// stands over it) lie in the shadow of the forms round them, and on a black face the short hair of the
|
|
459
|
-
// ridges (nasal bone, brow, cheek, the edge of the jaw) catches the sky's light. Lit only by the sky (the
|
|
460
|
-
// shade side, a low sun), a uniform black face read as one featureless shape and a white face ran into the
|
|
461
|
-
// neck with no break at the jaw. Occlusion from the sculpt (SDF samples 5-30 mm out along the normal), the
|
|
462
|
-
// ridges from samples inside (a form thinner than ~1-2 cm), the sheen from the hair facing up and forward.
|
|
463
|
-
{
|
|
464
|
-
const occl = model.prims.filter((q) => !q.carve && q.part !== 'horn' && (q.bone === 'head' || q.bone === 'jaw' || q.bone === 'neck2' || (q.bone === 'neck1' && /neck|throat/.test(q.tag))));
|
|
465
|
-
// (at the mouth each lip is shaded by its own part's form only: the other lip's occlusion drew the crease
|
|
466
|
-
// where they meet as a dark gap at the corner of the mouth, an open mouth)
|
|
467
|
-
const occlUpper = occl.filter((q) => q.part !== 'jaw'), occlLower = occl.filter((q) => q.part === 'jaw');
|
|
468
|
-
const skull = model.prims.filter((q) => !q.carve && (q.bone === 'head' || q.bone === 'jaw') && q.part !== 'horn');
|
|
469
|
-
const dark = !!COL.black;
|
|
470
|
-
for (let v = 0; v < nV; v++) {
|
|
471
|
-
const r = region[v];
|
|
472
|
-
if ((r !== 2 && r !== 6) || tint[v * 4 + 3] !== MAT.FUR || wool[v] > 0.5) continue;
|
|
473
|
-
const p = P(v), n = N(v);
|
|
474
|
-
const hm = HL(p), atMouth = hm[2] > 0.1 && hm[1] < -0.025;
|
|
475
|
-
const ol = r === 6 ? occlLower : atMouth ? occlUpper : occl;
|
|
476
|
-
let occ = 0;
|
|
477
|
-
for (const [i, d] of [[1, 0.005], [2, 0.011], [3, 0.019], [4, 0.03]]) {
|
|
478
|
-
const sd = SDFModel.evalList(ol, p[0] + n[0] * d, p[1] + n[1] * d, p[2] + n[2] * d);
|
|
479
|
-
occ += (Math.max(0, d - sd) / d) / i;
|
|
480
|
-
}
|
|
481
|
-
let ridge = 0;
|
|
482
|
-
for (const d of [0.007, 0.014]) {
|
|
483
|
-
const si = SDFModel.evalList(skull, p[0] - n[0] * d, p[1] - n[1] * d, p[2] - n[2] * d);
|
|
484
|
-
ridge += 0.5 * clamp((d + si) / d, 0, 1);
|
|
485
|
-
}
|
|
486
|
-
const h = HL(p);
|
|
487
|
-
const nh = [n[0], dot(n, HY), dot(n, HZ)];
|
|
488
|
-
// (the forehead and the nasal bone face the sky; the underside of the jaw does not)
|
|
489
|
-
const sky = smoothstep(-0.2, 0.8, nh[1] * 0.8 + nh[2] * 0.35);
|
|
490
|
-
const ao = clamp(1 - (dark ? 0.75 : 0.6) * occ, dark ? 0.45 : 0.6, 1);
|
|
491
|
-
const lift = dark ? 1 + 0.9 * smoothstep(0.08, 0.5, ridge) + 0.9 * sky : 1 + 0.04 * smoothstep(0.1, 0.5, ridge);
|
|
492
|
-
// (the muzzle and nose keep their own tone)
|
|
493
|
-
const k = ao * mix(lift, 1, smoothstep(0.165, 0.185, h[2]));
|
|
494
|
-
tint[v * 4] *= k; tint[v * 4 + 1] *= k; tint[v * 4 + 2] *= k;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
// --- the locks: thin, broken dark crevices between the staple blocks (a crisp pattern line along the
|
|
499
|
-
// border of each lock's cell; the cells are the sculpted locks, stretched down the fall), so the fleece
|
|
500
|
-
// reads as locks by its texture rather than by big soft bumps (whose shading under a low sun made the
|
|
501
|
-
// grey blotches)
|
|
502
|
-
if (lockPrims.length) {
|
|
503
|
-
// (on a dark fleece the gaps between the locks hardly show: the tips are the lighter part)
|
|
504
|
-
// (COL.black marks a black face; the fleece is dark only in the black / brown morph)
|
|
505
|
-
const darkFleece = COL.clean[0] + COL.clean[1] + COL.clean[2] < 0.6;
|
|
506
|
-
const crev = darkFleece ? mix3(COL.crevice, COL.clean, 0.4) : mix3(COL.crevice, COL.clean, 0.15);
|
|
507
|
-
for (let v = 0; v < nV; v++) {
|
|
508
|
-
if (wool[v] < 0.5 || tint[v * 4 + 3] !== MAT.FUR) continue;
|
|
509
|
-
const p = P(v);
|
|
510
|
-
let d1 = 1e9, d2 = 1e9;
|
|
511
|
-
for (const q of lists[v]) {
|
|
512
|
-
if (q.tag !== 'lock' && q.tag !== 'lockleg') continue;
|
|
513
|
-
const dx = p[0] - q.P[0], dy = p[1] - q.P[1], dz = p[2] - q.P[2];
|
|
514
|
-
const al = dx * q.P[9] + dy * q.P[10] + dz * q.P[11];
|
|
515
|
-
const dd = Math.sqrt(Math.max(0, dx * dx + dy * dy + dz * dz - 0.55 * al * al));
|
|
516
|
-
if (dd < d1) { d2 = d1; d1 = dd; } else if (dd < d2) d2 = dd;
|
|
517
|
-
}
|
|
518
|
-
if (d2 > 1e8) continue;
|
|
519
|
-
const brk = vnoise3(p[0] * 30 + 3, p[1] * 30, p[2] * 30 + 7);
|
|
520
|
-
const wdt = 0.0011 + 0.0012 * vnoise3(p[0] * 90, p[1] * 90 + 5, p[2] * 90);
|
|
521
|
-
pattern[v] = clamp((d2 - d1) * 0.5 - wdt, -0.02, 0.02);
|
|
522
|
-
const k = wool[v] * smoothstep(0.3, 0.6, brk) * (merino ? 0.4 : darkFleece ? 0.35 : 0.5);
|
|
523
|
-
patternColor[v * 4] = crev[0]; patternColor[v * 4 + 1] = crev[1]; patternColor[v * 4 + 2] = crev[2]; patternColor[v * 4 + 3] = k;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
if (globalThis.__sheepDbg) globalThis.__sheepDbg.push({ region, wool, tint, furLen, pos, HL, nV });
|
|
527
|
-
void R; void mix;
|
|
528
|
-
return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: {}, region, wool };
|
|
529
|
-
}
|