@bitefoundry/retrieviewer 0.0.0-stage → 0.0.1
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 +43 -0
- package/ASSETS-LICENSE.md +58 -0
- package/ATTRIBUTION.md +54 -0
- package/EMBEDDING.md +96 -0
- package/INSTALLING.md +107 -0
- package/LICENSE +16 -0
- package/LICENSE-CODE +21 -0
- package/PLUGIN.md +110 -0
- package/PUPPY-MEMORY.md +57 -0
- package/PUPPY-Q5-COMPARISON.md +73 -0
- package/README.md +61 -2
- package/RELEASING.md +61 -0
- package/THIRD-PARTY-NOTICES.md +17 -0
- package/TRADEMARKS.md +21 -0
- package/braille.mjs +45 -0
- package/examples/bridge.py +62 -0
- package/examples/embed.mjs +15 -0
- package/examples/live.mjs +40 -0
- package/examples/plugin.mjs +21 -0
- package/integrations/opencode/runtime.mjs +195 -0
- package/integrations/opencode/tui.tsx +59 -0
- package/locomotion-core.mjs +21 -0
- package/locomotion-puppy.animal +0 -0
- package/package.json +125 -4
- package/pet-q5-manifest.json +118 -0
- package/puppy-clean.mjs +51 -0
- package/puppy-companion-interactions-v3-q5.bin.gz +0 -0
- package/puppy-companion-interactions-v3.json +1 -0
- package/puppy-companion-life.mjs +182 -0
- package/puppy-companion-objects.mjs +30 -0
- package/puppy-companion-sprites.mjs +14 -0
- package/puppy-continuous.mjs +57 -0
- package/puppy-face.mjs +46 -0
- package/puppy-frame-predictive.mjs +147 -0
- package/puppy-frame-store.mjs +108 -0
- package/puppy-mouth-anchor.json +1 -0
- package/puppy-mouth-anchor.mjs +33 -0
- package/puppy-paper-actions-v9-q5.bin.gz +0 -0
- package/puppy-paper-actions-v9.json +1 -0
- package/puppy-paper-actions.mjs +48 -0
- package/puppy-paper-approach-q5.bin.gz +0 -0
- package/puppy-paper-approach.json +1 -0
- package/puppy-paper-area.mjs +15 -0
- package/puppy-paper-arrival-seat-q5.bin.gz +0 -0
- package/puppy-paper-arrival-seat.json +1 -0
- package/puppy-paper-behaviors-q5.bin.gz +0 -0
- package/puppy-paper-behaviors.json +1 -0
- package/puppy-paper-behaviors.mjs +8 -0
- package/puppy-paper-contact.mjs +103 -0
- package/puppy-paper-director.mjs +21 -0
- package/puppy-paper-paw-transport.mjs +76 -0
- package/puppy-paper-rear-gallop-q5.bin.gz +0 -0
- package/puppy-paper-rear-gallop.json +1 -0
- package/puppy-paper-seat-q5.bin.gz +0 -0
- package/puppy-paper-seat.json +14 -0
- package/puppy-paper-seat.mjs +13 -0
- package/puppy-paper-trick-timing.mjs +58 -0
- package/puppy-paper-world.mjs +108 -0
- package/puppy-paper.mjs +271 -0
- package/puppy-stable-meta.json +1606 -0
- package/puppy-stable.mjs +72 -0
- package/puppy-stride-meta.json +343 -0
- package/puppy-stride-tweens-q5.bin.gz +0 -0
- package/puppy-zoomies.mjs +27 -0
- package/puppy-zoomies30.mjs +12 -0
- package/retrieviewer-cli.mjs +81 -0
- package/retrieviewer-config.mjs +49 -0
- package/retrieviewer-install.mjs +157 -0
- package/retrieviewer-plugin.d.mts +44 -0
- package/retrieviewer-plugin.mjs +97 -0
- package/retrieviewer-stdio.mjs +38 -0
- package/retrieviewer.d.mts +85 -0
- package/retrieviewer.mjs +142 -0
- package/vendor/procedural-animals/LICENSE +21 -0
- package/vendor/procedural-animals/package.json +40 -0
- package/vendor/procedural-animals/src/animal.js +146 -0
- package/vendor/procedural-animals/src/bake.js +51 -0
- package/vendor/procedural-animals/src/build.js +81 -0
- package/vendor/procedural-animals/src/core/build/coatKit.js +121 -0
- package/vendor/procedural-animals/src/core/build/featherCards.js +189 -0
- package/vendor/procedural-animals/src/core/build/finEdges.js +85 -0
- package/vendor/procedural-animals/src/core/build/furSlope.js +115 -0
- package/vendor/procedural-animals/src/core/build/mesh.js +67 -0
- package/vendor/procedural-animals/src/core/build/pipeline.js +272 -0
- package/vendor/procedural-animals/src/core/build/seams.js +183 -0
- package/vendor/procedural-animals/src/core/build/warp.js +112 -0
- package/vendor/procedural-animals/src/core/build/weights.js +393 -0
- package/vendor/procedural-animals/src/core/math/vec.js +81 -0
- package/vendor/procedural-animals/src/core/motion/actions.js +800 -0
- package/vendor/procedural-animals/src/core/motion/bird.js +3063 -0
- package/vendor/procedural-animals/src/core/motion/birdActions.js +1059 -0
- package/vendor/procedural-animals/src/core/motion/common.js +158 -0
- package/vendor/procedural-animals/src/core/motion/index.js +22 -0
- package/vendor/procedural-animals/src/core/motion/quadruped.js +2875 -0
- package/vendor/procedural-animals/src/core/motion/snake.js +1594 -0
- package/vendor/procedural-animals/src/core/motion/spider.js +1810 -0
- package/vendor/procedural-animals/src/core/motion/swimmer.js +1047 -0
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +613 -0
- package/vendor/procedural-animals/src/core/render/animalObject.js +262 -0
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +1301 -0
- package/vendor/procedural-animals/src/core/render/dqs.js +159 -0
- package/vendor/procedural-animals/src/core/render/eyes.js +676 -0
- package/vendor/procedural-animals/src/core/render/membranes.js +77 -0
- package/vendor/procedural-animals/src/core/render/skeleton.js +41 -0
- package/vendor/procedural-animals/src/core/rig/bird.js +327 -0
- package/vendor/procedural-animals/src/core/rig/quadruped.js +76 -0
- package/vendor/procedural-animals/src/core/rig/rig.js +79 -0
- package/vendor/procedural-animals/src/core/rig/snake.js +76 -0
- package/vendor/procedural-animals/src/core/rig/spider.js +110 -0
- package/vendor/procedural-animals/src/core/rig/swimmer.js +181 -0
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +53 -0
- package/vendor/procedural-animals/src/core/sdf/mesher.js +238 -0
- package/vendor/procedural-animals/src/core/sdf/sdf.js +263 -0
- package/vendor/procedural-animals/src/index.d.ts +98 -0
- package/vendor/procedural-animals/src/index.js +29 -0
- package/vendor/procedural-animals/src/registry.js +48 -0
- package/vendor/procedural-animals/src/species/bear/behaviour.js +463 -0
- package/vendor/procedural-animals/src/species/bear/claws.js +147 -0
- package/vendor/procedural-animals/src/species/bear/coat.js +726 -0
- package/vendor/procedural-animals/src/species/bear/index.js +139 -0
- package/vendor/procedural-animals/src/species/bear/motion.js +112 -0
- package/vendor/procedural-animals/src/species/bear/regions.js +78 -0
- package/vendor/procedural-animals/src/species/bear/rig.js +112 -0
- package/vendor/procedural-animals/src/species/bear/sculpt.js +272 -0
- package/vendor/procedural-animals/src/species/boar/behaviour.js +335 -0
- package/vendor/procedural-animals/src/species/boar/coat.js +372 -0
- package/vendor/procedural-animals/src/species/boar/index.js +110 -0
- package/vendor/procedural-animals/src/species/boar/motion.js +85 -0
- package/vendor/procedural-animals/src/species/boar/regions.js +88 -0
- package/vendor/procedural-animals/src/species/boar/rig.js +111 -0
- package/vendor/procedural-animals/src/species/boar/sculpt.js +285 -0
- package/vendor/procedural-animals/src/species/cat/behaviour.js +559 -0
- package/vendor/procedural-animals/src/species/cat/coat.js +841 -0
- package/vendor/procedural-animals/src/species/cat/index.js +211 -0
- package/vendor/procedural-animals/src/species/cat/motion.js +101 -0
- package/vendor/procedural-animals/src/species/cat/regions.js +82 -0
- package/vendor/procedural-animals/src/species/cat/rig.js +100 -0
- package/vendor/procedural-animals/src/species/cat/sculpt.js +266 -0
- package/vendor/procedural-animals/src/species/cat/whiskers.js +110 -0
- package/vendor/procedural-animals/src/species/cheetah/coat.js +427 -0
- package/vendor/procedural-animals/src/species/cheetah/index.js +51 -0
- package/vendor/procedural-animals/src/species/cheetah/joints.js +3 -0
- package/vendor/procedural-animals/src/species/cheetah/motion.js +77 -0
- package/vendor/procedural-animals/src/species/cheetah/regions.js +95 -0
- package/vendor/procedural-animals/src/species/cheetah/rig.js +66 -0
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +158 -0
- package/vendor/procedural-animals/src/species/chicken/coat.js +313 -0
- package/vendor/procedural-animals/src/species/chicken/coverts.js +320 -0
- package/vendor/procedural-animals/src/species/chicken/index.js +110 -0
- package/vendor/procedural-animals/src/species/chicken/motion.js +177 -0
- package/vendor/procedural-animals/src/species/chicken/regions.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/rig.js +144 -0
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +389 -0
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +66 -0
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +124 -0
- package/vendor/procedural-animals/src/species/cow/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/cow/coat.js +465 -0
- package/vendor/procedural-animals/src/species/cow/index.js +118 -0
- package/vendor/procedural-animals/src/species/cow/motion.js +81 -0
- package/vendor/procedural-animals/src/species/cow/regions.js +98 -0
- package/vendor/procedural-animals/src/species/cow/rig.js +116 -0
- package/vendor/procedural-animals/src/species/cow/sculpt.js +339 -0
- package/vendor/procedural-animals/src/species/crow/coat.js +145 -0
- package/vendor/procedural-animals/src/species/crow/index.js +70 -0
- package/vendor/procedural-animals/src/species/crow/joints.js +3 -0
- package/vendor/procedural-animals/src/species/crow/motion.js +101 -0
- package/vendor/procedural-animals/src/species/crow/regions.js +50 -0
- package/vendor/procedural-animals/src/species/crow/rig.js +83 -0
- package/vendor/procedural-animals/src/species/crow/sculpt.js +118 -0
- package/vendor/procedural-animals/src/species/deer/behaviour.js +513 -0
- package/vendor/procedural-animals/src/species/deer/coat.js +391 -0
- package/vendor/procedural-animals/src/species/deer/index.js +106 -0
- package/vendor/procedural-animals/src/species/deer/motion.js +97 -0
- package/vendor/procedural-animals/src/species/deer/regions.js +60 -0
- package/vendor/procedural-animals/src/species/deer/rig.js +124 -0
- package/vendor/procedural-animals/src/species/deer/sculpt.js +297 -0
- package/vendor/procedural-animals/src/species/dog/behaviour.js +521 -0
- package/vendor/procedural-animals/src/species/dog/coat.js +495 -0
- package/vendor/procedural-animals/src/species/dog/index.js +144 -0
- package/vendor/procedural-animals/src/species/dog/motion.js +118 -0
- package/vendor/procedural-animals/src/species/dog/regions.js +61 -0
- package/vendor/procedural-animals/src/species/dog/rig.js +129 -0
- package/vendor/procedural-animals/src/species/dog/sculpt.js +246 -0
- package/vendor/procedural-animals/src/species/eagle/coat.js +250 -0
- package/vendor/procedural-animals/src/species/eagle/index.js +76 -0
- package/vendor/procedural-animals/src/species/eagle/joints.js +3 -0
- package/vendor/procedural-animals/src/species/eagle/motion.js +137 -0
- package/vendor/procedural-animals/src/species/eagle/regions.js +57 -0
- package/vendor/procedural-animals/src/species/eagle/rig.js +107 -0
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +137 -0
- package/vendor/procedural-animals/src/species/fish/coat.js +271 -0
- package/vendor/procedural-animals/src/species/fish/index.js +88 -0
- package/vendor/procedural-animals/src/species/fish/motion.js +92 -0
- package/vendor/procedural-animals/src/species/fish/regions.js +27 -0
- package/vendor/procedural-animals/src/species/fish/rig.js +143 -0
- package/vendor/procedural-animals/src/species/fish/sculpt.js +214 -0
- package/vendor/procedural-animals/src/species/fish/variants.js +126 -0
- package/vendor/procedural-animals/src/species/fox/behaviour.js +314 -0
- package/vendor/procedural-animals/src/species/fox/coat.js +444 -0
- package/vendor/procedural-animals/src/species/fox/index.js +120 -0
- package/vendor/procedural-animals/src/species/fox/motion.js +81 -0
- package/vendor/procedural-animals/src/species/fox/regions.js +59 -0
- package/vendor/procedural-animals/src/species/fox/rig.js +91 -0
- package/vendor/procedural-animals/src/species/fox/sculpt.js +202 -0
- package/vendor/procedural-animals/src/species/frog/behaviour.js +590 -0
- package/vendor/procedural-animals/src/species/frog/coat.js +305 -0
- package/vendor/procedural-animals/src/species/frog/index.js +97 -0
- package/vendor/procedural-animals/src/species/frog/motion.js +69 -0
- package/vendor/procedural-animals/src/species/frog/regions.js +46 -0
- package/vendor/procedural-animals/src/species/frog/rig.js +156 -0
- package/vendor/procedural-animals/src/species/frog/sculpt.js +240 -0
- package/vendor/procedural-animals/src/species/goat/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/goat/coat.js +590 -0
- package/vendor/procedural-animals/src/species/goat/index.js +149 -0
- package/vendor/procedural-animals/src/species/goat/motion.js +95 -0
- package/vendor/procedural-animals/src/species/goat/regions.js +98 -0
- package/vendor/procedural-animals/src/species/goat/rig.js +181 -0
- package/vendor/procedural-animals/src/species/goat/sculpt.js +547 -0
- package/vendor/procedural-animals/src/species/horse/coat.js +361 -0
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +122 -0
- package/vendor/procedural-animals/src/species/horse/index.js +80 -0
- package/vendor/procedural-animals/src/species/horse/motion.js +245 -0
- package/vendor/procedural-animals/src/species/horse/regions.js +60 -0
- package/vendor/procedural-animals/src/species/horse/rig.js +117 -0
- package/vendor/procedural-animals/src/species/horse/sculpt.js +316 -0
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +272 -0
- package/vendor/procedural-animals/src/species/lion/behaviour.js +509 -0
- package/vendor/procedural-animals/src/species/lion/coat.js +1016 -0
- package/vendor/procedural-animals/src/species/lion/index.js +111 -0
- package/vendor/procedural-animals/src/species/lion/motion.js +87 -0
- package/vendor/procedural-animals/src/species/lion/regions.js +106 -0
- package/vendor/procedural-animals/src/species/lion/rig.js +110 -0
- package/vendor/procedural-animals/src/species/lion/sculpt.js +384 -0
- package/vendor/procedural-animals/src/species/lion/whiskers.js +132 -0
- package/vendor/procedural-animals/src/species/pig/behaviour.js +575 -0
- package/vendor/procedural-animals/src/species/pig/coat.js +730 -0
- package/vendor/procedural-animals/src/species/pig/ears.js +242 -0
- package/vendor/procedural-animals/src/species/pig/index.js +161 -0
- package/vendor/procedural-animals/src/species/pig/motion.js +105 -0
- package/vendor/procedural-animals/src/species/pig/regions.js +100 -0
- package/vendor/procedural-animals/src/species/pig/rig.js +152 -0
- package/vendor/procedural-animals/src/species/pig/sculpt.js +382 -0
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +605 -0
- package/vendor/procedural-animals/src/species/rabbit/coat.js +285 -0
- package/vendor/procedural-animals/src/species/rabbit/index.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/motion.js +85 -0
- package/vendor/procedural-animals/src/species/rabbit/regions.js +48 -0
- package/vendor/procedural-animals/src/species/rabbit/rig.js +99 -0
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +148 -0
- package/vendor/procedural-animals/src/species/rat/behaviour.js +539 -0
- package/vendor/procedural-animals/src/species/rat/coat.js +341 -0
- package/vendor/procedural-animals/src/species/rat/index.js +111 -0
- package/vendor/procedural-animals/src/species/rat/motion.js +90 -0
- package/vendor/procedural-animals/src/species/rat/regions.js +92 -0
- package/vendor/procedural-animals/src/species/rat/rig.js +112 -0
- package/vendor/procedural-animals/src/species/rat/sculpt.js +163 -0
- package/vendor/procedural-animals/src/species/rat/whiskers.js +119 -0
- package/vendor/procedural-animals/src/species/shark/coat.js +383 -0
- package/vendor/procedural-animals/src/species/shark/index.js +104 -0
- package/vendor/procedural-animals/src/species/shark/motion.js +69 -0
- package/vendor/procedural-animals/src/species/shark/regions.js +34 -0
- package/vendor/procedural-animals/src/species/shark/rig.js +194 -0
- package/vendor/procedural-animals/src/species/shark/sculpt.js +343 -0
- package/vendor/procedural-animals/src/species/shark/variants.js +105 -0
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +522 -0
- package/vendor/procedural-animals/src/species/sheep/coat.js +529 -0
- package/vendor/procedural-animals/src/species/sheep/index.js +152 -0
- package/vendor/procedural-animals/src/species/sheep/motion.js +86 -0
- package/vendor/procedural-animals/src/species/sheep/neck.js +41 -0
- package/vendor/procedural-animals/src/species/sheep/regions.js +62 -0
- package/vendor/procedural-animals/src/species/sheep/rig.js +163 -0
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +542 -0
- package/vendor/procedural-animals/src/species/snake/coat.js +315 -0
- package/vendor/procedural-animals/src/species/snake/index.js +73 -0
- package/vendor/procedural-animals/src/species/snake/motion.js +51 -0
- package/vendor/procedural-animals/src/species/snake/regions.js +51 -0
- package/vendor/procedural-animals/src/species/snake/rig.js +108 -0
- package/vendor/procedural-animals/src/species/snake/sculpt.js +122 -0
- package/vendor/procedural-animals/src/species/spider/coat.js +279 -0
- package/vendor/procedural-animals/src/species/spider/index.js +78 -0
- package/vendor/procedural-animals/src/species/spider/motion.js +72 -0
- package/vendor/procedural-animals/src/species/spider/regions.js +87 -0
- package/vendor/procedural-animals/src/species/spider/rig.js +118 -0
- package/vendor/procedural-animals/src/species/spider/sculpt.js +182 -0
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +160 -0
- package/vendor/procedural-animals/src/species/wolf/coat.js +749 -0
- package/vendor/procedural-animals/src/species/wolf/index.js +107 -0
- package/vendor/procedural-animals/src/species/wolf/motion.js +80 -0
- package/vendor/procedural-animals/src/species/wolf/regions.js +57 -0
- package/vendor/procedural-animals/src/species/wolf/rig.js +87 -0
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +339 -0
- package/vendor/procedural-animals/src/worker.js +29 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
// The cow's coat: breed colours (Holstein black-and-white piebald, Red Holstein, Hereford red with a
|
|
2
|
+
// white face, crest and underline, black Angus, fawn Jersey with a dark face and a pale muzzle ring,
|
|
3
|
+
// Highland ginger / black / dun with a long shaggy coat), Holstein patches grown as a seeded signed
|
|
4
|
+
// distance field on the skin (unique per individual, crisp slightly ragged edges), white socks and
|
|
5
|
+
// switch, pink skin and pale hooves under white hair, the moist pebbled muzzle (pink or black), long
|
|
6
|
+
// lashes on the upper lids, the udder's bare pink skin, keratin claws and horns (pale base, dark tip),
|
|
7
|
+
// yellow ear tags, a steel nose ring, and hair flow along the bovine hair tracts (forehead whorl,
|
|
8
|
+
// down the face, backward and down the barrel, down the legs).
|
|
9
|
+
import { HEAD_O, HZ, HY, BONE_SEGS } from './rig.js';
|
|
10
|
+
import { neckS } from './regions.js';
|
|
11
|
+
import { EYE } from './sculpt.js';
|
|
12
|
+
import { eyeFrameOf } from '../../core/sdf/eyeSocket.js';
|
|
13
|
+
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
14
|
+
import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
|
|
15
|
+
import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
|
|
16
|
+
|
|
17
|
+
// sRGB swatches sampled from the reference photos
|
|
18
|
+
export const BREEDS = {
|
|
19
|
+
holstein: { body: 0x141312, dorsal: 0x121110, belly: 0x1c1a19, head: 0x141312, white: 0xf1efe9, muzzleDark: 0x1e1a1a, pied: 1 },
|
|
20
|
+
redholstein: { body: 0x7e2e1c, dorsal: 0x72291a, belly: 0x8a3824, head: 0x7a2c1b, white: 0xf1efe9, muzzleDark: 0x9a6a5a, pied: 1 },
|
|
21
|
+
hereford: { body: 0x7e3a20, dorsal: 0x70321b, belly: 0x8a4428, head: 0x7e3a20, white: 0xefe9df, muzzleDark: 0xd8a090 },
|
|
22
|
+
angus: { body: 0x161413, dorsal: 0x141211, belly: 0x1e1a18, head: 0x161413, white: 0x161413, muzzleDark: 0x1a1818 },
|
|
23
|
+
redangus: { body: 0x6a2a18, dorsal: 0x5e2515, belly: 0x72301c, head: 0x6a2a18, white: 0x6a2a18, muzzleDark: 0x4a3028 },
|
|
24
|
+
jersey: { body: 0x9a7652, dorsal: 0x856444, belly: 0xae8c66, head: 0x5e4533, white: 0xd6c7a8, muzzleDark: 0x1e1a18, dark: 0x5e4533 },
|
|
25
|
+
highland: { body: 0xa0522d, dorsal: 0x96492a, belly: 0xae6238, head: 0xa0522d, white: 0xa0522d, muzzleDark: 0x2a2220 },
|
|
26
|
+
highlandblack: { body: 0x1c1816, dorsal: 0x181412, belly: 0x241e1a, head: 0x1c1816, white: 0x1c1816, muzzleDark: 0x1a1616 },
|
|
27
|
+
highlanddun: { body: 0xb89a6e, dorsal: 0xac8e62, belly: 0xc4a87c, head: 0xb09268, white: 0xb89a6e, muzzleDark: 0x3a3028 },
|
|
28
|
+
};
|
|
29
|
+
const PINK = 0xd99a8e, UDDER = 0xe0b0a0, HOOF = 0x2a2522, HOOF_PALE = 0x8c8174, HORN_BASE = 0xd8cbb0, HORN_TIP = 0x3a3530;
|
|
30
|
+
const TAG = 0xe8c020, RING = 0x8a8a86;
|
|
31
|
+
const LID_DARK = srgb(0x3a302b), LID_PINK = srgb(0xc8847a), LID_RING = srgb(0x2c2826);
|
|
32
|
+
|
|
33
|
+
function palette(p) {
|
|
34
|
+
const C = BREEDS[p.coat] || BREEDS.holstein;
|
|
35
|
+
const k = p.coatShade || 0, l = p.coatLightness || 0;
|
|
36
|
+
const tw = (hex) => { const c = srgb(hex); return [c[0] * (1 + 0.08 * k + l), c[1] * (1 + l), c[2] * (1 - 0.08 * k + l)]; };
|
|
37
|
+
const out = { body: tw(C.body), dorsal: tw(C.dorsal), belly: tw(C.belly), head: tw(C.head), white: srgb(C.white), muzzleDark: srgb(C.muzzleDark), pied: !!C.pied };
|
|
38
|
+
if (C.dark) out.dark = tw(C.dark);
|
|
39
|
+
out.pink = srgb(PINK); out.udder = srgb(UDDER); out.hoof = srgb(HOOF); out.hoofPale = srgb(HOOF_PALE);
|
|
40
|
+
out.hornBase = srgb(HORN_BASE); out.hornTip = srgb(HORN_TIP);
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const HOOF_TAGS = new Set(['hoof', 'heelbulb', 'dewclaw']);
|
|
45
|
+
const UDDER_TAGS = new Set(['udder', 'teat']);
|
|
46
|
+
|
|
47
|
+
export function cowCoat(ctx) {
|
|
48
|
+
const { model, rig, pos, nrm, regionOf: partOf, regionNames, weights, nV, params, lists } = ctx;
|
|
49
|
+
const { BONES } = rig;
|
|
50
|
+
const J = rig.J;
|
|
51
|
+
const COL = palette(params);
|
|
52
|
+
const calf = params.age === 'juvenile';
|
|
53
|
+
const breed = params.variant || 'holstein';
|
|
54
|
+
const { dominant, axialS, skinIndex, skinWeight, limbMember } = weights;
|
|
55
|
+
const AX_LEN = weights.axialLengths;
|
|
56
|
+
const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
57
|
+
const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
|
|
58
|
+
const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8];
|
|
59
|
+
const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
|
|
60
|
+
const eyeFrames = [1, -1].map((s) => eyeFrameOf(EYE, HEAD_O, s));
|
|
61
|
+
const jawPrims = model.forPart('jaw');
|
|
62
|
+
const headPrims = model.forPart('body').filter((p) => p.bone === 'head' && !p.carve);
|
|
63
|
+
const nostrilPrims = model.prims.filter((p) => p.tag === 'nostril');
|
|
64
|
+
const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof|femur|tibia|metatarsus|hpaw|hhoof)/.test(b.name));
|
|
65
|
+
const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|fhoof)/.test(b.name));
|
|
66
|
+
const tailIdx = (name) => (name.startsWith('tail') ? +name.slice(4) : -1);
|
|
67
|
+
const HL = (p) => { const d = sub(p, HEAD_O); return [d[0], dot(d, HY), dot(d, HZ)]; };
|
|
68
|
+
const rname = regionNames.map((n) => n);
|
|
69
|
+
|
|
70
|
+
// --- nearest primitive tag per vertex
|
|
71
|
+
const tagOf = new Array(nV);
|
|
72
|
+
for (let v = 0; v < nV; v++) {
|
|
73
|
+
const L = lists[v], p = P(v);
|
|
74
|
+
let best = 1e9, t = '';
|
|
75
|
+
for (const pr of L) {
|
|
76
|
+
if (pr.carve) continue;
|
|
77
|
+
const d = SDFModel.dist(pr, p[0], p[1], p[2]);
|
|
78
|
+
if (d < best) { best = d; t = pr.tag; }
|
|
79
|
+
}
|
|
80
|
+
tagOf[v] = t;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// --- regions: 0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 switch, 8 horn, 9 ear tag,
|
|
84
|
+
// 10 nose ring, 11 tongue
|
|
85
|
+
const region = new Uint8Array(nV);
|
|
86
|
+
const legness = new Float32Array(nV);
|
|
87
|
+
const legBone = new Int16Array(nV).fill(-1);
|
|
88
|
+
for (let v = 0; v < nV; v++) {
|
|
89
|
+
const bn = BONES[dominant[v]].name, p = P(v), tag = tagOf[v], rn = rname[partOf[v]];
|
|
90
|
+
let lb = -1, lw = 0;
|
|
91
|
+
for (let k = 0; k < 4; k++) {
|
|
92
|
+
const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
|
|
93
|
+
if (isLimb[b] && w > lw) { lw = w; lb = b; }
|
|
94
|
+
}
|
|
95
|
+
legBone[v] = lb;
|
|
96
|
+
legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, limbMember[v]) : 0;
|
|
97
|
+
const ti = tailIdx(bn);
|
|
98
|
+
if (rn === 'jaw') region[v] = 6;
|
|
99
|
+
else if (rn === 'horn') region[v] = 8;
|
|
100
|
+
else if (rn === 'tagL' || rn === 'tagR') region[v] = 9;
|
|
101
|
+
else if (rn === 'ring') region[v] = 10;
|
|
102
|
+
else if (rn === 'tongue') region[v] = 11;
|
|
103
|
+
else if (bn.startsWith('ear')) region[v] = 5;
|
|
104
|
+
else if (tag === 'switch') region[v] = 7;
|
|
105
|
+
else if (ti >= 0 && axialS[v] > sTailBase + 0.02) region[v] = 4;
|
|
106
|
+
else if (rn === 'feet' || (lb >= 0 && limbMember[v] > 0.5 && p[1] < 1.0)) region[v] = 0; // (legs: never head or neck by the axial coordinate)
|
|
107
|
+
else if (axialS[v] < sOcc + 0.02 || (rn === 'head' && neckS(p[0], p[1], p[2]) > 0.03)) region[v] = 2;
|
|
108
|
+
else if (axialS[v] < sNeckBase) region[v] = 1;
|
|
109
|
+
else region[v] = 0;
|
|
110
|
+
if (region[v] >= 2) legness[v] = 0;
|
|
111
|
+
if (rn === 'feet') legness[v] = 1;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- white: a signed distance to the white areas (< 0 white), per breed
|
|
115
|
+
// Holstein: seeded piebald patches (3D noise on the reference skin: unique per individual),
|
|
116
|
+
// biased white on the lower legs, belly, udder and switch; a seeded face (blaze, star, white or
|
|
117
|
+
// black face). Hereford: white face, crest, dewlap, brisket, underline, lower legs and switch.
|
|
118
|
+
const R = rng(9311 + (params.coatSeed || 0));
|
|
119
|
+
const off = [R() * 100, R() * 100, R() * 100];
|
|
120
|
+
const cover = params.blackCover ?? 0.55; // share of the body that is black (Holstein)
|
|
121
|
+
const face = params.face || 'blaze';
|
|
122
|
+
const faceW = params.faceWidth ?? 1;
|
|
123
|
+
const freq = params.patchFreq ?? 2.4;
|
|
124
|
+
const pied = (p) => {
|
|
125
|
+
// low-frequency patches with ragged, slightly jagged edges; value > 0 = white
|
|
126
|
+
const f = fbm3(p[0] * freq * 1.35 + off[0], p[1] * freq + off[1], p[2] * freq * 0.85 + off[2], 3);
|
|
127
|
+
const j = (vnoise3(p[0] * 26 + off[1], p[1] * 26, p[2] * 26 + off[2]) - 0.5) * 0.035;
|
|
128
|
+
return f + j - (0.5 + (cover - 0.55) * 0.55);
|
|
129
|
+
};
|
|
130
|
+
const faceField = (h) => {
|
|
131
|
+
// head-local: > 0 white (front of the face)
|
|
132
|
+
const ax = Math.abs(h[0]);
|
|
133
|
+
const front = h[1] + 0.06 - 0.25 * Math.max(0, -h[2] - 0.05); // dorsal side of the face
|
|
134
|
+
let f = -1;
|
|
135
|
+
const w = faceW;
|
|
136
|
+
if (face === 'blaze') f = Math.min(0.04 * w + 0.03 * smoothstep(0.2, 0.36, h[2]) + 0.02 * smoothstep(-0.05, -0.16, h[2]) - ax, front, 0.44 - h[2]);
|
|
137
|
+
else if (face === 'star') f = 0.035 * w - Math.hypot(ax, (h[2] - 0.0) * 0.6);
|
|
138
|
+
else if (face === 'white') f = Math.min(0.11 - Math.max(0, ax - 0.03) * 0.8 - Math.max(0, -h[1] - 0.05) * 0.8, 0.45 - h[2]);
|
|
139
|
+
else if (face === 'strip') f = Math.min(0.022 * w - ax, front, 0.3 - h[2], h[2] + 0.02);
|
|
140
|
+
return f;
|
|
141
|
+
};
|
|
142
|
+
// gradient-normalised field -> metres (so the shader's edge antialiasing has the right width)
|
|
143
|
+
const toSD = (fn, p) => {
|
|
144
|
+
const e = 0.01, f0 = fn(p);
|
|
145
|
+
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;
|
|
146
|
+
const g = Math.max(0.5, Math.hypot(gx, gy, gz));
|
|
147
|
+
return -f0 / g;
|
|
148
|
+
};
|
|
149
|
+
const whiteSDF = (v) => {
|
|
150
|
+
const p = P(v), r = region[v], n = N(v);
|
|
151
|
+
const lb = legBone[v];
|
|
152
|
+
if (breed === 'holstein') {
|
|
153
|
+
if (r === 8 || r === 9 || r === 10 || r === 11) return 1;
|
|
154
|
+
if (r === 2 || r === 6 || r === 5) {
|
|
155
|
+
// head: black with the seeded face marking (ears black; patches may reach the cheeks)
|
|
156
|
+
if (r === 5) return toSD((q) => pied(q) - 0.25, p) ;
|
|
157
|
+
const fn = (q) => Math.max(faceField(HL(q)) * 8, pied(q) - 0.3 - 0.25 * smoothstep(-0.1, 0.2, HL(q)[2]));
|
|
158
|
+
return toSD(fn, p);
|
|
159
|
+
}
|
|
160
|
+
if (r === 7) return -0.02; // white switch
|
|
161
|
+
const fn = (q) => {
|
|
162
|
+
let f = pied(q);
|
|
163
|
+
// white lower legs (below the knees / hocks), white belly and udder
|
|
164
|
+
const L = legness[v];
|
|
165
|
+
if (L > 0 || lb >= 0) {
|
|
166
|
+
const knee = lb >= 0 && isFront[lb] ? 0.46 : 0.58;
|
|
167
|
+
f += 0.9 * smoothstep(knee + 0.1, knee - 0.12, q[1]) * Math.max(L, 0.3);
|
|
168
|
+
}
|
|
169
|
+
f += 0.55 * smoothstep(0.8, 0.62, q[1]) * (1 - L * 0.5);
|
|
170
|
+
if (UDDER_TAGS.has(tagOf[v])) f += 0.5;
|
|
171
|
+
// the tail: usually black at the root, white further down
|
|
172
|
+
if (r === 4) f += 0.5 * smoothstep(1.2, 0.8, q[1]);
|
|
173
|
+
return f;
|
|
174
|
+
};
|
|
175
|
+
return toSD(fn, p);
|
|
176
|
+
}
|
|
177
|
+
if (breed === 'hereford') {
|
|
178
|
+
if (r === 8 || r === 9 || r === 10 || r === 11) return 1;
|
|
179
|
+
const jag = (fbm3(p[0] * 40 + off[0], p[1] * 40, p[2] * 40, 2) - 0.5) * 0.03;
|
|
180
|
+
if (r === 2 || r === 6) {
|
|
181
|
+
const h = HL(p);
|
|
182
|
+
// white face; red around the eyes on some ("spectacles"); the white runs over the poll
|
|
183
|
+
let d = -0.02 + Math.max(0, h[2] < -0.15 ? (-0.15 - h[2]) * 0.5 : 0);
|
|
184
|
+
if (params.spectacles) for (const ef of eyeFrames) d = Math.max(d, 0.03 - Math.hypot(p[0] - ef.c[0], p[1] - ef.c[1], p[2] - ef.c[2]));
|
|
185
|
+
return d + jag;
|
|
186
|
+
}
|
|
187
|
+
if (r === 5) return 0.02; // red ears
|
|
188
|
+
if (r === 7) return -0.02;
|
|
189
|
+
if (r === 4) return 0.02;
|
|
190
|
+
let d = 1;
|
|
191
|
+
// crest: white along the top of the neck behind the poll
|
|
192
|
+
if (r === 1) d = Math.min(d, Math.abs(p[0]) - 0.06 - 0.06 * smoothstep(0.85, 1.08, p[2]) + (1.36 - p[1]) * 0.6 + 0.2 * smoothstep(0.85, 0.7, p[2]));
|
|
193
|
+
// underline: throat, dewlap, brisket, belly
|
|
194
|
+
// (the white line along the belly rises into the brisket, dewlap and throat in front)
|
|
195
|
+
const lineY = 0.7 + 0.3 * smoothstep(0.45, 0.8, p[2]) + 0.05 * smoothstep(-0.3, -0.6, p[2]);
|
|
196
|
+
if (legness[v] < 0.5) d = Math.min(d, (p[1] - lineY) + 0.25 * Math.max(0, Math.abs(p[0]) - 0.12) * (p[2] < 0.5 ? 1 : 0));
|
|
197
|
+
// lower legs
|
|
198
|
+
if (legness[v] > 0.3 && lb >= 0) d = Math.min(d, p[1] - (isFront[lb] ? 0.3 : 0.36) + (fbm3(p[0] * 18 + off[1], p[1] * 9, p[2] * 18, 2) - 0.5) * 0.12);
|
|
199
|
+
if (UDDER_TAGS.has(tagOf[v])) d = -0.02;
|
|
200
|
+
return d + jag;
|
|
201
|
+
}
|
|
202
|
+
void n;
|
|
203
|
+
return 1;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// --- ventral factor (lighter belly, inner legs)
|
|
207
|
+
const ventral = new Float32Array(nV);
|
|
208
|
+
for (let v = 0; v < nV; v++) {
|
|
209
|
+
const n = N(v), r = region[v], p = P(v);
|
|
210
|
+
let w = 0;
|
|
211
|
+
if (r === 0) w = smoothstep(-0.2, -0.8, n[1]) * smoothstep(1.1, 0.85, p[1]);
|
|
212
|
+
else if (r === 1) w = smoothstep(0.0, -0.7, n[1]) * 0.5;
|
|
213
|
+
const L = legness[v];
|
|
214
|
+
if (L > 0) { const side = p[0] >= 0 ? 1 : -1; w = mix(w, smoothstep(0.1, -0.7, n[0] * side) * 0.6 * smoothstep(0.4, 0.8, p[1]), L); }
|
|
215
|
+
ventral[v] = w;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// --- comb (hair flow)
|
|
219
|
+
const comb = new Float32Array(nV * 3);
|
|
220
|
+
const whorl = [0, 0.06, -0.02]; // forehead whorl, head-local
|
|
221
|
+
for (let v = 0; v < nV; v++) {
|
|
222
|
+
const p = P(v), n = N(v), r = region[v];
|
|
223
|
+
const bone = BONES[dominant[v]];
|
|
224
|
+
let d;
|
|
225
|
+
if (r === 5) d = norm(sub(bone.tail, bone.head));
|
|
226
|
+
else if (r === 8) d = norm(sub(p, add(HEAD_O, [0, 0.06, -0.12]))); // horn: along its length
|
|
227
|
+
else if (r === 2 || r === 6 || r === 9 || r === 10 || r === 11) {
|
|
228
|
+
const h = HL(p);
|
|
229
|
+
const rel = [h[0] - whorl[0], h[1] - whorl[1], h[2] - whorl[2]];
|
|
230
|
+
const w = [rel[0], rel[1] * 0.4 - 0.05, rel[2] + 0.08];
|
|
231
|
+
d = norm(add(mul(HY, w[1]), add(mul(HZ, w[2]), [w[0], 0, 0])));
|
|
232
|
+
if (h[2] < -0.1) d = norm(add(d, [0, -0.6, -0.8]));
|
|
233
|
+
} else if (r === 7 || r === 4) {
|
|
234
|
+
d = norm(sub(bone.tail, bone.head));
|
|
235
|
+
} else {
|
|
236
|
+
const seg = weights.segT[v * 2] | 0;
|
|
237
|
+
const a = rig.AXIAL.points[seg], b = rig.AXIAL.points[Math.min(seg + 1, rig.AXIAL.points.length - 1)];
|
|
238
|
+
d = norm(sub(b, a));
|
|
239
|
+
if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
|
|
240
|
+
if (r === 1) d = norm(add(d, [0, -0.6, 0.1]));
|
|
241
|
+
const L = legness[v];
|
|
242
|
+
if (L > 0 && legBone[v] >= 0) {
|
|
243
|
+
const lbn = BONES[legBone[v]];
|
|
244
|
+
let dl = norm(sub(lbn.tail, lbn.head));
|
|
245
|
+
if (/hoof/.test(lbn.name)) dl = [0, -1, 0.1];
|
|
246
|
+
else dl = norm(add(dl, [0, -0.3, 0]));
|
|
247
|
+
d = norm(add(mul(d, 1 - L), mul(dl, L)));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
let t = sub(d, mul(n, dot(d, n)));
|
|
251
|
+
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
252
|
+
t = norm(t);
|
|
253
|
+
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// --- per-vertex fields
|
|
257
|
+
const pattern = new Float32Array(nV).fill(1);
|
|
258
|
+
const patInt = new Float32Array(nV);
|
|
259
|
+
const patCol = new Float32Array(nV * 3);
|
|
260
|
+
const markSDF = new Float32Array(nV).fill(1);
|
|
261
|
+
const furLen = new Float32Array(nV);
|
|
262
|
+
const tint = new Float32Array(nV * 4);
|
|
263
|
+
const surf = new Float32Array(nV * 4);
|
|
264
|
+
const shag = params.shaggy ? 1 : 0;
|
|
265
|
+
const hornLen = params.horns ? params.horns.len : 0;
|
|
266
|
+
|
|
267
|
+
for (let v = 0; v < nV; v++) {
|
|
268
|
+
const p = P(v), n = N(v), r = region[v], tag = tagOf[v];
|
|
269
|
+
let col, mat = MAT.FUR, fl = 0.008, mark = 1, gloss = 0.55, sy = 0;
|
|
270
|
+
const wsdf = whiteSDF(v);
|
|
271
|
+
const white = wsdf < 0;
|
|
272
|
+
const lb = legBone[v];
|
|
273
|
+
const lbName = lb >= 0 ? BONES[lb].name : '';
|
|
274
|
+
if (r === 0 || r === 1) {
|
|
275
|
+
const up = clamp(n[1], -1, 1);
|
|
276
|
+
col = mix3(COL.body, COL.dorsal, smoothstep(0.3, 0.95, up) * 0.8);
|
|
277
|
+
col = mix3(col, COL.belly, ventral[v]);
|
|
278
|
+
if (COL.dark) {
|
|
279
|
+
// Jersey: darker over the neck, shoulders and hips (much darker in bulls)
|
|
280
|
+
const dk = (r === 1 ? 0.55 : 0.25 * smoothstep(0.55, 0.85, p[2]) + 0.3 * smoothstep(-0.55, -0.85, p[2]) * smoothstep(1.0, 1.3, p[1])) * (1 + 0.8 * (params.bull || 0));
|
|
281
|
+
col = mix3(col, COL.dark, clamp(dk, 0, 0.85) * (1 - ventral[v]));
|
|
282
|
+
}
|
|
283
|
+
fl = mix(0.008, 0.011, ventral[v]);
|
|
284
|
+
if (r === 1 && (params.bull || 0) > 0.5) fl = 0.014; // bull: coarser curly neck hair
|
|
285
|
+
const L = legness[v];
|
|
286
|
+
if (L > 0) {
|
|
287
|
+
fl = mix(fl, mix(0.004, 0.008, smoothstep(0.25, 0.8, p[1])), L);
|
|
288
|
+
if (COL.dark) col = mix3(col, COL.dark, 0.5 * smoothstep(0.5, 0.15, p[1]) * L); // Jersey: dark lower legs
|
|
289
|
+
}
|
|
290
|
+
// hooves, heel bulbs and dew claws below the coronet line (higher at the toe than at the heel)
|
|
291
|
+
const Cj = /hoof|paw/.test(lbName) ? J[(lbName[0] === 'f' ? 'fcoffin' : 'hcoffin') + BONES[lb].side] : null;
|
|
292
|
+
if (Cj && HOOF_TAGS.has(tag) && (tag === 'dewclaw' || p[1] < Cj[1] + 0.016 + 0.35 * (p[2] - Cj[2]) * (lbName[0] === 'f' ? 1 : 1))) {
|
|
293
|
+
mat = MAT.KERATIN; fl = 0; gloss = 0.5;
|
|
294
|
+
// (under white hair: slate-horn claws with dark vertical pigment streaks, as in most pied
|
|
295
|
+
// cattle; pure pale claws read as white plastic slippers)
|
|
296
|
+
if (white) {
|
|
297
|
+
const streak = smoothstep(0.45, 0.62, fbm3(p[0] * 90 + (params.coatSeed % 97), p[1] * 6, p[2] * 90, 2));
|
|
298
|
+
col = mix3(COL.hoofPale, COL.hoof, 0.25 + 0.6 * streak);
|
|
299
|
+
} else col = COL.hoof;
|
|
300
|
+
if (tag === 'heelbulb') col = mix3(col, [0.08, 0.06, 0.05], 0.4);
|
|
301
|
+
}
|
|
302
|
+
if (UDDER_TAGS.has(tag)) {
|
|
303
|
+
// udder: bare pink skin with a sparse fine coat; teats bare
|
|
304
|
+
mat = MAT.SKIN; gloss = 0.35;
|
|
305
|
+
col = breed === 'angus' || breed === 'highland' ? [0.05, 0.04, 0.04] : breed === 'jersey' ? mix3(COL.udder, COL.body, 0.45) : COL.udder;
|
|
306
|
+
fl = tag === 'teat' ? 0 : 0.0025;
|
|
307
|
+
}
|
|
308
|
+
if (tag === 'sheath' || tag === 'scrotum') { fl = tag === 'sheath' ? 0.02 : 0.004; }
|
|
309
|
+
} else if (r === 4) {
|
|
310
|
+
col = mix3(COL.body, COL.dorsal, 0.4);
|
|
311
|
+
fl = 0.006;
|
|
312
|
+
} else if (r === 7) {
|
|
313
|
+
// switch: a long tassel (white on Holsteins and Herefords, black on Angus and Jerseys)
|
|
314
|
+
col = breed === 'jersey' ? srgb(0x1a1614) : breed === 'holstein' || breed === 'hereford' ? COL.white : mix3(COL.body, [0.02, 0.02, 0.02], 0.2);
|
|
315
|
+
fl = calf ? 0.03 : 0.05;
|
|
316
|
+
} else if (r === 5) {
|
|
317
|
+
// ears: coat colour outside, long pale hair inside the pinna
|
|
318
|
+
const earP = model.prims.find((q) => q.tag === 'ear' && (q.bone === (p[0] > 0 ? 'earL' : 'earR')));
|
|
319
|
+
const faceDir = [earP.P[9], earP.P[10], earP.P[11]];
|
|
320
|
+
const front = dot(n, faceDir);
|
|
321
|
+
col = COL.head;
|
|
322
|
+
fl = 0.006;
|
|
323
|
+
if (front > 0.3) { fl = 0.016; col = mix3(col, srgb(0x9a8a80), 0.12); }
|
|
324
|
+
else if (front > -0.2) fl = 0.012; // (the fringe of hair around the rim)
|
|
325
|
+
} else if (r === 8) {
|
|
326
|
+
// horn: pale waxy base, dark tip, growth rings
|
|
327
|
+
mat = MAT.KERATIN; fl = 0; gloss = 0.7;
|
|
328
|
+
const hb = [HEAD_O[0] + Math.sign(p[0]) * 0.082, 0, 0];
|
|
329
|
+
const along = clamp((Math.abs(p[0]) - Math.abs(hb[0])) / Math.max(0.08, hornLen * 0.85), 0, 1);
|
|
330
|
+
const hl2 = params.horns && (params.horns.style === 'down' || params.horns.style === 'lyre' || params.horns.style === 'wide') ? along : along;
|
|
331
|
+
col = mix3(COL.hornBase, COL.hornTip, smoothstep(0.55, 0.95, hl2));
|
|
332
|
+
col = mix3(col, mul(COL.hornBase, 0.75), 0.25 * smoothstep(0.15, 0.0, hl2));
|
|
333
|
+
} else if (r === 9) {
|
|
334
|
+
mat = MAT.SKIN; fl = 0; gloss = 0.75; col = srgb(TAG);
|
|
335
|
+
} else if (r === 10) {
|
|
336
|
+
mat = MAT.KERATIN; fl = 0; gloss = 0.95; col = srgb(RING);
|
|
337
|
+
} else if (r === 11) {
|
|
338
|
+
mat = MAT.MOUTH; fl = 0;
|
|
339
|
+
col = [0.3, 0.12, 0.13];
|
|
340
|
+
} else {
|
|
341
|
+
// head & jaw
|
|
342
|
+
col = COL.head;
|
|
343
|
+
fl = 0.006;
|
|
344
|
+
const h = HL(p);
|
|
345
|
+
// Jersey: the dished face darker, a pale ring around the black muzzle, mealy eye rings
|
|
346
|
+
if (COL.dark) col = mix3(col, COL.body, 0.25 * smoothstep(0.05, 0.25, h[2]));
|
|
347
|
+
// poll tuft / curly forehead (bulls, Herefords)
|
|
348
|
+
if (h[1] > 0.02 && h[2] < 0.12) fl = mix(fl, (params.bull || 0) > 0.5 || breed === 'hereford' ? 0.028 : 0.016, smoothstep(0.02, 0.06, h[1]) * smoothstep(0.12, -0.05, h[2]));
|
|
349
|
+
// moist pebbled muzzle plate (planum nasolabiale) with the nostrils; pink under white hair
|
|
350
|
+
const planum = smoothstep(0.33, 0.36, h[2]) * smoothstep(-0.115, -0.1, h[1]) * smoothstep(0.02, 0.0, h[1] - 0.02 * (h[2] - 0.35));
|
|
351
|
+
const muzzleWhite = breed === 'holstein' ? white || faceField(h) > -0.02 : breed === 'hereford';
|
|
352
|
+
const muzCol = muzzleWhite ? COL.pink : COL.muzzleDark;
|
|
353
|
+
if (planum > 0.5) { mat = MAT.WET_SKIN; col = muzCol; fl = 0; gloss = 0.8; sy = 0.0018; }
|
|
354
|
+
else {
|
|
355
|
+
// a fringe of short hair in the muzzle colour around the moist plate (hides the material step)
|
|
356
|
+
const fr = smoothstep(0.29, 0.34, h[2]) * smoothstep(-0.13, -0.105, h[1]);
|
|
357
|
+
if (fr > 0) { fl = mix(fl, 0.002, fr); col = mix3(col, muzCol, fr); }
|
|
358
|
+
}
|
|
359
|
+
if (COL.dark) {
|
|
360
|
+
// Jersey: pale ring around the dark muzzle
|
|
361
|
+
const ring = smoothstep(0.26, 0.3, h[2]) * (1 - smoothstep(0.33, 0.35, h[2]));
|
|
362
|
+
col = mix3(col, COL.white, ring * 0.85);
|
|
363
|
+
for (const ef of eyeFrames) { const de = Math.hypot(p[0] - ef.c[0], p[1] - ef.c[1], p[2] - ef.c[2]); col = mix3(col, mix3(COL.body, COL.white, 0.5), 0.6 * smoothstep(0.045, 0.028, de)); }
|
|
364
|
+
}
|
|
365
|
+
if (h[2] < -0.14) col = mix3(col, COL.body, smoothstep(-0.14, -0.2, h[2]) * 0.6);
|
|
366
|
+
// chin whiskers / tactile hairs are part of the short muzzle hair; lips: fine hair
|
|
367
|
+
// eye rims: the lid margin is bare, moist skin, dark grey-brown (a shade lighter than black hair:
|
|
368
|
+
// the lid line that separates the eye from a black face; pink in white faces, research 7), with no
|
|
369
|
+
// drawn line; lashes along the upper lid, rising from the margin
|
|
370
|
+
for (let e = 0; e < eyePrims.length; e++) {
|
|
371
|
+
const de = Math.abs(SDFModel.dist(eyePrims[e], p[0], p[1], p[2]));
|
|
372
|
+
// the fine short hair round the eye (a shade greyer on a dark face: skin shows through it)
|
|
373
|
+
if (de < 0.016 && !white) col = mix3(col, LID_RING, 0.45 * smoothstep(0.016, 0.005, de));
|
|
374
|
+
if (de < 0.016) fl = Math.min(fl, mix(0.003, fl, smoothstep(0.009, 0.016, de)));
|
|
375
|
+
if (de < 0.0035) { mat = MAT.SKIN; fl = 0; gloss = 0.6; col = white ? LID_PINK : LID_DARK; }
|
|
376
|
+
else if (de < 0.009) {
|
|
377
|
+
const ef = eyeFrames[p[0] > 0 ? 0 : 1];
|
|
378
|
+
const rel = sub(p, ef.c);
|
|
379
|
+
const upAmt = dot(rel, ef.y) / 0.018;
|
|
380
|
+
if (upAmt > 0.2) {
|
|
381
|
+
// lashes: up to 9 mm at the middle of the upper lid, sweeping out, forward and down over
|
|
382
|
+
// the eye; they rise over the 6 mm next to the margin (a longer fringe on a thin band
|
|
383
|
+
// stood up as a wall of fur)
|
|
384
|
+
fl = 0.003 + 0.006 * smoothstep(0.2, 0.55, upAmt) * smoothstep(0.009, 0.0035, de);
|
|
385
|
+
col = white ? mix3(col, [0.6, 0.55, 0.5], 0.3) : [0.012, 0.011, 0.01];
|
|
386
|
+
const lash = norm(add(add(mul(ef.z, 1.0), mul(ef.y, 0.15)), mul(norm(rel), 0.4)));
|
|
387
|
+
let t = sub(lash, mul(n, dot(lash, n)));
|
|
388
|
+
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]; }
|
|
389
|
+
} else fl = Math.min(fl, 0.003);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// nostrils: dark rims
|
|
393
|
+
for (const e of nostrilPrims) {
|
|
394
|
+
const dn = SDFModel.dist(e, p[0], p[1], p[2]);
|
|
395
|
+
if (dn < 0.004) { mat = MAT.WET_SKIN; col = mul(muzCol, 0.35); fl = 0; mark = Math.min(mark, dn - 0.003); gloss = 0.8; sy = 0.0015; }
|
|
396
|
+
}
|
|
397
|
+
// lips: head vertices near the jaw surface, jaw vertices near the head surface
|
|
398
|
+
if (r !== 6) {
|
|
399
|
+
const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
|
|
400
|
+
if (dj < 0.002 && h[2] > 0.18) { mat = MAT.MOUTH; fl = 0; }
|
|
401
|
+
else if (dj < 0.006 && h[2] > 0.18) { mark = Math.min(mark, dj - 0.005); fl = Math.min(fl, 0.002); }
|
|
402
|
+
} else {
|
|
403
|
+
const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
|
|
404
|
+
if (dh < 0.0) { mat = MAT.MOUTH; fl = 0; }
|
|
405
|
+
else if (dh < 0.005) { mark = Math.min(mark, dh - 0.004); fl = Math.min(fl, 0.002); }
|
|
406
|
+
if (mat === MAT.FUR && h[2] > 0.33) { col = mix3(col, muzCol, 0.6); fl = 0.003; }
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
// Highland: long shaggy coat over everything but the muzzle, lower legs and horns
|
|
410
|
+
if (shag && mat === MAT.FUR) {
|
|
411
|
+
let sl = 0.075;
|
|
412
|
+
if (r === 2 || r === 6) { const h = HL(p); sl = h[2] < 0.12 ? (h[1] > -0.02 ? 0.11 : 0.06) : mix(0.05, 0.01, smoothstep(0.15, 0.3, h[2])); }
|
|
413
|
+
if (r === 5) sl = 0.05;
|
|
414
|
+
if (r === 7) sl = 0.08;
|
|
415
|
+
if (legness[v] > 0) sl = mix(sl, 0.02, smoothstep(0.7, 0.3, p[1]) * legness[v]);
|
|
416
|
+
fl = Math.max(fl, sl * (calf ? 0.6 : 1));
|
|
417
|
+
}
|
|
418
|
+
// white hair (patches, face, socks): the pattern colour over the base (pink skin under it)
|
|
419
|
+
// (the pattern field stays continuous: capped just above zero where there is nothing)
|
|
420
|
+
// Bare skin, keratin, lids and muzzle lie inside the pattern with their own colour as its colour:
|
|
421
|
+
// at a white-hair / bare-skin border white then blends into pink; outside the pattern the base
|
|
422
|
+
// tint of the white hair beside them (a Holstein's base is black) showed as a dark rim round the
|
|
423
|
+
// udder, the muzzle and the coronets
|
|
424
|
+
const CAP = 0.02;
|
|
425
|
+
const bare = mat !== MAT.FUR;
|
|
426
|
+
const pd = bare ? -CAP : clamp(wsdf, -CAP, CAP);
|
|
427
|
+
// low-frequency colour variation
|
|
428
|
+
const cv = fbm3(p[0] * 5, p[1] * 5, p[2] * 5, 3) - 0.5;
|
|
429
|
+
if (mat === MAT.FUR) col = [col[0] * (1 + 0.14 * cv), col[1] * (1 + 0.12 * cv), col[2] * (1 + 0.1 * cv)];
|
|
430
|
+
if (r === 7 || shag) {
|
|
431
|
+
const c = [comb[v * 3], comb[v * 3 + 1], comb[v * 3 + 2]];
|
|
432
|
+
const along = dot(p, c), q = sub(p, mul(c, along));
|
|
433
|
+
const st = vnoise3(q[0] * 90, q[1] * 90, q[2] * 90 + along * 3);
|
|
434
|
+
col = mul(col, 0.8 + 0.4 * st);
|
|
435
|
+
}
|
|
436
|
+
pattern[v] = pd; patInt[v] = 1;
|
|
437
|
+
const wc = bare ? col : COL.white;
|
|
438
|
+
patCol[v * 3] = wc[0]; patCol[v * 3 + 1] = wc[1]; patCol[v * 3 + 2] = wc[2];
|
|
439
|
+
markSDF[v] = mark;
|
|
440
|
+
furLen[v] = fl;
|
|
441
|
+
tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
|
|
442
|
+
surf[v * 4] = gloss; surf[v * 4 + 1] = sy;
|
|
443
|
+
}
|
|
444
|
+
// hair outside the pattern (black) beside bare skin takes that skin's colour as its pattern colour:
|
|
445
|
+
// across the border the pattern colour then runs from the skin's own colour, not from white (a pale
|
|
446
|
+
// seam round a dark muzzle or hoof)
|
|
447
|
+
const nbr = weights.neighbors;
|
|
448
|
+
for (let v = 0; v < nV; v++) {
|
|
449
|
+
if (tint[v * 4 + 3] !== MAT.FUR || pattern[v] < 0) continue;
|
|
450
|
+
let n = 0, c0 = 0, c1 = 0, c2 = 0;
|
|
451
|
+
for (const u of nbr[v]) if (tint[u * 4 + 3] !== MAT.FUR) { n++; c0 += patCol[u * 3]; c1 += patCol[u * 3 + 1]; c2 += patCol[u * 3 + 2]; }
|
|
452
|
+
if (n) { patCol[v * 3] = c0 / n; patCol[v * 3 + 1] = c1 / n; patCol[v * 3 + 2] = c2 / n; }
|
|
453
|
+
}
|
|
454
|
+
// calves: soft fluffy body coat
|
|
455
|
+
if (calf) for (let v = 0; v < nV; v++) if (tint[v * 4 + 3] === MAT.FUR && region[v] <= 1) furLen[v] = Math.max(furLen[v], 0.014);
|
|
456
|
+
smoothField(furLen, weights.neighbors, 2, (v) => tint[v * 4 + 3] !== MAT.FUR);
|
|
457
|
+
|
|
458
|
+
const patternColor = new Float32Array(nV * 4);
|
|
459
|
+
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]; }
|
|
460
|
+
void BONE_SEGS; void skinWeight;
|
|
461
|
+
const dbg = {};
|
|
462
|
+
if (globalThis.COWDBG) for (let v = 0; v < nV; v++) { const p = P(v); if (p[1] < 0.06 && p[2] > 0 && p[0] > 0) { const k = region[v] + ' ' + tagOf[v] + ' ' + (legBone[v] >= 0 ? BONES[legBone[v]].name : '-') + ' m' + tint[v * 4 + 3]; dbg[k] = (dbg[k] || 0) + 1; } }
|
|
463
|
+
if (globalThis.COWDBG) console.log(dbg);
|
|
464
|
+
return { comb, tint, pattern, mark: markSDF, furLen, patternColor, surf, stats: {}, region, ventral };
|
|
465
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Domestic cattle (Bos taurus). Cloven-hoofed (two claws and two dew claws per foot, on the horse's
|
|
2
|
+
// hoof-bone rig), a big barrel with prominent hooks and pins, dewlap, udder or bull's crest, a moist
|
|
3
|
+
// pebbled muzzle, horizontal-bar pupils behind long lashes, a switch tail; seeded breeds:
|
|
4
|
+
// Holstein-Friesian (default: black-and-white piebald patches grown per individual), Hereford (red,
|
|
5
|
+
// white face), Angus (black, polled), Jersey (fawn, dark face, dished), Highland (shaggy, long horns).
|
|
6
|
+
// Behaviours: the walk's head bob, grazing with the tongue, chewing the cud (standing and lying),
|
|
7
|
+
// lying down front knees first and rising hind end first, fly swishes, ear flicks, nose licks, the
|
|
8
|
+
// bull's pawing charge and the cow's sideways kick.
|
|
9
|
+
import { cowRig, HEAD_O } from './rig.js';
|
|
10
|
+
import { sculptCow, EYE } from './sculpt.js';
|
|
11
|
+
import { cowRegions } from './regions.js';
|
|
12
|
+
import { cowCoat } from './coat.js';
|
|
13
|
+
import { srgb } from '../../core/build/coatKit.js';
|
|
14
|
+
import { motion } from './motion.js';
|
|
15
|
+
|
|
16
|
+
const pick = (R, table) => {
|
|
17
|
+
let x = R() * table.reduce((s, [, w]) => s + w, 0);
|
|
18
|
+
for (const [k, w] of table) { x -= w; if (x <= 0) return k; }
|
|
19
|
+
return table[table.length - 1][0];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// withers height (m) of adult cows / bulls per breed, frame and build
|
|
23
|
+
const BREED = {
|
|
24
|
+
holstein: { name: 'Holstein-Friesian', h: [1.45, 1.63], dairy: 1, udder: [0.8, 1.15], dewlap: 0.35, tags: 0.7, legs: 1, girth: 1, length: 1 },
|
|
25
|
+
hereford: { name: 'Hereford', h: [1.34, 1.5], dairy: 0, udder: [0.3, 0.45], dewlap: 0.85, tags: 0.5, legs: 0.9, girth: 1.08, length: 0.97 },
|
|
26
|
+
angus: { name: 'Aberdeen Angus', h: [1.3, 1.45], dairy: 0, udder: [0.3, 0.45], dewlap: 0.45, tags: 0.5, legs: 0.89, girth: 1.08, length: 0.96 },
|
|
27
|
+
jersey: { name: 'Jersey', h: [1.2, 1.4], dairy: 1, udder: [0.7, 0.95], dewlap: 0.35, tags: 0.7, legs: 1, girth: 0.94, length: 0.98 },
|
|
28
|
+
highland: { name: 'Highland', h: [1.12, 1.25], dairy: 0.2, udder: [0.25, 0.35], dewlap: 0.3, tags: 0.25, legs: 0.84, girth: 1.06, length: 0.95 },
|
|
29
|
+
};
|
|
30
|
+
const VARIANT_KEYS = Object.keys(BREED);
|
|
31
|
+
|
|
32
|
+
export default {
|
|
33
|
+
id: 'cow',
|
|
34
|
+
name: 'Cattle',
|
|
35
|
+
latin: 'Bos taurus',
|
|
36
|
+
group: 'hooves',
|
|
37
|
+
plan: 'quadruped',
|
|
38
|
+
covering: 'fur',
|
|
39
|
+
variants: VARIANT_KEYS.map((id) => ({ id, name: BREED[id].name, latin: 'Bos taurus' })),
|
|
40
|
+
|
|
41
|
+
// Seeded individual. opts.variant picks the breed (default Holstein); sex: cow (udder) or bull
|
|
42
|
+
// (crest, larger and thicker horns, sheath, sometimes a nose ring); age: adult or calf (juvenile:
|
|
43
|
+
// long legs, short body, big head, no horns, soft coat); size, frame, coat pattern / shade, face
|
|
44
|
+
// marking, horns (per breed: mostly disbudded Holsteins, polled Angus, forward-down Hereford horns,
|
|
45
|
+
// wide Highland horns), ear tags.
|
|
46
|
+
variation(R, o) {
|
|
47
|
+
const variant = BREED[o.variant] ? o.variant : 'holstein';
|
|
48
|
+
const B = BREED[variant];
|
|
49
|
+
const sex = o.sex || (R() < 0.7 ? 'female' : 'male');
|
|
50
|
+
const age = o.age || 'adult';
|
|
51
|
+
const calf = age === 'juvenile';
|
|
52
|
+
const male = sex === 'male';
|
|
53
|
+
const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
|
|
54
|
+
const bull = male ? (calf ? 0.25 : 0.85 + 0.15 * R()) : 0;
|
|
55
|
+
const frame = g();
|
|
56
|
+
const size = (B.h[male ? 1 : 0] / 1.45) * (1 + 0.035 * frame) * (calf ? 0.5 : 1);
|
|
57
|
+
// coat colour within the breed
|
|
58
|
+
let coat = variant;
|
|
59
|
+
if (variant === 'holstein' && R() < 0.12) coat = 'redholstein';
|
|
60
|
+
if (variant === 'angus' && R() < 0.15) coat = 'redangus';
|
|
61
|
+
if (variant === 'highland') coat = pick(R, [['highland', 60], ['highlandblack', 20], ['highlanddun', 20]]);
|
|
62
|
+
// horns
|
|
63
|
+
let horns = null;
|
|
64
|
+
const hr = R();
|
|
65
|
+
if (!calf) {
|
|
66
|
+
if (variant === 'holstein' && hr < (male ? 0.5 : 0.15)) horns = male ? { style: 'bull', len: 0.17 + 0.07 * R(), r: 0.042, a0: 12, a1: 60 } : { style: 'lyre', len: 0.24 + 0.08 * R(), r: 0.027, a0: 10, a1: 100 };
|
|
67
|
+
if (variant === 'hereford' && hr < 0.5) horns = male ? { style: 'down', len: 0.2 + 0.05 * R(), r: 0.045, a0: 8, a1: 70 } : { style: 'down', len: 0.26 + 0.07 * R(), r: 0.03, a0: 6, a1: 85 };
|
|
68
|
+
if (variant === 'jersey' && hr < (male ? 0.4 : 0.2)) horns = male ? { style: 'bull', len: 0.16 + 0.05 * R(), r: 0.036, a0: 12, a1: 65 } : { style: 'lyre', len: 0.2 + 0.06 * R(), r: 0.023, a0: 10, a1: 105 };
|
|
69
|
+
if (variant === 'highland') horns = male ? { style: 'wide', len: 0.4 + 0.08 * R(), r: 0.055, a0: 4, a1: 55, curlPow: 1.6 } : { style: 'wide', len: 0.52 + 0.14 * R(), r: 0.04, a0: 4, a1: 85, curlPow: 1.5 };
|
|
70
|
+
}
|
|
71
|
+
const face = variant === 'holstein' ? pick(R, [['blaze', 45], ['star', 12], ['white', 15], ['strip', 15], ['none', 13]]) : 'none';
|
|
72
|
+
return {
|
|
73
|
+
variant, coat, sex, age, size,
|
|
74
|
+
bull, dairy: B.dairy,
|
|
75
|
+
udder: male || calf ? 0 : B.udder[0] + (B.udder[1] - B.udder[0]) * R(),
|
|
76
|
+
dewlap: B.dewlap * (male ? 1.4 : 1) * (0.8 + 0.4 * R()),
|
|
77
|
+
horns,
|
|
78
|
+
polledPeak: variant === 'angus' ? 0.6 + 0.4 * R() : 0,
|
|
79
|
+
dished: variant === 'jersey' ? 1 : 0,
|
|
80
|
+
shaggy: variant === 'highland',
|
|
81
|
+
earTags: R() < (calf ? 0.85 : B.tags),
|
|
82
|
+
noseRing: male && !calf && variant !== 'highland' && R() < 0.5,
|
|
83
|
+
face, faceWidth: 0.7 + 0.7 * R(),
|
|
84
|
+
blackCover: 0.55 + 0.22 * g(),
|
|
85
|
+
patchFreq: 2.1 + 0.7 * R(),
|
|
86
|
+
spectacles: variant === 'hereford' && R() < 0.25,
|
|
87
|
+
switchLen: 0.8 + 0.3 * R(),
|
|
88
|
+
coatShade: g(), coatLightness: 0.06 * g(),
|
|
89
|
+
coatSeed: Math.floor(R() * 1e6),
|
|
90
|
+
minThick: 0.006,
|
|
91
|
+
warps: [
|
|
92
|
+
{ type: 'legs', k: B.legs * (1 + 0.03 * g()) * (calf ? 1.42 : 1), top: 0.72 },
|
|
93
|
+
{ type: 'length', k: B.length * (1 + 0.025 * g()) * (calf ? 0.84 : 1), z0: -0.7, z1: 0.62 },
|
|
94
|
+
{ type: 'girth', k: B.girth * (1 + 0.04 * g()) * (male && !calf ? 1.04 : 1) * (calf ? 0.9 : 1), cy: 1.05, z0: -0.85, z1: 0.75 },
|
|
95
|
+
{ type: 'scaleAbout', c: HEAD_O, k: (1 + 0.03 * g()) * (calf ? 1.3 : 1) * (male && !calf ? 1.06 : 1) * (variant === 'jersey' ? 0.95 : 1), r0: 0.22, r1: 0.42 },
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
rig: () => cowRig(),
|
|
100
|
+
sculpt: (m, rig, params) => sculptCow(m, rig, params),
|
|
101
|
+
regions: cowRegions,
|
|
102
|
+
coat: cowCoat,
|
|
103
|
+
eyeSpecs: () => [1, -1].map((side) => ({ side, spec: EYE, headOrigin: HEAD_O, bone: 'head', look: EYE_LOOK })),
|
|
104
|
+
motion,
|
|
105
|
+
render: { markColor: srgb(0x1a1616), strandDensity: 820, clumpDensity: 1, shellScale: 1, finWidth: 0.45, flatMaterial: true },
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Horizontal bar pupil in a dark brown iris (lighter brown fibres round the pupil, a dark limbus: the
|
|
109
|
+
// bar reads against it), a rim of white sclera at the corners, dark grey-brown lids (a shade lighter
|
|
110
|
+
// than black hair, so the lid line separates the eye from a black face).
|
|
111
|
+
export const EYE_LOOK = {
|
|
112
|
+
pupil: 'bar',
|
|
113
|
+
lids: 'mammal',
|
|
114
|
+
iris: { inner: srgb(0x2a180d), mid: srgb(0x4a2e1c), hi: srgb(0x7a5234), outer: srgb(0x160c06) },
|
|
115
|
+
sclera: { color: [0.72, 0.68, 0.64], visible: true },
|
|
116
|
+
lidColor: [0.055, 0.045, 0.04],
|
|
117
|
+
pupilSize: [0.14, 0.42],
|
|
118
|
+
};
|