@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,495 @@
|
|
|
1
|
+
// The dog's coat, painted per vertex in reference space, by variant and colour.
|
|
2
|
+
//
|
|
3
|
+
// shepherd black-and-tan saddle (FCI 166): black saddle from the withers over the back and into the
|
|
4
|
+
// tail top, reaching down the flanks; black mask on the muzzle and round the eyes, dark
|
|
5
|
+
// forehead and ear backs; rich tan legs, cheeks, chest and breeches; pale cream inner
|
|
6
|
+
// thighs and belly. Sable: grey-tan hair with black tips (the shader's agouti band) over
|
|
7
|
+
// the whole body, darker along the back, a dark mask. Solid black (rare). Medium double
|
|
8
|
+
// coat: ruff, breeches and a bushy tail are longer.
|
|
9
|
+
// retriever solid yellow (cream to fox red), black or chocolate (FCI 122); a little darker on the
|
|
10
|
+
// ears and along the back, paler on the belly and the feathering in yellows. Short, dense,
|
|
11
|
+
// glossy coat; liver nose and lids in chocolates.
|
|
12
|
+
// terrier white ground with crisp patches (pattern SDF): a tan head mask over the eyes and ears split
|
|
13
|
+
// by a white blaze, and in tricolours black body patches (saddle spot, tail root) with a tan
|
|
14
|
+
// rim. Smooth short coat.
|
|
15
|
+
// Pups: softer, fluffier, a little paler; shepherd pups are darker (sooty) until the tan comes in.
|
|
16
|
+
import { HEAD_O, MUZZLE_Z0, HS } from './rig.js';
|
|
17
|
+
import { neckS, NECK_CUT } from './regions.js';
|
|
18
|
+
import { harmonizeSeamWeights } from '../../core/build/seams.js';
|
|
19
|
+
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
20
|
+
import { norm, sub, dot, cross, len, add, mul, smoothstep, clamp, mix, rng, fbm3, vnoise3 } from '../../core/math/vec.js';
|
|
21
|
+
import { srgb, mix3, MAT, smoothField } from '../../core/build/coatKit.js';
|
|
22
|
+
|
|
23
|
+
// sRGB swatches, neutral-light albedo
|
|
24
|
+
const PAL = {
|
|
25
|
+
blackTan: {
|
|
26
|
+
saddle: 0x221b1a, tan: 0xb57a45, tanLight: 0xcb9d6c, cream: 0xd9c2a0, mask: 0x1c1615, headTop: 0x3c2a22,
|
|
27
|
+
earBack: 0x1d1715, earInner: 0xa07e5c, tailTop: 0x201a19, tailUnder: 0xc39a6c, paw: 0xc99a66,
|
|
28
|
+
ag: { back: 0.15, side: 0.1, head: 0.1, leg: 0, tail: 0.1 }, gloss: 0.25,
|
|
29
|
+
},
|
|
30
|
+
sable: {
|
|
31
|
+
saddle: 0x4a3d33, tan: 0x9c7f5e, tanLight: 0xb89c78, cream: 0xcfbd9f, mask: 0x241d19, headTop: 0x5a4a3c,
|
|
32
|
+
earBack: 0x2a221e, earInner: 0x9c8468, tailTop: 0x3a302a, tailUnder: 0xb49e80, paw: 0xb39776,
|
|
33
|
+
ag: { back: 0.85, side: 0.6, head: 0.45, leg: 0.15, tail: 0.75 }, gloss: 0.15,
|
|
34
|
+
},
|
|
35
|
+
black: {
|
|
36
|
+
saddle: 0x1b1817, tan: 0x221e1c, tanLight: 0x2a2522, cream: 0x332d29, mask: 0x171413, headTop: 0x1d1a18,
|
|
37
|
+
earBack: 0x171413, earInner: 0x2a2522, tailTop: 0x1a1716, tailUnder: 0x26221f, paw: 0x221e1c,
|
|
38
|
+
ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.35,
|
|
39
|
+
},
|
|
40
|
+
yellow: {
|
|
41
|
+
saddle: 0xcfae78, tan: 0xd9bb88, tanLight: 0xe2c89c, cream: 0xebdcbc, mask: 0xd6b886, headTop: 0xd2b27e,
|
|
42
|
+
earBack: 0xbf9a64, earInner: 0xdcc094, tailTop: 0xcfae78, tailUnder: 0xe2caa0, paw: 0xe0c79c,
|
|
43
|
+
ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.3,
|
|
44
|
+
},
|
|
45
|
+
choc: {
|
|
46
|
+
saddle: 0x5c3626, tan: 0x683f2b, tanLight: 0x734a34, cream: 0x7c5340, mask: 0x5f3828, headTop: 0x5d3727,
|
|
47
|
+
earBack: 0x4f2e20, earInner: 0x6c4634, tailTop: 0x5c3626, tailUnder: 0x6e4533, paw: 0x6c4533,
|
|
48
|
+
ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.45, liver: 0x6e4234,
|
|
49
|
+
},
|
|
50
|
+
labBlack: {
|
|
51
|
+
saddle: 0x19191b, tan: 0x1c1c1f, tanLight: 0x202023, cream: 0x232327, mask: 0x1a1a1d, headTop: 0x19191b,
|
|
52
|
+
earBack: 0x161618, earInner: 0x202023, tailTop: 0x19191b, tailUnder: 0x1e1e21, paw: 0x1e1e21,
|
|
53
|
+
ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.6,
|
|
54
|
+
},
|
|
55
|
+
terrier: {
|
|
56
|
+
saddle: 0xf0ede5, tan: 0xefece4, tanLight: 0xf2efe8, cream: 0xf4f1ea, mask: 0xefebe2, headTop: 0xeeeae1,
|
|
57
|
+
earBack: 0xefebe2, earInner: 0xe8dcd2, tailTop: 0xf0ede5, tailUnder: 0xf2efe8, paw: 0xeeebe3,
|
|
58
|
+
ag: { back: 0, side: 0, head: 0, leg: 0, tail: 0 }, gloss: 0.2,
|
|
59
|
+
patchTan: 0xa35f2c, patchBlack: 0x1d1b1d,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
const PAL_OF = { shepherd: { blackTan: 'blackTan', sable: 'sable', black: 'black' }, retriever: { yellow: 'yellow', black: 'labBlack', chocolate: 'choc' }, terrier: { white: 'terrier', tan: 'terrier', tricolour: 'terrier' } };
|
|
63
|
+
|
|
64
|
+
function palette(p) {
|
|
65
|
+
const key = (PAL_OF[p.variant] || PAL_OF.shepherd)[p.colour] || 'blackTan';
|
|
66
|
+
const base = PAL[key];
|
|
67
|
+
const k = p.coatWarmth || 0, l = p.coatLightness || 0, juv = p.juv || 0;
|
|
68
|
+
const out = { ag: base.ag, gloss: base.gloss, key };
|
|
69
|
+
for (const [name, hex] of Object.entries(base)) {
|
|
70
|
+
if (name === 'ag' || name === 'gloss') continue;
|
|
71
|
+
let c = srgb(hex);
|
|
72
|
+
// warmth / lightness within the colour's range (yellow labs: cream to fox red)
|
|
73
|
+
const kk = key === 'yellow' ? 2.2 * k : key === 'terrier' && !/patch/.test(name) ? 0.15 * k : k;
|
|
74
|
+
c = [c[0] * (1 + 0.1 * kk + l), c[1] * (1 + 0.01 * kk + l), c[2] * (1 - 0.14 * kk + l)];
|
|
75
|
+
// shepherd pups: sooty until the tan comes in
|
|
76
|
+
if (juv > 0 && key === 'blackTan' && /tan|cream|paw/.test(name)) c = mix3(c, srgb(0x6a5040), 0.45 * juv);
|
|
77
|
+
out[name] = c;
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function dogCoat(ctx) {
|
|
83
|
+
const { model, rig, pos, nrm, regionOf: partOf, weights, nV, params } = ctx;
|
|
84
|
+
const { BONES, AXIAL } = rig;
|
|
85
|
+
const AX_LEN = weights.axialLengths;
|
|
86
|
+
const C = palette(params);
|
|
87
|
+
const AG = C.ag;
|
|
88
|
+
const key = C.key;
|
|
89
|
+
const juv = params.juv || 0;
|
|
90
|
+
const hw = params.headW || 1, mz = params.muzzle || 1;
|
|
91
|
+
const variant = params.variant || 'shepherd';
|
|
92
|
+
const saddleExt = params.saddle ?? 0; // how far the saddle reaches down the flanks (-1..1)
|
|
93
|
+
const maskK = params.mask ?? 1;
|
|
94
|
+
const FK = params.furK || 1; // fur length factor of the variant
|
|
95
|
+
const nOff = (params.coatSeed || 0) % 997;
|
|
96
|
+
const R = rng(9173 + (params.coatSeed || 0));
|
|
97
|
+
const { dominant, axialS, skinIndex, skinWeight, limbMember, limbWhich } = weights;
|
|
98
|
+
const P = (v) => [pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2]];
|
|
99
|
+
const N = (v) => [nrm[v * 3], nrm[v * 3 + 1], nrm[v * 3 + 2]];
|
|
100
|
+
const sOcc = AX_LEN[1], sNeckBase = AX_LEN[3], sTailBase = AX_LEN[8], sTip = AX_LEN[AX_LEN.length - 1];
|
|
101
|
+
const tailLen = sTip - sTailBase;
|
|
102
|
+
const eyePrims = model.prims.filter((p) => p.tag === 'eyesocket');
|
|
103
|
+
const nosePrim = model.prims.find((p) => p.tag === 'nose');
|
|
104
|
+
const toothPrims = model.prims.filter((p) => p.tag === 'canine' || p.tag === 'lowercanine');
|
|
105
|
+
const jawPrims = model.forPart('jaw').filter((p) => p.tag !== 'lowercanine');
|
|
106
|
+
const bodyPrims = model.forPart('body');
|
|
107
|
+
const headPrims = bodyPrims.filter((p) => p.bone === 'head' && !p.carve && p.tag !== 'canine');
|
|
108
|
+
const clawPrims = model.prims.filter((p) => p.tag === 'claw');
|
|
109
|
+
const padPrims = model.prims.filter((p) => p.tag === 'pad' || p.tag === 'carpalpad');
|
|
110
|
+
const isLimb = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw|femur|tibia|metatarsus|hpaw)/.test(b.name));
|
|
111
|
+
const isPaw = BONES.map((b) => /^(fpaw|hpaw)/.test(b.name));
|
|
112
|
+
const isFront = BONES.map((b) => /^(scapula|humerus|radius|metacarpus|fpaw)/.test(b.name));
|
|
113
|
+
const regionNames = ctx.regionNames;
|
|
114
|
+
const jawRegion = regionNames.indexOf('jaw'), tongueRegion = regionNames.indexOf('tongue');
|
|
115
|
+
const liver = key === 'choc';
|
|
116
|
+
const noseCol = liver ? srgb(PAL.choc.liver) : key === 'yellow' && (params.noseFade || 0) > 0.5 ? srgb(0x5a4238) : srgb(0x161414);
|
|
117
|
+
const headLocal = (p) => {
|
|
118
|
+
const x = (p[0] - HEAD_O[0]) / hw / HS, y = (p[1] - HEAD_O[1]) / HS;
|
|
119
|
+
let z = (p[2] - HEAD_O[2]) / HS;
|
|
120
|
+
if (z > MUZZLE_Z0) z = MUZZLE_Z0 + (z - MUZZLE_Z0) / mz;
|
|
121
|
+
return [x, y, z];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// --- regions per vertex (0 torso, 1 neck, 2 head, 4 tail, 5 ear, 6 jaw, 7 tongue) and limb blend
|
|
125
|
+
const region = new Uint8Array(nV);
|
|
126
|
+
const legness = new Float32Array(nV);
|
|
127
|
+
const legBone = new Int16Array(nV).fill(-1);
|
|
128
|
+
const junction = new Float32Array(nV);
|
|
129
|
+
const tailT = new Float32Array(nV);
|
|
130
|
+
for (let v = 0; v < nV; v++) {
|
|
131
|
+
const bn = BONES[dominant[v]].name;
|
|
132
|
+
const p = P(v);
|
|
133
|
+
let lb = -1, lw = 0;
|
|
134
|
+
for (let k = 0; k < 4; k++) {
|
|
135
|
+
const b = skinIndex[v * 4 + k], w = skinWeight[v * 4 + k];
|
|
136
|
+
if (isLimb[b] && w > lw) { lw = w; lb = b; }
|
|
137
|
+
}
|
|
138
|
+
legBone[v] = lb;
|
|
139
|
+
const m = limbMember[v];
|
|
140
|
+
legness[v] = lb >= 0 ? smoothstep(0.15, 0.85, m) : 0;
|
|
141
|
+
junction[v] = lb >= 0 ? 4 * m * (1 - m) : 0;
|
|
142
|
+
if (partOf[v] === tongueRegion) region[v] = 7;
|
|
143
|
+
else if (partOf[v] === jawRegion) region[v] = 6;
|
|
144
|
+
else if (bn.startsWith('ear')) region[v] = 5;
|
|
145
|
+
else if (axialS[v] > sTailBase + 0.015 && bn.startsWith('tail')) { region[v] = 4; tailT[v] = clamp((axialS[v] - sTailBase) / tailLen, 0, 1); }
|
|
146
|
+
else if (axialS[v] < sOcc + 0.01 || (partOf[v] === 1 && neckS(p[0], p[1], p[2]) > 0.02)) region[v] = 2;
|
|
147
|
+
else if (axialS[v] < sNeckBase) region[v] = 1;
|
|
148
|
+
else region[v] = 0;
|
|
149
|
+
if (region[v] >= 2) { legness[v] = 0; junction[v] = 0; }
|
|
150
|
+
}
|
|
151
|
+
const underFactor = (v) => {
|
|
152
|
+
const n = N(v), p = P(v), li = limbWhich[v];
|
|
153
|
+
if (li < 0 || junction[v] <= 0) return 0;
|
|
154
|
+
if (li <= 1) return junction[v] * Math.max(smoothstep(0.15, -0.55, n[2]) * smoothstep(0.42, 0.32, p[1]), smoothstep(0.2, -0.6, n[1]));
|
|
155
|
+
return junction[v] * smoothstep(0.35, -0.45, n[1] + 0.35 * Math.abs(n[2]));
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// --- ventral (pale underside) factor
|
|
159
|
+
const ventral = new Float32Array(nV);
|
|
160
|
+
for (let v = 0; v < nV; v++) {
|
|
161
|
+
const p = P(v), n = N(v), r = region[v];
|
|
162
|
+
let w = 0;
|
|
163
|
+
if (r === 0) {
|
|
164
|
+
w = smoothstep(-0.05, -0.65, n[1]);
|
|
165
|
+
// chest front between the forelegs
|
|
166
|
+
if (p[2] > 0.2) w = Math.max(w, smoothstep(0.46, 0.36, p[1]) * smoothstep(0.07, 0.03, Math.abs(p[0])) * smoothstep(-0.3, 0.4, n[2]));
|
|
167
|
+
w *= smoothstep(0.5, 0.4, p[1]) * 0.55 + 0.45;
|
|
168
|
+
} else if (r === 1) {
|
|
169
|
+
w = smoothstep(0.1, -0.5, n[1]) * smoothstep(-0.4, 0.3, n[2]) + smoothstep(0.52, 0.44, p[1]) * 0.6;
|
|
170
|
+
} else if (r === 2 || r === 6) {
|
|
171
|
+
const h = headLocal(p);
|
|
172
|
+
w = smoothstep(-0.03, -0.06, h[1]) * smoothstep(-0.2, -0.7, n[1]) + (r === 6 ? 0.7 : 0);
|
|
173
|
+
}
|
|
174
|
+
const L = legness[v];
|
|
175
|
+
if (L > 0) {
|
|
176
|
+
const side = p[0] >= 0 ? 1 : -1;
|
|
177
|
+
const inner = smoothstep(0.1, -0.6, n[0] * side);
|
|
178
|
+
const wl = inner * 0.8 * smoothstep(0.06, 0.25, p[1]) + smoothstep(-0.3, -0.85, n[1]) * 0.5;
|
|
179
|
+
w = mix(w, wl, L);
|
|
180
|
+
}
|
|
181
|
+
w = Math.max(w, underFactor(v) * 0.95);
|
|
182
|
+
ventral[v] = clamp(w, 0, 1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// --- comb (hair flow), bind space
|
|
186
|
+
const comb = new Float32Array(nV * 3);
|
|
187
|
+
for (let v = 0; v < nV; v++) {
|
|
188
|
+
const p = P(v), n = N(v), r = region[v];
|
|
189
|
+
let d;
|
|
190
|
+
const bone = BONES[dominant[v]];
|
|
191
|
+
if (r === 5 || r === 7) {
|
|
192
|
+
d = norm(sub(bone.tail, bone.head));
|
|
193
|
+
} else if (r === 2 || r === 6) {
|
|
194
|
+
const h = headLocal(p);
|
|
195
|
+
// out from the nose over the face, back over the skull and down the cheeks
|
|
196
|
+
const fromNose = norm(sub(h, [0, -0.01, 0.16]));
|
|
197
|
+
d = norm(add(fromNose, [0, -0.2, -0.5]));
|
|
198
|
+
if (h[2] < 0.0 && Math.abs(h[0]) > 0.035) d = norm(add(d, [Math.sign(h[0]) * 0.4, -0.5, -0.8]));
|
|
199
|
+
} else {
|
|
200
|
+
// axial direction, blended across the joints (a per-segment direction steps at every joint:
|
|
201
|
+
// visible as bands in the fur of a bent tail)
|
|
202
|
+
const seg = weights.segT[v * 2] | 0, st = weights.segT[v * 2 + 1];
|
|
203
|
+
const nP = AXIAL.points.length;
|
|
204
|
+
const dirOf = (i) => { i = clamp(i, 0, nP - 2); return norm(sub(AXIAL.points[i + 1], AXIAL.points[i])); };
|
|
205
|
+
const d0 = dirOf(seg);
|
|
206
|
+
d = st < 0.5 ? norm(add(mul(norm(add(dirOf(seg - 1), d0)), 0.5 - st), mul(d0, 0.5 + st))) : norm(add(mul(d0, 1.5 - st), mul(norm(add(d0, dirOf(seg + 1))), st - 0.5)));
|
|
207
|
+
if (r === 0) d = norm(add(d, [0, -0.45 * Math.abs(n[0]), 0]));
|
|
208
|
+
if (r === 1) d = norm(add(d, [0, -0.5, 0]));
|
|
209
|
+
const L = legness[v];
|
|
210
|
+
if (L > 0 && legBone[v] >= 0) {
|
|
211
|
+
const lbn = BONES[legBone[v]];
|
|
212
|
+
let dl = norm(sub(lbn.tail, lbn.head));
|
|
213
|
+
if (isPaw[legBone[v]]) dl = norm(add(dl, [0, 0, 0.4]));
|
|
214
|
+
else dl = norm(add(dl, [0, -0.2, -0.3]));
|
|
215
|
+
d = norm(add(mul(d, 1 - L), mul(dl, L)));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
let t = sub(d, mul(n, dot(d, n)));
|
|
219
|
+
if (len(t) < 1e-4) t = cross(n, [1, 0, 0]);
|
|
220
|
+
t = norm(t);
|
|
221
|
+
comb[v * 3] = t[0]; comb[v * 3 + 1] = t[1]; comb[v * 3 + 2] = t[2];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// --- terrier patches (crisp pattern): tan head mask split by a white blaze, black body patches
|
|
225
|
+
// (tricolour) with a tan rim; seeded layout. Distances are approximate metres.
|
|
226
|
+
const patches = [];
|
|
227
|
+
if (variant === 'terrier') {
|
|
228
|
+
const J = rig.J;
|
|
229
|
+
const tri = params.colour === 'tricolour';
|
|
230
|
+
const nb = params.patches ?? 1;
|
|
231
|
+
// body patches, in reference space: saddle spot over the back / flank, tail-root patch, head
|
|
232
|
+
if (tri || R() < 0.35) {
|
|
233
|
+
const z0 = mix(J.thoraxRear[2], J.lumbarMid[2], R()), side = R() < 0.5 ? 1 : -1;
|
|
234
|
+
patches.push({ c: [0.03 * side * R(), J.thoraxRear[1] + 0.02, z0], r: [0.09 + 0.05 * R() * nb, 0.1 + 0.05 * R(), 0.07 + 0.06 * R() * nb], black: tri });
|
|
235
|
+
}
|
|
236
|
+
if (tri ? R() < 0.85 : R() < 0.4) patches.push({ c: [0, J.tailBase[1] + 0.01, J.tailBase[2] + 0.015], r: [0.07 + 0.02 * R(), 0.06, 0.06 + 0.03 * R()], black: tri });
|
|
237
|
+
if (R() < 0.3 * nb) patches.push({ c: [0.06 * (R() < 0.5 ? 1 : -1), J.chestMid[1] - 0.02, mix(J.chestMid[2], J.thoraxRear[2], R())], r: [0.05, 0.06 + 0.03 * R(), 0.06 + 0.03 * R()], black: tri && R() < 0.6 });
|
|
238
|
+
}
|
|
239
|
+
const blaze = params.blaze ?? 0.5; // width of the white blaze (terrier)
|
|
240
|
+
const maskSide = params.maskSide ?? 0; // asymmetric head mask (-1..1)
|
|
241
|
+
const patchD = (p) => {
|
|
242
|
+
let d = 1, black = 0, dB = 1;
|
|
243
|
+
for (const q of patches) {
|
|
244
|
+
const u = [(p[0] - q.c[0]) / q.r[0], (p[1] - q.c[1]) / q.r[1], (p[2] - q.c[2]) / q.r[2]];
|
|
245
|
+
const nz = fbm3(p[0] * 18 + nOff, p[1] * 18, p[2] * 18, 2) - 0.5;
|
|
246
|
+
const di = (Math.hypot(...u) - 1 + 0.5 * nz) * Math.min(...q.r) * 0.8;
|
|
247
|
+
if (di < d) d = di;
|
|
248
|
+
if (q.black && di < dB) { dB = di; }
|
|
249
|
+
}
|
|
250
|
+
// tricolour: black patches carry a thin tan rim (~5 mm)
|
|
251
|
+
if (dB < 0.02) black = smoothstep(-0.002, -0.009, dB);
|
|
252
|
+
return { d, black };
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// --- per-vertex paint
|
|
256
|
+
const furLen = new Float32Array(nV);
|
|
257
|
+
const tint = new Float32Array(nV * 4);
|
|
258
|
+
const markSDF = new Float32Array(nV).fill(1);
|
|
259
|
+
const surf = new Float32Array(nV * 4);
|
|
260
|
+
const pattern = new Float32Array(nV).fill(1);
|
|
261
|
+
const patCol = new Float32Array(nV * 4);
|
|
262
|
+
const g3 = (h, c, rr) => Math.exp(-(((Math.abs(h[0]) - c[0]) / rr[0]) ** 2 + ((h[1] - c[1]) / rr[1]) ** 2 + ((h[2] - c[2]) / rr[2]) ** 2));
|
|
263
|
+
// (the tail direction blended by the skin weights: no colour step at the joints)
|
|
264
|
+
const tailSegDorsal = (v) => {
|
|
265
|
+
let d = [0, 0, 0];
|
|
266
|
+
for (let k = 0; k < 4; k++) {
|
|
267
|
+
const bone = BONES[skinIndex[v * 4 + k]], w = skinWeight[v * 4 + k];
|
|
268
|
+
if (w > 0 && bone.name.startsWith('tail')) d = add(d, mul(norm(sub(bone.tail, bone.head)), w));
|
|
269
|
+
}
|
|
270
|
+
if (len(d) < 1e-6) { const bone = BONES[dominant[v]]; d = sub(bone.tail, bone.head); }
|
|
271
|
+
d = norm(d);
|
|
272
|
+
return norm([0, -d[2], d[1]]);
|
|
273
|
+
};
|
|
274
|
+
const tan = C.tan, black = C.saddle;
|
|
275
|
+
const shep = key === 'blackTan' || key === 'sable';
|
|
276
|
+
|
|
277
|
+
for (let v = 0; v < nV; v++) {
|
|
278
|
+
const p = P(v), n = N(v), r = region[v];
|
|
279
|
+
let col, fl = 0.02, ag = 0, mat = MAT.FUR, mark = 1, under = 0.35, gloss = C.gloss;
|
|
280
|
+
const nz = fbm3(p[0] * 7 + nOff, p[1] * 7, p[2] * 7, 3) - 0.5;
|
|
281
|
+
const nz2 = vnoise3(p[0] * 38 + nOff, p[1] * 38, p[2] * 38) - 0.5;
|
|
282
|
+
if (r === 0 || r === 1) {
|
|
283
|
+
const up = n[1];
|
|
284
|
+
const vent = ventral[v];
|
|
285
|
+
col = mix3(C.tan, C.tanLight, smoothstep(0.2, -0.4, up + 0.2 * nz) * 0.5);
|
|
286
|
+
ag = mix(AG.side * 0.7, AG.side, smoothstep(-0.3, 0.3, up));
|
|
287
|
+
if (r === 0) {
|
|
288
|
+
// saddle: from the withers over the back into the tail top, down the flanks (ragged edge)
|
|
289
|
+
const dors = smoothstep(-0.08, 0.12, up + 0.3 * nz + 0.22 * saddleExt + 0.2 * smoothstep(0.42, 0.55, p[1]));
|
|
290
|
+
const along = smoothstep(0.3, 0.2, p[2]) * smoothstep(-0.36, -0.26, p[2]);
|
|
291
|
+
if (key === 'blackTan') {
|
|
292
|
+
col = mix3(col, black, dors * along);
|
|
293
|
+
// the tan runs up in front of the thigh and on the shoulder
|
|
294
|
+
} else {
|
|
295
|
+
col = mix3(col, C.saddle, dors * along * (key === 'sable' ? 0.7 : 0.25));
|
|
296
|
+
ag = mix(ag, AG.back, dors * along);
|
|
297
|
+
}
|
|
298
|
+
col = mix3(col, C.cream, vent);
|
|
299
|
+
ag *= 1 - vent;
|
|
300
|
+
fl = mix(0.02, 0.027, smoothstep(0.2, 0.85, up)) * (1 + 0.1 * nz2);
|
|
301
|
+
fl = mix(fl, 0.02, smoothstep(0.3, 0.9, vent));
|
|
302
|
+
// longer hair behind the thighs (breeches) and over the withers
|
|
303
|
+
if (p[2] < -0.28) fl = Math.max(fl, 0.036 * smoothstep(-0.2, -0.7, n[2]));
|
|
304
|
+
} else {
|
|
305
|
+
// neck: dark nape (saddle into the neck top in black-and-tans), tan / cream throat
|
|
306
|
+
const top = smoothstep(0.2, 0.85, up + 0.25 * nz);
|
|
307
|
+
if (key === 'blackTan') col = mix3(col, mix3(black, C.headTop, 0.5), top * 0.9);
|
|
308
|
+
else col = mix3(col, C.saddle, top * (key === 'sable' ? 0.55 : 0.2));
|
|
309
|
+
ag = mix(AG.side * 0.7, AG.back, top);
|
|
310
|
+
col = mix3(col, C.cream, smoothstep(0.35, 0.8, vent) * (shep ? 0.8 : 0.6));
|
|
311
|
+
ag *= 1 - smoothstep(0.3, 0.7, vent);
|
|
312
|
+
fl = mix(0.03, 0.026, vent) * (1 + 0.12 * nz2);
|
|
313
|
+
}
|
|
314
|
+
const L = legness[v];
|
|
315
|
+
if (L > 0) {
|
|
316
|
+
const lb = legBone[v];
|
|
317
|
+
const front = lb >= 0 && isFront[lb];
|
|
318
|
+
let lc = mix3(C.tan, C.cream, smoothstep(0.2, 0.8, ventral[v]) * 0.6);
|
|
319
|
+
lc = mix3(lc, col, smoothstep(0.26, 0.42, p[1]) * 0.6);
|
|
320
|
+
let lf = mix(0.008, 0.018, smoothstep(0.08, 0.34, p[1]));
|
|
321
|
+
if (!front) lf = Math.max(lf, 0.04 * smoothstep(-0.1, -0.7, n[2]) * smoothstep(0.2, 0.38, p[1]));
|
|
322
|
+
else lf = Math.max(lf, 0.02 * smoothstep(-0.2, -0.8, n[2]) * smoothstep(0.12, 0.24, p[1]));
|
|
323
|
+
if (lb >= 0 && isPaw[lb]) { lf = 0.004; lc = C.paw; }
|
|
324
|
+
col = mix3(col, lc, L);
|
|
325
|
+
ag = mix(ag, AG.leg * smoothstep(0.12, 0.35, p[1]), L);
|
|
326
|
+
fl = mix(fl, lf, L);
|
|
327
|
+
}
|
|
328
|
+
} else if (r === 4) {
|
|
329
|
+
const t = tailT[v];
|
|
330
|
+
const dors = dot(n, tailSegDorsal(v));
|
|
331
|
+
const top = smoothstep(-0.35, 0.55, dors + 0.3 * nz);
|
|
332
|
+
col = mix3(C.tailUnder, C.tailTop, top);
|
|
333
|
+
ag = AG.tail * top;
|
|
334
|
+
if (key === 'blackTan') col = mix3(col, black, smoothstep(0.75, 0.92, t + 0.05 * nz));
|
|
335
|
+
if (key === 'sable') col = mix3(col, srgb(0x1c1716), smoothstep(0.82, 0.95, t));
|
|
336
|
+
fl = variant === 'shepherd' ? mix(0.03, 0.045, smoothstep(0.05, 0.4, t)) * (1 - 0.25 * smoothstep(0.85, 1, t)) : mix(0.016, 0.02, t);
|
|
337
|
+
under = 0.4;
|
|
338
|
+
} else if (r === 5) {
|
|
339
|
+
// ears: short fine hair, dark backs (shepherd), inner pinna paler with fine hair
|
|
340
|
+
const facing = params.earType === 'prick' ? dot(n, norm([0.5 * Math.sign(p[0]), 0.05, 1])) : -n[0] * Math.sign(p[0]);
|
|
341
|
+
col = C.earBack;
|
|
342
|
+
if (facing > 0.3) { col = mix3(C.earBack, C.earInner, smoothstep(0.3, 0.75, facing)); fl = 0.006; }
|
|
343
|
+
else fl = params.earType === 'drop' ? 0.009 : 0.006;
|
|
344
|
+
ag = AG.head * 0.5;
|
|
345
|
+
} else if (r === 7) {
|
|
346
|
+
mat = MAT.MOUTH; col = srgb(0xc86070); fl = 0;
|
|
347
|
+
} else {
|
|
348
|
+
// ---------------- head & jaw
|
|
349
|
+
const h = headLocal(p);
|
|
350
|
+
col = mix3(C.headTop, C.tan, smoothstep(0.03, -0.02, h[1] + 0.1 * nz));
|
|
351
|
+
ag = AG.head;
|
|
352
|
+
// mask: muzzle and round the eyes (shepherds), darker forehead line
|
|
353
|
+
const muzzle = smoothstep(0.035, 0.075, h[2]);
|
|
354
|
+
const eyeRing = g3(h, [0.033, 0.022, 0.038], [0.02, 0.014, 0.02]);
|
|
355
|
+
const cheek = g3(h, [0.04, -0.02, 0.0], [0.022, 0.022, 0.035]);
|
|
356
|
+
const brow = g3(h, [0.025, 0.04, 0.036], [0.01, 0.007, 0.01]); // tan "eyebrow" spots
|
|
357
|
+
if (key === 'blackTan' || key === 'sable') {
|
|
358
|
+
let mk = clamp(Math.max(muzzle * (0.9 - 0.4 * smoothstep(-0.03, -0.06, h[1])), eyeRing * 0.9) * maskK, 0, 1);
|
|
359
|
+
col = mix3(col, C.tan, cheek * 0.9);
|
|
360
|
+
col = mix3(col, C.mask, mk);
|
|
361
|
+
col = mix3(col, C.tanLight, brow * 0.6 * (1 - juv));
|
|
362
|
+
ag *= 1 - 0.6 * mk;
|
|
363
|
+
// pale chin / lower lips
|
|
364
|
+
col = mix3(col, C.cream, smoothstep(-0.05, -0.07, h[1]) * smoothstep(-0.2, -0.6, n[1]) * 0.5);
|
|
365
|
+
} else if (key === 'yellow') {
|
|
366
|
+
col = mix3(col, C.tanLight, cheek * 0.4);
|
|
367
|
+
col = mix3(col, C.cream, smoothstep(-0.04, -0.065, h[1]) * 0.4);
|
|
368
|
+
}
|
|
369
|
+
// fur length: sleek muzzle and face, longer at the back of the head
|
|
370
|
+
fl = mix(0.009, 0.004, smoothstep(0.02, 0.1, h[2]));
|
|
371
|
+
if (r === 6) fl = mix(0.005, 0.01, smoothstep(0.08, 0.0, h[2]));
|
|
372
|
+
if (r === 2 && h[2] < -0.07) {
|
|
373
|
+
const b = smoothstep(-0.07, -0.11, h[2]);
|
|
374
|
+
fl = mix(fl, 0.03, b);
|
|
375
|
+
}
|
|
376
|
+
if (r === 2 && (params.ruff || 0) > 0.2) fl = Math.max(fl, 0.022 * params.ruff * smoothstep(-0.02, -0.06, h[2]) * smoothstep(0.035, 0.055, Math.abs(h[0])) * smoothstep(0.02, -0.01, h[1]));
|
|
377
|
+
// eye rims (dark lid margins; liver in chocolates)
|
|
378
|
+
for (const e of eyePrims) {
|
|
379
|
+
const de = SDFModel.dist(e, p[0], p[1], p[2]);
|
|
380
|
+
if (Math.abs(de) < 0.0042) mark = Math.min(mark, Math.abs(de) - 0.0017);
|
|
381
|
+
if (Math.abs(de) < 0.0015) { mat = liver ? MAT.SKIN : MAT.DARK_SKIN; col = srgb(0x4a2a22); fl = 0; }
|
|
382
|
+
else if (Math.abs(de) < 0.0042) fl = Math.min(fl, 0.0015);
|
|
383
|
+
}
|
|
384
|
+
// nose leather (liver in chocolates: bare skin with the liver colour)
|
|
385
|
+
const dn = SDFModel.dist(nosePrim, p[0], p[1], p[2]);
|
|
386
|
+
if (dn < 0.0022 && h[2] > 0.135) { mat = liver || key === 'yellow' ? MAT.SKIN : MAT.NOSE; col = noseCol; fl = 0; gloss = 0.55; }
|
|
387
|
+
else if (dn < 0.005 && h[2] > 0.13) { mark = Math.min(mark, dn - 0.0032); fl = Math.min(fl, 0.002); }
|
|
388
|
+
// lips: head vertices near the jaw surface, jaw vertices near the head surface (dark lip line)
|
|
389
|
+
if (r !== 6) {
|
|
390
|
+
const dj = SDFModel.evalList(jawPrims, p[0], p[1], p[2]);
|
|
391
|
+
if (dj < -0.0005) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
|
|
392
|
+
else if (dj < 0.0045 && h[2] > -0.02) { mark = Math.min(mark, dj - 0.0032); fl = Math.min(fl, 0.002); }
|
|
393
|
+
} else {
|
|
394
|
+
const dh = SDFModel.evalList(headPrims, p[0], p[1], p[2]);
|
|
395
|
+
if (dh < 0.0) { mat = MAT.MOUTH; col = srgb(0x6e3438); fl = 0; }
|
|
396
|
+
else if (dh < 0.0035) { mark = Math.min(mark, dh - 0.0026); fl = Math.min(fl, 0.002); }
|
|
397
|
+
}
|
|
398
|
+
for (const c of toothPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0012) { mat = MAT.KERATIN; col = srgb(0xe8e0cc); fl = 0; mark = 1; }
|
|
399
|
+
under = 0.25;
|
|
400
|
+
}
|
|
401
|
+
// claws and pads (keratin claws; dark pads)
|
|
402
|
+
if (legness[v] > 0.5 && legBone[v] >= 0 && isPaw[legBone[v]]) {
|
|
403
|
+
for (const c of clawPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0009) { mat = MAT.KERATIN; col = key === 'terrier' || key === 'yellow' ? srgb(0xb8a894) : srgb(0x2a2420); fl = 0; }
|
|
404
|
+
if (mat === MAT.FUR) for (const c of padPrims) if (SDFModel.dist(c, p[0], p[1], p[2]) < 0.0012 && n[1] < -0.3) { mat = MAT.SKIN; col = liver ? srgb(0x5a3a30) : srgb(0x2a2524); fl = 0; gloss = 0.2; }
|
|
405
|
+
}
|
|
406
|
+
// terrier patches
|
|
407
|
+
if (patches.length || variant === 'terrier') {
|
|
408
|
+
let pd = 1, pc = C.patchTan, blackK = 0;
|
|
409
|
+
if (patches.length && (r === 0 || r === 1 || r === 4)) { const q = patchD(p); pd = q.d; blackK = q.black; }
|
|
410
|
+
if (r === 2 || r === 5) {
|
|
411
|
+
// tan head mask: the ears, round the eyes, the cheeks and the skull; the muzzle, chin and
|
|
412
|
+
// throat stay white, and a white blaze runs up the middle from the nose (seeded width, length
|
|
413
|
+
// and asymmetry). 'white' terriers keep tan only on the ears and round one or both eyes.
|
|
414
|
+
const h = headLocal(p);
|
|
415
|
+
const side = Math.sign(h[0]) || 1;
|
|
416
|
+
const ext = 1 + 0.35 * maskSide * side;
|
|
417
|
+
const wob = 0.005 * (fbm3(p[0] * 30 + nOff, p[1] * 30, p[2] * 30, 2) - 0.5);
|
|
418
|
+
const mostlyWhite = params.colour === 'white';
|
|
419
|
+
const zFront = (mostlyWhite ? 0.03 : 0.068) * ext, yLow = mostlyWhite ? 0.0 : -0.022 - 0.006 * ext;
|
|
420
|
+
const dMask = Math.max(h[2] - zFront, yLow - h[1], -0.09 - h[2]) + wob;
|
|
421
|
+
const bw = (0.004 + 0.01 * blaze) * (0.5 + 0.8 * smoothstep(0.0, 0.07, h[2]));
|
|
422
|
+
const dBlaze = Math.max(Math.abs(h[0]) - bw, (params.blazeEnd ?? -0.02) - h[2]);
|
|
423
|
+
pd = r === 5 ? -0.02 : Math.max(dMask, -dBlaze);
|
|
424
|
+
if (mostlyWhite && r === 2 && maskSide * side < -0.3) pd = Math.max(pd, 0.01); // one eye patch only
|
|
425
|
+
}
|
|
426
|
+
if (pd < 1) {
|
|
427
|
+
pattern[v] = pd;
|
|
428
|
+
pc = mix3(C.patchTan, C.patchBlack, blackK);
|
|
429
|
+
patCol[v * 4] = pc[0]; patCol[v * 4 + 1] = pc[1]; patCol[v * 4 + 2] = pc[2]; patCol[v * 4 + 3] = mat === MAT.FUR ? 1 : 0;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// pups: fluffy and soft
|
|
433
|
+
if (juv > 0 && mat === MAT.FUR) {
|
|
434
|
+
fl = mix(fl, Math.max(fl * 0.8, r === 2 || r === 6 ? 0.01 : 0.018), juv);
|
|
435
|
+
ag *= 1 - 0.6 * juv;
|
|
436
|
+
}
|
|
437
|
+
if (mat === MAT.FUR) fl *= FK;
|
|
438
|
+
// low-frequency colour variation
|
|
439
|
+
const cvh = fbm3(p[0] * 4 + 5 + nOff, p[1] * 4, p[2] * 4, 2) - 0.5;
|
|
440
|
+
if (mat === MAT.FUR) {
|
|
441
|
+
const a = key === 'terrier' ? 0.25 : 1;
|
|
442
|
+
col = [col[0] * (1 + a * (0.12 * nz + 0.08 * cvh + 0.06 * nz2)), col[1] * (1 + a * (0.1 * nz + 0.06 * nz2)), col[2] * (1 + a * (0.08 * nz - 0.06 * cvh + 0.06 * nz2))];
|
|
443
|
+
}
|
|
444
|
+
markSDF[v] = mark;
|
|
445
|
+
furLen[v] = fl;
|
|
446
|
+
tint[v * 4] = col[0]; tint[v * 4 + 1] = col[1]; tint[v * 4 + 2] = col[2]; tint[v * 4 + 3] = mat;
|
|
447
|
+
surf[v * 4] = mat === MAT.FUR ? gloss : mat === MAT.KERATIN ? 0.7 : mat === MAT.NOSE || mat === MAT.SKIN ? gloss : 0;
|
|
448
|
+
surf[v * 4 + 2] = mat === MAT.FUR ? clamp(ag * (0.85 + 0.3 * nz2), 0, 1) : 0;
|
|
449
|
+
surf[v * 4 + 3] = mat === MAT.FUR ? under : 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
smoothField(furLen, weights.neighbors, 3, (v) => tint[v * 4 + 3] > 0);
|
|
453
|
+
// the neck's long ruff and the head's short throat coat meet at the neck cut, where the head and body surfaces
|
|
454
|
+
// overlap and the coat's regions switch: the hair went from 25 to 9 mm within 15 mm of the cut, and the two
|
|
455
|
+
// surfaces took different lengths at one place, so the ruff ended in a crisp collar line round the throat. Near the
|
|
456
|
+
// cut the hair length and the colour (the head's and the neck's colour rules also stepped there, 3 mm wide) are
|
|
457
|
+
// averaged in space over both surfaces (Gaussian, 13 mm): one field, fading over a few cm
|
|
458
|
+
{
|
|
459
|
+
const R = 0.013, cell = 2 * R, grid = new Map(), near = [];
|
|
460
|
+
const ck = (x, y, z) => `${Math.floor(x / cell)},${Math.floor(y / cell)},${Math.floor(z / cell)}`;
|
|
461
|
+
for (let v = 0; v < nV; v++) {
|
|
462
|
+
if (tint[v * 4 + 3] !== MAT.FUR) continue;
|
|
463
|
+
const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
|
|
464
|
+
if (Math.abs(neckS(x, y, z)) > 0.09) continue;
|
|
465
|
+
const k = ck(x, y, z); let l = grid.get(k); if (!l) grid.set(k, (l = [])); l.push(v);
|
|
466
|
+
if (Math.abs(neckS(x, y, z)) < 0.06) near.push(v);
|
|
467
|
+
}
|
|
468
|
+
const out = new Float32Array(near.length), outC = new Float32Array(near.length * 3);
|
|
469
|
+
near.forEach((v, i) => {
|
|
470
|
+
const x = pos[v * 3], y = pos[v * 3 + 1], z = pos[v * 3 + 2];
|
|
471
|
+
const gx = Math.floor(x / cell), gy = Math.floor(y / cell), gz = Math.floor(z / cell);
|
|
472
|
+
let a = 0, w = 0, cr = 0, cg = 0, cb = 0;
|
|
473
|
+
for (let ia = -1; ia <= 1; ia++) for (let ib = -1; ib <= 1; ib++) for (let ic = -1; ic <= 1; ic++) {
|
|
474
|
+
const l = grid.get(`${gx + ia},${gy + ib},${gz + ic}`); if (!l) continue;
|
|
475
|
+
for (const u of l) {
|
|
476
|
+
const d2 = (pos[u * 3] - x) ** 2 + (pos[u * 3 + 1] - y) ** 2 + (pos[u * 3 + 2] - z) ** 2;
|
|
477
|
+
if (d2 > 4 * R * R) continue;
|
|
478
|
+
const wt = Math.exp(-d2 / (2 * R * R)); a += wt * furLen[u]; w += wt;
|
|
479
|
+
cr += wt * tint[u * 4]; cg += wt * tint[u * 4 + 1]; cb += wt * tint[u * 4 + 2];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
out[i] = w > 0 ? a / w : furLen[v];
|
|
483
|
+
if (w > 0) { outC[i * 3] = cr / w; outC[i * 3 + 1] = cg / w; outC[i * 3 + 2] = cb / w; } else { outC[i * 3] = tint[v * 4]; outC[i * 3 + 1] = tint[v * 4 + 1]; outC[i * 3 + 2] = tint[v * 4 + 2]; }
|
|
484
|
+
});
|
|
485
|
+
near.forEach((v, i) => {
|
|
486
|
+
const k = 1 - smoothstep(0.035, 0.06, Math.abs(neckS(pos[v * 3], pos[v * 3 + 1], pos[v * 3 + 2])));
|
|
487
|
+
furLen[v] = furLen[v] + (out[i] - furLen[v]) * k;
|
|
488
|
+
for (let c = 0; c < 3; c++) tint[v * 4 + c] += (outC[i * 3 + c] - tint[v * 4 + c]) * k;
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
// the head and body surfaces' skin weights made equal across the neck cut (regions.js): they parted at the throat
|
|
492
|
+
// and the fur rooted on the hidden surface drew a crisp line across the ruff
|
|
493
|
+
harmonizeSeamWeights(ctx, neckS, NECK_CUT.band);
|
|
494
|
+
return { comb, tint, pattern, mark: markSDF, furLen, patternColor: patCol, surf, region, ventral, tailT };
|
|
495
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Domestic dog (Canis familiaris). Digitigrade trotter with three variants (opts.variant):
|
|
2
|
+
// 'shepherd' (the default: a medium shepherd / mixed type), 'retriever' (Labrador type) and 'terrier'
|
|
3
|
+
// (a small Jack Russell type). One canid rig; the variants differ in size, proportions, head, ears,
|
|
4
|
+
// tail, coat and colours.
|
|
5
|
+
import { dogRig, HEAD_O } from './rig.js';
|
|
6
|
+
import { sculptDog, eyeOf } from './sculpt.js';
|
|
7
|
+
import { dogRegions } from './regions.js';
|
|
8
|
+
import { dogCoat } from './coat.js';
|
|
9
|
+
import { motion } from './motion.js';
|
|
10
|
+
import { rng } from '../../core/math/vec.js';
|
|
11
|
+
|
|
12
|
+
export const VARIANTS = {
|
|
13
|
+
shepherd: { name: 'Shepherd dog', latin: 'Canis familiaris (shepherd type)' },
|
|
14
|
+
retriever: { name: 'Retriever', latin: 'Canis familiaris (Labrador type)' },
|
|
15
|
+
terrier: { name: 'Terrier', latin: 'Canis familiaris (Jack Russell type)' },
|
|
16
|
+
};
|
|
17
|
+
const COLOURS = {
|
|
18
|
+
shepherd: [['blackTan', 0.68], ['sable', 0.27], ['black', 0.05]],
|
|
19
|
+
retriever: [['yellow', 0.45], ['black', 0.4], ['chocolate', 0.15]],
|
|
20
|
+
terrier: [['tricolour', 0.45], ['tan', 0.4], ['white', 0.15]],
|
|
21
|
+
};
|
|
22
|
+
const pick = (R, table) => {
|
|
23
|
+
let x = R(), acc = 0;
|
|
24
|
+
for (const [k, w] of table) { acc += w; if (x < acc) return k; }
|
|
25
|
+
return table[table.length - 1][0];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
id: 'dog',
|
|
30
|
+
name: 'Dog',
|
|
31
|
+
latin: 'Canis familiaris',
|
|
32
|
+
group: 'paws',
|
|
33
|
+
plan: 'quadruped',
|
|
34
|
+
covering: 'fur',
|
|
35
|
+
variants: Object.entries(VARIANTS).map(([id, v]) => ({ id, name: v.name, latin: v.latin })),
|
|
36
|
+
|
|
37
|
+
// Seeded individual: variant (from opts.variant, default 'shepherd'), colour within the
|
|
38
|
+
// variant, sex (males larger, broader head, heavier neck and ruff), puppies (age 'juvenile', 8-12
|
|
39
|
+
// weeks: ~half size, big domed head, short muzzle, big paws, short legs, soft ears, fluffy coat),
|
|
40
|
+
// and individual proportions: leg length, body length, head size, muzzle, ears, tail, colour tones,
|
|
41
|
+
// saddle extent, mask, the terrier's patch layout and blaze.
|
|
42
|
+
variation(R, o) {
|
|
43
|
+
const variant = o.variant && VARIANTS[o.variant] ? o.variant : 'shepherd';
|
|
44
|
+
R();
|
|
45
|
+
const sex = o.sex || (R() < 0.5 ? 'male' : 'female');
|
|
46
|
+
const age = o.age || 'adult';
|
|
47
|
+
const juv = age === 'juvenile' ? 1 : 0;
|
|
48
|
+
const male = sex === 'male';
|
|
49
|
+
const g = () => (R() + R() + R() - 1.5) / 1.5; // ~[-1, 1], peaked
|
|
50
|
+
// colour from its own hashed stream (consecutive seeds must not correlate)
|
|
51
|
+
const M = rng(Math.imul((o.seed ?? 1) + 0x2b7e1516, 0x9e3779b1) ^ 0x68e31da4);
|
|
52
|
+
for (let i = 0; i < 3; i++) M();
|
|
53
|
+
const colour = pick(M, COLOURS[variant]);
|
|
54
|
+
const shep = variant === 'shepherd', ret = variant === 'retriever', ter = variant === 'terrier';
|
|
55
|
+
// withers height relative to the 0.60 m reference: shepherd M 0.62 / F 0.575, Labrador M 0.57 /
|
|
56
|
+
// F 0.55, Jack Russell 0.25-0.30; pups (10 weeks) about half
|
|
57
|
+
const base = shep ? (male ? 1.035 : 0.96) : ret ? (male ? 0.95 : 0.915) : 0.46 * (male ? 1.02 : 0.98);
|
|
58
|
+
const size = base * (1 + (ter ? 0.06 : 0.03) * g()) * (juv ? (ter ? 0.6 : 0.5) : 1);
|
|
59
|
+
const legs = 1 + 0.03 * g() - 0.16 * juv + (ret ? -0.04 : ter ? 0.1 : 0);
|
|
60
|
+
const head = (male ? 1.03 : 0.985) * (1 + 0.02 * g()) * (ret ? 1.15 : ter ? 1.12 : 1) * (juv ? 1.3 : 1);
|
|
61
|
+
const earType = ret ? 'drop' : ter ? 'button' : juv ? 'button' : 'prick';
|
|
62
|
+
const tailK = (1 + 0.05 * g()) * (juv ? 0.7 : 1) * (ter ? (M() < 0.35 ? 0.62 : 0.9 + 0.1 * g()) : 1);
|
|
63
|
+
const warps = [
|
|
64
|
+
{ type: 'legs', k: legs, top: 0.36 },
|
|
65
|
+
{ type: 'length', k: 1 + 0.03 * g() - 0.05 * juv + (ret ? -0.09 : ter ? -0.03 : 0), z0: -0.26, z1: 0.3 },
|
|
66
|
+
{ type: 'scaleAbout', c: HEAD_O, k: head, r0: 0.075, r1: 0.19 },
|
|
67
|
+
];
|
|
68
|
+
// a shorter, thicker-looking neck (the head sits closer over the forechest)
|
|
69
|
+
if (ret || ter) warps.push({ type: 'length', k: ret ? 0.78 : 0.78, z0: 0.3, z1: 0.44 });
|
|
70
|
+
if (ret) warps.push({ type: 'girth', k: (male ? 1.1 : 1.07) * (1 + 0.03 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
|
|
71
|
+
if (ter) warps.push({ type: 'girth', k: 0.95 * (1 + 0.03 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
|
|
72
|
+
if (shep) warps.push({ type: 'girth', k: (male ? 1.02 : 0.98) * (1 + 0.02 * g()), cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
|
|
73
|
+
// chubby pups
|
|
74
|
+
if (juv) warps.push({ type: 'girth', k: 1.12, cy: 0.42, z0: -0.3, z1: 0.42, fade: 0.1 });
|
|
75
|
+
// big pup paws
|
|
76
|
+
if (juv) for (const [x, z] of [[0.046, 0.26], [-0.046, 0.26], [0.052, -0.28], [-0.052, -0.28]]) warps.push({ type: 'scaleAbout', c: [x, 0.025, z], k: 1.22, r0: 0.03, r1: 0.06 });
|
|
77
|
+
return {
|
|
78
|
+
sex, age, size, variant, colour, juv, headScale: head,
|
|
79
|
+
earType, tailType: shep ? 'brush' : ret ? 'otter' : 'thin',
|
|
80
|
+
muzzle: (1 + 0.04 * g()) * (ret ? 0.96 : ter ? 0.88 : 1.02) * (juv ? 0.72 : 1),
|
|
81
|
+
headW: (male ? 1.04 : 0.99) * (1 + 0.02 * g()) * (ret ? 1.12 : ter ? 1.12 : 1.05) * (juv ? 1.05 : 1),
|
|
82
|
+
stop: shep ? 0.35 + 0.1 * g() : ret ? 0.75 : 1.0,
|
|
83
|
+
flews: ret ? 0.8 + 0.2 * g() : 0,
|
|
84
|
+
muzzleDepth: ret ? 1.15 : ter ? 1.08 : 1,
|
|
85
|
+
zyg: ret ? 0.7 : 1,
|
|
86
|
+
dome: ret ? 0.6 : ter ? 0.2 : 0,
|
|
87
|
+
ruff: shep ? (male ? 0.7 : 0.5) + 0.1 * g() : ret ? 0.12 : 0,
|
|
88
|
+
neckK: (ret ? 1.22 : ter ? 1.04 : 1) * (male ? 1.03 : 0.98),
|
|
89
|
+
chestK: ret ? 1.06 : 1,
|
|
90
|
+
boneK: (ret ? 1.35 : ter ? 1.1 : 1) * (juv ? 1.1 : 1),
|
|
91
|
+
pawK: (ret ? 1.1 : ter ? 0.92 : 1),
|
|
92
|
+
tuck: shep ? 1 : ter ? 1.0 : 0.2,
|
|
93
|
+
eyeK: (ter ? 1.3 : 1) * (juv ? 1.25 : 1),
|
|
94
|
+
eyeRound: ret ? 0.6 : ter ? 0.5 : 0.1 * juv,
|
|
95
|
+
ear: (1 + 0.05 * g()) * (juv ? 1.08 : 1) * (shep && !juv ? 0.95 : ter ? 1.35 : 1),
|
|
96
|
+
tail: tailK,
|
|
97
|
+
tailBrush: (1 + 0.06 * g()) * (juv ? 0.85 : 1) * (ret ? 1.2 : 1),
|
|
98
|
+
furK: (shep ? 1 : ret ? 0.32 : 0.3) * (1 + 0.06 * g()),
|
|
99
|
+
saddle: 1.1 * g(),
|
|
100
|
+
mask: 1 + 0.15 * g(),
|
|
101
|
+
coatWarmth: 0.9 * g(),
|
|
102
|
+
coatLightness: 0.1 * g(),
|
|
103
|
+
noseFade: ret && colour === 'yellow' ? M() : 0,
|
|
104
|
+
patches: 0.7 + 0.6 * M(),
|
|
105
|
+
blaze: 0.2 + 0.8 * M(),
|
|
106
|
+
blazeEnd: -0.05 + 0.08 * M(),
|
|
107
|
+
maskSide: 2 * M() - 1,
|
|
108
|
+
coatSeed: Math.floor(R() * 1e6),
|
|
109
|
+
minThick: 0.003,
|
|
110
|
+
warps,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
rig: (params) => dogRig(params),
|
|
114
|
+
sculpt: (m, rig, params) => sculptDog(m, rig, params),
|
|
115
|
+
regions: dogRegions,
|
|
116
|
+
coat: dogCoat,
|
|
117
|
+
eyeSpecs: (params) => [1, -1].map((side) => ({ side, spec: eyeOf(params), headOrigin: HEAD_O, bone: 'head', look: eyeLook(params) })),
|
|
118
|
+
motion,
|
|
119
|
+
render: (params) => ({
|
|
120
|
+
markColor: params.colour === 'chocolate' ? [0.06, 0.028, 0.02] : [0.012, 0.01, 0.009],
|
|
121
|
+
strandDensity: params.variant === 'shepherd' ? 720 : 1200,
|
|
122
|
+
clumpDensity: params.variant === 'shepherd' ? 0.8 : 0.3,
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Round pupil; brown iris (dark brown in shepherds and terriers, hazel / amber in chocolates), no
|
|
127
|
+
// visible sclera at rest; black lid margins (liver in chocolates); pups blue-grey.
|
|
128
|
+
const IRIS = {
|
|
129
|
+
dark: { inner: [0.08, 0.035, 0.012], mid: [0.16, 0.075, 0.025], hi: [0.3, 0.16, 0.06], outer: [0.035, 0.016, 0.006] },
|
|
130
|
+
brown: { inner: [0.12, 0.055, 0.018], mid: [0.24, 0.12, 0.04], hi: [0.42, 0.24, 0.08], outer: [0.05, 0.022, 0.008] },
|
|
131
|
+
amber: { inner: [0.22, 0.11, 0.03], mid: [0.42, 0.24, 0.07], hi: [0.6, 0.38, 0.12], outer: [0.1, 0.05, 0.015] },
|
|
132
|
+
pup: { inner: [0.1, 0.12, 0.15], mid: [0.2, 0.24, 0.3], hi: [0.34, 0.4, 0.48], outer: [0.06, 0.07, 0.09] },
|
|
133
|
+
};
|
|
134
|
+
function eyeLook(params) {
|
|
135
|
+
const iris = params.juv ? IRIS.pup : params.colour === 'chocolate' ? IRIS.amber : params.variant === 'retriever' ? IRIS.brown : IRIS.dark;
|
|
136
|
+
return {
|
|
137
|
+
pupil: 'round',
|
|
138
|
+
lids: 'mammal',
|
|
139
|
+
iris,
|
|
140
|
+
sclera: { color: [0.55, 0.45, 0.38], visible: false },
|
|
141
|
+
lidColor: params.colour === 'chocolate' ? [0.08, 0.04, 0.03] : [0.02, 0.017, 0.015],
|
|
142
|
+
pupilSize: [0.3, 0.6],
|
|
143
|
+
};
|
|
144
|
+
}
|