@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,730 +0,0 @@
|
|
|
1
|
-
// The pig's covering: bare skin (material 4, subsurface-lit) under sparse bristles (short thin hair
|
|
2
|
-
// shells: 10-30 mm on the body, longer along the dorsal midline, the jowls and the tail tip, fine on
|
|
3
|
-
// the ears), per breed:
|
|
4
|
-
// largewhite / landrace pink skin, white bristles, flushed ears, snout and belly
|
|
5
|
-
// duroc red (golden to mahogany) skin and bristles
|
|
6
|
-
// hampshire black with a white belt over the shoulders and both forelegs
|
|
7
|
-
// berkshire black with six white points (feet, face blaze and snout, tail tip)
|
|
8
|
-
// spotted pink with seeded black spots (Gloucester Old Spot / Pietrain)
|
|
9
|
-
// the rostral disc (moist pink or slate skin, dark nostrils), long lashes on the upper lids, pale or
|
|
10
|
-
// dark horn claws, ivory tusks, the teats, skin creases on the neck and behind the joints (darkened
|
|
11
|
-
// tint), seeded mud on outdoor pigs, and bristle flow nose -> tail, down the legs, back from the
|
|
12
|
-
// snout on the face.
|
|
13
|
-
import { HEAD_O, HZ, HY, TAIL_SEGS, SNOUT_K } from './rig.js';
|
|
14
|
-
import { neckS } from './regions.js';
|
|
15
|
-
import { EYE, tuskCones } from './sculpt.js';
|
|
16
|
-
import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
|
|
17
|
-
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
18
|
-
import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
|
|
19
|
-
import { srgb, mix3, MAT, smoothField, projectToSurface } from '../../core/build/coatKit.js';
|
|
20
|
-
|
|
21
|
-
// sRGB swatches sampled from the reference photos. Pink skin is
|
|
22
|
-
// given as albedo (a little deeper than the lit photo values).
|
|
23
|
-
// (the white breeds paler and less saturated, s 0.15 (was 0.20, #ECC4BC): lw_belagro3 in sun reads
|
|
24
|
-
// s 0.11-0.14 lit and a mauve grey in shade; the showcase's tone mapping saturates a dim pink, and the old albedo
|
|
25
|
-
// turned terracotta to maroon wherever the sun did not reach)
|
|
26
|
-
export const COATS = {
|
|
27
|
-
pink: { body: 0xf2d6ce, dorsal: 0xf4ddd6, belly: 0xf0cdc5, flush: 0xeab2aa, ear: 0xeec2ba, disc: 0xe4aca6, hoof: 0xc8ab98, bristle: 0xf2ece2, dark: false },
|
|
28
|
-
// (the Duroc less saturated, s 0.49 (was 0.58), a touch lighter: lit it still matches du_boar7 /
|
|
29
|
-
// du_belagro1 / head_duroc_sau (133-153, 89-116, 73-108; s 0.29-0.45), and in shade, where the showcase's tone
|
|
30
|
-
// mapping crushes a dim red, the face under the ear read a near-black maroon, s 0.8, v 0.13, the eye lost in it)
|
|
31
|
-
red: { body: 0x9e6a58, dorsal: 0x94604f, belly: 0xa87462, flush: 0x9e6250, ear: 0x98604f, disc: 0xa87a6a, hoof: 0x4a3a32, bristle: 0xb87a48, dark: false },
|
|
32
|
-
// (the black breeds' charcoal skin, bk_porco / bk_boar: (30-80) sRGB over the body in daylight, median
|
|
33
|
-
// ~(50, 45, 50); #201D1D rendered (3-8) lit in the showcase, #3A3434 (18), a black hole of a face with the eye lost in it)
|
|
34
|
-
black: { body: 0x504847, dorsal: 0x413a3a, belly: 0x524948, flush: 0x524544, ear: 0x4a3f3f, disc: 0x5a4e4e, hoof: 0x3a3330, bristle: 0x2c2727, dark: true },
|
|
35
|
-
};
|
|
36
|
-
const WHITE = 0xe8d6cc; // white points / belt: white bristles over pink skin
|
|
37
|
-
// (tusks: ivory, the tip whitest (khaki in studio light at #F2EADB / #D9C9A8 under the keratin striations);
|
|
38
|
-
// mud: a warm grey-brown, wet #857060 to dry #A89C8C (threequarter2, face3; #8F8068 read olive))
|
|
39
|
-
const NOSTRIL = 0x5a3230, TEAT = 0xd49a8e, TUSK = 0xfbf6ec, TUSK_ROOT = 0xece2cc, MUD = 0x857060, MUD_DRY = 0xa89c8c;
|
|
40
|
-
// the mouth line (head-local): its half width on each surface, and where it starts to fade toward the corner
|
|
41
|
-
const LIP_W = 0.0008, LIP_Z0 = 0.045;
|
|
42
|
-
// inside the mouth (palate, gums, the inside of the lips): moist pink-red mucosa
|
|
43
|
-
// (a little less saturated: the inside of the lower lip read as a lipstick-red ring round the open mouth)
|
|
44
|
-
const MUCOSA = srgb(0x9a5552), TONGUE = 0xc47a72;
|
|
45
|
-
|
|
46
|
-
const BASE_OF = { largewhite: 'pink', landrace: 'pink', duroc: 'red', hampshire: 'black', berkshire: 'black', spotted: 'pink' };
|
|
47
|
-
// meshing cell per surface at the hero tier (regions.js)
|
|
48
|
-
const CELL = { body: 0.0078, head: 0.0041, feet: 0.0035, jaw: 0.003, tail: 0.002 };
|
|
49
|
-
|
|
50
|
-
function palette(p) {
|
|
51
|
-
const C = COATS[BASE_OF[p.variant] || 'pink'];
|
|
52
|
-
const k = p.coatShade || 0, l = p.coatLightness || 0;
|
|
53
|
-
const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.06 * k + l), c[1] * (1 + l), c[2] * (1 - 0.05 * k + l)]; };
|
|
54
|
-
const out = {};
|
|
55
|
-
for (const [key, v] of Object.entries(C)) out[key] = typeof v === 'number' ? tw(v) : v;
|
|
56
|
-
out.white = srgb(WHITE);
|
|
57
|
-
out.spot = srgb(0x3a3436); // (Gloucester Old Spot: slate-black skin under black hair)
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const mul3 = (a, b) => [a[0] * b[0], a[1] * b[1], a[2] * b[2]];
|
|
62
|
-
const segSD2 = (px, py, ax, ay, bx, by) => {
|
|
63
|
-
const dx = bx - ax, dy = by - ay, t = clamp(((px - ax) * dx + (py - ay) * dy) / (dx * dx + dy * dy), 0, 1);
|
|
64
|
-
return [Math.hypot(px - ax - dx * t, py - ay - dy * t), t];
|
|
65
|
-
};
|
|
66
|
-
// The veins on the back of an ear leaf, in leaf units (u 0 root .. 1 tip along the ear bone, w across, out from the
|
|
67
|
-
// midrib, both / the bone's length): signed distance to the nearest vein's edge (< 0 inside). A central vein and two
|
|
68
|
-
// marginal ones fanning out from the root, each with one or two branches toward the rim; widths 1.8 -> 0.5 % of the
|
|
69
|
-
// length (3.6 -> 1 mm wide on a 0.2 m ear), tapering; a little seeded variation per pig.
|
|
70
|
-
const VEINS = [
|
|
71
|
-
// [u0, w0, u1, w1, width0, width1]
|
|
72
|
-
[0.04, 0.0, 0.5, 0.02, 0.009, 0.006], [0.5, 0.02, 0.88, 0.0, 0.006, 0.0025],
|
|
73
|
-
[0.06, 0.05, 0.42, 0.17, 0.008, 0.0055], [0.42, 0.17, 0.8, 0.22, 0.0055, 0.0025],
|
|
74
|
-
[0.06, -0.05, 0.4, -0.15, 0.008, 0.0055], [0.4, -0.15, 0.78, -0.2, 0.0055, 0.0025],
|
|
75
|
-
[0.28, 0.12, 0.5, 0.29, 0.004, 0.002], [0.55, 0.19, 0.7, 0.31, 0.0035, 0.0018],
|
|
76
|
-
[0.3, -0.12, 0.52, -0.27, 0.004, 0.002], [0.6, -0.18, 0.72, -0.29, 0.0035, 0.0018],
|
|
77
|
-
[0.62, 0.015, 0.8, 0.11, 0.003, 0.0016], [0.66, 0.01, 0.82, -0.1, 0.003, 0.0016],
|
|
78
|
-
];
|
|
79
|
-
function earVeinSD(u, w, seed) {
|
|
80
|
-
const j = ((seed % 13) / 13 - 0.5) * 0.04;
|
|
81
|
-
let d = 1;
|
|
82
|
-
for (const [u0, w0, u1, w1, r0, r1] of VEINS) {
|
|
83
|
-
const [dd, t] = segSD2(u, w, u0, w0 * (1 + j * 3), u1, w1 * (1 + j * 3) + j);
|
|
84
|
-
d = Math.min(d, dd - 0.8 * (r0 + (r1 - r0) * t));
|
|
85
|
-
}
|
|
86
|
-
return d;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function pigCoat(ctx) {
|
|
90
|
-
const { model, rig, pos, nrm, regionOf: partOf, regionNames, weights, nV, params, lists } = ctx;
|
|
91
|
-
// lashes only where the shells are fine enough to draw them (hero, high): at medium and low 8-16
|
|
92
|
-
// shells draw a pale halo over the eye
|
|
93
|
-
const lashes = !ctx.Q || ctx.Q.res <= 1.35;
|
|
94
|
-
const { BONES } = rig;
|
|
95
|
-
const J = rig.J;
|
|
96
|
-
const COL = palette(params);
|
|
97
|
-
const piglet = params.age === 'juvenile';
|
|
98
|
-
const breed = params.variant || 'largewhite';
|
|
99
|
-
const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
|
|
100
|
-
const AX_LEN = weights.axialLengths;
|
|
101
|
-
const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
102
|
-
const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
|
|
103
|
-
const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
|
|
104
|
-
const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
|
|
105
|
-
const eyeFrames = [1, -1].map((s) => eyeFrameOf(EYE, HEAD_O, s));
|
|
106
|
-
const jawPrims = model.forPart('jaw');
|
|
107
|
-
const headPrims = model.forPart('body').filter((p) => (p.bone === 'head' || p.bone === 'snout') && !p.carve);
|
|
108
|
-
const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril');
|
|
109
|
-
// tusks: root and tip per side (the colour runs from a creamy base to the white point along the tusk)
|
|
110
|
-
const tuskSegs = (params.tusks || 0) > 0.05 ? [1, -1].map((s) => tuskCones(params, s)) : [];
|
|
111
|
-
const tuskEnds = (params.tusks || 0) > 0.05 ? [1, -1].map((s) => { const C = tuskCones(params, s); return { root: C[0].a, tip: C[C.length - 1].b }; }) : [];
|
|
112
|
-
const JAW_BONE = rig.BONE.jaw ? rig.BONE.jaw.index : -1;
|
|
113
|
-
const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
|
|
114
|
-
const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(b.name));
|
|
115
|
-
const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
|
|
116
|
-
const rname = regionNames;
|
|
117
|
-
|
|
118
|
-
// --- nearest primitive tag per vertex
|
|
119
|
-
const tagOf = new Array(nV);
|
|
120
|
-
for (let v = 0; v < nV; v++) {
|
|
121
|
-
const L = lists[v], p = P(v);
|
|
122
|
-
let best = 1e9, t = '';
|
|
123
|
-
for (const pr of L) {
|
|
124
|
-
if (pr.carve) continue;
|
|
125
|
-
const d = SDFModel.dist(pr, p[0], p[1], p[2]);
|
|
126
|
-
if (d < best) { best = d; t = pr.tag; }
|
|
127
|
-
}
|
|
128
|
-
tagOf[v] = t;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// --- regions: 0 torso / legs, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 snout disc
|
|
132
|
-
const region = new Uint8Array(nV);
|
|
133
|
-
const legness = new Float32Array(nV);
|
|
134
|
-
const legBone = new Int16Array(nV).fill(-1);
|
|
135
|
-
const tailT = new Float32Array(nV); // 0 root .. 1 tip
|
|
136
|
-
for (let v = 0; v < nV; v++) {
|
|
137
|
-
const bn = BONES[dominant[v]].name, p = P(v), rn = rname[partOf[v]];
|
|
138
|
-
let lb = -1, lw = 0;
|
|
139
|
-
for (let k = 0; k < 4; k++) {
|
|
140
|
-
const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
|
|
141
|
-
if (isLimb[b] && w > lw) { lw = w; lb = b; }
|
|
142
|
-
}
|
|
143
|
-
legBone[v] = lb;
|
|
144
|
-
legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
|
|
145
|
-
if (rn === 'jaw' || rn === 'tuskL' || rn === 'tuskR') region[v] = 6;
|
|
146
|
-
else if (bn.startsWith('ear')) region[v] = 5;
|
|
147
|
-
// (and the floor of the nostrils, which the snout cone's vertices form under the head bone: painted as head skin
|
|
148
|
-
// it showed as pink patches deep in the Berkshire's nostrils)
|
|
149
|
-
else if (bn === 'snout' || tagOf[v] === 'disc' || tagOf[v] === 'snoutend' || nostrilPrims.some((e) => SDFModel.dist(e, p[0], p[1], p[2]) < 0.003)) region[v] = 7;
|
|
150
|
-
else if (rn === 'tail' || (bn.startsWith('tail') && axialS[v] > sTailBase + 0.015)) region[v] = 4;
|
|
151
|
-
else if (rn === 'feet' || (lb >= 0 && limbMember[v] > 0.5 && p[1] < 0.5)) region[v] = 0;
|
|
152
|
-
else if (axialS[v] < sOcc + 0.02 || (rn === 'head' && neckS(p[0], p[1], p[2]) > 0.03)) region[v] = 2;
|
|
153
|
-
else if (axialS[v] < sNeckBase) region[v] = 1;
|
|
154
|
-
else region[v] = 0;
|
|
155
|
-
if (region[v] >= 2) legness[v] = 0;
|
|
156
|
-
if (rn === 'feet') legness[v] = 1;
|
|
157
|
-
if (region[v] === 4) tailT[v] = clamp((axialS[v] - sTailBase) / Math.max(1e-3, AX_LEN[AX_LEN.length - 1] - sTailBase), 0, 1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// --- pattern: signed distance (m) to the second colour (< 0 inside), per breed
|
|
161
|
-
const R = rng(7717 + (params.coatSeed || 0));
|
|
162
|
-
const off = [R() * 100, R() * 100, R() * 100];
|
|
163
|
-
const toSD = (fn, p) => {
|
|
164
|
-
const e = 0.01, f0 = fn(p);
|
|
165
|
-
const gx = (fn([p[0] + e, p[1], p[2]]) - f0) / e, gy = (fn([p[0], p[1] + e, p[2]]) - f0) / e, gz = (fn([p[0], p[1], p[2] + e]) - f0) / e;
|
|
166
|
-
const g = Math.max(0.5, Math.hypot(gx, gy, gz));
|
|
167
|
-
return -f0 / g;
|
|
168
|
-
};
|
|
169
|
-
const rag = (p, f, a) => (fbm3(p[0] * f + off[0], p[1] * f + off[1], p[2] * f + off[2], 2) - 0.5) * a;
|
|
170
|
-
// spotted: seeded black blotches on the body, neck and head (Gloucester Old Spot: 4-12 spots, 5-15 cm;
|
|
171
|
-
// Pietrain: more, smaller): each one 2-3 overlapping lobes with a ragged, hairy outline, none on the snout.
|
|
172
|
-
// The centres are drawn in space and projected onto the sculpt, with a fixed number of random draws per
|
|
173
|
-
// candidate, so every quality tier gets the same spots (drawn from the mesh's vertex list, they moved
|
|
174
|
-
// when the tier changed); the nearest vertex only says whether a place may carry one.
|
|
175
|
-
const spots = [];
|
|
176
|
-
const offSnout = (v) => { if (region[v] === 7 || region[v] === 6) return false; if (region[v] !== 2) return true; return HL(P(v))[2] < 0.06; };
|
|
177
|
-
if (breed === 'spotted') {
|
|
178
|
-
const nSpots = params.spotCount ?? 8;
|
|
179
|
-
const body = model.forPart('body').filter((q) => !q.carve);
|
|
180
|
-
const cell = 0.025, grid = new Map(), gk = (x, y, z) => (Math.floor(x / cell) + 64) * 16384 + (Math.floor(y / cell) + 64) * 128 + (Math.floor(z / cell) + 64);
|
|
181
|
-
for (let v = 0; v < nV; v++) { const k = gk(pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]); let a = grid.get(k); if (!a) grid.set(k, (a = [])); a.push(v); }
|
|
182
|
-
const nearest = (p) => {
|
|
183
|
-
let best = -1, bd = 1e9;
|
|
184
|
-
for (let dx = -1; dx <= 1; dx++) for (let dy = -1; dy <= 1; dy++) for (let dz = -1; dz <= 1; dz++) {
|
|
185
|
-
const a = grid.get(gk(p[0] + dx * cell, p[1] + dy * cell, p[2] + dz * cell));
|
|
186
|
-
if (a) for (const v of a) { const d = (pos[v * 3] - p[0]) ** 2 + (pos[v * 3 + 1] - p[1]) ** 2 + (pos[v * 3 + 2] - p[2]) ** 2; if (d < bd) { bd = d; best = v; } }
|
|
187
|
-
}
|
|
188
|
-
return best;
|
|
189
|
-
};
|
|
190
|
-
// (off the neck and the limb junctions, whose skin stretches most when the legs swing and the head
|
|
191
|
-
// moves: a spot's edge there smeared; keep-outs from the joints, the same on every tier)
|
|
192
|
-
const allowed = (v) => v >= 0 && (region[v] === 0 || region[v] === 2) && legness[v] < 0.08 && tagOf[v] !== 'teat' && offSnout(v);
|
|
193
|
-
const segD = (p, a, b) => { const ab = sub(b, a), t = clamp(dot(sub(p, a), ab) / dot(ab, ab), 0, 1); return len(sub(p, add(a, mul(ab, t)))); };
|
|
194
|
-
const clearOf = (p, r) => {
|
|
195
|
-
if (p[2] > J.neckBase[2] - 0.03 && HL(p)[2] < -0.1) return false;
|
|
196
|
-
for (const S of ['L', 'R']) {
|
|
197
|
-
if (segD(p, J['scapTop' + S], J['elbow' + S]) < r + 0.1) return false;
|
|
198
|
-
if (segD(p, J['elbow' + S], add(J['elbow' + S], [0, 0.05, -0.16])) < r + 0.07) return false;
|
|
199
|
-
if (segD(p, J['hip' + S], J['knee' + S]) < r + 0.07) return false;
|
|
200
|
-
if (Math.hypot(...sub(p, J['knee' + S])) < r + 0.12) return false;
|
|
201
|
-
}
|
|
202
|
-
return true;
|
|
203
|
-
};
|
|
204
|
-
for (let i = 0; i < 400 && spots.length < nSpots; i++) {
|
|
205
|
-
const q = [(R() - 0.5) * 0.5, 0.3 + 0.55 * R(), -0.62 + 1.3 * R()];
|
|
206
|
-
const rr = R(), nl = 1 + Math.floor(R() * 2.2), L = Array.from({ length: 12 }, () => R());
|
|
207
|
-
const p = projectToSurface(body, q);
|
|
208
|
-
const v = nearest(p);
|
|
209
|
-
if (!allowed(v)) continue;
|
|
210
|
-
const r = (0.045 + 0.07 * rr) * (params.spotSize ?? 1) * (region[v] === 2 ? 0.55 : 1);
|
|
211
|
-
if (region[v] !== 2 && !clearOf(p, 1.3 * r)) continue;
|
|
212
|
-
if (spots.some((s) => Math.hypot(p[0] - s.p[0], p[1] - s.p[1], p[2] - s.p[2]) < (r + s.r) * 1.15)) continue;
|
|
213
|
-
const lobes = [{ c: p, r }];
|
|
214
|
-
for (let j = 0; j < nl; j++) {
|
|
215
|
-
const d = [L[j * 4] - 0.5, L[j * 4 + 1] - 0.5, L[j * 4 + 2] - 0.5], dl = Math.hypot(...d) || 1;
|
|
216
|
-
lobes.push({ c: add(p, mul(d, (0.55 * r) / dl)), r: r * (0.45 + 0.3 * L[j * 4 + 3]) });
|
|
217
|
-
}
|
|
218
|
-
spots.push({ p, r, lobes });
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const beltF = params.belt ? params.belt[0] : -0.055, beltB = params.belt ? params.belt[1] : 0.02;
|
|
222
|
-
// patternSD(v) < 0: the second colour (spots: black; belt / points: white)
|
|
223
|
-
const patternSD = (v) => {
|
|
224
|
-
const p = P(v), r = region[v], lb = legBone[v];
|
|
225
|
-
if (breed === 'spotted') {
|
|
226
|
-
// (none on the ears either: a spot running onto the pink leaf's root read as a dark smudge above the eye)
|
|
227
|
-
if (!offSnout(v) || tagOf[v] === 'teat' || r === 5) return 1;
|
|
228
|
-
let d = 1;
|
|
229
|
-
for (const s of spots) for (const l of s.lobes) d = Math.min(d, Math.hypot(p[0] - l.c[0], p[1] - l.c[1], p[2] - l.c[2]) - l.r);
|
|
230
|
-
// (a coarse wander of the outline and a fine hairy fray)
|
|
231
|
-
return d + rag(p, 12, 0.06) + rag(p, 45, 0.012);
|
|
232
|
-
}
|
|
233
|
-
if (breed === 'hampshire') {
|
|
234
|
-
if (r === 7 || r === 6 || r === 5 || r === 4) return 1;
|
|
235
|
-
// white belt: over the shoulders, down both forelegs and the chest, forward up the neck to the head
|
|
236
|
-
// (the head and ears black): the front edge is a plane across the head behind the eyes (head-local
|
|
237
|
-
// z = beltF), where the skin rides on the skull. On the neck, any edge sat in the skin that
|
|
238
|
-
// stretches most (a lying pig's neck bends sideways to lay its head down: 65-78 % of the edge
|
|
239
|
-
// triangles stretched > 1.5x, against 15 % here); the back edge at mid-ribcage (none stretched)
|
|
240
|
-
if (lb >= 0 && isFront[lb] && legness[v] > 0.4) return -0.03;
|
|
241
|
-
const zb = beltB + 0.05 * (p[1] - 0.45);
|
|
242
|
-
const h = HL(p);
|
|
243
|
-
// (and a black cap over the poll to behind the ears, so the ears stand in black skin)
|
|
244
|
-
const cap = Math.min(h[1] - 0.025, h[2] + 0.115);
|
|
245
|
-
const d = Math.max(Math.max(h[2] - beltF, cap), zb - p[2]);
|
|
246
|
-
return d + rag(p, 18, 0.03);
|
|
247
|
-
}
|
|
248
|
-
if (breed === 'berkshire') {
|
|
249
|
-
if (r === 7) return -0.02;
|
|
250
|
-
const h = r === 2 || r === 6 ? HL(p) : null;
|
|
251
|
-
if (h) {
|
|
252
|
-
// blaze: a white stripe down the face widening onto the snout
|
|
253
|
-
const w = 0.012 + 0.03 * smoothstep(0.05, 0.16, h[2]) * (params.blaze ?? 1);
|
|
254
|
-
// (the white snout's rear edge slants forward toward the chin: the jaw behind the mouth corner stays black)
|
|
255
|
-
const snoutEdge = 0.08 + 0.5 * Math.max(0, -0.03 - h[1]);
|
|
256
|
-
// (the blaze on the face only: on the jaw its midline drew a white bar down the chin)
|
|
257
|
-
return Math.min(r === 6 ? 1 : Math.abs(h[0]) - w, snoutEdge - h[2]) + rag(p, 30, 0.012);
|
|
258
|
-
}
|
|
259
|
-
if (r === 4) return (0.55 - tailT[v]) * 0.05 + rag(p, 40, 0.004);
|
|
260
|
-
if (lb >= 0 && legness[v] > 0.2) {
|
|
261
|
-
const top = isFront[lb] ? 0.13 : 0.14;
|
|
262
|
-
return p[1] - top * (params.socks ?? 1) + rag(p, 25, 0.03);
|
|
263
|
-
}
|
|
264
|
-
return 1;
|
|
265
|
-
}
|
|
266
|
-
return 1;
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
// --- ventral factor (lighter belly, inner legs)
|
|
270
|
-
const ventral = new Float32Array(nV);
|
|
271
|
-
for (let v = 0; v < nV; v++) {
|
|
272
|
-
const n = N(v), r = region[v], p = P(v);
|
|
273
|
-
let w = 0;
|
|
274
|
-
if (r <= 2 || r === 6) w = smoothstep(-0.2, -0.8, n[1]) * smoothstep(0.55, 0.35, p[1]);
|
|
275
|
-
ventral[v] = w;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// --- comb (bristle flow)
|
|
279
|
-
const comb = new Float32Array(nV * 3);
|
|
280
|
-
for (let v = 0; v < nV; v++) {
|
|
281
|
-
const p = P(v), n = N(v), r = region[v];
|
|
282
|
-
const bone = BONES[dominant[v]];
|
|
283
|
-
let d;
|
|
284
|
-
if (r === 5) d = norm(sub(bone.tail, bone.head));
|
|
285
|
-
else if (r === 2 || r === 6 || r === 7) {
|
|
286
|
-
// face: back from the snout, then down and back over the jowls
|
|
287
|
-
const h = HL(p);
|
|
288
|
-
d = norm(add(mul(HZ, -1), add(mul(HY, -0.35 * Math.sign(h[1] + 0.02)), [Math.sign(h[0]) * 0.25, 0, 0])));
|
|
289
|
-
if (h[2] < -0.04) d = norm(add(d, [0, -0.4, -0.5]));
|
|
290
|
-
} else if (r === 4) {
|
|
291
|
-
d = norm(sub(bone.tail, bone.head));
|
|
292
|
-
} else {
|
|
293
|
-
const seg = weights.segT[v * 2] | 0;
|
|
294
|
-
const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
|
|
295
|
-
d = norm(sub(b, a));
|
|
296
|
-
if (r === 0) d = norm(add(d, [0, -0.5 * Math.abs(n[0]), 0]));
|
|
297
|
-
if (r === 1) d = norm(add(d, [0, -0.6, 0.1]));
|
|
298
|
-
const L = legness[v];
|
|
299
|
-
if (L > 0 && legBone[v] >= 0) {
|
|
300
|
-
const lbn = BONES[legBone[v]];
|
|
301
|
-
let dl = norm(sub(lbn.tail, lbn.head));
|
|
302
|
-
if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
|
|
303
|
-
else dl = norm(add(dl, [0, -0.3, 0]));
|
|
304
|
-
d = norm(add(mul(d, 1 - L), mul(dl, L)));
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
// tusks: combed along the tusk (the keratin shader's striations run along the comb: combed like the face they
|
|
308
|
-
// ran across the tusk as crinkled bark rings)
|
|
309
|
-
const rnv = rname[partOf[v]];
|
|
310
|
-
if ((rnv === 'tuskL' || rnv === 'tuskR') && tuskSegs.length) {
|
|
311
|
-
let best = Infinity;
|
|
312
|
-
for (const sg of tuskSegs[rnv === 'tuskL' ? 0 : 1] || tuskSegs[0]) {
|
|
313
|
-
const ab = sub(sg.b, sg.a), u = clamp(dot(sub(p, sg.a), ab) / Math.max(1e-9, dot(ab, ab)), 0, 1);
|
|
314
|
-
const dd = len(sub(p, add(sg.a, mul(ab, u))));
|
|
315
|
-
if (dd < best) { best = dd; d = norm(ab); }
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
let t = sub(d, mul(n, dot(d, n)));
|
|
319
|
-
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
320
|
-
t = norm(t);
|
|
321
|
-
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// --- per-vertex fields
|
|
325
|
-
const pattern = new Float32Array(nV).fill(1);
|
|
326
|
-
const patInt = new Float32Array(nV);
|
|
327
|
-
const patCol = new Float32Array(nV * 3);
|
|
328
|
-
const markSDF = new Float32Array(nV).fill(1);
|
|
329
|
-
const furLen = new Float32Array(nV);
|
|
330
|
-
const tint = new Float32Array(nV * 4);
|
|
331
|
-
const surf = new Float32Array(nV * 4);
|
|
332
|
-
const mud = params.mud || 0;
|
|
333
|
-
// (bristles: at medium and low 8-16 shells draw each one as a dot, a pepper over the skin: shorter there)
|
|
334
|
-
// (a piglet's fine hair about as long as an adult's bristles: at 0.45x, 1.3-1.5 mm, every shell drew it at the
|
|
335
|
-
// same place and it piled up into a crisp white speck: 9.5 % bright specks on a piglet's cheek; it
|
|
336
|
-
// is also finer and denser, index.js render())
|
|
337
|
-
const bristleK = (params.bristle ?? 1) * (piglet ? 0.9 : 1) * (breed === 'duroc' ? 1.15 : 1) * (ctx.Q && ctx.Q.res > 1.5 ? 0.5 : 1);
|
|
338
|
-
const dark = COL.dark;
|
|
339
|
-
const pink = !dark && BASE_OF[breed] === 'pink';
|
|
340
|
-
// satin-matte skin under the bristles (0.3 read as vinyl on pink and latex on black skin; black 0.08 still
|
|
341
|
-
// made bright specular sheets in the sun)
|
|
342
|
-
// (black skin below 0: the shader reads a bare skin's gloss only for its roughness, mix(0.62, 0.3, gloss), so
|
|
343
|
-
// -0.4 is roughness 0.75, a dull matte black; at 0.03 (0.61) a Berkshire in the sun showed two broad grey sheen
|
|
344
|
-
// patches on shoulder and rump)
|
|
345
|
-
// (pink and red skin smooth and slightly glossy)
|
|
346
|
-
const SKIN_GLOSS = dark ? -0.4 : pink ? 0.24 : 0.2;
|
|
347
|
-
const SPOT_GLOSS = -0.3; // (the Old Spot's dark spots: the black breeds' matte, roughness 0.71) // (the Duroc's red skin under its denser red hair: satin, 0.24 read as leather)
|
|
348
|
-
// the rostral disc: its colour blends in along the head axis over the last 2 cm before the plate's face
|
|
349
|
-
// (a per-vertex switch at the snout bone's region drew a jagged edge)
|
|
350
|
-
const snoutK = (params.snoutLen ?? 1) * SNOUT_K;
|
|
351
|
-
const DZ = 0.06 + (0.178 - 0.06) * snoutK;
|
|
352
|
-
const discOf = (h) => smoothstep(DZ - 0.02, DZ - 0.002, h[2]);
|
|
353
|
-
|
|
354
|
-
for (let v = 0; v < nV; v++) {
|
|
355
|
-
const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
|
|
356
|
-
// (sy: surf.y, for bare skin the share of the light that shines through when lit from behind: index.js
|
|
357
|
-
// render.skinTrans; 0 on the thick body)
|
|
358
|
-
let col, mat = MAT.SKIN, fl = 0.012, gloss = SKIN_GLOSS, sy = 0, lashW = 0, nostrilK = 0, nostrilCol = null, veinSD = 1, veinCol = null, earRimK = 0;
|
|
359
|
-
const lb = legBone[v];
|
|
360
|
-
const lbName = lb >= 0 ? BONES[lb].name : '';
|
|
361
|
-
const up = clamp(n[1], -1, 1);
|
|
362
|
-
if (r === 0 || r === 1) {
|
|
363
|
-
col = mix3(COL.body, COL.dorsal, smoothstep(0.3, 0.95, up) * 0.7);
|
|
364
|
-
col = mix3(col, COL.belly, ventral[v]);
|
|
365
|
-
// bristles: longer along the dorsal midline (the "mane" of the back), shorter low down
|
|
366
|
-
// (fine bristles lying along the skin, long enough for the silhouette fins (>= 5.8 mm):
|
|
367
|
-
// fine sparse pale bristles visible mostly in silhouette and up close)
|
|
368
|
-
fl = mix(0.0065, 0.01, smoothstep(0.75, 0.97, up) * smoothstep(0.05, 0.015, Math.abs(p[0])));
|
|
369
|
-
fl = mix(fl, 0.004, ventral[v]);
|
|
370
|
-
const L = legness[v];
|
|
371
|
-
if (L > 0) {
|
|
372
|
-
fl = mix(fl, mix(0.0025, 0.004, smoothstep(0.12, 0.4, p[1])), L);
|
|
373
|
-
// lower legs a touch more flushed (thin skin over the tendons)
|
|
374
|
-
if (pink) col = mix3(col, COL.flush, 0.25 * smoothstep(0.3, 0.1, p[1]) * L);
|
|
375
|
-
}
|
|
376
|
-
// hooves, heel bulbs and dew claws below the coronet line
|
|
377
|
-
const Cj = /hoof|paw/.test(lbName) ? J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side] : null;
|
|
378
|
-
if (Cj && (tag === 'hoof' || tag === 'heelbulb' || tag === 'dewclaw') && (tag === 'dewclaw' || p[1] < Cj[1] + 0.009 + 0.3 * (p[2] - Cj[2]) * (lbName[0] === 'f' ? 1 : 1))) {
|
|
379
|
-
mat = MAT.KERATIN; fl = 0; gloss = -0.3;
|
|
380
|
-
const streak = smoothstep(0.45, 0.65, fbm3(p[0] * 140 + (params.coatSeed % 97), p[1] * 9, p[2] * 140, 2));
|
|
381
|
-
col = pink ? mix3(COL.hoof, mul(COL.hoof, 0.55), 0.35 * streak) : mix3(COL.hoof, mul(COL.hoof, 1.5), 0.3 * streak);
|
|
382
|
-
if (tag === 'heelbulb') col = mix3(col, pink ? srgb(0xc89c8a) : mul(col, 0.8), 0.5);
|
|
383
|
-
}
|
|
384
|
-
if (tag === 'teat') { col = pink ? srgb(TEAT) : mix3(COL.body, srgb(0x6a5050), 0.3); fl = 0; gloss = 0.4; }
|
|
385
|
-
if (tag === 'scrotum' || tag === 'sheath') { col = mix3(col, COL.flush, pink ? 0.35 : 0.1); fl = 0.005; }
|
|
386
|
-
} else if (r === 4) {
|
|
387
|
-
col = mix3(COL.body, COL.dorsal, 0.4);
|
|
388
|
-
fl = 0.004 + 0.02 * smoothstep(0.8, 1, tailT[v]); // the terminal tuft
|
|
389
|
-
sy = 0.3 + 0.3 * tailT[v]; // (thin: the light shines through it, render.skinTrans)
|
|
390
|
-
if (pink) col = mix3(col, COL.flush, 0.15);
|
|
391
|
-
} else if (r === 5) {
|
|
392
|
-
// ears: thin skin, flushed and translucent in pink pigs (inner pinna redder), fine short hair
|
|
393
|
-
// (the hollow side: the nearest leaf tile's normal, which earShape points into the hollow)
|
|
394
|
-
let dn = 1e9, tile = null;
|
|
395
|
-
for (const q of lists[v]) if (q.tag === 'ear') { const d = Math.abs(SDFModel.dist(q, p[0], p[1], p[2])); if (d < dn) { dn = d; tile = q; } }
|
|
396
|
-
const faceDir = tile ? [tile.P[9], tile.P[10], tile.P[11]] : [0, 0, 1];
|
|
397
|
-
const inner = dot(n, faceDir) > 0.2;
|
|
398
|
-
// how far out along the ear (0 root .. 1 tip)
|
|
399
|
-
const eb = BONES[dominant[v]], ev = sub(eb.tail, eb.head);
|
|
400
|
-
const sE = clamp(dot(sub(p, eb.head), ev) / Math.max(1e-6, dot(ev, ev)), 0, 1);
|
|
401
|
-
// (pink ears, face1 / front1 / face2: the inner pinna a light, bright pink, redder deep in the hollow at
|
|
402
|
-
// the base (the research's backlit #E8998C); the renderer's red subsurface darkens it in shade, so the
|
|
403
|
-
// albedo is kept light: sampled #E8998C everywhere rendered brick red)
|
|
404
|
-
// (the colour switched at a fixed angle on the rim, which drew the rim as a cut edge round a
|
|
405
|
-
// slab; now graded across the rounded rim, so the two faces turn into each other)
|
|
406
|
-
const inCol = pink ? mix3(srgb(0xe8a098), srgb(0xf2bcb2), smoothstep(0.08, 0.55, sE)) : mul(mix3(COL.ear, COL.flush, 0.1), dark ? 1 : 1.06);
|
|
407
|
-
const outCol = mix3(COL.ear, COL.body, 0.5);
|
|
408
|
-
const kin = smoothstep(-0.45, 0.6, dot(n, faceDir));
|
|
409
|
-
col = mix3(outCol, inCol, kin);
|
|
410
|
-
// (short, skin-coloured hair in the hollow: longer pale hair there frosted the pink hollow with white
|
|
411
|
-
// specks in the studio light, hero)
|
|
412
|
-
// (and almost none in the hollow itself: at hero the key light's strand glints frosted the pink hollow white, and the
|
|
413
|
-
// Duroc's sparser, coarser hair drew a grey speckle over it; the rim fringe below stays)
|
|
414
|
-
fl = mix(0.003, 0.0022, kin) * (1 - 0.85 * kin);
|
|
415
|
-
// (a soft fringe of longer hair along the rim, where the leaf's surface turns edge-on: a thin hard outline
|
|
416
|
-
// read as cut paper; face3's Duroc ears are hairy at the edge)
|
|
417
|
-
const rimK = 1 - smoothstep(0.3, 0.7, Math.abs(dot(n, faceDir)));
|
|
418
|
-
// (a shorter fringe; the 5.5 mm rim hair drew the edge as a frayed slab)
|
|
419
|
-
// (shorter again, 3 / 4.5 mm: the Duroc's 6.5 mm red-gold fringe and hollow hair drew a frosted, torn edge)
|
|
420
|
-
// (the Duroc's 4.5 mm fringe, in strands darker than the skin, drew the rim as a torn edge with a
|
|
421
|
-
// dark speckle in the showcase; 3.2 mm in the body's red-gold)
|
|
422
|
-
// (almost none on the pink ears: their fine pale fringe, seen against a shaded head or the sky, drew a
|
|
423
|
-
// speckled frayed rim on the Large White and Landrace in the showcase; lw_show / head_gifu show a clean rim)
|
|
424
|
-
fl = mix(fl, breed === 'duroc' ? 0.0032 : pink ? 0.0012 : 0.003, rimK);
|
|
425
|
-
earRimK = rimK;
|
|
426
|
-
// veins on the back of the leaf (pink ears: head_gifu, head_hausschwein5, lw_show: a few dark-pink veins from
|
|
427
|
-
// the root toward the tip, branching outward; they show most against the light, render.skinTrans): drawn in
|
|
428
|
-
// the pattern channel (per pixel: per-vertex paint on the 3 mm ear cells came out as blobs)
|
|
429
|
-
if (pink && kin < 0.2 && rimK < 0.3) {
|
|
430
|
-
const evn = norm(ev), Le = len(ev), lat = norm(cross(evn, faceDir));
|
|
431
|
-
const q = sub(p, eb.head), u = dot(q, evn) / Le;
|
|
432
|
-
const w = dot(q, lat) / Le + 0.008 * (fbm3(p[0] * 35 + off[0], p[1] * 35, p[2] * 35, 2) - 0.5);
|
|
433
|
-
veinSD = earVeinSD(u, w * (p[0] > 0 ? 1 : -1), params.coatSeed || 0) * Le;
|
|
434
|
-
veinCol = mul3(col, [0.9, 0.74, 0.78]);
|
|
435
|
-
// (the back of the leaf nearly hairless: the tint here is the vein's colour, and the shells draw hair in it)
|
|
436
|
-
fl = 0;
|
|
437
|
-
}
|
|
438
|
-
gloss = dark ? 0.08 : 0.12;
|
|
439
|
-
// thin skin: the light shines through the leaf, most toward the tip (render.skinTrans; research 6: "ears
|
|
440
|
-
// are thin and translucent, strong red backlight")
|
|
441
|
-
// (above 0.9 the skin shader also passes the light that falls on the other face through the leaf,
|
|
442
|
-
// tinted by render.skinScatter: an ear turned away from the sun glows pink instead of the dark maroon of the
|
|
443
|
-
// shade; at 0.35-0.8 only the view-dependent back-light term applied)
|
|
444
|
-
// (only the pink ears: the Duroc's red and the black breeds' pigmented leaves passed the sun through
|
|
445
|
-
// as a pale grey-pink hollow, a pale speckled strip along the rim seen edge-on; they keep the back-light glow only)
|
|
446
|
-
sy = pink ? mix(0.92, 1.0, smoothstep(0.1, 0.8, sE)) : mix(dark ? 0.3 : 0.5, dark ? 0.5 : 0.75, smoothstep(0.1, 0.8, sE));
|
|
447
|
-
} else if (r === 7) {
|
|
448
|
-
// rostral disc: moist, glossy, finely pitted; the snout skin behind it blends in along the head axis.
|
|
449
|
-
// Skin material throughout (nose leather next to skin interpolated through the lid and mouth ids:
|
|
450
|
-
// a red ring round a dark disc)
|
|
451
|
-
const h = HL(p);
|
|
452
|
-
const dw = discOf(h);
|
|
453
|
-
const face = dw * smoothstep(0.2, 0.6, dot(n, HZ));
|
|
454
|
-
const snoutCol = mix3(mix3(COL.body, COL.dorsal, smoothstep(0.3, 0.95, up) * 0.7), COL.belly, ventral[v]);
|
|
455
|
-
col = mix3(pink ? mix3(snoutCol, COL.flush, 0.25) : snoutCol, mix3(COL.disc, pink ? COL.flush : COL.disc, 0.2), dw);
|
|
456
|
-
fl = mix(0.003, 0, dw);
|
|
457
|
-
gloss = mix(SKIN_GLOSS, 0.6, face);
|
|
458
|
-
let nk = 0;
|
|
459
|
-
for (const e of nostrilPrims) {
|
|
460
|
-
const dn = SDFModel.dist(e, p[0], p[1], p[2]);
|
|
461
|
-
if (dn < 0.002) nk = Math.max(nk, smoothstep(0.002, 0.0002, dn));
|
|
462
|
-
// (and whatever lies recessed beside it: the carve's fillet at the inner upper end left a patch of disc colour
|
|
463
|
-
// inside the opening)
|
|
464
|
-
if (dn < 0.006) nk = Math.max(nk, smoothstep(0.001, 0.004, DZ + 0.012 - h[2]) * smoothstep(0.006, 0.002, dn));
|
|
465
|
-
}
|
|
466
|
-
if (nk > 0) {
|
|
467
|
-
// (darker with depth: the floor of a nostril, lit straight on, read as the pale centre of a dark ring; the
|
|
468
|
-
// nostril's colour also goes into the marking tint below, the Berkshire's white snout covered them as
|
|
469
|
-
// pale lilac dimples)
|
|
470
|
-
const depth = DZ + 0.012 - h[2];
|
|
471
|
-
const deep = smoothstep(0.002, 0.014, depth);
|
|
472
|
-
nostrilCol = mix3(dark ? mul(COL.disc, 0.4) : srgb(NOSTRIL), dark ? srgb(0x140a0a) : srgb(0x2a1214), deep);
|
|
473
|
-
// (matt in the depth: a glossy floor caught the light as a pale glint in the middle of the pit)
|
|
474
|
-
col = mix3(col, nostrilCol, nk); gloss = mix(gloss, dark ? -0.4 : mix(0.1, -0.2, deep), nk); fl *= 1 - nk; nostrilK = nk;
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
// head & jaw
|
|
478
|
-
const h = HL(p);
|
|
479
|
-
col = mix3(mix3(COL.body, COL.dorsal, smoothstep(0.3, 0.95, up) * 0.7), COL.belly, ventral[v]);
|
|
480
|
-
fl = 0.003; // (fine short hair on the face)
|
|
481
|
-
// bristly jowls and cheeks; short hair on the snout
|
|
482
|
-
if (h[2] < 0.02 && h[1] < -0.02) fl = mix(fl, 0.007, smoothstep(-0.02, -0.07, h[1]));
|
|
483
|
-
fl = mix(fl, 0.003, smoothstep(0.08, 0.16, h[2]));
|
|
484
|
-
if (pink) col = mix3(col, COL.flush, 0.25 * smoothstep(0.06, 0.17, h[2]));
|
|
485
|
-
if (r === 2) col = mix3(col, mix3(COL.disc, pink ? COL.flush : COL.disc, 0.2), discOf(h));
|
|
486
|
-
// eye rims (lid skin, no hair: a pink or dark margin, face1) and the pale lashes of the upper lid
|
|
487
|
-
for (let e = 0; e < eyePrims.length; e++) {
|
|
488
|
-
const pr = eyePrims[e], ec = eyeFrames[e].c;
|
|
489
|
-
if (Math.hypot(p[0] - ec[0], p[1] - ec[1], p[2] - ec[2]) > 0.03) continue;
|
|
490
|
-
const de = Math.abs(SDFModel.dist(pr, p[0], p[1], p[2]));
|
|
491
|
-
// (the lid margin a pink rim and the skin round the eye a little flushed on the pink breeds: front1, face2)
|
|
492
|
-
if (pink && de < 0.014) col = mix3(col, COL.flush, 0.42 * (1 - smoothstep(0.003, 0.014, de)));
|
|
493
|
-
if (de < 0.003) { fl = 0; col = dark ? mul(COL.body, 0.6) : mix3(col, COL.flush, 0.55 * (1 - smoothstep(0.002, 0.003, de)) + 0.25); gloss = 0.5; }
|
|
494
|
-
else if (de < 0.0115) {
|
|
495
|
-
const ef = eyeFrames[p[0] > 0 ? 0 : 1];
|
|
496
|
-
const rel = sub(p, ef.c);
|
|
497
|
-
const upAmt = dot(rel, ef.y) / 0.011;
|
|
498
|
-
// short fine hair on the lids, growing from the margin (no step at the rim)
|
|
499
|
-
fl = Math.min(fl, mix(0.0012, 0.004, smoothstep(0.002, 0.0095, de)));
|
|
500
|
-
if (lashes && upAmt > 0.15) {
|
|
501
|
-
// lashes: a pale fringe standing up and out from the upper lid (face1), longest mid-lid and
|
|
502
|
-
// tapered to both corners; they rise from the margin no faster than the skin runs (shells
|
|
503
|
-
// cannot draw single lashes: a long band combed over the eye read as a fuzzy blob). They
|
|
504
|
-
// stay bristles on the skin material (a fur-material band drew a black stepped outline: the
|
|
505
|
-
// interpolated material id passes 1-3 between fur 0 and skin 4), their colour from the tint
|
|
506
|
-
// (also tapered toward the corners across the eye: past the corners the lid patch rises
|
|
507
|
-
// above upAmt 0.15 too, and a tuft of long pale lashes there caught the back light as a white fleck)
|
|
508
|
-
const xc = Math.abs(dot(rel, ef.x));
|
|
509
|
-
const along = smoothstep(0.15, 0.55, upAmt) * (1 - smoothstep(0.004, 0.0095, xc));
|
|
510
|
-
// (up to 7.5 mm mid-lid, a clear pale fringe; the band ends 2 mm inside the eyelid patch and
|
|
511
|
-
// fades back to the lid hair over 3 mm, no faster than the skin runs)
|
|
512
|
-
const L = Math.min(0.0075, 0.95 * (de - 0.0018)) * along;
|
|
513
|
-
const k = smoothstep(0.0085, 0.0115, de);
|
|
514
|
-
const Lf = mix(L, fl, k);
|
|
515
|
-
if (Lf > fl) {
|
|
516
|
-
fl = Lf; lashW = along * (1 - k);
|
|
517
|
-
const lash = norm(add(add(mul(ef.y, 0.7), mul(norm(rel), 0.5)), mul(ef.z, 0.3)));
|
|
518
|
-
let t = sub(lash, mul(n, dot(lash, n)));
|
|
519
|
-
if (len(t) > 1e-4) { t = norm(t); comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2]; }
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
// the lid folds (eye_lwshow_R, eye_mochyn: a soft crease above the upper lid's roll and a
|
|
523
|
-
// fainter one under the lower lid, running out past the corners): darker skin in thin bands, painted on the
|
|
524
|
-
// fine eyelid patch
|
|
525
|
-
const xr = Math.abs(dot(rel, ef.x)), along = smoothstep(0.0125, 0.006, xr);
|
|
526
|
-
const fold = upAmt > 0 ? Math.exp(-(((de - 0.0045) / 0.0011) ** 2)) * 0.2 : Math.exp(-(((de - 0.0036) / 0.0009) ** 2)) * 0.1;
|
|
527
|
-
col = mul(col, 1 - fold * along);
|
|
528
|
-
// (at medium and low, where no lashes are drawn, a pale lash line along the upper lid's margin)
|
|
529
|
-
if (!lashes && !dark && upAmt > 0.2) col = mix3(col, srgb(0xf6efe6), 0.45 * smoothstep(0.2, 0.5, upAmt) * (1 - smoothstep(0.0032, 0.0048, de)));
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
// lips: head vertices near the jaw surface, jaw vertices near the head surface. Only the mouth line is
|
|
533
|
-
// drawn: where the upper lip overhangs the lower one (the other surface lies above / below this vertex),
|
|
534
|
-
// in front of the mouth corner and fading out over the last 2 cm to it (face1: a short crease rising a
|
|
535
|
-
// little to the corner). The line is the mark channel (interpolated per pixel: a crease painted per
|
|
536
|
-
// vertex in a band narrower than a face cell drew a sawtooth), with a soft flush of lip skin beside it.
|
|
537
|
-
// Where the chin's sides and underside meet the throat nothing is painted (a dark crease all round the
|
|
538
|
-
// jaw outlined it as a separate slab). The mucosa only on the underside of the upper lip, facing the jaw.
|
|
539
|
-
if (r !== 6) {
|
|
540
|
-
const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
|
|
541
|
-
if (h[2] > 0.03 && h[1] < -0.04 && dj < 0.008) {
|
|
542
|
-
const q = sub(p, mul(HY, 0.006));
|
|
543
|
-
const lipK = (SDFModel.evalList(jawPrims, q[0], q[1], q[2]) < dj ? 1 : 0) * smoothstep(-0.086, -0.078, h[1]) * smoothstep(LIP_Z0, LIP_Z0 + 0.02, h[2]);
|
|
544
|
-
fl = mix(fl, Math.min(fl, 0.002 * smoothstep(0.001, 0.005, dj)), lipK);
|
|
545
|
-
if (pink) col = mix3(col, COL.flush, 0.3 * lipK * smoothstep(0.008, 0.002, dj));
|
|
546
|
-
// (a line along the lip margin, dj = 0, not the whole overlap under the lip: a filled mark showed as dark
|
|
547
|
-
// scribbles at the mouth corners when the mouth opened)
|
|
548
|
-
if (lipK > 0) markSDF[v] = mix(0.003, Math.abs(dj) - LIP_W, lipK);
|
|
549
|
-
// the roof of the mouth (hidden by the lower lip until the mouth opens): mucosa, graded in by the depth
|
|
550
|
-
// inside the lower lip, in the skin material (a switch to the mouth material drew its boundary in steps,
|
|
551
|
-
// and its wet coat mirrored the sky: a white plate in an open mouth)
|
|
552
|
-
// (deeper in, the roof a darker, matte red: it faces down into the mouth, which no light reaches;
|
|
553
|
-
// a glossy roof mirrored the sky blue-white, the mouth material's clear coat included). The roof also
|
|
554
|
-
// where the jaw lies just below the skin (not inside it), in front of the mouth corner: it shows when
|
|
555
|
-
// the mouth opens (painted skin there read as a pale plate)
|
|
556
|
-
let jb = 1;
|
|
557
|
-
if (-dot(n, HY) > 0.3 && h[2] > LIP_Z0) for (const dd of [0.004, 0.009, 0.015, 0.022]) { const q2 = sub(p, mul(HY, dd)); if (SDFModel.evalList(jawPrims, q2[0], q2[1], q2[2]) < 0) { jb = dd; break; } }
|
|
558
|
-
const roof = jb < 1 ? smoothstep(0.024, 0.012, jb) * smoothstep(LIP_Z0, LIP_Z0 + 0.015, h[2]) * smoothstep(0.3, 0.6, -dot(n, HY)) : 0;
|
|
559
|
-
// (and the upper face of the skin sheet under the chin that rides on the jaw, sculpt.js 'underjaw': it is
|
|
560
|
-
// the floor of the open mouth behind the lower lip)
|
|
561
|
-
let wj = 0;
|
|
562
|
-
for (let k = 0; k < 4; k++) if (skinIndex[v * 4 + k] === JAW_BONE) wj += skinWeight[v * 4 + k];
|
|
563
|
-
const floor = smoothstep(0.3, 0.7, wj) * smoothstep(0.1, 0.4, dot(n, HY));
|
|
564
|
-
const mu = Math.max(smoothstep(-0.001, -0.007, dj), roof, floor), deep = Math.max(smoothstep(-0.006, -0.016, dj), 0.6 * roof, 0.8 * floor);
|
|
565
|
-
// (the palate deeper in a ridged dusky pink rather than near black: it faces down into the mouth, which little
|
|
566
|
-
// light reaches, and read as a flat black slot when the mouth opened)
|
|
567
|
-
if (mu > 0) { col = mix3(col, mix3(MUCOSA, mul(MUCOSA, 0.6), deep), mu); fl *= 1 - mu; gloss = mix(gloss, mix(0.3, 0.05, deep), mu); }
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
|
|
571
|
-
if (tag === 'tongue') { mat = MAT.SKIN; fl = 0; gloss = 0.45; col = mix3(srgb(TONGUE), mul(srgb(TONGUE), 0.75), smoothstep(0.03, 0.1, -h[2] + 0.12)); }
|
|
572
|
-
else if (tag === 'tusk') {
|
|
573
|
-
mat = MAT.SKIN; fl = 0; gloss = 0.85; // (smooth enamel: skin material with no hair; the keratin shader's growth rings and striations, made for horn and hoof, crinkled the thin tusk like bark)
|
|
574
|
-
const E = tuskEnds[p[0] > 0 ? 0 : 1] || tuskEnds[0];
|
|
575
|
-
const t = E ? clamp(1 - len(sub(p, E.tip)) / Math.max(1e-4, len(sub(E.root, E.tip))), 0, 1) : 1;
|
|
576
|
-
col = mix3(srgb(TUSK_ROOT), srgb(TUSK), smoothstep(0.25, 0.85, t));
|
|
577
|
-
}
|
|
578
|
-
// the floor of the mouth and the inside of the lower lip: mucosa on the jaw's upper face, graded in by the
|
|
579
|
-
// depth inside the upper lip (skin material, as above); where the chin's sides and underside run into the
|
|
580
|
-
// head it stays skin (red there bled along the whole intersection and outlined the jaw as a slab)
|
|
581
|
-
else if (dh < 0.0) {
|
|
582
|
-
const mu = smoothstep(-0.001, -0.007, dh) * smoothstep(0.1, 0.4, dot(n, HY));
|
|
583
|
-
fl = Math.min(fl, 0.002) * (1 - mu);
|
|
584
|
-
if (mu > 0) { col = mix3(col, MUCOSA, mu); gloss = mix(gloss, 0.3, mu); }
|
|
585
|
-
} else if (dh < 0.008) {
|
|
586
|
-
const q = add(p, mul(HY, 0.006));
|
|
587
|
-
const lipK = (SDFModel.evalList(headPrims, q[0], q[1], q[2]) < dh ? 1 : 0) * smoothstep(-0.086, -0.078, h[1]) * smoothstep(LIP_Z0, LIP_Z0 + 0.02, h[2]);
|
|
588
|
-
fl = mix(fl, Math.min(fl, 0.002), lipK * smoothstep(0.008, 0.004, dh));
|
|
589
|
-
if (pink) col = mix3(col, COL.flush, 0.3 * lipK * smoothstep(0.008, 0.002, dh));
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
// skin creases: folds across the neck behind the jowls, over the knees and hocks (darkened tint
|
|
594
|
-
// in thin lines, deeper in older / fatter pigs)
|
|
595
|
-
if (mat === MAT.SKIN && (r === 1 || r === 0 || r === 2 || r === 7) && !piglet) {
|
|
596
|
-
let crease = 0;
|
|
597
|
-
// (wrinkles kept only where real (snout base, neck); drawn only where the cells are fine enough for a
|
|
598
|
-
// fold a few mm wide: hero and high)
|
|
599
|
-
const fineK = !ctx.Q || ctx.Q.res <= 1.35 ? 1 : 0;
|
|
600
|
-
const wr = 0.6 + 0.6 * (params.wrinkles ?? 0.5);
|
|
601
|
-
if (fineK && r === 1 && mat === MAT.SKIN) {
|
|
602
|
-
// neck folds: two or three soft folds round the side and underside of the neck behind the jowls (lw_show,
|
|
603
|
-
// head_pietrain), ~3 cm apart, fading out over the top of the neck
|
|
604
|
-
const sN = axialS[v] - sOcc;
|
|
605
|
-
const ph = (sN + 0.012 * (fbm3(p[0] * 25 + off[1], p[1] * 25, p[2] * 25, 2) - 0.5)) / 0.03;
|
|
606
|
-
const fr = Math.abs(ph - Math.round(ph));
|
|
607
|
-
crease = Math.exp(-(((fr * 0.03) / 0.0045) ** 2)) * smoothstep(0.015, 0.035, sN) * smoothstep(0.13, 0.08, sN) * smoothstep(0.5, -0.1, n[1]) * 0.55 * wr;
|
|
608
|
-
}
|
|
609
|
-
if (fineK && (r === 2 || r === 7) && mat === MAT.SKIN && nostrilK === 0) {
|
|
610
|
-
// the snout base: three or four transverse folds over the top of the snout a few cm behind the disc (face1,
|
|
611
|
-
// lw_show, head_hausschwein5), slightly arched, fading out down the sides
|
|
612
|
-
const h = HL(p), dz = DZ - h[2] + 6 * h[0] * h[0];
|
|
613
|
-
const ph = (dz + 0.003 * (fbm3(p[0] * 40 + off[0], p[1] * 40, p[2] * 40, 2) - 0.5)) / 0.0095;
|
|
614
|
-
const fr = Math.abs(ph - Math.round(ph));
|
|
615
|
-
crease = Math.exp(-(((fr * 0.0095) / 0.0018) ** 2)) * smoothstep(0.015, 0.025, dz) * smoothstep(0.065, 0.04, dz) * smoothstep(0.15, 0.55, dot(n, HY)) * 0.5 * wr;
|
|
616
|
-
}
|
|
617
|
-
if (legness[v] > 0.5 && lb >= 0) {
|
|
618
|
-
const jn = isFront[lb] ? J['wrist' + BONES[lb].side] : J['hock' + BONES[lb].side];
|
|
619
|
-
const dy = p[1] - jn[1];
|
|
620
|
-
const ph = Math.abs((dy / 0.012) % 1);
|
|
621
|
-
crease = Math.max(crease, smoothstep(0.2, 0.0, Math.min(ph, 1 - ph)) * smoothstep(0.03, 0.01, Math.abs(dy)) * 0.7 * (params.wrinkles ?? 0.5));
|
|
622
|
-
}
|
|
623
|
-
if (crease > 0) col = mul(col, 1 - 0.28 * Math.min(1, crease));
|
|
624
|
-
}
|
|
625
|
-
// Colours. The shells draw a bristle in 70 % of the vertex tint and 30 % of the albedo under it, so the
|
|
626
|
-
// skin itself is carried by the pattern channel and the tint is the BRISTLE colour: pale bristles on pink
|
|
627
|
-
// skin (face1: a soft white haze, not a dark pepper), golden on the Duroc, black on black skin, white on
|
|
628
|
-
// white markings. pattern < 0 (inside) shows patternColor = the skin; outside a marking's edge (belt,
|
|
629
|
-
// points, spots: pdRaw < 0) the tint shows, so near a marking the tint is the marking's colour (its
|
|
630
|
-
// bristles fray over the edge: a hairy boundary) and the crisp edge still comes from the distance field.
|
|
631
|
-
const CAP = 0.02;
|
|
632
|
-
let pdRaw = 1;
|
|
633
|
-
let second = breed === 'spotted' ? COL.spot : COL.white;
|
|
634
|
-
if ((mat === MAT.SKIN || mat === MAT.FUR) && breed !== 'largewhite' && breed !== 'landrace' && breed !== 'duroc') pdRaw = patternSD(v);
|
|
635
|
-
if (breed === 'berkshire' && (r === 7 || r === 2)) second = mix3(second, srgb(0xe2b4aa), 0.7 * discOf(HL(p)));
|
|
636
|
-
if (nostrilK > 0) second = mix3(second, nostrilCol, nostrilK);
|
|
637
|
-
// (the Old Spot's spots and the Hampshire's belt are painted per vertex in the skin's own colour channel,
|
|
638
|
-
// graded over 2-3 cm (gos_1: the spots' edges are soft, the pale hair over the dark skin), with the bristles following
|
|
639
|
-
// the skin under them. Drawn as a crisp per-pixel pattern edge, with the marking's colour in the tint that also colours
|
|
640
|
-
// the bristles, the spots read as holes cut in the skin, ringed by a halo of grey and white bristles: the tint changes
|
|
641
|
-
// over a whole cell beside an edge 1 mm wide. The spots also matte (the pink skin's satin over a dark albedo mirrored
|
|
642
|
-
// the sky as a grey sheen))
|
|
643
|
-
const col0 = col;
|
|
644
|
-
let softK = -1;
|
|
645
|
-
if (pdRaw < 1 && (breed === 'spotted' || breed === 'hampshire')) {
|
|
646
|
-
const cellM = (CELL[rname[partOf[v]]] || 0.0078) * (ctx.Q ? ctx.Q.res : 1);
|
|
647
|
-
const Wv = Math.max(r === 2 || r === 6 ? 0.007 : 0.011, Math.min(1.1 * cellM, 0.016)); // (capped: at low and crowd a ramp of a whole cell blurred the spots into grey blobs)
|
|
648
|
-
softK = smoothstep(Wv, -Wv, pdRaw);
|
|
649
|
-
col = mix3(col, second, softK);
|
|
650
|
-
if (breed === 'spotted') gloss = mix(gloss, SPOT_GLOSS, softK);
|
|
651
|
-
}
|
|
652
|
-
// (the bristle a little paler than the skin it grows from, not a white dot: at 1-3 m they blend into a
|
|
653
|
-
// faint pale haze over pink skin, face1; 1.6 mm white dots on a 5.5 mm grid read as frost)
|
|
654
|
-
// (a piglet's fine hair the same: on its finer strand grid, index.js render(), it is a pale velvet, front2)
|
|
655
|
-
// (the Duroc's red-gold hair, face3: redder than the skin is dark: a fine pale speckle like sandpaper)
|
|
656
|
-
// (the pale tint, mix 0.4 toward #F4EFE6, covered the face in white speckle; the shells draw a
|
|
657
|
-
// bristle in 0.7 x (tint x 1.1 + 0.03) + 0.3 x the skin, so this tint makes it the skin's colour a few per cent
|
|
658
|
-
// paler: the bristles show in the silhouette fins and against the light, not as dots over the lit skin)
|
|
659
|
-
const skinMatch = (c, k) => mix3([Math.max(0, 0.93 * c[0] - 0.027), Math.max(0, 0.93 * c[1] - 0.027), Math.max(0, 0.93 * c[2] - 0.027)], srgb(0xf4efe6), k);
|
|
660
|
-
// (the hair shader draws a bristle at ~0.85 of its albedo (strand shade 0.8-0.98, root 0.9) and lights it
|
|
661
|
-
// as hair, so a tint at the skin's own colour drew dark dashes over the lit face; 1.22 x the skin puts the lit bristle a
|
|
662
|
-
// touch paler than the skin under it: pale hairs up close, no pepper)
|
|
663
|
-
const paleMatch = (c) => mix3([Math.min(0.97, 1.22 * c[0] - 0.027), Math.min(0.97, 1.22 * c[1] - 0.027), Math.min(0.97, 1.22 * c[2] - 0.027)], srgb(0xf8f4ee), 0.12);
|
|
664
|
-
// (the Duroc's ear hair the body's red-gold too: the darker skin-matched strands specked the rim)
|
|
665
|
-
// (the pink ears' rim fringe in the pale body tint: skin-matched strands, drawn a little darker than the skin by
|
|
666
|
-
// the hair shader, outlined the Large White's and Landrace's rims with a speckled reddish fringe in the showcase)
|
|
667
|
-
let bristle = pink ? (r === 5 ? mix3(skinMatch(col0, 0.05), paleMatch(col0), earRimK) : paleMatch(col0)) : dark ? COL.bristle : mix3(skinMatch(col0, 0), srgb(0xc89064), 0.3);
|
|
668
|
-
// (in a spot, black hair a little darker than its skin, as on the black breeds; in the belt, white hair)
|
|
669
|
-
if (softK > 0) bristle = mix3(bristle, breed === 'spotted' ? mul(second, 0.6) : second, softK);
|
|
670
|
-
// (the lashes whiter than the body bristles: a pale fringe on the upper lid, face1)
|
|
671
|
-
if (lashW > 0 && !dark) bristle = mix3(bristle, srgb(pink ? 0xf2eae2 : 0xd8b890), lashW);
|
|
672
|
-
// mud (threequarter2, face3): only caked on the hooves and pasterns, dried pale grey or still wet and dark
|
|
673
|
-
// (the blotches and splashes on the belly, flanks and snout read as a dirty, blotchy skin; a
|
|
674
|
-
// clean pig's skin is an even pink)
|
|
675
|
-
if (mud > 0 && mat !== MAT.MOUTH && tag !== 'tusk' && r !== 5 && r !== 4 && r !== 7 && r !== 2 && r !== 6) {
|
|
676
|
-
const nz = (f, o) => fbm3(p[0] * f + off[0] + o, p[1] * f + off[1], p[2] * f + off[2] - o, 3);
|
|
677
|
-
const caked = smoothstep(0.075, 0.035, p[1] + 0.02 * (nz(9, 3) - 0.5));
|
|
678
|
-
const mk = clamp(caked * Math.min(1, 1.6 * mud), 0, 0.85);
|
|
679
|
-
if (mk > 0) {
|
|
680
|
-
const mc = mix3(srgb(MUD), srgb(MUD_DRY), smoothstep(0.4, 0.65, nz(5, 9)));
|
|
681
|
-
// (the bristles caked faster than the skin and flattened: black ones drew a black pepper over pale mud)
|
|
682
|
-
col = mix3(col, mc, mk); second = mix3(second, mc, mk); bristle = mix3(bristle, mul(mc, 0.9), Math.min(1, 1.6 * mk));
|
|
683
|
-
gloss = mix(gloss, 0.05, mk); fl *= 1 - 0.65 * mk;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
// (black bristles on black skin read as a glitter of strand highlights: shorter)
|
|
687
|
-
if (dark && mat === MAT.SKIN && softK >= 0) fl *= mix(0.26, 1, softK);
|
|
688
|
-
else if (dark && mat === MAT.SKIN && pdRaw > 0) fl *= 0.26;
|
|
689
|
-
// (and the spots' black hair shorter, sparse on the dark skin: a glitter of strand highlights otherwise)
|
|
690
|
-
if (breed === 'spotted' && softK > 0) fl *= mix(1, 0.5, softK); // (0.3 on the 6000 /m grid: a grey glitter of strand highlights over the black skin; 0.18 left it bare like latex)
|
|
691
|
-
// faint, broad warm variation of the skin (a +-10 % red fbm at 17 cm read as dirty blotches)
|
|
692
|
-
const cv = fbm3(p[0] * 3 + off[2], p[1] * 3, p[2] * 3, 2) - 0.5;
|
|
693
|
-
if (mat === MAT.SKIN && r !== 7) col = [col[0] * (1 + 0.035 * cv), col[1] * (1 + 0.012 * cv), col[2] * (1 + 0.012 * cv)];
|
|
694
|
-
if (mat === MAT.SKIN && fl > 0) fl *= bristleK;
|
|
695
|
-
// (the tint turns to the marking's colour a body cell before its edge: the triangles across the edge
|
|
696
|
-
// show it whole on the marking's side)
|
|
697
|
-
// (one cell of the vertex's own surface: the face's 4 mm, not the body's 8: a white fringe 8-11 mm wide round the
|
|
698
|
-
// Berkshire's blaze read as overspray at hero; outside the marking, where that tint lies over
|
|
699
|
-
// the other skin, the bristles are almost gone: the skin's own pattern edge stays crisp and the per-vertex tint
|
|
700
|
-
// cannot be narrower than a cell)
|
|
701
|
-
// (Berkshire only: the spots and the belt are painted per vertex above, softK)
|
|
702
|
-
if (softK >= 0) pdRaw = 1;
|
|
703
|
-
const margin = (CELL[rname[partOf[v]]] || 0.0078) * 1.05 * (ctx.Q ? ctx.Q.res : 1);
|
|
704
|
-
const mw = smoothstep(1.4 * margin, margin, pdRaw);
|
|
705
|
-
// (none at all there: at 15 % length the white bristles of that band still sprinkled the black round the
|
|
706
|
-
// Berkshire's blaze and the Hampshire's belt with white specks at hero)
|
|
707
|
-
// (and fading in over the next cell out: the triangles from those vertices to the next ring blended the white tint
|
|
708
|
-
// into the black skin's bristles, a grey halo)
|
|
709
|
-
if (pdRaw < 1) fl *= 1 - smoothstep(2.6 * margin, 1.4 * margin, pdRaw) * smoothstep(-0.002, 0.002, pdRaw);
|
|
710
|
-
// (and the marking's own bristles short in its last cell: the white ones there are drawn over the black pixels
|
|
711
|
-
// just past the crisp edge, a sparkling halo)
|
|
712
|
-
if (pdRaw < 0) fl *= mix(0.25, 1, smoothstep(-0.2 * margin, -1.2 * margin, pdRaw));
|
|
713
|
-
const tc = mat === MAT.SKIN || mat === MAT.FUR ? mix3(bristle, second, mw) : col;
|
|
714
|
-
pattern[v] = clamp(-pdRaw, -CAP, CAP); patInt[v] = 1;
|
|
715
|
-
patCol[v * 3] = col[0]; patCol[v * 3 + 1] = col[1]; patCol[v * 3 + 2] = col[2];
|
|
716
|
-
furLen[v] = fl;
|
|
717
|
-
tint[v * 4] = tc[0]; tint[v * 4 + 1] = tc[1]; tint[v * 4 + 2] = tc[2]; tint[v * 4 + 3] = mat;
|
|
718
|
-
// ear veins: the vein is the 'marking' (pattern > 0 shows the tint), the skin round it the pattern colour
|
|
719
|
-
if (veinCol && pdRaw > 0.02 && !(softK > 0.05)) { pattern[v] = clamp(-veinSD, -CAP, CAP); tint[v * 4] = veinCol[0]; tint[v * 4 + 1] = veinCol[1]; tint[v * 4 + 2] = veinCol[2]; }
|
|
720
|
-
surf[v * 4] = gloss; surf[v * 4 + 1] = sy;
|
|
721
|
-
}
|
|
722
|
-
smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.SKIN);
|
|
723
|
-
|
|
724
|
-
const patternColor = new Float32Array(nV * 4);
|
|
725
|
-
for (let v = 0; v < nV; v++) { patternColor[v * 4] = patCol[v * 3]; patternColor[v * 4 + 1] = patCol[v * 3 + 1]; patternColor[v * 4 + 2] = patCol[v * 3 + 2]; patternColor[v * 4 + 3] = patInt[v]; }
|
|
726
|
-
void vnoise3; void TAIL_SEGS; void add;
|
|
727
|
-
const perRegion = {};
|
|
728
|
-
for (let v = 0; v < nV; v++) perRegion[rname[partOf[v]]] = (perRegion[rname[partOf[v]]] || 0) + 1;
|
|
729
|
-
return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: { spots: spots.length, perRegion }, region, ventral };
|
|
730
|
-
}
|