@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,726 +0,0 @@
|
|
|
1
|
-
// The brown bear's coat: long shaggy guard hair over a dense underfur, painted per vertex in
|
|
2
|
-
// reference space.
|
|
3
|
-
//
|
|
4
|
-
// Colour fields: the back, hump and head are the
|
|
5
|
-
// palest parts (sun-bleached guard-hair tips), the legs darkest (near black on the lower legs and
|
|
6
|
-
// feet), the belly dark; the face is a little paler and greyer than the crown, the muzzle paler
|
|
7
|
-
// again, the ears dark with darker rims, the nose pad, lips and soles black, the claws horn coloured
|
|
8
|
-
// (dark in Eurasian bears, pale cream in grizzlies). Grizzled coats use the shader's agouti band
|
|
9
|
-
// (surf.z): dark roots and a dark band below a pale (silver-cream) tip. Coats (params.coat): 'dark'
|
|
10
|
-
// (Eurasian chocolate), 'red' (reddish brown), 'medium' (coastal brown), 'blonde' (straw, dark
|
|
11
|
-
// legs), 'grizzled' (silver-tipped grizzly) and 'black' (American black bear: black with a tan
|
|
12
|
-
// muzzle). Cubs are fluffy and uniform, Eurasian cubs often with a pale neck collar.
|
|
13
|
-
// Fur length: longest on the hump, nape and neck ruff (winter guard hair 9-12 cm), long on the
|
|
14
|
-
// flanks, belly fringe, forearm "sleeves" and thigh "trousers", short on the face, lower legs and
|
|
15
|
-
// feet (research 6.1).
|
|
16
|
-
import { HEAD_O, MUZZLE_Z0 } from './rig.js';
|
|
17
|
-
import { EYE_BASE } from './sculpt.js';
|
|
18
|
-
import { neckS, eyePatchesAt } from './regions.js';
|
|
19
|
-
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
20
|
-
import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, fbm3, vnoise3 } from '../../core/math/vec.js';
|
|
21
|
-
import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
|
|
22
|
-
|
|
23
|
-
// sRGB swatches (neutral-light albedo). `tip`: hump / back guard-hair tips, `ag`: agouti
|
|
24
|
-
// amounts (dark band below the tip) per region.
|
|
25
|
-
const COATS = {
|
|
26
|
-
dark: {
|
|
27
|
-
back: 0x5c4029, flank: 0x4c3422, tip: 0x76573a, head: 0x634833, face: 0x6e5540, muzzle: 0x86684c,
|
|
28
|
-
leg: 0x35251a, belly: 0x33241a, ear: 0x3a291d, earRim: 0x1f1610, earInner: 0x2a1e16, claw: 0x3a3530,
|
|
29
|
-
ag: { back: 0.12, head: 0.08, leg: 0.1 },
|
|
30
|
-
},
|
|
31
|
-
// (side3: a deep red-brown with paler tips on the back, the shoulders darker; less orange than before)
|
|
32
|
-
red: {
|
|
33
|
-
back: 0x7a4c30, flank: 0x5e3924, tip: 0x9a6a48, head: 0x7a5236, face: 0x846044, muzzle: 0x9a7658,
|
|
34
|
-
leg: 0x38241a, belly: 0x3a2518, ear: 0x4a2e1c, earRim: 0x241810, earInner: 0x2e1f16, claw: 0x3c3530,
|
|
35
|
-
ag: { back: 0.12, head: 0.06, leg: 0.1 },
|
|
36
|
-
},
|
|
37
|
-
medium: {
|
|
38
|
-
back: 0x80593a, flank: 0x6e4b30, tip: 0xa57b4c, head: 0x8a653e, face: 0x92704e, muzzle: 0x9e7c58,
|
|
39
|
-
leg: 0x45301f, belly: 0x352315, ear: 0x4e3622, earRim: 0x251a12, earInner: 0x2e2118, claw: 0x8e8272,
|
|
40
|
-
ag: { back: 0.1, head: 0.06, leg: 0.1 },
|
|
41
|
-
},
|
|
42
|
-
// (front3, tq3: golden-blonde tips on the hump and shoulders, a darker face, near-black legs and chest)
|
|
43
|
-
// (front3's medians, forehead #735644, cheeks #755c34, hump #89553d with its locks ranging #5b2d00 to
|
|
44
|
-
// #c69665 and a pale muzzle #c8a68b: the old head / face 0x8e6e50 / 0x967656 with little agouti rendered as an
|
|
45
|
-
// even pale cream, a plush toy (the default showcase bear, grizzly s1, is blonde))
|
|
46
|
-
blonde: {
|
|
47
|
-
back: 0x96683e, flank: 0x74522f, tip: 0xc8965c, head: 0x7c5a3c, face: 0x846240, muzzle: 0xc0a486,
|
|
48
|
-
leg: 0x3a2c22, belly: 0x3e2e22, ear: 0x5e4632, earRim: 0x2e2218, earInner: 0x3a2c20, claw: 0xb8ab94,
|
|
49
|
-
ag: { back: 0.3, head: 0.32, leg: 0.1 },
|
|
50
|
-
},
|
|
51
|
-
// (tq4, tq1, front1: a dark chocolate-grey coat, silver-tipped on the back and shoulders, a dark face and
|
|
52
|
-
// legs; the pale grey-beige of before read as a sheep's fleece)
|
|
53
|
-
grizzled: {
|
|
54
|
-
back: 0x7c7064, flank: 0x5a4e46, tip: 0x9e9282, head: 0x6e5c4e, face: 0x7a6858, muzzle: 0x8a7866,
|
|
55
|
-
leg: 0x302826, belly: 0x383028, ear: 0x4a3e34, earRim: 0x241e1a, earInner: 0x2a2420, claw: 0xcfc2a8,
|
|
56
|
-
ag: { back: 0.6, head: 0.4, leg: 0.12 },
|
|
57
|
-
},
|
|
58
|
-
black: {
|
|
59
|
-
back: 0x1b1816, flank: 0x171412, tip: 0x24201d, head: 0x1c1917, face: 0x2a2420, muzzle: 0x8a6a4a,
|
|
60
|
-
leg: 0x121010, belly: 0x151311, ear: 0x1a1715, earRim: 0x0f0d0c, earInner: 0x1c1816, claw: 0x2a2622,
|
|
61
|
-
ag: { back: 0.0, head: 0.0, leg: 0 },
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
// desaturation of the head, face and muzzle swatches toward their luminance, per coat (palette())
|
|
65
|
-
// (0.7 for the brown coats drew a grey hood on the Eurasian bear's rust body)
|
|
66
|
-
const FACE_DESAT = { dark: 0.55, red: 0.55, medium: 0.55, blonde: 0.45, grizzled: 0.65, black: 0.35 };
|
|
67
|
-
const TOUSLE = 0.8;
|
|
68
|
-
const CUB = 0x6a4c34; // cubs: a uniform soft brown natal coat
|
|
69
|
-
const COLLAR = 0xe8e0cc; // the pale neck collar of many Eurasian cubs
|
|
70
|
-
|
|
71
|
-
// Value noise stretched along a flow direction `t` (unit, reference space): `k` cycles per metre across the flow, `s` times
|
|
72
|
-
// longer along it, so the pattern runs in streaks with the hair (locks). The along-flow coordinate is measured from
|
|
73
|
-
// anchors snapped to 4 cm cells and two lattices offset by half a cell are cross-faded, so curving flow does not shear
|
|
74
|
-
// it (as the coat shader's own streak() for the base without shells).
|
|
75
|
-
function streak(p, t, k, s, o) {
|
|
76
|
-
const fr = (x) => x - Math.floor(x);
|
|
77
|
-
const edge = (a, b, c) => Math.min(a, 1 - a, b, 1 - b, c, 1 - c);
|
|
78
|
-
const g = [p[0] * 25, p[1] * 25, p[2] * 25];
|
|
79
|
-
const ea = edge(fr(g[0]), fr(g[1]), fr(g[2])), eb = edge(fr(g[0] + 0.5), fr(g[1] + 0.5), fr(g[2] + 0.5));
|
|
80
|
-
const st = k * (1 - 1 / s);
|
|
81
|
-
const at = (a, oo) => {
|
|
82
|
-
const d = ((p[0] - a[0]) * t[0] + (p[1] - a[1]) * t[1] + (p[2] - a[2]) * t[2]) * st;
|
|
83
|
-
return vnoise3(p[0] * k - t[0] * d + oo, p[1] * k - t[1] * d, p[2] * k - t[2] * d);
|
|
84
|
-
};
|
|
85
|
-
const nA = at(g.map((x) => Math.floor(x) * 0.04), o), nB = at(g.map((x) => (Math.floor(x + 0.5) - 0.5) * 0.04), o + 17.3);
|
|
86
|
-
const w = (ea * ea) / (ea * ea + eb * eb + 1e-6);
|
|
87
|
-
return (nB + (nA - nB) * w - 0.5) / Math.sqrt(w * w + (1 - w) * (1 - w)) + 0.5;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const desat = (c, k) => { const y = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; return c.map((x) => x + (y - x) * k); };
|
|
91
|
-
|
|
92
|
-
// claw horn (claws.js): the palette's claw colour at the base, paler worn horn toward the tip (side1, tq2, detail_bipedal)
|
|
93
|
-
export function clawColour(params) {
|
|
94
|
-
const c = palette(params).claw;
|
|
95
|
-
// dark horn at the base (a near-black grey-brown, darker than the swatch for the pale grizzly claws), worn paler
|
|
96
|
-
// toward the tip. (Mixed toward 0x6a5e52 / 0xd2c4a8 the bare horn, lit by the sun without the fur's self-shadowing,
|
|
97
|
-
// rendered pale ivory along its whole length, ~(148, 139, 126) median, where photos show dark horn with
|
|
98
|
-
// lighter tips. The dark base still reads against the ground the claws lie on.)
|
|
99
|
-
return { base: mix3(c, srgb(0x2a2420), 0.6), tip: mix3(c, srgb(0xcdbfa6), 0.7) };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function palette(p) {
|
|
103
|
-
const base = COATS[p.coat] || COATS.dark;
|
|
104
|
-
const k = p.coatWarmth || 0, l = p.coatLightness || 0, juv = p.juv || 0;
|
|
105
|
-
const cub = srgb(CUB).map((c) => c * (p.coat === 'black' ? 0.2 : p.coat === 'blonde' ? 1.5 : p.coat === 'grizzled' ? 1.2 : 1));
|
|
106
|
-
const out = { ag: base.ag };
|
|
107
|
-
for (const [key, hex] of Object.entries(base)) {
|
|
108
|
-
if (key === 'ag') continue;
|
|
109
|
-
let c = srgb(hex);
|
|
110
|
-
if (key !== 'claw') c = [c[0] * (1 + 0.1 * k + l), c[1] * (1 + 0.02 * k + l), c[2] * (1 - 0.12 * k + l)];
|
|
111
|
-
// (photos of brown bears in neutral light read greyer than the swatches: desaturate a fifth)
|
|
112
|
-
// (the head more, per coat (FACE_DESAT): face1 / face2 / tq1 medians, forehead (156, 138, 116) / (198, 164, 149) /
|
|
113
|
-
// (177, 156, 133), R/B 1.33-1.34, saturation 0.25; the render's red / dark faces at a third were an even orange-brown,
|
|
114
|
-
// R/B 1.8-1.9. The blonde's head stays golden like its hump (front3: forehead R/B 2.1, saturation 0.5):
|
|
115
|
-
// greyed, it was a grey mask framed by the golden ruff)
|
|
116
|
-
if (key !== 'claw') { const y = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; c = c.map((x) => x + (y - x) * (/^(head|face|muzzle)$/.test(key) && !(p.coat === 'black' && key === 'muzzle') ? FACE_DESAT[p.coat] ?? 0.6 : 0.22)); }
|
|
117
|
-
// cubs: the pattern is mostly washed into the uniform natal coat (legs stay darker)
|
|
118
|
-
const keep = /leg|earRim|claw/.test(key) ? 0.55 : /muzzle/.test(key) ? 0.4 : 0.15;
|
|
119
|
-
if (juv > 0 && key !== 'claw') c = mix3(c, cub, juv * (1 - keep));
|
|
120
|
-
out[key] = c;
|
|
121
|
-
}
|
|
122
|
-
return out;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function bearCoat(ctx) {
|
|
126
|
-
const { model, rig, pos, nrm, regionOf: partOf, weights, nV, params } = ctx;
|
|
127
|
-
// the head's mesh cell (regions.js): the mouth interior is painted only deeper than a cell or so inside
|
|
128
|
-
// the other part, so the facets of the lip line never show it with the mouth shut
|
|
129
|
-
const cellH = 0.0036 * (ctx.Q?.res || 1), mouthIn = Math.max(0.004, 1.3 * cellH);
|
|
130
|
-
const { BONES, AXIAL } = rig;
|
|
131
|
-
const AX_LEN = weights.axialLengths;
|
|
132
|
-
const C = palette(params);
|
|
133
|
-
const AG = C.ag;
|
|
134
|
-
const juv = params.juv || 0;
|
|
135
|
-
const hw = params.headW || 1, mz = params.muzzle || 1;
|
|
136
|
-
const shag = params.shag ?? 1; // coat length (summer 0.7 .. winter 1.2)
|
|
137
|
-
const bleach = params.bleach ?? 0; // sun-bleached back and head (0..1)
|
|
138
|
-
const collar = params.collar ?? 0; // cub neck collar (0..1)
|
|
139
|
-
const nOff = (params.coatSeed || 0) % 997;
|
|
140
|
-
const { dominant, axialS, skinIndex, skinWeight, limbMember, limbWhich } = weights;
|
|
141
|
-
const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
142
|
-
const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
|
|
143
|
-
const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
|
|
144
|
-
const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
|
|
145
|
-
const nosePrims = model.prims.filter((p) => p.tag === 'nose');
|
|
146
|
-
const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril');
|
|
147
|
-
const caninePrims = model.prims.filter((p) => p.tag === 'canine');
|
|
148
|
-
const carpalPrims = model.prims.filter((p) => p.tag === 'carpalpad');
|
|
149
|
-
const jawPrims = model.forPart('jaw').filter((p) => p.tag !== 'canine');
|
|
150
|
-
const bodyPrims = model.forPart('body');
|
|
151
|
-
const headPrims = bodyPrims.filter((p) => p.bone === 'head' && !p.carve && p.tag !== 'canine');
|
|
152
|
-
const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|femur|tibia|metatarsus|hpaw)/.test(b.name));
|
|
153
|
-
const isPaw = BONES.map((b) => /^(fpaw|hpaw|metacarpus|metatarsus)/.test(b.name));
|
|
154
|
-
const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw)/.test(b.name));
|
|
155
|
-
// head-local coordinates of a reference-space point (undo head width and muzzle stretch)
|
|
156
|
-
const headLocal = (p) => {
|
|
157
|
-
const x = (p[0] - HEAD_O[0]) / hw, y = p[1] - HEAD_O[1];
|
|
158
|
-
let z = p[2] - HEAD_O[2];
|
|
159
|
-
if (z > MUZZLE_Z0) z = MUZZLE_Z0 + (z - MUZZLE_Z0) / mz;
|
|
160
|
-
return [x, y, z];
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// --- regions per vertex (0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw) and limb blend
|
|
164
|
-
const region = new Uint8Array(nV);
|
|
165
|
-
const legness = new Float32Array(nV);
|
|
166
|
-
const legBone = new Int16Array(nV).fill(-1);
|
|
167
|
-
for (let v = 0; v < nV; v++) {
|
|
168
|
-
const bn = BONES[dominant[v]].name;
|
|
169
|
-
const p = P(v);
|
|
170
|
-
let lb = -1, lw = 0;
|
|
171
|
-
for (let k = 0; k < 4; k++) {
|
|
172
|
-
const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
|
|
173
|
-
if (isLimb[b] && w > lw) { lw = w; lb = b; }
|
|
174
|
-
}
|
|
175
|
-
legBone[v] = lb;
|
|
176
|
-
legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
|
|
177
|
-
if (partOf[v] === 2) region[v] = 6;
|
|
178
|
-
else if (bn.startsWith('ear')) region[v] = 5;
|
|
179
|
-
else if (axialS[v] > sTailBase - 0.01 && bn.startsWith('tail')) region[v] = 4;
|
|
180
|
-
else if (axialS[v] < sOcc + 0.01 || (partOf[v] === 1 && neckS(p[0], p[1], p[2]) > 0.03)) region[v] = 2;
|
|
181
|
-
else if (axialS[v] < sNeckBase) region[v] = 1;
|
|
182
|
-
else region[v] = 0;
|
|
183
|
-
if (region[v] >= 2) legness[v] = 0;
|
|
184
|
-
}
|
|
185
|
-
void limbWhich;
|
|
186
|
-
|
|
187
|
-
// --- comb (hair flow), bind space. Every field blends continuously (a hard switch in the flow
|
|
188
|
-
// direction parts the shells along a line: it drew dark rings round the face and the neck)
|
|
189
|
-
const segDir = (k) => {
|
|
190
|
-
const K = Math.max(0, Math.min(k, AXIAL.points.length - 2));
|
|
191
|
-
return norm(sub(AXIAL.points[K + 1], AXIAL.points[K]));
|
|
192
|
-
};
|
|
193
|
-
const comb = new Float32Array(nV * 3);
|
|
194
|
-
const earBone = BONES.map((b) => /^ear/.test(b.name));
|
|
195
|
-
// the pinna's frame: its centre, the cup's facing and up (base -> tip)
|
|
196
|
-
const earFrame = (s) => {
|
|
197
|
-
const b = BONES.find((bb) => bb.name === (s > 0 ? 'earL' : 'earR'));
|
|
198
|
-
const up = norm(sub(b.tail, b.head));
|
|
199
|
-
return { b, up, facing: norm([0.6 * s, 0.05, 1]), c: add(b.head, mul(sub(b.tail, b.head), 0.4)) };
|
|
200
|
-
};
|
|
201
|
-
const EARF = { 1: earFrame(1), [-1]: earFrame(-1) };
|
|
202
|
-
const earW = (v) => { let ew = 0; for (let k = 0; k < 4; k++) if (earBone[skinIndex[v * 4 + k]]) ew += skinWeight[v * 4 + k]; return ew; };
|
|
203
|
-
// ear hair lies out from the middle of the pinna: the rim fringe sticks out past the edge (a ragged outline, not a
|
|
204
|
-
// clean circle), the cup's hair grows out of the cup, the back's up and out
|
|
205
|
-
// (the cup's hair grows up across the hollow from its lower margin (face1: the cup filled with paler hair; lying in
|
|
206
|
-
// from the whole margin it swirled into a vortex), the fringe out past the rim, the back's up and out: the flow
|
|
207
|
-
// parts along the rim, as on a real ear)
|
|
208
|
-
const earComb = (p, n) => {
|
|
209
|
-
const E = EARF[p[0] > 0 ? 1 : -1];
|
|
210
|
-
let rv = sub(p, E.c);
|
|
211
|
-
rv = sub(rv, mul(E.facing, dot(rv, E.facing)));
|
|
212
|
-
const radial = len(rv) > 1e-5 ? norm(rv) : E.up;
|
|
213
|
-
const front = dot(n, E.facing);
|
|
214
|
-
const wf = smoothstep(0.15, 0.55, front), wb = smoothstep(-0.1, -0.5, front);
|
|
215
|
-
const out = norm(add(radial, mul(E.up, 0.5 * wb)));
|
|
216
|
-
return norm(add(mul(out, 1 - wf), mul(norm(add(E.up, mul(radial, 0.25))), wf)));
|
|
217
|
-
};
|
|
218
|
-
for (let v = 0; v < nV; v++) {
|
|
219
|
-
const p = P(v), n = N(v), r = region[v];
|
|
220
|
-
let d;
|
|
221
|
-
{
|
|
222
|
-
// body flow: along the axial chain (blended across the joints), hanging down the sides of the
|
|
223
|
-
// body and, on the neck, the ruff hanging down and back
|
|
224
|
-
const seg = weights.segT[v * 2] | 0, tt = weights.segT[v * 2 + 1];
|
|
225
|
-
let db = add(add(segDir(seg), mul(segDir(seg - 1), 1 - smoothstep(0, 0.35, tt))), mul(segDir(seg + 1), smoothstep(0.65, 1, tt)));
|
|
226
|
-
db = norm(db);
|
|
227
|
-
const nk = 1 - smoothstep(sNeckBase - 0.06, sNeckBase + 0.12, axialS[v]);
|
|
228
|
-
// (the chest front and the rump face along the body axis: the axial flow projects to nothing there
|
|
229
|
-
// and the hair parted in a line across the brisket; it hangs down instead)
|
|
230
|
-
const endOn = smoothstep(0.3, 0.8, Math.abs(n[2]));
|
|
231
|
-
db = norm(add(db, [0, -(0.6 * Math.abs(n[0]) + 0.25 * smoothstep(0.1, -0.6, n[1]) + 1.5 * endOn) * (1 - nk) - 0.7 * nk, -0.1 * nk]));
|
|
232
|
-
// head flow: out from the nose over the face and crown; the cheek ruff sweeps back, out and down
|
|
233
|
-
const h = headLocal(p);
|
|
234
|
-
let dh = norm(add(norm(sub(h, [0, -0.01, 0.22])), [0, -0.15, -0.4]));
|
|
235
|
-
const cheekW = smoothstep(0.035, -0.03, h[2]) * smoothstep(0.035, 0.09, Math.abs(h[0]));
|
|
236
|
-
dh = norm(add(dh, mul([Math.sign(h[0] || 1) * 0.5, -0.7, -0.6], cheekW)));
|
|
237
|
-
// round the eye the hair lies away from it, radially (as the lashes and the hair of the lids do)
|
|
238
|
-
{
|
|
239
|
-
const ex = Math.sign(h[0] || 1) * EYE_BASE.c[0];
|
|
240
|
-
const rv = [h[0] - ex, h[1] - EYE_BASE.c[1], h[2] - EYE_BASE.c[2]];
|
|
241
|
-
const dE = len(rv);
|
|
242
|
-
// (close to the lids only, and half way: a full radial flow out to 3 cm parted the hair in a crinkled line in
|
|
243
|
-
// front of the eye, where it met the face's flow back from the nose)
|
|
244
|
-
const wE = 0.6 * (1 - smoothstep(0.013, 0.024, dE));
|
|
245
|
-
if (wE > 0 && dE > 1e-4) dh = norm(add(mul(dh, 1 - wE), mul(norm([rv[0] * hw, rv[1], rv[2]]), wE)));
|
|
246
|
-
}
|
|
247
|
-
const toBody = r === 6 ? 0 : smoothstep(sOcc - 0.05, sOcc + 0.1, axialS[v]);
|
|
248
|
-
d = norm(add(mul(dh, 1 - toBody), mul(db, toBody)));
|
|
249
|
-
const L = legness[v];
|
|
250
|
-
if (L > 0 && legBone[v] >= 0) {
|
|
251
|
-
const lbn = BONES[legBone[v]];
|
|
252
|
-
let dl = norm(sub(lbn.tail, lbn.head));
|
|
253
|
-
if (/^(fpaw|hpaw|metacarpus|metatarsus)/.test(lbn.name)) dl = norm(add(dl, [0, -0.3, 0.6]));
|
|
254
|
-
else dl = norm(add(dl, [0, -0.3, -0.25]));
|
|
255
|
-
d = norm(add(mul(d, 1 - L), mul(dl, L)));
|
|
256
|
-
}
|
|
257
|
-
if (r === 5 || r === 2) {
|
|
258
|
-
const ew = earW(v);
|
|
259
|
-
if (ew > 0.02) d = norm(add(mul(d, 1 - smoothstep(0.15, 0.7, ew)), mul(earComb(p, n), smoothstep(0.15, 0.7, ew))));
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
// tousled: the long coat's locks lean a little this way and that (+-~20 deg, ~3 cm locks), so its tufts do not stand
|
|
263
|
-
// in regular rows; at medium the evenly combed neck, shoulders and cheek ruff drew rows of round scale-like tufts,
|
|
264
|
-
// a pine cone (the face, ears and feet keep their flow)
|
|
265
|
-
{
|
|
266
|
-
const hh = headLocal(p);
|
|
267
|
-
const tw = TOUSLE * (r === 6 || r === 5 ? 0 : r === 2 ? smoothstep(0.035, -0.03, hh[2]) * smoothstep(0.03, 0.09, Math.abs(hh[0])) : smoothstep(0.08, 0.18, p[1]));
|
|
268
|
-
if (tw > 0) {
|
|
269
|
-
const q = [p[0] * 32 + nOff, p[1] * 32, p[2] * 32];
|
|
270
|
-
d = norm(add(d, mul([vnoise3(q[0], q[1], q[2]) - 0.5, vnoise3(q[0] + 41.7, q[1], q[2]) - 0.5, vnoise3(q[0], q[1] + 23.1, q[2]) - 0.5], 2 * tw)));
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
let t = sub(d, mul(n, dot(d, n)));
|
|
274
|
-
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
275
|
-
t = norm(t);
|
|
276
|
-
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// --- per-vertex paint
|
|
280
|
-
const furLen = new Float32Array(nV);
|
|
281
|
-
const tint = new Float32Array(nV * 4);
|
|
282
|
-
// marks are signed distances capped at MARK_FAR everywhere (the shader antialiases with fwidth: a value
|
|
283
|
-
// that jumped from a few mm to 1 at the edge of a band drew a faint line or dashes there)
|
|
284
|
-
const MARK_FAR = 0.02;
|
|
285
|
-
const markSDF = new Float32Array(nV).fill(MARK_FAR);
|
|
286
|
-
const surf = new Float32Array(nV * 4);
|
|
287
|
-
// per-vertex fur-slope limit (furSlope.js: 1 = the default, Infinity = as painted) and hairless fur-id skin
|
|
288
|
-
const slopeV = new Float32Array(nV).fill(1);
|
|
289
|
-
const bareFur = new Uint8Array(nV);
|
|
290
|
-
const g3 = (h, c, rr) => Math.exp(-(((Math.abs(h[0]) - c[0]) / rr[0]) ** 2 + ((h[1] - c[1]) / rr[1]) ** 2 + ((h[2] - c[2]) / rr[2]) ** 2));
|
|
291
|
-
|
|
292
|
-
// Head, neck and ear paints are computed separately and blended across their junctions by position
|
|
293
|
-
// (the head and body meshes overlap across the neck cut and cross-fade: a paint that switched by mesh
|
|
294
|
-
// or by a region test drew a hard edge, a dark collar round the neck and a notched patch at the ear
|
|
295
|
-
// bases whenever the nape stretched)
|
|
296
|
-
const bodyPaint = (v, p, n, rb, nz, nz2) => {
|
|
297
|
-
let col, fl, ag, mat = MAT.FUR, mark = MARK_FAR;
|
|
298
|
-
const up = n[1];
|
|
299
|
-
// dorsal: paler guard-hair tips on the back, hump and nape; darker flanks; dark belly
|
|
300
|
-
const dors = smoothstep(-0.15, 0.7, up + 0.3 * nz);
|
|
301
|
-
col = mix3(C.flank, C.back, dors);
|
|
302
|
-
const humpW = smoothstep(0.02, 0.3, p[2]) * smoothstep(0.62, 0.5, p[2]) * smoothstep(0.2, 0.8, up) + (rb === 1 ? 0.7 * smoothstep(0.0, 0.7, up) : 0);
|
|
303
|
-
// (the pale tips vary lock to lock, +-30 %, so the guard-hair locks of the hump, nape and back separate, front3 /
|
|
304
|
-
// tq4; an even tip colour read as plush)
|
|
305
|
-
const tipK = clamp(humpW * 0.75 + 0.35 * bleach * dors, 0, 1);
|
|
306
|
-
col = mix3(col, C.tip, clamp(tipK * (1 + 0.6 * nz2) + 0.15 * nz2 * dors, 0, 1));
|
|
307
|
-
// (a dark band under the pale tips of the hump and nape guard hair, so its locks separate, tq4 / front3)
|
|
308
|
-
ag = mix(AG.back * 0.6, AG.back, dors) + 0.15 * clamp(humpW, 0, 1);
|
|
309
|
-
const vent = smoothstep(-0.1, -0.7, up) * smoothstep(0.62, 0.45, p[1]);
|
|
310
|
-
col = mix3(col, C.belly, vent * 0.85);
|
|
311
|
-
ag *= 1 - 0.8 * vent;
|
|
312
|
-
// hair length: longest on the hump and nape, long flanks, a hanging belly fringe
|
|
313
|
-
// (the belly fringe hangs 12 cm: side1 / side2 / side3 show the belly line at 0.23-0.30 of the hump
|
|
314
|
-
// height; at 9.5 cm the render's was 0.34-0.36)
|
|
315
|
-
fl = mix(0.06, 0.075, smoothstep(-0.2, 0.6, up));
|
|
316
|
-
// (8.5 cm on the hump: at 9.5 cm over the hump's sculpted swell its outline stood up as a mound)
|
|
317
|
-
fl = Math.max(fl, 0.085 * clamp(humpW, 0, 1));
|
|
318
|
-
// (on the brisket and the chest front between the forelegs, skin facing forward, 6.5 cm: seen from the front, a
|
|
319
|
-
// 12 cm fringe there showed the bright ground through its shells as pale horizontal slices; the
|
|
320
|
-
// flanks keep the 12 cm that makes the side silhouette's belly line)
|
|
321
|
-
// (and on the chest's underside between the forelegs, whose fringe hangs into the gap between them)
|
|
322
|
-
const chestFront = Math.max(smoothstep(0.15, 0.5, n[2]) * smoothstep(0.08, 0.22, p[2]), smoothstep(0.04, 0.14, p[2]) * smoothstep(0.14, 0.07, Math.abs(p[0])));
|
|
323
|
-
// (and along the midline of the belly's front half, 8 cm: seen from the front the gap between the forelegs looks along
|
|
324
|
-
// the belly, and its 12 cm fringe hung there edge-on as pale-gapped slices over the floor; the side
|
|
325
|
-
// silhouette's belly line is the flanks' fringe)
|
|
326
|
-
const midFront = smoothstep(0.12, 0.05, Math.abs(p[0])) * smoothstep(-0.2, 0.05, p[2]);
|
|
327
|
-
fl = Math.max(fl, 0.12 * vent * (1 - 0.46 * chestFront) * (1 - 0.35 * midFront));
|
|
328
|
-
if (rb === 1) {
|
|
329
|
-
// neck ruff: long, hanging; darker toward the throat. It starts at the back of the head as long
|
|
330
|
-
// as the crown hair there (4.5 cm) and grows to the ruff (a short ring between them read as a collar)
|
|
331
|
-
// (below the ears and behind the jaw angles 4 cm too, where the cheek ruff ends: in a look turn the neck's side
|
|
332
|
-
// behind the far jaw corner is seen edge-on, and longer hair there stood off as torn dark sheets)
|
|
333
|
-
const sideN = 1 - smoothstep(0.2, 0.6, up);
|
|
334
|
-
// (5.2-5.6 cm at the head, where the crown's 4.5 cm and the cheek ruff's tapered 5-6 cm end: at 2-4 cm the neck behind
|
|
335
|
-
// the head was a trench of short hair between the cheek ruff and the long neck hair, a dark ring round the neck, a
|
|
336
|
-
// collar, in the live page; torn dark sheets there are the shell shadows,
|
|
337
|
-
// which look up at the hair's root, render.shellShadowRoot)
|
|
338
|
-
fl = Math.max(fl, 0.08) * mix(mix(0.7, 0.65, sideN), 1, smoothstep(sOcc + 0.02, sOcc + 0.26, axialS[v]));
|
|
339
|
-
col = mix3(col, C.flank, smoothstep(0.0, -0.6, up) * 0.6);
|
|
340
|
-
// (little agouti on the neck's first 20 cm behind the head: the dark band under the tips is a dark shell layer at 3/4 of
|
|
341
|
-
// the hair's height, and where the neck's long hair is seen edge-on behind the jaw in a look turn that layer showed
|
|
342
|
-
// as torn dark sheets)
|
|
343
|
-
ag *= mix(0.3, 1, smoothstep(sOcc + 0.08, sOcc + 0.3, axialS[v]));
|
|
344
|
-
// (greyed toward the head like the cheek ruff over the neck's first 25 cm, so the face's grey runs into the neck;
|
|
345
|
-
// over 15 cm the grey face ended in a ring, a hood)
|
|
346
|
-
col = desat(col, 0.5 * (FACE_DESAT[params.coat] ?? 0.6) * (1 - smoothstep(sOcc, sOcc + 0.25, axialS[v])));
|
|
347
|
-
}
|
|
348
|
-
if (rb === 4) { fl = 0.05; col = mix3(C.flank, C.back, 0.5); }
|
|
349
|
-
// (lock to lock: +-30 % in length, so the long coat breaks into locks instead of an even pile; +-15 % on the neck's
|
|
350
|
-
// first 15 cm behind the head, whose long locks stood off as sheets behind the jaw corners in look turns)
|
|
351
|
-
// (and by lock along the flow, ~2.5 cm across and ~8 cm long, +-45 %: at medium (16 shells) the evenly long neck and
|
|
352
|
-
// shoulder hair drew rows of round scale-like tufts; locks of unequal length break the rows)
|
|
353
|
-
const lkB = streak(p, [comb[v * 3], comb[v * 3 + 1], comb[v * 3 + 2]], 40, 3.2, 57 + nOff) - 0.5;
|
|
354
|
-
fl *= 1 + (0.3 * nz2 + 0.9 * lkB * smoothstep(0.03, 0.06, fl)) * (rb === 1 ? mix(0.5, 1, smoothstep(sOcc + 0.05, sOcc + 0.2, axialS[v])) : 1);
|
|
355
|
-
// (between the forelegs, under the chest, 4.5 cm and without the lock length noise: seen from the front the 6-9 cm
|
|
356
|
-
// fringe hung into the gap between the forelegs, and the floor and its strands' sheen showed through its shells as a
|
|
357
|
-
// pale banded patch below the chin; tq2 / front2 / front3 show a dark, dense chest there)
|
|
358
|
-
const gapW = smoothstep(0.13, 0.07, Math.abs(p[0])) * smoothstep(0.06, 0.16, p[2]) * smoothstep(0.1, -0.4, n[1]) * smoothstep(0.62, 0.5, p[1]);
|
|
359
|
-
if (gapW > 0) fl = mix(fl, Math.min(fl, 0.045), gapW);
|
|
360
|
-
const L = legness[v];
|
|
361
|
-
if (L > 0) {
|
|
362
|
-
const lb = legBone[v];
|
|
363
|
-
const front = lb >= 0 && isFront[lb];
|
|
364
|
-
// legs darken toward the feet (near black below the elbow / stifle)
|
|
365
|
-
const dark = smoothstep(0.62, 0.3, p[1] + 0.06 * nz);
|
|
366
|
-
let lc = mix3(col, C.leg, dark);
|
|
367
|
-
// (heavy hairy columns, tq2 / front3: the lower legs 5 cm, "sleeves" 9 cm behind the forearm, "trousers"
|
|
368
|
-
// 10 cm behind the thighs; thinner, they read as long bare-looking legs)
|
|
369
|
-
let lf = mix(0.05, 0.07, smoothstep(0.12, 0.45, p[1]));
|
|
370
|
-
if (!front) lf = Math.max(lf, 0.1 * smoothstep(-0.1, -0.7, n[2]) * smoothstep(0.25, 0.5, p[1]));
|
|
371
|
-
else lf = Math.max(lf, 0.09 * smoothstep(-0.2, -0.8, n[2]) * smoothstep(0.12, 0.3, p[1]));
|
|
372
|
-
let la = AG.leg;
|
|
373
|
-
// (short hair over the toes: the claws show their whole length)
|
|
374
|
-
if (lb >= 0 && isPaw[lb]) { lf = mix(0.008, 0.035, smoothstep(0.025, 0.12, p[1])); lc = C.leg; la = 0; }
|
|
375
|
-
col = mix3(col, lc, L);
|
|
376
|
-
ag = mix(ag, la + (AG.back - la) * (1 - dark) * 0.6, L);
|
|
377
|
-
fl = mix(fl, lf, L);
|
|
378
|
-
// pads: bare, matte black leather where the paw meets the ground (the forefoot's palm and toe pads,
|
|
379
|
-
// the whole long sole of the hind foot) and on the carpal pad; the rest of the underside is hairy
|
|
380
|
-
// (the whole bare underside of the wrist flashed as a glossy black ball in every swing)
|
|
381
|
-
if (lb >= 0 && isPaw[lb]) {
|
|
382
|
-
let dc = 1;
|
|
383
|
-
if (front) for (const c of carpalPrims) dc = Math.min(dc, SDFModel.dist(c, p[0], p[1], p[2]));
|
|
384
|
-
const pad = front ? (p[1] < 0.016 && n[1] < -0.55) || (dc < 0.004 && n[1] < -0.2) : p[1] < 0.03 && n[1] < -0.35;
|
|
385
|
-
// (nose leather: pebbled, satin; its id sits next to fur's, so the interpolated material id never
|
|
386
|
-
// passes the mouth's pink at the rim, as skin (4) did)
|
|
387
|
-
if (pad) { mat = MAT.NOSE; col = srgb(0x1c1814); fl = 0; }
|
|
388
|
-
else if (p[1] < 0.035 && n[1] < -0.3) fl = Math.min(fl, 0.008);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
// cub collar: a pale band around the neck base
|
|
392
|
-
if (collar > 0 && (rb === 1 || (rb === 0 && p[2] > 0.3))) {
|
|
393
|
-
const band = Math.exp(-(((p[2] - 0.47 + 0.04 * nz) / 0.06) ** 2)) * smoothstep(0.2, -0.3, up + 0.4 * smoothstep(0.3, 0.9, Math.abs(n[0])));
|
|
394
|
-
col = mix3(col, srgb(COLLAR), clamp(band * collar * 1.3, 0, 0.9));
|
|
395
|
-
}
|
|
396
|
-
if (mat === MAT.FUR) fl *= shag;
|
|
397
|
-
return { col, fl, ag, mat, mark, under: 0.35 };
|
|
398
|
-
};
|
|
399
|
-
const EAR_PALE = srgb(0xd9c3a2);
|
|
400
|
-
const earPaint = (v, p, n) => {
|
|
401
|
-
const E = EARF[p[0] > 0 ? 1 : -1];
|
|
402
|
-
const front = dot(n, E.facing);
|
|
403
|
-
const ht = clamp(dot(sub(p, E.b.head), E.up) / 0.1, 0, 1);
|
|
404
|
-
const nzF = vnoise3(p[0] * 110 + nOff, p[1] * 110, p[2] * 110) - 0.5;
|
|
405
|
-
// distance from the middle of the cup in the ear's plane
|
|
406
|
-
let rv = sub(p, E.c);
|
|
407
|
-
rv = sub(rv, mul(E.facing, dot(rv, E.facing)));
|
|
408
|
-
// the edge of the pinna (where the cup turns into the back), above its buried lower part
|
|
409
|
-
const rim = (1 - smoothstep(0.25, 0.7, Math.abs(front))) * smoothstep(0.2, 0.45, ht);
|
|
410
|
-
const cup = smoothstep(0.2, 0.6, front) * (1 - rim);
|
|
411
|
-
// ears are furred all over in the head colour (a darker ear read as a teddy's cup on a pale head); the fringe
|
|
412
|
-
// round the rim pale-tipped (tq2, tq1, front2), the cup's hollow dark only in its deep middle (face2) and
|
|
413
|
-
// filled with paler hair growing in from its margin (face1, tq2). (A dark cup under a fringe ~15 % paler than
|
|
414
|
-
// the head read as a Mickey Mouse disc)
|
|
415
|
-
const pale = mix3(C.tip, EAR_PALE, 0.15);
|
|
416
|
-
let col = mix3(C.head, C.ear, 0.15);
|
|
417
|
-
col = mix3(col, pale, rim * 0.55);
|
|
418
|
-
// (the dark hollow is the lower middle of the cup, running down into the crown hair, face1 / face2; a round dark
|
|
419
|
-
// middle read as a hole)
|
|
420
|
-
const lat = Math.abs(dot(rv, norm(cross(E.up, E.facing))));
|
|
421
|
-
// (kept off the ear's base: running down to the crown it drew a dark rim / notch round the base)
|
|
422
|
-
const hollow = cup * (1 - smoothstep(0.012, 0.026, lat)) * (1 - smoothstep(0.42, 0.7, ht)) * smoothstep(0.1, 0.28, ht);
|
|
423
|
-
col = mix3(col, mix3(C.head, pale, 0.45), cup * (1 - hollow));
|
|
424
|
-
col = mix3(col, C.earInner, 0.7 * hollow);
|
|
425
|
-
// hair: the back 20 mm, the rim fringe ~33 mm and ragged (+-20 % lock to lock), the cup's margin 22 mm lying in
|
|
426
|
-
// over the hollow, 12 mm in its middle (at 23 mm round a thin pinna the fringe drew the ear as a clean circle, a
|
|
427
|
-
// cup with a smooth thick rim)
|
|
428
|
-
let fl = mix(0.02, 0.033, rim);
|
|
429
|
-
fl = mix(fl, mix(0.022, 0.013, hollow), cup);
|
|
430
|
-
fl *= 1 + 0.4 * nzF;
|
|
431
|
-
// (the rim's fringe and the back's outer band ragged by lock, +-40 % at ~1.5 cm: from behind the evenly long fringe drew
|
|
432
|
-
// the ear as a round disc with a smooth outline; tq2 / side3 show tufted, uneven ear outlines)
|
|
433
|
-
const back = smoothstep(-0.2, -0.6, front) * smoothstep(0.35, 0.7, ht);
|
|
434
|
-
const nzR = vnoise3(p[0] * 65 + 13 + nOff, p[1] * 65, p[2] * 65) - 0.5;
|
|
435
|
-
fl *= 1 + 0.8 * nzR * Math.max(rim, back);
|
|
436
|
-
fl = mix(fl, fl * 1.2, back);
|
|
437
|
-
col = mix3(col, pale, 0.3 * back * smoothstep(-0.1, 0.25, nzR));
|
|
438
|
-
return { col, fl, ag: AG.head * 0.5, mat: MAT.FUR, mark: MARK_FAR, under: 0.35, rim };
|
|
439
|
-
};
|
|
440
|
-
const headPaint = (v, p, n, jaw, nz, nz2) => {
|
|
441
|
-
const h = headLocal(p);
|
|
442
|
-
const nzF = vnoise3(p[0] * 90 + nOff, p[1] * 90, p[2] * 90) - 0.5;
|
|
443
|
-
let col = C.head, ag = AG.head, mat = MAT.FUR, mark = MARK_FAR, fl, nostril = 0, nrim = 0, bareLid = 0;
|
|
444
|
-
// the face grades from the crown into a paler, greyer muzzle; the pale muzzle is the bridge and the
|
|
445
|
-
// front of the snout (face1 / face2): the sides of the face below and behind the eyes stay darker
|
|
446
|
-
// (the paler face colour on the front of the face, fading out over the cheeks: across the whole front half of the head,
|
|
447
|
-
// the cheeks included, the grey face was a round disc framed by the ruff, a balloon round the muzzle in the live page's
|
|
448
|
-
// low sun; face1 / face2 / tq1: the cheeks are the crown's colour)
|
|
449
|
-
const face = smoothstep(-0.02, 0.07, h[2]) * (1 - 0.6 * smoothstep(0.04, 0.085, Math.abs(h[0])));
|
|
450
|
-
col = mix3(col, C.face, face);
|
|
451
|
-
// (graded over 10 cm from between the eyes to the nose, palest on the bridge and round the nose and
|
|
452
|
-
// mouth; the sides of the snout under the eyes stay the face colour: a pale snout all round, sharply
|
|
453
|
-
// bounded under the eyes, read as a teddy's muzzle patch)
|
|
454
|
-
const lat = smoothstep(0.02, 0.05, Math.abs(h[0]));
|
|
455
|
-
const muzz = smoothstep(0.06, 0.16, h[2]) * (1 - 0.7 * lat * smoothstep(0.2, 0.12, h[2]));
|
|
456
|
-
col = mix3(col, C.muzzle, 0.7 * muzz);
|
|
457
|
-
// (mottled lock to lock, lighter and darker locks: face1 / face2 / tq1 show a salt-and-pepper face with the hair's flow
|
|
458
|
-
// in it; the body's 3 cm shade noise left the face an even plush, round 1 cm blotches read as spots on felt: locks are
|
|
459
|
-
// streaks along the comb, ~7 mm across and ~3 cm long)
|
|
460
|
-
const cv = [comb[v * 3], comb[v * 3 + 1], comb[v * 3 + 2]];
|
|
461
|
-
const lock = streak(p, cv, 140, 4.5, 31 + nOff) - 0.5;
|
|
462
|
-
const mot = 0.7 * lock + 0.3 * (vnoise3(p[0] * 110 + 31 + nOff, p[1] * 110, p[2] * 110) - 0.5);
|
|
463
|
-
col = mul(col, 1 + 0.65 * mot * (1 - 0.4 * muzz));
|
|
464
|
-
// (grizzled: pale-tipped guard hairs over a dark band on the forehead and cheeks, face1 / face2 / tq1; stronger in the
|
|
465
|
-
// paler locks)
|
|
466
|
-
ag = clamp(Math.max(ag, 0.7) * (1 - 0.6 * muzz) * (1 + 0.5 * lock), 0, 1);
|
|
467
|
-
// (the long cheek ruff with a third of it: its dark band layer, seen edge-on at the jaw corner, drew torn dark sheets)
|
|
468
|
-
ag *= mix(1, 0.35, smoothstep(0.25, 0.75, smoothstep(0.13, -0.05, h[2]) * smoothstep(-0.02, 0.14, Math.abs(h[0])) * smoothstep(0.125, 0.02, h[1]) * smoothstep(-0.14, -0.06, h[1])));
|
|
469
|
-
// cheeks below and behind the eyes darker (the face is framed by darker hair)
|
|
470
|
-
const cheek = smoothstep(0.045, 0.09, Math.abs(h[0])) * smoothstep(0.11, 0.0, h[2]) * smoothstep(0.045, -0.03, h[1]);
|
|
471
|
-
col = mix3(col, mix3(C.head, C.flank, 0.5), cheek * 0.35);
|
|
472
|
-
// the cheek ruff: long hair from beside and below the eyes back to the jaw angle and the neck
|
|
473
|
-
// (the ruff starts beside and below the eyes: face1 / face2 are a disk of hair 3.1-3.4 x the eye
|
|
474
|
-
// separation wide at eye level)
|
|
475
|
-
// (down to the jaw line: the cheek hair hangs to below the mouth, so the face is widest at and below the
|
|
476
|
-
// eyes, face1 / face2; a ruff ending at the eyes left a round head narrowing to the snout, a teddy's)
|
|
477
|
-
// (it rises from ~2 cm under the eye to 9.5 cm at the jaw angle; over ~5 cm across the cheek it drew a hard edge
|
|
478
|
-
// where the face's short hair met it, from the front a seam down the side of the face: the lateral ramp
|
|
479
|
-
// runs over ~8 cm)
|
|
480
|
-
const ruffW = smoothstep(0.13, -0.05, h[2]) * smoothstep(-0.02, 0.14, Math.abs(h[0])) * smoothstep(0.125, 0.02, h[1]) * smoothstep(-0.14, -0.06, h[1]);
|
|
481
|
-
// (and it takes the neck ruff's colour as it lengthens: the face's grey-brown ending in a ring of the neck's paler,
|
|
482
|
-
// golden-tipped hair drew the face as a disk; front3 / tq1: the hair round the face is the neck's)
|
|
483
|
-
// (greyed half as much as the face: face1 / face2's cheek ruff, saturation 0.13-0.42, against the neck's 0.6-0.8 in the
|
|
484
|
-
// render; the face's grey ending in the neck's saturated red-gold drew a grey mask framed by a ring)
|
|
485
|
-
if (!jaw && ruffW > 0.02) col = mix3(col, desat(bodyPaint(v, p, n, 1, nz, nz2).col, 0.5 * (FACE_DESAT[params.coat] ?? 0.6)), 0.7 * smoothstep(0.1, 0.7, ruffW));
|
|
486
|
-
// dark patches round the eyes, running a little down and forward
|
|
487
|
-
// (a ring of dark hair right round the eye, 2.5 cm, and a darker streak from its front corner down toward
|
|
488
|
-
// the side of the muzzle, face2 / front3: the eyes sit in shadow; a patch centred behind the eye left a pale
|
|
489
|
-
// ring of short hair round a bead)
|
|
490
|
-
const dEye = Math.hypot(Math.abs(h[0]) - EYE_BASE.c[0], h[1] - EYE_BASE.c[1], h[2] - EYE_BASE.c[2]);
|
|
491
|
-
// (darker and wider below the eye: face1 / face2 show the eyes in patches of dark hair as dark as the cheeks or darker,
|
|
492
|
-
// (78, 60, 63) under face2's eye against (113, 86, 77) on its cheek; the render's were as pale as the forehead)
|
|
493
|
-
const eyeDark = Math.max(Math.exp(-((dEye / 0.03) ** 2)), 0.8 * g3(h, [0.036, 0.012, 0.085], [0.014, 0.02, 0.026]), 0.7 * g3(h, [0.048, 0.006, 0.066], [0.02, 0.016, 0.02]));
|
|
494
|
-
col = mix3(col, mix3(C.head, C.leg, 0.8), eyeDark * 0.9);
|
|
495
|
-
// lower jaw and throat a little darker
|
|
496
|
-
// (the lower jaw small, dark and in shadow under the upper lip: face1 / tq1 / front1 show a small dark hairy
|
|
497
|
-
// chin; a paler front read as a pale rounded lower lip under a smile)
|
|
498
|
-
if (jaw) col = mix3(col, mix3(C.face, C.belly, 0.55), 0.75);
|
|
499
|
-
// crown sun-bleached on some individuals
|
|
500
|
-
col = mix3(col, C.tip, bleach * 0.4 * smoothstep(0.2, 0.8, n[1]) * (1 - muzz));
|
|
501
|
-
// fur length (face1 / face2 / face3_profile): tousled strands 2-4 cm on the forehead and the cheeks, growing
|
|
502
|
-
// gradually from the short muzzle out to the cheek ruff and back to the long crown, with the ears sunk in the
|
|
503
|
-
// crown hair. Muzzle 9 mm at the nose, the bridge 13-14 mm between the eyes, the forehead and the face beside
|
|
504
|
-
// and below the eyes 20 mm (they get a little clumping: texture, not curls, render.clumpLen). At 9-12 mm with
|
|
505
|
-
// no clumping the face rendered as a suede mask framed by the long ruff
|
|
506
|
-
// (the snout sleek from just in front of the eyes, the bridge 11 mm: face3_profile / side2 show a smooth snout line;
|
|
507
|
-
// 14-18 mm on its top and sides stood up in 3/4 views as a pale sheet of shells over the far eye)
|
|
508
|
-
fl = mix(0.02, 0.009, smoothstep(0.06, 0.15, h[2]));
|
|
509
|
-
fl = mix(fl, Math.min(fl, 0.011), smoothstep(0.04, 0.012, Math.abs(h[0])) * smoothstep(0.05, 0.09, h[2]) * smoothstep(-0.01, 0.02, h[1]));
|
|
510
|
-
// the forehead grades into the crown over its whole length (42 mm between the ears), ~50 mm round the ear
|
|
511
|
-
// bases (face2, tq2: the lower third of each ear is buried in the crown hair)
|
|
512
|
-
if (!jaw) {
|
|
513
|
-
// (30 mm on the crown: at 42 mm, fully clumped, it drew rows of round tufts between the ears seen from the front,
|
|
514
|
-
// lamb's wool; at 34 mm still rows)
|
|
515
|
-
fl = Math.max(fl, mix(0.02, 0.03, smoothstep(0.045, -0.07, h[2])) * smoothstep(0.015, 0.06, h[1]));
|
|
516
|
-
const dEarB = Math.hypot(Math.abs(h[0]) - 0.122, h[1] - 0.082, h[2] + 0.05);
|
|
517
|
-
fl = Math.max(fl, 0.05 * Math.exp(-((dEarB / 0.045) ** 2)));
|
|
518
|
-
}
|
|
519
|
-
// (the cheek ruff, ruffW above: 9.5 cm at the jaw angle)
|
|
520
|
-
// (tapering to ~5 cm toward the neck cut, where the neck's hair starts at that length: the cheek ruff's 9.5 cm hair
|
|
521
|
-
// ended in a shelf over it, torn dark sheets at the far jaw corner in a look turn)
|
|
522
|
-
// (by lock along the flow, +-45 %: the evenly long ruff drew a grid of round scale-like tufts behind the eye at medium)
|
|
523
|
-
const lkR = streak(p, cv, 55, 3.2, 77 + nOff) - 0.5;
|
|
524
|
-
fl = Math.max(fl, 0.095 * ruffW * mix(1, 0.65, smoothstep(-0.05, -0.14, h[2])) * (1 + 0.9 * lkR * smoothstep(0.2, 0.6, ruffW)));
|
|
525
|
-
if (!jaw) fl = mix(fl, Math.max(fl, 0.045), smoothstep(-0.04, -0.15, h[2]));
|
|
526
|
-
// (the throat skin under the jaw carries the jaw's long hair: 12 mm there beside a 4 cm jaw was a wall)
|
|
527
|
-
if (!jaw) fl = Math.max(fl, mix(0.012, 0.04, smoothstep(0.1, -0.02, h[2])) * smoothstep(-0.03, -0.075, h[1]));
|
|
528
|
-
// (the jaw: the lower lip 12 mm, hanging down over its rim, long under the cheek ruff and toward the throat,
|
|
529
|
-
// 4 cm: a 2 cm jaw under the 7-9 cm ruff stood up as a wall along the jaw line)
|
|
530
|
-
if (jaw) fl = mix(0.012, 0.04, smoothstep(0.1, -0.02, h[2]));
|
|
531
|
-
// round the eyes the face hair is shorter, 12 mm out to ~3 cm from the eye (face3_profile), so the short hair
|
|
532
|
-
// at the lids grows back within ~1 cm (a 2 cm ring of short hair round the eye drew a pale crater)
|
|
533
|
-
const dEyeC = Math.hypot(Math.abs(h[0]) - EYE_BASE.c[0], h[1] - EYE_BASE.c[1], h[2] - EYE_BASE.c[2]);
|
|
534
|
-
fl = Math.min(fl, mix(0.012, 0.1, smoothstep(0.028, 0.048, dEyeC)));
|
|
535
|
-
// (in front of the eye, toward the bridge, shorter still, 5.5 mm at 1.6 cm: the far eye in a 3/4 view is seen past
|
|
536
|
-
// the bridge, and the 12 mm hair there, combed out from the eye, stood off it as pale flaps over the eye)
|
|
537
|
-
const eyeFront = smoothstep(-0.004, 0.008, h[2] - EYE_BASE.c[2]) * (1 - smoothstep(0.024, 0.034, dEyeC));
|
|
538
|
-
fl = Math.min(fl, mix(fl, mix(0.0055, 0.012, smoothstep(0.016, 0.03, dEyeC)), eyeFront));
|
|
539
|
-
// (the upper side of the bridge from the eyes forward ~4 cm, between the bridge's top and the eye: 5 mm, darker, and
|
|
540
|
-
// without the lock-to-lock length noise. In a 3/4 view the snout's silhouette runs along it across the far eye, and its
|
|
541
|
-
// 11-14 mm hair stood off there as a stepped edge of shells over the eye)
|
|
542
|
-
const bSide = smoothstep(0.007, 0.016, Math.abs(h[0])) * (1 - smoothstep(0.04, 0.055, Math.abs(h[0])))
|
|
543
|
-
* smoothstep(0.05, 0.064, h[2]) * (1 - smoothstep(0.105, 0.13, h[2]))
|
|
544
|
-
* smoothstep(-0.012, 0.004, h[1]) * (1 - smoothstep(0.044, 0.058, h[1]));
|
|
545
|
-
fl = Math.min(fl, mix(fl, 0.005, bSide));
|
|
546
|
-
col = mix3(col, mix3(C.head, C.leg, 0.6), 0.4 * bSide * (1 - eyeDark));
|
|
547
|
-
// lock to lock +-25 % at ~1 cm (the body's coat locks; an even length read as a pile)
|
|
548
|
-
fl *= 1 + 0.5 * nzF * (1 - 0.8 * bSide);
|
|
549
|
-
// (the crown and the top of the forehead also by lock, the locks running with the flow: its fully clumped 34 mm hair drew
|
|
550
|
-
// rows of round shingle-like tufts between the ears from the front; with +-45 % about half the locks
|
|
551
|
-
// fall under the 22 mm at which the tufts form fully)
|
|
552
|
-
const crownW = smoothstep(0.015, 0.05, h[1]) * smoothstep(0.07, 0.02, h[2]) * (jaw ? 0 : 1);
|
|
553
|
-
fl *= 1 + 0.55 * lock * crownW;
|
|
554
|
-
// eye rims (dark lid margins)
|
|
555
|
-
// (without eyelid patches, medium and below, the aperture is meshed at the head cell: a lid band one
|
|
556
|
-
// cell wide, dark, so the stepped cut reads as the lid margin instead of lit grey facets)
|
|
557
|
-
const lidW = ctx.Q && !eyePatchesAt(ctx.Q) ? Math.max(0.0018, 0.5 * cellH) : 0.0018;
|
|
558
|
-
for (const e of eyePrims) {
|
|
559
|
-
const de = SDFModel.dist(e, p[0], p[1], p[2]);
|
|
560
|
-
mark = Math.min(mark, Math.abs(de) - Math.max(0.002, lidW));
|
|
561
|
-
// (the lid margin: hairless skin painted black with the fur id, matte like the deep coat: as moist bare dark skin
|
|
562
|
-
// (id 2) it mirrored the sky outdoors, a light blue-grey ring that made the eyes read as glassy marbles in the
|
|
563
|
-
// showcase; the eyeball's cornea keeps the wet catchlight)
|
|
564
|
-
if (Math.abs(de) < lidW) { mat = MAT.FUR; col = srgb(0x0b0908); fl = 0; ag = 0; bareLid = 1; }
|
|
565
|
-
else if (Math.abs(de) < lidW + 0.0015) fl = Math.min(fl, 0.003);
|
|
566
|
-
// (round the eye the hair lies away from it, like the hair of the lids (the comb): 5 mm at the margin, the
|
|
567
|
-
// 12 mm eye-surround hair within ~7 mm; a 2-3 cm ring of 2-3 mm hair rendered as a pale smooth felt, a crater)
|
|
568
|
-
else if (de < lidW + 0.0085) fl = Math.min(fl, mix(0.005, fl, smoothstep(lidW + 0.0015, lidW + 0.0085, de)));
|
|
569
|
-
}
|
|
570
|
-
// nose leather
|
|
571
|
-
let dn = 1;
|
|
572
|
-
for (const q of nosePrims) dn = Math.min(dn, SDFModel.dist(q, p[0], p[1], p[2]));
|
|
573
|
-
// (the crisp rim is a mark band round the pad; a mark over the whole pad painted it and the nostrils one flat
|
|
574
|
-
// mark colour)
|
|
575
|
-
mark = Math.min(mark, Math.max(dn - 0.0045 + Math.max(0, 0.17 - h[2]), 0.0012 - dn));
|
|
576
|
-
// (the leather wraps round the nostrils: the muzzle's pale hair reaching into a nostril's outer corner lit up as a pale
|
|
577
|
-
// glint)
|
|
578
|
-
let dq0 = 1;
|
|
579
|
-
if (h[2] > 0.185 && !jaw) for (const q of nostrilPrims) dq0 = Math.min(dq0, SDFModel.dist(q, p[0], p[1], p[2]));
|
|
580
|
-
if ((dn < 0.0035 || (dq0 < 0.0028 && dn < 0.007)) && h[2] > 0.19) {
|
|
581
|
-
// (face2: a cool dark grey in shade, #282a37, the nostrils near black)
|
|
582
|
-
mat = MAT.NOSE; col = srgb(0x25242a); fl = 0;
|
|
583
|
-
// (the nostrils are openings: black inside, darkest deepest, so they read from the front and below as in
|
|
584
|
-
// face2 even where the carve is shallow)
|
|
585
|
-
let dq = 1;
|
|
586
|
-
for (const q of nostrilPrims) dq = Math.min(dq, SDFModel.dist(q, p[0], p[1], p[2]));
|
|
587
|
-
// (black from ~2 mm outside the carve: the leather's fixed grey on the inner walls of the rims, lit from above, drew
|
|
588
|
-
// a pale lining round the lower half of each opening)
|
|
589
|
-
nostril = smoothstep(0.0026, 0.0006, dq) * smoothstep(0.007, 0.0, dn);
|
|
590
|
-
// (the leather round each nostril matte: its rim, a tight convex edge, caught the light as pale glints)
|
|
591
|
-
nrim = smoothstep(0.0045, 0.001, dq);
|
|
592
|
-
col = mix3(col, srgb(0x040303), nostril);
|
|
593
|
-
// (the shader draws nose leather in its own fixed grey whatever the tint, and bare dark skin (lids, lips) moist
|
|
594
|
-
// with a sharp highlight: two glossy beads. The nostril walls are hairless fur-id skin painted black, which the
|
|
595
|
-
// base pass shades as the deep coat: dark and matte; the id lies next to the leather's)
|
|
596
|
-
if (nostril > 0.5) { mat = MAT.FUR; col = srgb(0x030202); ag = 0; }
|
|
597
|
-
// the groove between the nostrils: a crisp dark line down the lower half of the pad
|
|
598
|
-
mark = Math.min(mark, Math.abs(h[0]) - 0.0011 + 2 * Math.max(0, h[1] + 0.012));
|
|
599
|
-
} else if (dn < 0.008 && h[2] > 0.18) fl = Math.min(fl, 0.003);
|
|
600
|
-
// (short dark hair round the nostrils' outer corners)
|
|
601
|
-
if (mat === MAT.FUR && dq0 < 0.008) { const kq = smoothstep(0.008, 0.003, dq0); fl = Math.min(fl, mix(fl, 0.002, kq)); col = mix3(col, srgb(0x1a1512), 0.8 * kq); }
|
|
602
|
-
if (h[2] > 0.185 && !jaw && mat !== MAT.NOSE) {
|
|
603
|
-
// (a nostril wall reaching past the pad: bare and black like the rest of the nostril)
|
|
604
|
-
let dq = 1;
|
|
605
|
-
for (const q of nostrilPrims) dq = Math.min(dq, SDFModel.dist(q, p[0], p[1], p[2]));
|
|
606
|
-
// (within 2.5 mm of the carve: its smooth subtraction leaves the deep walls 1-2 mm off the carver's own surface, and
|
|
607
|
-
// there they took the muzzle's colour, a brown lining seen through the openings)
|
|
608
|
-
if (dq < 0.0025) { mat = MAT.FUR; fl = 0; nostril = 1; col = srgb(0x030202); ag = 0; }
|
|
609
|
-
}
|
|
610
|
-
// below the pad the groove runs on as a dark philtrum, ~2 cm, fading toward the mouth (face2, face3_profile):
|
|
611
|
-
// a soft dark cleft with the lip hair short along it (a crisp black line to the lip read as a teddy's stitching)
|
|
612
|
-
// (short, soft and dim, the hair left as long as the lip's: a 3 cm black stripe of 3 mm hair down the pale lip read as
|
|
613
|
-
// a drip under the nose; shorter hair there showed as a pale notch)
|
|
614
|
-
// (not on the nostril walls, which are fur-id skin too: the lip's colour mixed into them drew a brown lining)
|
|
615
|
-
if (h[2] > 0.17 && !jaw && mat === MAT.FUR && nostril < 0.5) {
|
|
616
|
-
// (darker and a little wider right under the pad, fading over 1.5 cm: the hair's flow fans out from the pad's lower
|
|
617
|
-
// edge there and a lit strand at the fan's root showed as a pale tick under the pad)
|
|
618
|
-
const along = smoothstep(-0.046, -0.034, h[1]) * smoothstep(-0.016, -0.026, h[1]);
|
|
619
|
-
const cleft = (1 - smoothstep(0.0015, 0.0045, Math.abs(h[0]))) * along;
|
|
620
|
-
col = mix3(col, srgb(0x2a201a), 0.7 * cleft);
|
|
621
|
-
// (the upper lip right under the pad darker, fading down and out: tq1 / face1; the pale lip hair under the
|
|
622
|
-
// septum's point lit up as a pale tab)
|
|
623
|
-
const under = smoothstep(-0.052, -0.034, h[1]) * (1 - smoothstep(0.006, 0.02, Math.abs(h[0])));
|
|
624
|
-
col = mix3(col, mix3(C.face, C.belly, 0.5), 0.6 * under);
|
|
625
|
-
}
|
|
626
|
-
// lips: head vertices near the jaw surface, jaw vertices near the head surface (black lip line)
|
|
627
|
-
// (the bare black lip shows only round the front of the mouth, under the nose: behind the canines the
|
|
628
|
-
// lip hair covers the mouth line back to the corner below the eye; a black line drawn all the way back
|
|
629
|
-
// read as a puppet's mouth, a frown in the low 3/4)
|
|
630
|
-
const lipF = smoothstep(0.08, 0.12, h[2]);
|
|
631
|
-
if (!jaw) {
|
|
632
|
-
const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
|
|
633
|
-
if (dj < -mouthIn) { mat = MAT.MOUTH; col = srgb(0x5e2e30); fl = 0; }
|
|
634
|
-
else if (dj < 0.0015 * lipF) { mat = MAT.DARK_SKIN; col = srgb(0x1a1614); fl = 0; }
|
|
635
|
-
// (toward the mouth line the hair shortens at under 1 mm per mm of skin, however long it is: the lip
|
|
636
|
-
// hair lies over the line without standing up as a wall along it)
|
|
637
|
-
else fl = Math.min(fl, mix(0.006, 0.003, lipF) + 0.85 * Math.max(0, dj - 0.0015 * lipF));
|
|
638
|
-
mark = Math.min(mark, dj - 0.003 * lipF + 0.004 * (1 - lipF));
|
|
639
|
-
} else {
|
|
640
|
-
const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
|
|
641
|
-
if (dh < -mouthIn) { mat = MAT.MOUTH; col = srgb(0x5e2e30); fl = 0; }
|
|
642
|
-
else if (dh < 0.0015 * lipF) { mat = MAT.DARK_SKIN; col = srgb(0x1a1614); fl = 0; }
|
|
643
|
-
else fl = Math.min(fl, mix(0.005, 0.003, lipF) + 0.85 * Math.max(0, dh - 0.0015 * lipF));
|
|
644
|
-
mark = Math.min(mark, dh - 0.003 * lipF + 0.004 * (1 - lipF));
|
|
645
|
-
}
|
|
646
|
-
// (the shells of short, unclumped hair render as a pale smooth felt, much lighter than longer hair of the same
|
|
647
|
-
// colour, whose strands show the darker coat between them: the lid margins and the muzzle a little darker)
|
|
648
|
-
if (mat === MAT.FUR) col = mul(col, mix(0.72, 1, smoothstep(0.003, 0.012, fl)));
|
|
649
|
-
// teeth
|
|
650
|
-
for (const c of caninePrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0015) { mat = MAT.KERATIN; col = srgb(0xe4dac4); fl = 0; mark = MARK_FAR; }
|
|
651
|
-
void nz;
|
|
652
|
-
return { col, fl, ag, mat, mark, under: 0.3, nostril, nrim, bare: nostril > 0.5 || bareLid > 0 };
|
|
653
|
-
};
|
|
654
|
-
const mixPaint = (a, b, t) => ({
|
|
655
|
-
col: mix3(a.col, b.col, t), fl: mix(a.fl, b.fl, t), ag: mix(a.ag, b.ag, t), mark: Math.min(a.mark, b.mark),
|
|
656
|
-
mat: a.mat === MAT.FUR ? b.mat : a.mat, under: mix(a.under, b.under, t), nostril: Math.max(a.nostril || 0, b.nostril || 0), nrim: Math.max(a.nrim || 0, b.nrim || 0), bare: !!(a.bare || b.bare),
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
for (let v = 0; v < nV; v++) {
|
|
660
|
-
const p = P(v), n = N(v), r = region[v];
|
|
661
|
-
const nz = fbm3(p[0] * 5 + nOff, p[1] * 5, p[2] * 5, 3) - 0.5;
|
|
662
|
-
const nz2 = vnoise3(p[0] * 30 + nOff, p[1] * 30, p[2] * 30) - 0.5;
|
|
663
|
-
let o;
|
|
664
|
-
if (r === 6) o = headPaint(v, p, n, true, nz, nz2);
|
|
665
|
-
else {
|
|
666
|
-
// head vs neck by position along the neck (the same for both meshes of the overlap), 10 cm wide
|
|
667
|
-
const hn = r === 4 ? 0 : smoothstep(-0.05, 0.05, neckS(p[0], p[1], p[2]));
|
|
668
|
-
const rb = r === 4 ? 4 : axialS[v] < sNeckBase ? 1 : 0;
|
|
669
|
-
if (hn >= 0.999) o = headPaint(v, p, n, false, nz, nz2);
|
|
670
|
-
else if (hn <= 0.001) o = bodyPaint(v, p, n, rb, nz, nz2);
|
|
671
|
-
else o = mixPaint(bodyPaint(v, p, n, 1, nz, nz2), headPaint(v, p, n, false, nz, nz2), hn);
|
|
672
|
-
// ears: blended into the head by their skin weight
|
|
673
|
-
let ew = 0;
|
|
674
|
-
for (let k = 0; k < 4; k++) if (earBone[skinIndex[v * 4 + k]]) ew += skinWeight[v * 4 + k];
|
|
675
|
-
// (the hair length over a wider band than the colour: the 8-9 cm cheek ruff below the ear falls to the
|
|
676
|
-
// ear's 2 cm without a wall at the ear base)
|
|
677
|
-
if (ew > 0.02) {
|
|
678
|
-
const fl0 = o.fl, ep = earPaint(v, p, n); o = mixPaint(o, ep, smoothstep(0.1, 0.7, ew)); o.fl = mix(fl0, ep.fl, smoothstep(0.02, 0.95, ew));
|
|
679
|
-
// (the rim fringe stays as painted: the fur-slope limit bevelled it down to the back's 20 mm)
|
|
680
|
-
if (ep.rim > 0.4 && ew > 0.6) slopeV[v] = Infinity;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
let { col, fl, ag, mat, mark, under } = o;
|
|
684
|
-
const nostril = o.nostril || 0, nrim = o.nrim || 0;
|
|
685
|
-
if (o.bare && mat === MAT.FUR) bareFur[v] = 1;
|
|
686
|
-
// cubs: fluffy and uniform
|
|
687
|
-
if (juv > 0 && mat === MAT.FUR) {
|
|
688
|
-
fl = mix(fl, Math.max(fl * 0.7, r === 2 || r === 6 ? 0.02 : 0.04), juv);
|
|
689
|
-
ag *= 1 - 0.8 * juv;
|
|
690
|
-
}
|
|
691
|
-
// low-frequency colour variation (patchy moult, sun-bleach) and a finer lock-to-lock shade
|
|
692
|
-
const cvh = fbm3(p[0] * 3 + 5 + nOff, p[1] * 3, p[2] * 3, 2) - 0.5;
|
|
693
|
-
// (grizzled coats: a stronger lock-to-lock speckle of silver-tipped and dark locks)
|
|
694
|
-
const sp = params.coat === 'grizzled' ? 0.2 : 0.15;
|
|
695
|
-
if (mat === MAT.FUR) col = [col[0] * (1 + 0.18 * nz + 0.12 * cvh + sp * nz2), col[1] * (1 + 0.16 * nz + 0.1 * cvh + sp * nz2), col[2] * (1 + 0.12 * nz + 0.04 * cvh + sp * nz2)];
|
|
696
|
-
markSDF[v] = Math.min(MARK_FAR, mark);
|
|
697
|
-
furLen[v] = fl;
|
|
698
|
-
tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
|
|
699
|
-
surf[v * 4 + 2] = mat === MAT.FUR ? clamp(ag * (0.85 + 0.3 * nz2), 0, 1) : 0;
|
|
700
|
-
surf[v * 4 + 3] = mat === MAT.FUR ? under : 0;
|
|
701
|
-
if (mat === MAT.KERATIN) surf[v * 4] = 0.6;
|
|
702
|
-
// (nose leather: pebbled and matte, roughness 0.76: at the shader's default 0.42 it drew a white sky highlight in the
|
|
703
|
-
// showcase, at 0.62 a broad highlight across the top of a glossy dome; the paw pads dry and matte, 0.75)
|
|
704
|
-
// (inside the nostrils and round their rims rough: a satin sheen there drew each nostril as a glossy bowl)
|
|
705
|
-
if (mat === MAT.NOSE) surf[v * 4 + 1] = legBone[v] >= 0 && region[v] < 2 ? -0.75 : -mix(0.76, 0.97, Math.max(nostril, nrim));
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
// smooth fur length so the shells do not step (bare skin and the hairless nostril walls stay locked)
|
|
709
|
-
smoothField(furLen, weights.neighbors, 3, (v) => tint[v * 4 + 3] > 0 || bareFur[v] === 1);
|
|
710
|
-
// (the fur-slope limit lets hair painted over 25 mm rise proportionally faster; head hair painted just over it and
|
|
711
|
-
// cut back under it kept that allowance and stood 1.1-1.2 mm per mm above the forehead hair at the ear bases, fur
|
|
712
|
-
// walls: on the head, hair of 25-46 mm (the crown, the ear bases) rises at 1 mm per mm like shorter hair, and the ear's rim fringe is left as
|
|
713
|
-
// painted only where it is over 25 mm)
|
|
714
|
-
// (likewise on the feet and wrists, where the 3.5-5 cm hair of the lower leg meets the short hair of the toes)
|
|
715
|
-
for (let v = 0; v < nV; v++) {
|
|
716
|
-
const r = region[v];
|
|
717
|
-
if (r !== 2 && r !== 5 && r !== 6 && !(legBone[v] >= 0 && pos[v * 3 + 1] < 0.15)) continue;
|
|
718
|
-
const L = furLen[v];
|
|
719
|
-
if (slopeV[v] === Infinity) { if (L < 0.025) slopeV[v] = 1; continue; }
|
|
720
|
-
if (L > 0.025 && L < 0.046) slopeV[v] = Math.min(slopeV[v], 0.025 / L);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
const pattern = new Float32Array(nV).fill(1);
|
|
724
|
-
const patternColor = new Float32Array(nV * 4);
|
|
725
|
-
return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, region, furSlope: slopeV };
|
|
726
|
-
}
|