@bitefoundry/retrieviewer 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ASSET-PROVENANCE.md +6 -0
- package/ASSETS-LICENSE.md +2 -1
- package/ATTRIBUTION.md +6 -1
- package/COMFORT-ART.md +65 -0
- package/DESKTOP-PLATFORMS.md +6 -1
- package/PLUGIN.md +13 -10
- package/PREREQUISITES.md +95 -0
- package/PUPPY-MEMORY.md +102 -1
- package/README.ko.md +110 -0
- package/README.md +117 -59
- package/RELEASING.md +23 -12
- package/THIRD-PARTY-NOTICES.md +5 -3
- package/braille.mjs +35 -9
- package/examples/live.mjs +2 -2
- package/integrations/linux-overlay/overlay.py +2 -2
- package/integrations/macos-overlay/overlay.jxa +8 -6
- package/integrations/opencode/runtime.mjs +4 -4
- package/integrations/opencode/tui.tsx +1 -1
- package/integrations/windows-overlay/Overlay.cs +4 -4
- package/integrations/windows-overlay/protocol.mjs +11 -7
- package/package.json +34 -13
- package/puppy-comfort-sprites.mjs +25 -0
- package/puppy-companion-comfort-q5.bin.gz +0 -0
- package/puppy-companion-comfort.json +77 -0
- package/puppy-companion-life.mjs +49 -16
- package/puppy-companion-objects.mjs +12 -0
- package/puppy-frame-predictive.mjs +15 -9
- package/puppy-frame-store.mjs +2 -2
- package/puppy-paper-world.mjs +14 -0
- package/puppy-paper.mjs +65 -14
- package/retrieviewer-plugin.d.mts +1 -1
- package/retrieviewer-plugin.mjs +1 -1
- package/retrieviewer.d.mts +10 -4
- package/retrieviewer.mjs +20 -14
- package/locomotion-core.mjs +0 -21
- package/locomotion-puppy.animal +0 -0
- package/vendor/procedural-animals/package.json +0 -40
- package/vendor/procedural-animals/src/animal.js +0 -146
- package/vendor/procedural-animals/src/bake.js +0 -51
- package/vendor/procedural-animals/src/build.js +0 -81
- package/vendor/procedural-animals/src/core/build/coatKit.js +0 -121
- package/vendor/procedural-animals/src/core/build/featherCards.js +0 -189
- package/vendor/procedural-animals/src/core/build/finEdges.js +0 -85
- package/vendor/procedural-animals/src/core/build/furSlope.js +0 -115
- package/vendor/procedural-animals/src/core/build/mesh.js +0 -67
- package/vendor/procedural-animals/src/core/build/pipeline.js +0 -272
- package/vendor/procedural-animals/src/core/build/seams.js +0 -183
- package/vendor/procedural-animals/src/core/build/warp.js +0 -112
- package/vendor/procedural-animals/src/core/build/weights.js +0 -393
- package/vendor/procedural-animals/src/core/math/vec.js +0 -81
- package/vendor/procedural-animals/src/core/motion/actions.js +0 -800
- package/vendor/procedural-animals/src/core/motion/bird.js +0 -3063
- package/vendor/procedural-animals/src/core/motion/birdActions.js +0 -1059
- package/vendor/procedural-animals/src/core/motion/common.js +0 -158
- package/vendor/procedural-animals/src/core/motion/index.js +0 -22
- package/vendor/procedural-animals/src/core/motion/quadruped.js +0 -2875
- package/vendor/procedural-animals/src/core/motion/snake.js +0 -1594
- package/vendor/procedural-animals/src/core/motion/spider.js +0 -1810
- package/vendor/procedural-animals/src/core/motion/swimmer.js +0 -1047
- package/vendor/procedural-animals/src/core/motion/swimmerActions.js +0 -613
- package/vendor/procedural-animals/src/core/render/animalObject.js +0 -262
- package/vendor/procedural-animals/src/core/render/coatMaterial.js +0 -1301
- package/vendor/procedural-animals/src/core/render/dqs.js +0 -159
- package/vendor/procedural-animals/src/core/render/eyes.js +0 -676
- package/vendor/procedural-animals/src/core/render/membranes.js +0 -77
- package/vendor/procedural-animals/src/core/render/skeleton.js +0 -41
- package/vendor/procedural-animals/src/core/rig/bird.js +0 -327
- package/vendor/procedural-animals/src/core/rig/quadruped.js +0 -76
- package/vendor/procedural-animals/src/core/rig/rig.js +0 -79
- package/vendor/procedural-animals/src/core/rig/snake.js +0 -76
- package/vendor/procedural-animals/src/core/rig/spider.js +0 -110
- package/vendor/procedural-animals/src/core/rig/swimmer.js +0 -181
- package/vendor/procedural-animals/src/core/sdf/eyeSocket.js +0 -53
- package/vendor/procedural-animals/src/core/sdf/mesher.js +0 -238
- package/vendor/procedural-animals/src/core/sdf/sdf.js +0 -263
- package/vendor/procedural-animals/src/index.d.ts +0 -98
- package/vendor/procedural-animals/src/index.js +0 -29
- package/vendor/procedural-animals/src/registry.js +0 -48
- package/vendor/procedural-animals/src/species/bear/behaviour.js +0 -463
- package/vendor/procedural-animals/src/species/bear/claws.js +0 -147
- package/vendor/procedural-animals/src/species/bear/coat.js +0 -726
- package/vendor/procedural-animals/src/species/bear/index.js +0 -139
- package/vendor/procedural-animals/src/species/bear/motion.js +0 -112
- package/vendor/procedural-animals/src/species/bear/regions.js +0 -78
- package/vendor/procedural-animals/src/species/bear/rig.js +0 -112
- package/vendor/procedural-animals/src/species/bear/sculpt.js +0 -272
- package/vendor/procedural-animals/src/species/boar/behaviour.js +0 -335
- package/vendor/procedural-animals/src/species/boar/coat.js +0 -372
- package/vendor/procedural-animals/src/species/boar/index.js +0 -110
- package/vendor/procedural-animals/src/species/boar/motion.js +0 -85
- package/vendor/procedural-animals/src/species/boar/regions.js +0 -88
- package/vendor/procedural-animals/src/species/boar/rig.js +0 -111
- package/vendor/procedural-animals/src/species/boar/sculpt.js +0 -285
- package/vendor/procedural-animals/src/species/cat/behaviour.js +0 -559
- package/vendor/procedural-animals/src/species/cat/coat.js +0 -841
- package/vendor/procedural-animals/src/species/cat/index.js +0 -211
- package/vendor/procedural-animals/src/species/cat/motion.js +0 -101
- package/vendor/procedural-animals/src/species/cat/regions.js +0 -82
- package/vendor/procedural-animals/src/species/cat/rig.js +0 -100
- package/vendor/procedural-animals/src/species/cat/sculpt.js +0 -266
- package/vendor/procedural-animals/src/species/cat/whiskers.js +0 -110
- package/vendor/procedural-animals/src/species/cheetah/coat.js +0 -427
- package/vendor/procedural-animals/src/species/cheetah/index.js +0 -51
- package/vendor/procedural-animals/src/species/cheetah/joints.js +0 -3
- package/vendor/procedural-animals/src/species/cheetah/motion.js +0 -77
- package/vendor/procedural-animals/src/species/cheetah/regions.js +0 -95
- package/vendor/procedural-animals/src/species/cheetah/rig.js +0 -66
- package/vendor/procedural-animals/src/species/cheetah/sculpt.js +0 -158
- package/vendor/procedural-animals/src/species/chicken/coat.js +0 -313
- package/vendor/procedural-animals/src/species/chicken/coverts.js +0 -320
- package/vendor/procedural-animals/src/species/chicken/index.js +0 -110
- package/vendor/procedural-animals/src/species/chicken/motion.js +0 -177
- package/vendor/procedural-animals/src/species/chicken/regions.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/rig.js +0 -144
- package/vendor/procedural-animals/src/species/chicken/sculpt.js +0 -389
- package/vendor/procedural-animals/src/species/chicken/wingLook.js +0 -66
- package/vendor/procedural-animals/src/species/chicken/wingStack.js +0 -124
- package/vendor/procedural-animals/src/species/cow/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/cow/coat.js +0 -465
- package/vendor/procedural-animals/src/species/cow/index.js +0 -118
- package/vendor/procedural-animals/src/species/cow/motion.js +0 -81
- package/vendor/procedural-animals/src/species/cow/regions.js +0 -98
- package/vendor/procedural-animals/src/species/cow/rig.js +0 -116
- package/vendor/procedural-animals/src/species/cow/sculpt.js +0 -339
- package/vendor/procedural-animals/src/species/crow/coat.js +0 -145
- package/vendor/procedural-animals/src/species/crow/index.js +0 -70
- package/vendor/procedural-animals/src/species/crow/joints.js +0 -3
- package/vendor/procedural-animals/src/species/crow/motion.js +0 -101
- package/vendor/procedural-animals/src/species/crow/regions.js +0 -50
- package/vendor/procedural-animals/src/species/crow/rig.js +0 -83
- package/vendor/procedural-animals/src/species/crow/sculpt.js +0 -118
- package/vendor/procedural-animals/src/species/deer/behaviour.js +0 -513
- package/vendor/procedural-animals/src/species/deer/coat.js +0 -391
- package/vendor/procedural-animals/src/species/deer/index.js +0 -106
- package/vendor/procedural-animals/src/species/deer/motion.js +0 -97
- package/vendor/procedural-animals/src/species/deer/regions.js +0 -60
- package/vendor/procedural-animals/src/species/deer/rig.js +0 -124
- package/vendor/procedural-animals/src/species/deer/sculpt.js +0 -297
- package/vendor/procedural-animals/src/species/dog/behaviour.js +0 -521
- package/vendor/procedural-animals/src/species/dog/coat.js +0 -495
- package/vendor/procedural-animals/src/species/dog/index.js +0 -144
- package/vendor/procedural-animals/src/species/dog/motion.js +0 -118
- package/vendor/procedural-animals/src/species/dog/regions.js +0 -61
- package/vendor/procedural-animals/src/species/dog/rig.js +0 -129
- package/vendor/procedural-animals/src/species/dog/sculpt.js +0 -246
- package/vendor/procedural-animals/src/species/eagle/coat.js +0 -250
- package/vendor/procedural-animals/src/species/eagle/index.js +0 -76
- package/vendor/procedural-animals/src/species/eagle/joints.js +0 -3
- package/vendor/procedural-animals/src/species/eagle/motion.js +0 -137
- package/vendor/procedural-animals/src/species/eagle/regions.js +0 -57
- package/vendor/procedural-animals/src/species/eagle/rig.js +0 -107
- package/vendor/procedural-animals/src/species/eagle/sculpt.js +0 -137
- package/vendor/procedural-animals/src/species/fish/coat.js +0 -271
- package/vendor/procedural-animals/src/species/fish/index.js +0 -88
- package/vendor/procedural-animals/src/species/fish/motion.js +0 -92
- package/vendor/procedural-animals/src/species/fish/regions.js +0 -27
- package/vendor/procedural-animals/src/species/fish/rig.js +0 -143
- package/vendor/procedural-animals/src/species/fish/sculpt.js +0 -214
- package/vendor/procedural-animals/src/species/fish/variants.js +0 -126
- package/vendor/procedural-animals/src/species/fox/behaviour.js +0 -314
- package/vendor/procedural-animals/src/species/fox/coat.js +0 -444
- package/vendor/procedural-animals/src/species/fox/index.js +0 -120
- package/vendor/procedural-animals/src/species/fox/motion.js +0 -81
- package/vendor/procedural-animals/src/species/fox/regions.js +0 -59
- package/vendor/procedural-animals/src/species/fox/rig.js +0 -91
- package/vendor/procedural-animals/src/species/fox/sculpt.js +0 -202
- package/vendor/procedural-animals/src/species/frog/behaviour.js +0 -590
- package/vendor/procedural-animals/src/species/frog/coat.js +0 -305
- package/vendor/procedural-animals/src/species/frog/index.js +0 -97
- package/vendor/procedural-animals/src/species/frog/motion.js +0 -69
- package/vendor/procedural-animals/src/species/frog/regions.js +0 -46
- package/vendor/procedural-animals/src/species/frog/rig.js +0 -156
- package/vendor/procedural-animals/src/species/frog/sculpt.js +0 -240
- package/vendor/procedural-animals/src/species/goat/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/goat/coat.js +0 -590
- package/vendor/procedural-animals/src/species/goat/index.js +0 -149
- package/vendor/procedural-animals/src/species/goat/motion.js +0 -95
- package/vendor/procedural-animals/src/species/goat/regions.js +0 -98
- package/vendor/procedural-animals/src/species/goat/rig.js +0 -181
- package/vendor/procedural-animals/src/species/goat/sculpt.js +0 -547
- package/vendor/procedural-animals/src/species/horse/coat.js +0 -361
- package/vendor/procedural-animals/src/species/horse/coatSeam.js +0 -122
- package/vendor/procedural-animals/src/species/horse/index.js +0 -80
- package/vendor/procedural-animals/src/species/horse/motion.js +0 -245
- package/vendor/procedural-animals/src/species/horse/regions.js +0 -60
- package/vendor/procedural-animals/src/species/horse/rig.js +0 -117
- package/vendor/procedural-animals/src/species/horse/sculpt.js +0 -316
- package/vendor/procedural-animals/src/species/horse/seamWeights.js +0 -272
- package/vendor/procedural-animals/src/species/lion/behaviour.js +0 -509
- package/vendor/procedural-animals/src/species/lion/coat.js +0 -1016
- package/vendor/procedural-animals/src/species/lion/index.js +0 -111
- package/vendor/procedural-animals/src/species/lion/motion.js +0 -87
- package/vendor/procedural-animals/src/species/lion/regions.js +0 -106
- package/vendor/procedural-animals/src/species/lion/rig.js +0 -110
- package/vendor/procedural-animals/src/species/lion/sculpt.js +0 -384
- package/vendor/procedural-animals/src/species/lion/whiskers.js +0 -132
- package/vendor/procedural-animals/src/species/pig/behaviour.js +0 -575
- package/vendor/procedural-animals/src/species/pig/coat.js +0 -730
- package/vendor/procedural-animals/src/species/pig/ears.js +0 -242
- package/vendor/procedural-animals/src/species/pig/index.js +0 -161
- package/vendor/procedural-animals/src/species/pig/motion.js +0 -105
- package/vendor/procedural-animals/src/species/pig/regions.js +0 -100
- package/vendor/procedural-animals/src/species/pig/rig.js +0 -152
- package/vendor/procedural-animals/src/species/pig/sculpt.js +0 -382
- package/vendor/procedural-animals/src/species/rabbit/behaviour.js +0 -605
- package/vendor/procedural-animals/src/species/rabbit/coat.js +0 -285
- package/vendor/procedural-animals/src/species/rabbit/index.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/motion.js +0 -85
- package/vendor/procedural-animals/src/species/rabbit/regions.js +0 -48
- package/vendor/procedural-animals/src/species/rabbit/rig.js +0 -99
- package/vendor/procedural-animals/src/species/rabbit/sculpt.js +0 -148
- package/vendor/procedural-animals/src/species/rat/behaviour.js +0 -539
- package/vendor/procedural-animals/src/species/rat/coat.js +0 -341
- package/vendor/procedural-animals/src/species/rat/index.js +0 -111
- package/vendor/procedural-animals/src/species/rat/motion.js +0 -90
- package/vendor/procedural-animals/src/species/rat/regions.js +0 -92
- package/vendor/procedural-animals/src/species/rat/rig.js +0 -112
- package/vendor/procedural-animals/src/species/rat/sculpt.js +0 -163
- package/vendor/procedural-animals/src/species/rat/whiskers.js +0 -119
- package/vendor/procedural-animals/src/species/shark/coat.js +0 -383
- package/vendor/procedural-animals/src/species/shark/index.js +0 -104
- package/vendor/procedural-animals/src/species/shark/motion.js +0 -69
- package/vendor/procedural-animals/src/species/shark/regions.js +0 -34
- package/vendor/procedural-animals/src/species/shark/rig.js +0 -194
- package/vendor/procedural-animals/src/species/shark/sculpt.js +0 -343
- package/vendor/procedural-animals/src/species/shark/variants.js +0 -105
- package/vendor/procedural-animals/src/species/sheep/behaviour.js +0 -522
- package/vendor/procedural-animals/src/species/sheep/coat.js +0 -529
- package/vendor/procedural-animals/src/species/sheep/index.js +0 -152
- package/vendor/procedural-animals/src/species/sheep/motion.js +0 -86
- package/vendor/procedural-animals/src/species/sheep/neck.js +0 -41
- package/vendor/procedural-animals/src/species/sheep/regions.js +0 -62
- package/vendor/procedural-animals/src/species/sheep/rig.js +0 -163
- package/vendor/procedural-animals/src/species/sheep/sculpt.js +0 -542
- package/vendor/procedural-animals/src/species/snake/coat.js +0 -315
- package/vendor/procedural-animals/src/species/snake/index.js +0 -73
- package/vendor/procedural-animals/src/species/snake/motion.js +0 -51
- package/vendor/procedural-animals/src/species/snake/regions.js +0 -51
- package/vendor/procedural-animals/src/species/snake/rig.js +0 -108
- package/vendor/procedural-animals/src/species/snake/sculpt.js +0 -122
- package/vendor/procedural-animals/src/species/spider/coat.js +0 -279
- package/vendor/procedural-animals/src/species/spider/index.js +0 -78
- package/vendor/procedural-animals/src/species/spider/motion.js +0 -72
- package/vendor/procedural-animals/src/species/spider/regions.js +0 -87
- package/vendor/procedural-animals/src/species/spider/rig.js +0 -118
- package/vendor/procedural-animals/src/species/spider/sculpt.js +0 -182
- package/vendor/procedural-animals/src/species/wolf/behaviour.js +0 -160
- package/vendor/procedural-animals/src/species/wolf/coat.js +0 -749
- package/vendor/procedural-animals/src/species/wolf/index.js +0 -107
- package/vendor/procedural-animals/src/species/wolf/motion.js +0 -80
- package/vendor/procedural-animals/src/species/wolf/regions.js +0 -57
- package/vendor/procedural-animals/src/species/wolf/rig.js +0 -87
- package/vendor/procedural-animals/src/species/wolf/sculpt.js +0 -339
- package/vendor/procedural-animals/src/worker.js +0 -29
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
// The goat, sculpted as a signed distance field.
|
|
2
|
-
// Every primitive is attached to a bone (skinning) and a group (axial / limb / ear / jaw). The head is
|
|
3
|
-
// modelled in its own frame (rig.js: hl()), measured from the profile and frontal references (side2,
|
|
4
|
-
// side3, face1, face3); the body relative to the joints so the sculpt follows the skeleton.
|
|
5
|
-
// Separate surfaces ("parts"): 'jaw' (lower lip, chin and the beard: the mouth opens and the beard
|
|
6
|
-
// swings with it) and 'horn' (keratin horns, rigid on the skull).
|
|
7
|
-
import { HEAD_O, HZ, HY, hl, hdir, TAIL_SEGS, hqOf, faceZ, KID_FACE } from './rig.js';
|
|
8
|
-
import { add, sub, mul, lerp, norm, cross, rng, clamp, smoothstep } from '../../core/math/vec.js';
|
|
9
|
-
import { apertureTiltAlong, sculptEyeSocket } from '../../core/sdf/eyeSocket.js';
|
|
10
|
-
import { SDFModel } from '../../core/sdf/sdf.js';
|
|
11
|
-
|
|
12
|
-
// Eye: globe ~29 mm across, visible opening ~24 x 16 mm, set high on the side
|
|
13
|
-
// of the head, prominent, looking out and a little forward.
|
|
14
|
-
const EYE_LOCAL = [0.057, -0.004, 0.0];
|
|
15
|
-
const eyeDir = (() => {
|
|
16
|
-
const a = (24 * Math.PI) / 180;
|
|
17
|
-
return norm(add([Math.cos(a), 0, 0], mul(HZ, Math.sin(a))));
|
|
18
|
-
})();
|
|
19
|
-
const EYE_BASE = {
|
|
20
|
-
c: sub(hl(EYE_LOCAL), HEAD_O), r: 0.0145, back: 0.003, yaw: Math.atan2(eyeDir[0], eyeDir[2]), pitch: Math.asin(eyeDir[1]),
|
|
21
|
-
lid: 0.0017, R: 0.0132, d: 0.0052, off: -0.0006, tilt: 0, irisZ: 0.0081, irisR: 0.0112,
|
|
22
|
-
};
|
|
23
|
-
// roll the almond: its long axis ~20 deg from the horizontal with the inner corner low (side2, face4,
|
|
24
|
-
// walk1), i.e. rolled back ~22 deg from the head axis (a search over a limited range stood it on end)
|
|
25
|
-
EYE_BASE.tilt = apertureTiltAlong(EYE_BASE, HEAD_O, HZ) - 0.38;
|
|
26
|
-
export const EYE = EYE_BASE;
|
|
27
|
-
|
|
28
|
-
const X = [1, 0, 0];
|
|
29
|
-
|
|
30
|
-
// The horn's mesh cell at this individual's quality tier (params.tierRes): its girth sets it (regions.js)
|
|
31
|
-
export const hornCell = (params, res = params.tierRes ?? 1.3) => clamp((params.horns?.base || 0.012) * 0.13, 0.0025, 0.0039) * Math.min(res, 3);
|
|
32
|
-
|
|
33
|
-
// Growth ridges round the horn's base half (face2, face4: transverse corrugations, the keratin added ring by ring
|
|
34
|
-
// at the base), shared by the sculpt (raised collars where the tier's cells resolve them) and the coat (a dark
|
|
35
|
-
// groove before each ridge at every tier). Spacing S along the horn (m), height amp x the horn radius, fading out
|
|
36
|
-
// toward tEnd (fraction of the length). null for horn buds.
|
|
37
|
-
export function hornRidges(params) {
|
|
38
|
-
const H = params.horns;
|
|
39
|
-
if (!H || H.len < 0.06) return null;
|
|
40
|
-
const buck = H.base > 0.02;
|
|
41
|
-
const S = clamp(0.45 * H.base, 0.0075, 0.0135);
|
|
42
|
-
// (resolved: at least ~2.6 cells per ridge; at the medium tier and coarser the ridges are painted only)
|
|
43
|
-
const geo = smoothstep(2.4, 3.0, S / hornCell(params));
|
|
44
|
-
// (s0: the first crest, clear of the skin collar round the base: buried in it, a ridge lifted the hair)
|
|
45
|
-
return { S, s0: 0.012 + 0.5 * S, amp: buck ? 0.075 : 0.06, tEnd: buck ? 0.62 : 0.5, geo };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// At the low and crowd tiers the horn is meshed as two closed solids: the base ('horn', the tier's horn cells)
|
|
49
|
-
// and the tapering outer part ('horntip', at the medium tier's horn cells), the tip's start buried in the base
|
|
50
|
-
// and a hair thinner, so the base's rounded end meets it as a fine ring. Returns the first path index of the
|
|
51
|
-
// base's end (the tip runs from two segments before it), or -1 when the horn is one solid.
|
|
52
|
-
// (one solid at the low tier's ~1 cm cells: the last fifth of a buck's horn, 3-9 mm thick, meshed as a pinched
|
|
53
|
-
// neck and a twisted, ragged flake at the tip, the tip read as snapped off; kept ~0.45 of a cell thick, it was a
|
|
54
|
-
// sub-cell tube the grid could not resolve)
|
|
55
|
-
export function hornSplit(params, pth) {
|
|
56
|
-
if ((params.tierRes ?? 1.3) < 2.5 || !pth || pth.length < 6 || params.horns.len < 0.06) return -1;
|
|
57
|
-
const hc = hornCell(params);
|
|
58
|
-
let j = 1;
|
|
59
|
-
while (j < pth.length - 3 && (pth[j].t < 0.3 || pth[j].r > 1.3 * hc) && pth[j].t < 0.75) j++;
|
|
60
|
-
return j;
|
|
61
|
-
}
|
|
62
|
-
export const hornTipCell = (params) => hornCell(params, (params.tierRes ?? 1.3) > 3.5 ? 2.25 : 2);
|
|
63
|
-
|
|
64
|
-
// Horn centre line (reference space), shared by the sculpt and the coat: a scimitar arc from the
|
|
65
|
-
// poll, up and back, sweeping further back and out along its length. Returns [{ p, r, t }].
|
|
66
|
-
// (at the medium tier and coarser the horn is never thinner than ~0.8 of its cell (the tip solid's cell at the low
|
|
67
|
-
// and crowd tiers, hornSplit): at the crowd tier's 16 mm cells the tapering half of a buck's horn, 2-8 mm thick,
|
|
68
|
-
// broke into ragged fragments along its length, and with ~0.45 of a cell (and at medium with none) the last
|
|
69
|
-
// centimetre ended in a black chisel facet or a needle-thin twisted flake past a pinch, a tip that read as snapped
|
|
70
|
-
// off; at ~0.8 the tip is a blunt rounded end, as at the high tier)
|
|
71
|
-
export function hornPath(params, s) {
|
|
72
|
-
const H = params.horns;
|
|
73
|
-
if (!H || H.len <= 0) return [];
|
|
74
|
-
const rMin = (params.tierRes ?? 1.3) >= 1.9 ? 0.8 * hornTipCell(params) : 0;
|
|
75
|
-
const base = hl([0.021 * s, 0.035, -0.052]);
|
|
76
|
-
// sagittal start direction (head-local y, z) and the backward-rotated perpendicular
|
|
77
|
-
const d0 = norm([0, 0.15 + 0.35 * (H.upright || 0), -1]); // up and back from the poll
|
|
78
|
-
const v0 = [0, d0[2], -d0[1]]; // d0 rotated 90 degrees backward in the sagittal plane (back and down)
|
|
79
|
-
// (~1 cm segments: at 2.5-3 cm the cones made a faceted curve that caught the light segment by segment)
|
|
80
|
-
const n = Math.max(8, Math.round(20 * Math.min(1.6, H.len / 0.2)));
|
|
81
|
-
const out = [];
|
|
82
|
-
let p = base.slice();
|
|
83
|
-
const ds = H.len / n;
|
|
84
|
-
for (let i = 0; i <= n; i++) {
|
|
85
|
-
const t = i / n;
|
|
86
|
-
const th = H.curve * Math.pow(t, 1.15);
|
|
87
|
-
const dl = [
|
|
88
|
-
s * (H.spread * (0.35 + 1.1 * t)),
|
|
89
|
-
d0[1] * Math.cos(th) + v0[1] * Math.sin(th),
|
|
90
|
-
d0[2] * Math.cos(th) + v0[2] * Math.sin(th),
|
|
91
|
-
];
|
|
92
|
-
const r = Math.max(rMin, H.base * Math.pow(1 - 0.92 * t, 0.85) + 0.0015);
|
|
93
|
-
out.push({ p: p.slice(), r, t });
|
|
94
|
-
p = add(p, mul(norm(hdir(dl)), ds));
|
|
95
|
-
}
|
|
96
|
-
return out;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// the direction the inner (cupped) side of an ear faces: forward, a little out and down (erect), toward the
|
|
100
|
-
// face and a little forward (lop); shared with the coat (inner-ear skin, the rim of a lop ear)
|
|
101
|
-
export function earFacing(params, s) {
|
|
102
|
-
return params.ear === 'lop' ? norm(add([-0.9 * s, 0, 0], mul(hdir([0, 0, 1]), 0.4))) : norm([0.25 * s, -0.3, 1]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function sculptGoat(m, rig, params = {}) {
|
|
106
|
-
const J = rig.J;
|
|
107
|
-
const kid = params.age === 'juvenile';
|
|
108
|
-
const HQ = hqOf(params);
|
|
109
|
-
const male = params.sex === 'male';
|
|
110
|
-
const buck = male && !kid;
|
|
111
|
-
const heavy = params.heavy ?? (buck ? 1 : 0); // 0 doe .. 1 heavy buck
|
|
112
|
-
const R = rng(5311 + (params.coatSeed || 0));
|
|
113
|
-
const roman = params.roman || 0; // Nubian / Boer convex nose
|
|
114
|
-
void R;
|
|
115
|
-
|
|
116
|
-
const ellY = (o) => {
|
|
117
|
-
const y = norm(o.ydir);
|
|
118
|
-
const lat = o.lateral || X;
|
|
119
|
-
const z = norm(cross(lat, y));
|
|
120
|
-
return m.ell({ ...o, axis: z, up: y, r: [o.r[0], o.r[1], o.r[2]] });
|
|
121
|
-
};
|
|
122
|
-
const H = { bone: 'head' };
|
|
123
|
-
// (head-local points in adult coordinates; a kid's face in front of the eyes is shorter, rig.js faceZ)
|
|
124
|
-
const hk = (v) => hl([v[0], v[1], faceZ(v[2], kid)]);
|
|
125
|
-
const shortR = (o) => (kid && o.c[2] > 0.02 ? [o.r[0], o.r[1], o.r[2] * KID_FACE] : o.r);
|
|
126
|
-
const hell = (o) => m.ell({ ...H, ...o, c: hk(o.c), r: shortR(o), axis: o.axisL ? norm(hdir(o.axisL)) : HZ, up: o.upL ? norm(hdir(o.upL)) : HY });
|
|
127
|
-
|
|
128
|
-
// ---------------------------------------------------------------- TORSO
|
|
129
|
-
// dairy type: deep barrel, sharp withers, level back, prominent hip bones and pin bones, sloping rump
|
|
130
|
-
const gw = 1 + 0.12 * heavy; // bucks are broader
|
|
131
|
-
m.ell({ tag: 'ribcage', bone: 'spine3', c: [0, 0.585, 0.105], r: [0.122 * gw, 0.18, 0.25], axis: norm([0, 0.08, 1]), k: 0 });
|
|
132
|
-
m.ell({ tag: 'girth', bone: 'chest', c: [0, 0.575, 0.28], r: [0.1 * gw, 0.15, 0.12], k: 0.08 });
|
|
133
|
-
// (a doe is deep through the belly, deeper in milk: side2, side3; the barrel hung 2-5 cm higher than theirs;
|
|
134
|
-
// the Boer's barrel is already deep through its girth, and deeper still its short forelegs could not fold
|
|
135
|
-
// under the chest when it lay down)
|
|
136
|
-
const milk = !male && !kid ? (params.udder ?? 0.5) * (params.variant === 'boer' ? 0.7 : 1) : 0;
|
|
137
|
-
const doe = !male && !kid && params.variant !== 'boer' ? 1 : 0;
|
|
138
|
-
// (the trunk pieces blend over 8-10 cm: one smooth barrel with a straight top line, threequarter1 / side2;
|
|
139
|
-
// at 5-7 cm the withers, back, loin and flank showed as separate bumps, and the coat parted over the
|
|
140
|
-
// junctions)
|
|
141
|
-
m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.57 - 0.015 * milk, -0.085], r: [0.14 * gw, 0.19 - 0.02 * heavy + 0.02 * milk, 0.165], axis: norm([0, 0.1, -1]), k: 0.09 });
|
|
142
|
-
// (the deeper belly of a doe hangs between the elbows and the stifles, clear of the stifles: lowered as a
|
|
143
|
-
// whole, the belly's side took the stifle's place and the swinging stifle poked through it at every step)
|
|
144
|
-
if (doe) m.ell({ tag: 'abdomen', bone: 'spine2', c: [0, 0.43 - 0.008 * milk, -0.02], r: [0.1, 0.075 + 0.012 * milk, 0.125], k: 0.08 });
|
|
145
|
-
m.ell({ tag: 'flank', bone: 'spine1', c: [0, 0.62, -0.19], r: [0.12 * gw, 0.12, 0.11], k: 0.09 });
|
|
146
|
-
m.ell({ tag: 'withers', bone: 'chest', c: [0, 0.735, 0.22], r: [0.045, 0.05, 0.15], axis: norm([0, -0.12, 1]), k: 0.09 });
|
|
147
|
-
m.ell({ tag: 'back', bone: 'spine3', c: [0, 0.72, 0.05], r: [0.09 * gw, 0.05, 0.2], k: 0.1 });
|
|
148
|
-
m.ell({ tag: 'loin', bone: 'spine1', c: [0, 0.725, -0.13], r: [0.1 * gw, 0.045, 0.12], k: 0.1 });
|
|
149
|
-
// hindquarters: croup sloping to the tail head, hooks (hip points) and pins
|
|
150
|
-
m.ell({ tag: 'pelvis', bone: 'pelvis', c: [0, 0.645, -0.3], r: [0.115 * gw, 0.115, 0.105], k: 0.05 });
|
|
151
|
-
m.ell({ tag: 'croup', bone: 'pelvis', c: [0, 0.715, -0.29], r: [0.085 * gw, 0.045, 0.11], axis: norm([0, -0.25, 1]), k: 0.05 });
|
|
152
|
-
for (const s of [1, -1]) {
|
|
153
|
-
m.sphere({ tag: 'hippoint', bone: 'pelvis', c: [0.095 * s * gw, 0.715, -0.195], rad: 0.021, k: 0.05 });
|
|
154
|
-
m.sphere({ tag: 'pinbone', bone: 'pelvis', c: [0.045 * s, 0.69, -0.362], rad: 0.022, k: 0.04 });
|
|
155
|
-
m.ell({ tag: 'rump', bone: 'pelvis', c: [0.05 * s, 0.6, -0.335], r: [0.055, 0.1, 0.055], k: 0.06 });
|
|
156
|
-
}
|
|
157
|
-
// chest front: the breast between the forelegs, running smoothly up into the throat (side2: the point
|
|
158
|
-
// of the shoulder shows, the breast does not bulge; it stood 6 cm ahead of the shoulder joints as a knob)
|
|
159
|
-
// (front1, front2, threequarter1: one rounded front, the neck flaring into it, the points of the shoulder
|
|
160
|
-
// at most a gentle angle at its sides. A breast as wide as the shoulders with the humerus heads at its
|
|
161
|
-
// corners read as a box with two round lumps in every front and 3/4 view: the shoulder cones stood
|
|
162
|
-
// 1.2 cm proud of a groove, and 2 cm outside every primitive there, where five of them blended. Now the
|
|
163
|
-
// breast is narrower and centred, the shoulders 2.5-3 cm behind it; the chest front stands 0.06 / 0.09 /
|
|
164
|
-
// 0.11 SH ahead of the forearm at 0.54 / 0.56 / 0.59 SH, side3 0.09 / 0.13 / 0.14 with its near leg)
|
|
165
|
-
m.ell({ tag: 'brisket', bone: 'chest', c: [0, 0.482, 0.272], r: [0.075, 0.07, 0.07], k: 0.05 });
|
|
166
|
-
for (const s of [1, -1]) m.ell({ tag: 'pectoral', bone: 'chest', c: [0.042 * s, 0.475, 0.3], r: [0.04, 0.055, 0.026], k: 0.05 });
|
|
167
|
-
// the breast muscles over the sternum
|
|
168
|
-
// (bias: its skin counts as the chest's for the weights, so a goat lying on its side rests on it: skinned
|
|
169
|
-
// half to the neck and legs, the fuller chest front went 13 mm into the ground in death)
|
|
170
|
-
m.ell({ tag: 'pectoral', bone: 'chest', c: [0, 0.525, 0.325], r: [0.075, 0.065, 0.065], k: 0.05, bias: 0.03 });
|
|
171
|
-
// udder (adult does): two halves and teats, pink skin (size: dry doe .. in milk); a buck's scrotum.
|
|
172
|
-
// Their own closed surface ('udder' part), rigid on the udder bone, its top sunk into the belly: meshed
|
|
173
|
-
// with the body, the udder hangs between the gaskins and took 74 % tibia weights (the core gives the limbs
|
|
174
|
-
// their share before an appendage), so its skin crumpled and the hair shells over it tore into dark shards
|
|
175
|
-
// at every step. Narrow enough to hang between the thighs (their inner skin is ~5 cm off the midline).
|
|
176
|
-
const UD = { bone: 'udder', group: 'udder', part: 'udder' };
|
|
177
|
-
if (!male && !kid && (params.udder ?? 0.5) > 0.05) {
|
|
178
|
-
const u = params.udder ?? 0.5;
|
|
179
|
-
for (const s of [1, -1]) {
|
|
180
|
-
m.ell({ ...UD, tag: 'udder', c: [0.025 * s, 0.35 - 0.03 * u, -0.338 + HQ], r: [0.032 + 0.013 * u, 0.047 + 0.03 * u, 0.052 + 0.022 * u], k: 0.03 });
|
|
181
|
-
const a = [0.033 * s, 0.315 - 0.055 * u, -0.322 + HQ], b = add(a, [0.01 * s, -0.035 - 0.01 * u, 0.013]);
|
|
182
|
-
m.cone({ ...UD, tag: 'teat', a, b, ra: 0.0105, rb: 0.0078, k: 0.012 });
|
|
183
|
-
}
|
|
184
|
-
// the attachment: from the udder up into the belly (hidden inside the body above the groin)
|
|
185
|
-
m.ell({ ...UD, tag: 'udder', c: [0, 0.43, -0.345 + HQ], r: [0.045, 0.085, 0.072], k: 0.035 });
|
|
186
|
-
}
|
|
187
|
-
if (male && !kid) {
|
|
188
|
-
// scrotum between the hind legs, under the groin (walk1: a short-haired pendulous bag hanging behind the stifles to
|
|
189
|
-
// about the middle of the gaskin): a neck of skin from deep in the groin, narrowing to 4.2 cm below the underline,
|
|
190
|
-
// and a pear-shaped bag of two testes side by side (7 cm across, 8 cm deep, 26 cm round once scaled to a buck:
|
|
191
|
-
// dairy bucks 26-34 cm), its bottom 18 cm below the groin. The neck is anchored 7 cm inside the body (the old
|
|
192
|
-
// sausage's top was 1 cm inside: as the groin skin moved with the thighs its end showed, a separate object). Inside
|
|
193
|
-
// the thighs' inner skin (x 4.8-6.4 cm off the midline beside it) with 1.3 cm to spare. Sheath under the belly.
|
|
194
|
-
const zc = -0.385 + HQ;
|
|
195
|
-
// (widening up into the groin; a flare filling the top of the arch between the thighs read as a second bulb from behind)
|
|
196
|
-
m.cone({ ...UD, tag: 'scrotum', a: [0, 0.575, zc + 0.012], b: [0, 0.448, zc - 0.002], ra: 0.034, rb: 0.021, k: 0.02 });
|
|
197
|
-
m.ell({ ...UD, tag: 'scrotum', c: [0, 0.392, zc - 0.004], r: [0.03, 0.05, 0.036], k: 0.03 });
|
|
198
|
-
for (const s of [1, -1]) m.ell({ ...UD, tag: 'scrotum', c: [0.013 * s, 0.375, zc - 0.006], r: [0.022, 0.05, 0.04], axis: norm([0, 0.12, 1]), k: 0.02 });
|
|
199
|
-
m.ell({ tag: 'sheath', bone: 'spine2', c: [0, 0.375, -0.02], r: [0.017, 0.02, 0.045], axis: norm([0, 0.3, 1]), k: 0.03 });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ---------------------------------------------------------------- NECK
|
|
203
|
-
// long and slender, flattened sideways, deep at the base; a buck's neck is thick with a crest
|
|
204
|
-
const nb = J.neckBase, nm = J.neckMid, occ = J.occiput;
|
|
205
|
-
const nd1 = norm(sub(nm, nb)), nd2 = norm(sub(occ, nm));
|
|
206
|
-
// (perpendiculars in the sagittal plane: fwd = toward the throat, back = toward the crest)
|
|
207
|
-
const fwdN = (d) => [0, -d[2], d[1]], backN = (d) => [0, d[2], -d[1]];
|
|
208
|
-
const ndM = norm(add(nd1, nd2));
|
|
209
|
-
const nk = 1 + 0.3 * heavy;
|
|
210
|
-
// (the pieces blend over 7 cm: over a sharper junction the 9 mm coat fanned open along a ragged dark line)
|
|
211
|
-
m.ell({ tag: 'neck', bone: 'neck1', c: add(nb, [0, 0.008, 0.006]), r: [0.072 * nk, 0.1, 0.095], axis: nd1, up: [0, 1, -0.6], k: 0.07 });
|
|
212
|
-
m.ell({ tag: 'neck', bone: 'neck1', c: add(lerp(nb, nm, 0.72), [0, 0, 0.012]), r: [0.052 * nk, 0.075 * (1 + 0.15 * heavy), 0.1], axis: nd1, up: [0, 1, -0.6], k: 0.07 });
|
|
213
|
-
// (a bridge at the mid neck: the neck tapers continuously instead of stepping from one piece to the next)
|
|
214
|
-
m.ell({ tag: 'neck', bone: 'neck2', c: add(nm, [0, 0, 0.018]), r: [0.047 * nk, 0.064 * (1 + 0.15 * heavy), 0.075], axis: ndM, up: [0, 1, -0.6], k: 0.07 });
|
|
215
|
-
m.ell({ tag: 'neck', bone: 'neck2', c: add(lerp(nm, occ, 0.39), [0, 0, 0.028]), r: [0.042 * nk, 0.052 * (1 + 0.15 * heavy), 0.08], axis: nd2, up: [0, 1, -0.6], k: 0.06 });
|
|
216
|
-
// a buck's crest: the thick upper neck behind the axis (a narrow crest stood out as a ridge along the side
|
|
217
|
-
// of a doe's neck, where the coat parted)
|
|
218
|
-
if (heavy > 0.05) m.ell({ tag: 'crest', bone: 'neck1', c: add(lerp(nb, nm, 0.9), mul(backN(nd1), 0.045)), r: [0.05 + 0.02 * heavy, 0.035 + 0.025 * heavy, 0.13], axis: norm(add(nd1, [0, 0.1, 0])), k: 0.07 });
|
|
219
|
-
// underline of the neck (trachea) to the throat
|
|
220
|
-
const thr = add(nm, mul(fwdN(ndM), 0.07));
|
|
221
|
-
m.cone({ tag: 'throat', bone: 'neck1', a: add(add(nb, mul(nd1, -0.02)), mul(fwdN(nd1), 0.07)), b: thr, ra: 0.038, rb: 0.031, k: 0.045 });
|
|
222
|
-
m.cone({ tag: 'throat', bone: 'neck2', a: add(thr, mul(ndM, -0.01)), b: hl([0, -0.105 + (kid ? 0.015 : 0), -0.06]), ra: 0.03, rb: 0.027, k: 0.04 });
|
|
223
|
-
// the throatlatch: the larynx fills the corner between the underside of the jaw and the throat (a sharp
|
|
224
|
-
// notch there folded over as the head moved, back faces showing at the throat outline); not in kids (their
|
|
225
|
-
// throats stretched 3x on 4.2 % of the mesh when grazing)
|
|
226
|
-
if (!kid) m.ell({ tag: 'throat', bone: 'neck2', c: hl([0, -0.13, 0.036]), r: [0.02, 0.015, 0.022], k: 0.03 });
|
|
227
|
-
// wattles (toggles): two hair-covered tassels hanging from the throat, behind and below the jaw angle
|
|
228
|
-
// (at head-local y -0.082 they hung from the cheek beside the eye once the head was carried higher)
|
|
229
|
-
if (params.wattles) {
|
|
230
|
-
for (const s of [1, -1]) {
|
|
231
|
-
const a = hl([0.018 * s, -0.128, -0.068]);
|
|
232
|
-
const b = add(a, [0.006 * s, -0.052, -0.006]);
|
|
233
|
-
m.cone({ tag: 'wattle', bone: 'neck2', part: 'wattle', a, b, ra: 0.0065, rb: 0.0095, k: 0.008 });
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// ---------------------------------------------------------------- HEAD (head-local, see rig.js)
|
|
238
|
-
// A goat's head from the side is a wedge (side2, walk1, face2, face4; skull anatomy): the eye sits high
|
|
239
|
-
// on the side of a deep head, the flat, broad forehead rises above it to the horn bases, the straight
|
|
240
|
-
// nasal line runs steeply down (~60 deg below the horizontal, 20 deg below the eye -> nose line) to a
|
|
241
|
-
// compact nose, and the lower border of the jaw runs straight from the chin back and down to a deep jaw
|
|
242
|
-
// angle behind and 13 cm below the eye (the ramus); the cheek is a flat masseter plate, not a jowl. The
|
|
243
|
-
// lips and chin sit well below the nose. From the front the head is widest at the eyes and tapers to
|
|
244
|
-
// the muzzle (face1, face3).
|
|
245
|
-
// landmarks: poll (0, 0, -0.07), eyes (+-0.057, -0.004, 0), forehead top y 0.052 at z 0.02, nose top
|
|
246
|
-
// (0, -0.004, 0.17), nose tip (0, -0.022, 0.205), mouth line y -0.066, chin bottom (0, -0.097, 0.165),
|
|
247
|
-
// jaw angle (+-0.031, -0.138, -0.045)
|
|
248
|
-
// (kd: a kid's head: a domed braincase, a shallow jaw without the adult's masseter plate)
|
|
249
|
-
const kd = kid ? 1 : 0;
|
|
250
|
-
hell({ tag: 'cranium', c: [0, 0.012 + 0.004 * kd, -0.042], r: [0.055, 0.041 + 0.01 * kd, 0.058], k: 0.03 });
|
|
251
|
-
hell({ tag: 'poll', c: [0, -0.014, -0.068], r: [0.035, 0.037, 0.03], k: 0.03 });
|
|
252
|
-
// the dorsal line: one straight line from the forehead between the horn bases to the nose, 16 deg below
|
|
253
|
-
// the head axis (58 deg below the horizontal in the bind carriage; side2 55, walk1 60): the forehead
|
|
254
|
-
// plate and the nasal bridge are laid along it (a level forehead dropping onto a lower nasal bridge
|
|
255
|
-
// made a dog's stop)
|
|
256
|
-
const NL = [0, -0.29, 1];
|
|
257
|
-
hell({ tag: 'forehead', c: [0, 0.026, 0.012], r: [0.062, 0.018, 0.07], axisL: NL, k: 0.025 });
|
|
258
|
-
// nasal bridge: straight / slightly dished in Swiss breeds, convex (Roman) in Nubians and Boers
|
|
259
|
-
// (the arch: a separate ridge at mid-face, up to 26 mm for roman = 1)
|
|
260
|
-
hell({ tag: 'face', c: [0, -0.004, 0.105], r: [0.029, 0.023, 0.095], axisL: NL, k: 0.025 });
|
|
261
|
-
if (roman > 0.05) hell({ tag: 'face', c: [0, 0.0 + 0.013 * roman, 0.105], r: [0.022 + 0.004 * roman, 0.016 + 0.012 * roman, 0.07], axisL: NL, k: 0.03 });
|
|
262
|
-
// (the muzzle is broad: face1 / face3, the nose nearly as wide as the face at the mouth; at 5 cm across it
|
|
263
|
-
// made a horse's narrow nose)
|
|
264
|
-
hell({ tag: 'lowerface', c: [0, -0.042 + 0.004 * kd, 0.082], r: [0.035, 0.043 - 0.006 * kd, 0.088], k: 0.03 });
|
|
265
|
-
hell({ tag: 'muzzle', c: [0, -0.032, 0.17], r: [0.031 + 0.003 * roman, 0.027, 0.03], k: 0.024 });
|
|
266
|
-
hell({ tag: 'upperlip', c: [0, -0.053, 0.181], r: [0.025, 0.014, 0.021], k: 0.016 });
|
|
267
|
-
for (const s of [1, -1]) {
|
|
268
|
-
// the face below the eye (maxilla, facial crest): broad under the eyes, tapering to the muzzle
|
|
269
|
-
// (face1, face3: the face narrowed straight from the eyes into a horse's nose)
|
|
270
|
-
hell({ tag: 'maxilla', c: [0.026 * s, -0.03, 0.045], r: [0.018, 0.033, 0.065], axisL: [0.18 * s, 0, 1], k: 0.03 });
|
|
271
|
-
// masseter: a flat swell over the ramus, from below the eye down to the jaw angle
|
|
272
|
-
// (its rear edge ended 7 cm behind the eye as a 16 mm step against the jaw angle and throat, blended
|
|
273
|
-
// over only 3 cm: a dark strap from under the eye down to the throat on every adult; face1 / face3 /
|
|
274
|
-
// side2 show a flat cheek running into the throat, the masseter only a gentle swell)
|
|
275
|
-
// (a kid has no plate: its small one ended 6 cm behind the eye in an earlier strap from the eye to the
|
|
276
|
-
// throat, and its outline stood as a knob on the throat behind the jaw; the adults' plate moved forward
|
|
277
|
-
// stretched a kid's throat 3x on 4.15 % of the mesh when grazing, the skin of the lower head riding on
|
|
278
|
-
// the neck. Without it a kid's > 3x is lower, 3.49-3.54 %)
|
|
279
|
-
// ... only a fill inside the jaw's hollow below the eye (between the braincase, the maxilla and the jaw,
|
|
280
|
-
// raised on a kid: without anything there the kid's head had a pit down to the midline behind the eye)
|
|
281
|
-
if (kd) hell({ tag: 'cheek', c: [0.012 * s, -0.055, -0.02], r: [0.02, 0.028, 0.03], k: 0.03 });
|
|
282
|
-
else {
|
|
283
|
-
hell({ tag: 'cheek', c: [0.035 * s, -0.072, 0.0], r: [0.012, 0.056, 0.045], k: 0.05 });
|
|
284
|
-
// behind the ramus (parotid): the side of the head slopes from the cheek into the neck over 9 cm (the
|
|
285
|
-
// back of the jaw fell 3-4 cm in as many, a crease from below the eye down to the throat in every
|
|
286
|
-
// 3/4 view)
|
|
287
|
-
hell({ tag: 'parotid', c: [0.022 * s, -0.072, -0.085], r: [0.024, 0.05, 0.05], k: 0.035 });
|
|
288
|
-
}
|
|
289
|
-
// the lower border of the jaw, from the jaw angle forward (the front of the mandible is the jaw part)
|
|
290
|
-
// (it runs forward over the sides of the chin: the jaw part shows only its lower lip at the front and
|
|
291
|
-
// the chin underneath, so the seam between the two surfaces lies along the mouth line and under the
|
|
292
|
-
// chin, not on the side of the jaw)
|
|
293
|
-
// (a kid's jaw angle 2.2 cm higher, its end 4 mm further in and blended over 3.7 cm: at the side of the
|
|
294
|
-
// throat it poked out as a small bare knob, a white fleck at the jaw angle; lower, the throat stretched)
|
|
295
|
-
m.cone({ ...H, tag: 'mandible', a: hk([0.029 * s - 0.004 * s * kd, -0.124 + 0.022 * kd, -0.045 + 0.01 * kd]), b: hk([0.013 * s, -0.083, 0.17]), ra: 0.014 - 0.002 * kd, rb: 0.0085, k: 0.025 + 0.012 * kd });
|
|
296
|
-
// the cheek between the upper and lower jaws (buccinator), flush from the eye to the mouth corner: the
|
|
297
|
-
// face showed a 5-7 mm groove from below the eye forward to the chin between the maxilla and the
|
|
298
|
-
// mandible, a long crease in every front and 3/4 view
|
|
299
|
-
// (a kid's shallow jaw: the fill stays above its jaw line)
|
|
300
|
-
hell({ tag: 'buccal', c: [0.022 * s, -0.073 + 0.005 * kd, 0.085], r: [0.012, 0.019 - 0.004 * kd, 0.07], axisL: [-0.14 * s, 0.14, 1], k: 0.025 });
|
|
301
|
-
hell({ tag: 'brow', c: [0.048 * s, 0.017, -0.005], r: [0.012, 0.008, 0.016], k: 0.015 });
|
|
302
|
-
hell({ tag: 'nostrilwing', c: [0.02 * s, -0.026, 0.177], r: [0.012, 0.014, 0.014], k: 0.013 });
|
|
303
|
-
sculptEyeSocket(m, EYE, HEAD_O, s, { orbit: { r: [0.016, 0.012, 0.01], at: [0.0, 0.001, 0.017], k: 0.007 } });
|
|
304
|
-
// nostril: a comma-shaped slit on the front of the nose, the lower ends toward the philtrum and the
|
|
305
|
-
// upper ends turned out (face3)
|
|
306
|
-
// (its middle, not its tip, breaks the surface: a slit 2 cm long, not a round hole)
|
|
307
|
-
hell({ tag: 'nostril', c: [0.018 * s, -0.026, 0.199], r: [0.0026, 0.0105, 0.0065], axisL: [-0.4 * s, 0.15, 1], upL: [0.5 * s, 1, 0], k: 0.0025, carve: true });
|
|
308
|
-
}
|
|
309
|
-
// the soft floor between the two mandibles (a midline notch under the jaw between the mandible cones)
|
|
310
|
-
hell({ tag: 'intermandible', c: [0, -0.094 + 0.008 * kd, 0.068 + 0.012 * kd], r: [0.018, 0.012 - 0.003 * kd, 0.062 - 0.012 * kd], axisL: [0, 0.18, 1], k: 0.02 });
|
|
311
|
-
// (no philtrum groove: at mesh resolution its notch at the lip margin showed the lower lip behind it as a
|
|
312
|
-
// white fang)
|
|
313
|
-
|
|
314
|
-
// ---------------------------------------------------------------- JAW (lower lip, chin, beard)
|
|
315
|
-
const JW = { bone: 'jaw', group: 'jaw', part: 'jaw' };
|
|
316
|
-
// a small lower lip tucked under the upper lip, and a rounded chin below it (walk1, face2)
|
|
317
|
-
// (the lower lip reaches 6 mm up inside the upper lip, so the mouth stays shut while the jaw grinds)
|
|
318
|
-
m.ell({ ...JW, tag: 'chin', c: hk([0, -0.086, 0.168]), r: [0.017, 0.013, 0.02], axis: HZ, up: HY, k: 0 });
|
|
319
|
-
m.ell({ ...JW, tag: 'lowerlip', c: hk([0, -0.071, 0.182]), r: [0.02, 0.0105, 0.015], axis: HZ, up: HY, k: 0.012 });
|
|
320
|
-
// (the front of the mandible, inside the head's jaw: it carries the chin when the mouth opens; its
|
|
321
|
-
// ends reached out between the lips as two white incisors)
|
|
322
|
-
for (const s of [1, -1]) m.cone({ ...JW, tag: 'mandible', a: hk([0.009 * s, -0.085, 0.115]), b: hk([0.008 * s, -0.084, 0.152]), ra: 0.006, rb: 0.0065, k: 0.015 });
|
|
323
|
-
const beard = params.beard || 0; // length (m)
|
|
324
|
-
if (beard > 0.01) {
|
|
325
|
-
// hangs from under the chin in three loose locks (face2, face4: a beard of separate locks, the middle one longest),
|
|
326
|
-
// each a thin round core with long hair: one 3 cm wide core read as a flat, smooth ribbon inside the hair
|
|
327
|
-
const a = hk([0, -0.094, 0.12]);
|
|
328
|
-
const down = norm([0, -1, -0.05]);
|
|
329
|
-
const nB = 4;
|
|
330
|
-
// (each lock a chain of round cones: ellipsoid segments ended in points thinner than a medium-tier cell, whose
|
|
331
|
-
// tips folded over)
|
|
332
|
-
for (const j of [-1, 0, 1]) {
|
|
333
|
-
const L = beard * (j === 0 ? 1 : 0.78);
|
|
334
|
-
const at = (t) => add(add(a, mul(down, L * t)), [j * (0.006 + 0.008 * t), 0, -0.024 * t - 0.004 * Math.abs(j)]);
|
|
335
|
-
const rad = (t) => (0.0045 + 0.0015 * Math.min(1, beard / 0.15)) * (1 - 0.5 * t);
|
|
336
|
-
for (let i = 0; i < nB; i++) {
|
|
337
|
-
const t0 = i / nB, t1 = (i + 1) / nB;
|
|
338
|
-
m.cone({ ...JW, bone: 'beard', part: 'beard', tag: 'beard', thin: true, a: at(t0), b: at(t1), ra: rad(t0), rb: rad(t1), k: 0.008 });
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
// the root of the beard under the chin (a 9 cm lock along the underside of the jaw hung below the
|
|
342
|
-
// mandibles as a pale strap)
|
|
343
|
-
m.ell({ ...JW, tag: 'beard', c: hk([0, -0.093, 0.13]), r: [0.019, 0.013, 0.026 * (kid ? KID_FACE : 1)], axis: HZ, up: HY, k: 0.016 });
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// ---------------------------------------------------------------- EARS
|
|
347
|
-
const lop = params.ear === 'lop';
|
|
348
|
-
for (const S of ['L', 'R']) {
|
|
349
|
-
const s = S === 'L' ? 1 : -1;
|
|
350
|
-
const base = J['earBase' + S], tip = J['earTip' + S];
|
|
351
|
-
const up = norm(sub(tip, base));
|
|
352
|
-
const len = Math.hypot(...sub(tip, base));
|
|
353
|
-
// the cup opens forward and down (erect), inward toward the face (lop)
|
|
354
|
-
// (erect: the broad inner side faces forward, a little out and down, as in face1 / face3 / front1;
|
|
355
|
-
// it faced straight down, so the ears showed their edge from the front)
|
|
356
|
-
const facing = earFacing(params, s);
|
|
357
|
-
const lat = norm(cross(up, facing));
|
|
358
|
-
const fc = norm(cross(lat, up));
|
|
359
|
-
const ear = { bone: 'ear' + S, group: 'ear' + S, thin: true };
|
|
360
|
-
// a leaf: a rolled, narrow base, the blade widest at ~40 % of the length, a pointed tip (Swiss:
|
|
361
|
-
// ~6 cm wide on 14-16 cm, face1 / face3 / front2); the lop ear a broad drape with a rounded tip
|
|
362
|
-
// (it was an 8.4 cm wide disc: mouse ears)
|
|
363
|
-
// (a kid's lop ear: its thickness, root roll and rim scaled with the ear's length, x0.82, and a little
|
|
364
|
-
// narrower: at the adult's 7.5 mm blades and 5 cm width the blades and the rolled root read through the
|
|
365
|
-
// outer face of the short ear as lumps and a raised pad, round the kid's face like a bonnet)
|
|
366
|
-
const ek = lop && kid ? 0.82 : 1;
|
|
367
|
-
const wd = lop ? 0.05 * (kid ? 0.88 : 1) : 0.029;
|
|
368
|
-
if (lop) {
|
|
369
|
-
// Nubian / Boer: a long, broad, pendulous ear (the Nubian standard: close to the head at the temple,
|
|
370
|
-
// flaring out and forward to the rounded tip, a bell round the face; side_boer). It was one flat oval
|
|
371
|
-
// blade pasted on the cheek: short, no roll at the root, no rim.
|
|
372
|
-
const out = [s, 0, 0];
|
|
373
|
-
const fwd = lat[2] >= 0 ? lat : mul(lat, -1); // the blade's front edge direction
|
|
374
|
-
const fwdF = norm(add(fwd, mul(out, 0.5))); // ... turned out in the lower, flaring half
|
|
375
|
-
// the root: rolled and narrow, leaving the skull sideways before it drops
|
|
376
|
-
// (a 36 mm thick end on the 15 mm blade stood out as a lump with a shadowed step below it)
|
|
377
|
-
m.cone({ ...ear, tag: 'ear', a: add(base, mul(up, 0.004)), b: add(lerp(base, tip, 0.28), mul(out, 0.003 * ek)), ra: 0.0115 * ek, rb: 0.0062 * ek, k: 0.018 * ek });
|
|
378
|
-
// the upper blade, along the side of the head behind the eye
|
|
379
|
-
// (the blades follow the ear bone, which already hangs out 24 deg: set further out on their own they
|
|
380
|
-
// stepped out one after the other, an oval lump on the outside of the ear)
|
|
381
|
-
ellY({ ...ear, tag: 'ear', c: add(lerp(base, tip, 0.44), mul(fwd, 0.002)), ydir: up, lateral: fwd, r: [wd * 0.84, len * 0.32, 0.0075 * ek], k: 0.016 });
|
|
382
|
-
// the lower blade and the rounded tip, turned a little out
|
|
383
|
-
ellY({ ...ear, tag: 'ear', c: add(lerp(base, tip, 0.69), mul(out, 0.0015 * ek)), ydir: up, lateral: fwdF, r: [wd, len * 0.28, 0.0072 * ek], k: 0.018 });
|
|
384
|
-
ellY({ ...ear, tag: 'eartip', c: add(lerp(base, tip, 0.86), mul(out, 0.002 * ek)), ydir: up, lateral: fwdF, r: [wd * 0.8, len * 0.15, 0.0065 * ek], k: 0.016 });
|
|
385
|
-
// the front edge rolled out into a thick rim
|
|
386
|
-
m.cone({ ...ear, tag: 'ear', a: add(lerp(base, tip, 0.14), mul(fwd, wd * 0.4)), b: add(lerp(base, tip, 0.64), add(mul(fwdF, wd * 0.86), mul(out, 0.003 * ek))), ra: 0.003 * ek, rb: 0.0055 * ek, k: 0.01 * ek });
|
|
387
|
-
// the cup: the inner face hollowed down the middle of the blade (short of the root: medium-tier hole)
|
|
388
|
-
// (a kid's cup as deep as an adult's: the coarse tiers inflate thin blades to 1 cm, not carvers, and a cup
|
|
389
|
-
// scaled with the kid's blade only grazed the inflated face there, a ragged speckle over the inner face)
|
|
390
|
-
ellY({ ...ear, tag: 'earinner', c: add(lerp(base, tip, 0.55), mul(fc, 0.0068)), ydir: up, lateral: fwd, r: [wd * 0.6, len * 0.28, 0.0052], k: 0.004, carve: true });
|
|
391
|
-
} else {
|
|
392
|
-
// (the rolled base no thicker than the blade where it ends: a 32 mm round end on the 17 mm blade folded
|
|
393
|
-
// the skin over at the medium tier, back faces showing through the ear)
|
|
394
|
-
m.cone({ ...ear, tag: 'ear', a: add(base, mul(up, 0.004)), b: lerp(base, tip, 0.27), ra: 0.011, rb: 0.0105, k: 0.014 });
|
|
395
|
-
ellY({ ...ear, tag: 'ear', c: lerp(base, tip, 0.44), ydir: up, lateral: lat, r: [wd, len * 0.42, 0.0085], k: 0.014 });
|
|
396
|
-
ellY({ ...ear, tag: 'eartip', c: lerp(base, tip, 0.78), ydir: up, lateral: lat, r: [wd * 0.5, len * 0.24, 0.0065], k: 0.012 });
|
|
397
|
-
// (the cup stops short of the narrow root: reaching it, it broke through the ear's floor at the medium
|
|
398
|
-
// tier, a ragged hole beside the root; it leaves a 12.5 mm floor at hero and 14 mm at medium: 9.5-12 mm,
|
|
399
|
-
// about two medium cells, still showed pinholes through the ear)
|
|
400
|
-
// (its root end soft, k 8 mm, and 1 cm further from the root: with a 4 mm blend the end of the carve was a
|
|
401
|
-
// corner the medium tier's 5.6 mm cells folded into a white notch)
|
|
402
|
-
ellY({ ...ear, tag: 'earinner', c: add(lerp(base, tip, 0.6), mul(fc, 0.0085)), ydir: up, lateral: lat, r: [wd * 0.66, len * 0.28, 0.0045], k: 0.008, carve: true });
|
|
403
|
-
}
|
|
404
|
-
m.sphere({ ...H, tag: 'earbase', c: add(base, mul(up, -0.006)), rad: 0.016, k: 0.02 });
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// ---------------------------------------------------------------- HORNS (own rigid surface)
|
|
408
|
-
for (const s of [1, -1]) {
|
|
409
|
-
const pth = hornPath(params, s);
|
|
410
|
-
const J = hornSplit(params, pth);
|
|
411
|
-
for (let i = 0; i + 1 < pth.length; i++) {
|
|
412
|
-
if (J < 0 || i < J) m.cone({ bone: 'head', part: 'horn', group: 'axial', tag: 'horn', a: pth[i].p, b: pth[i + 1].p, ra: pth[i].r, rb: pth[i + 1].r, k: 0.004 });
|
|
413
|
-
if (J >= 0 && i >= J - 2) m.cone({ bone: 'head', part: 'horntip', group: 'axial', tag: 'horn', a: pth[i].p, b: pth[i + 1].p, ra: pth[i].r * (i <= J ? 0.96 : 1), rb: pth[i + 1].r * (i + 1 <= J ? 0.96 : 1), k: 0.004 });
|
|
414
|
-
}
|
|
415
|
-
// a horn is flattened sideways at its base (a keel along the front)
|
|
416
|
-
if (pth.length) {
|
|
417
|
-
// (over the first ~2.5 cm of the horn)
|
|
418
|
-
let j = 1;
|
|
419
|
-
while (j + 1 < pth.length && Math.hypot(...sub(pth[j].p, pth[0].p)) < 0.025) j++;
|
|
420
|
-
m.ell({ bone: 'head', part: 'horn', group: 'axial', tag: 'horn', c: lerp(pth[0].p, pth[j].p, 0.5), r: [pth[0].r * 0.75, pth[0].r * 1.15, Math.hypot(...sub(pth[j].p, pth[0].p)) * 0.8], axis: norm(sub(pth[j].p, pth[0].p)), up: HZ, k: 0.004 });
|
|
421
|
-
}
|
|
422
|
-
// growth ridges: a raised collar every S along the base half, rising over a third of the spacing to its crest and
|
|
423
|
-
// falling back to the horn over the rest toward the tip (two round cones about the crest), lower toward tEnd
|
|
424
|
-
const RG = hornRidges(params);
|
|
425
|
-
if (pth.length && RG && RG.geo > 0.01) {
|
|
426
|
-
const L = params.horns.len, n = pth.length - 1;
|
|
427
|
-
const at = (sArc) => {
|
|
428
|
-
const u = clamp(sArc / L, 0, 1) * n, i = Math.min(n - 1, Math.floor(u)), f = u - i;
|
|
429
|
-
return { p: lerp(pth[i].p, pth[i + 1].p, f), r: pth[i].r + (pth[i + 1].r - pth[i].r) * f, t: u / n };
|
|
430
|
-
};
|
|
431
|
-
for (let sA = RG.s0; sA < RG.tEnd * L; sA += RG.S) {
|
|
432
|
-
const c = at(sA), a = at(sA - 0.35 * RG.S), b = at(sA + 0.6 * RG.S);
|
|
433
|
-
const h = RG.amp * RG.geo * (1 - smoothstep(0.55 * RG.tEnd, RG.tEnd, c.t));
|
|
434
|
-
if (h < 0.01) continue;
|
|
435
|
-
const R = { bone: 'head', part: 'horn', group: 'axial', tag: 'horn', k: 0.0015 };
|
|
436
|
-
m.cone({ ...R, a: a.p, b: c.p, ra: a.r * (1 + 0.15 * h), rb: c.r * (1 + h) });
|
|
437
|
-
m.cone({ ...R, a: c.p, b: b.p, ra: c.r * (1 + h), rb: b.r * (1 + 0.1 * h) });
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
// horn base boss on the skull (also the polled goat's small knobs)
|
|
441
|
-
if (params.horns && params.horns.len > 0) m.sphere({ ...H, tag: 'hornboss', c: hl([0.021 * s, 0.03, -0.05]), rad: Math.min(0.016, params.horns.base * 0.9), k: 0.015 });
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// ---------------------------------------------------------------- MANE (bucks: long hair along the crest and back)
|
|
445
|
-
if (buck && (params.mane || 0) > 0.05) {
|
|
446
|
-
const mn = params.mane;
|
|
447
|
-
// the crest line: the skin of the neck behind its axis (marched out from the axis), then the back
|
|
448
|
-
const body = m.prims.filter((q) => (q.part || 'body') === 'body' && !q.carve);
|
|
449
|
-
const skin = (p, d) => { let t = 0; while (t < 0.25 && SDFModel.evalList(body, p[0] + d[0] * t, p[1] + d[1] * t, p[2] + d[2] * t) < 0) t += 0.002; return add(p, mul(d, t - 0.012)); };
|
|
450
|
-
const line = [
|
|
451
|
-
skin(lerp(nm, occ, 0.75), backN(nd2)), skin(lerp(nm, occ, 0.35), backN(nd2)), skin(lerp(nb, nm, 0.85), backN(nd1)),
|
|
452
|
-
skin(lerp(nb, nm, 0.4), backN(nd1)), [0, 0.765, 0.2], [0, 0.745, 0.08],
|
|
453
|
-
];
|
|
454
|
-
const bones = ['neck2', 'neck2', 'neck1', 'neck1', 'spine3', 'spine3'];
|
|
455
|
-
for (let i = 0; i < line.length; i++) {
|
|
456
|
-
m.ell({ tag: 'mane', bone: bones[i], c: line[i], r: [0.028 * mn + 0.01, 0.022 * mn + 0.006, 0.055], axis: i + 1 < line.length ? norm(sub(line[i], line[i + 1])) : [0, 0, 1], k: 0.03 });
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// ---------------------------------------------------------------- LEGS (slender, long cannons)
|
|
461
|
-
const lk = 1 + 0.12 * heavy;
|
|
462
|
-
for (const S of ['L', 'R']) {
|
|
463
|
-
const s = S === 'L' ? 1 : -1;
|
|
464
|
-
const lat = [s, 0, 0];
|
|
465
|
-
const sx = (v) => [v[0] * s, v[1], v[2]];
|
|
466
|
-
const g = 'F' + S;
|
|
467
|
-
const Sc = J['scapTop' + S], Sh = J['shoulder' + S], E = J['elbow' + S], W = J['wrist' + S], M = J['mcp' + S], C = J['fcoffin' + S], T = J['ftoe' + S];
|
|
468
|
-
ellY({ tag: 'scapmuscle', bone: 'scapula' + S, group: g, c: add(lerp(Sc, Sh, 0.45), sx([0.03, 0, -0.01])), ydir: sub(Sh, Sc), lateral: lat, r: [0.022 * lk, 0.1, 0.062], k: 0.06 });
|
|
469
|
-
// (the point of the shoulder only a gentle angle: no ball on the humerus head, and the upper arm's cone
|
|
470
|
-
// starts a quarter of the way down: seen end-on from the front, a cone from the joint drew a round disc)
|
|
471
|
-
m.cone({ tag: 'upperarm', bone: 'humerus' + S, group: g, a: lerp(Sh, E, 0.25), b: E, ra: 0.036 * lk, rb: 0.032 * lk, k: 0.04 });
|
|
472
|
-
ellY({ tag: 'triceps', bone: 'humerus' + S, group: g, c: add(lerp(Sh, E, 0.55), sx([0.005, 0.01, -0.045])), ydir: sub(E, Sh), lateral: lat, r: [0.03 * lk, 0.07, 0.048], k: 0.05 });
|
|
473
|
-
m.sphere({ tag: 'olecranon', bone: 'radius' + S, group: g, c: add(E, [0, 0.012, -0.034]), rad: 0.024, k: 0.025 });
|
|
474
|
-
m.cone({ tag: 'forearm', bone: 'radius' + S, group: g, a: add(E, [0, 0, -0.005]), b: W, ra: 0.034 * lk, rb: 0.019, k: 0.025 });
|
|
475
|
-
ellY({ tag: 'forearmmuscle', bone: 'radius' + S, group: g, c: add(lerp(E, W, 0.25), sx([0.004, 0, 0.006])), ydir: sub(W, E), lateral: lat, r: [0.032 * lk, 0.075, 0.035 * lk], k: 0.025 });
|
|
476
|
-
m.cone({ tag: 'forearmweb', bone: 'radius' + S, group: g, a: add(E, sx([-0.02, 0.03, -0.01])), b: add(lerp(E, W, 0.3), sx([-0.01, 0, 0])), ra: 0.028, rb: 0.02, k: 0.03 });
|
|
477
|
-
// knee (carpus, with its callus in front)
|
|
478
|
-
ellY({ tag: 'knee', bone: 'metacarpus' + S, group: g, c: add(W, [0, 0.0, 0.002]), ydir: [0, 1, 0], lateral: lat, r: [0.021, 0.026, 0.021], k: 0.012 });
|
|
479
|
-
m.cone({ tag: 'cannon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.01, 0.0]), b: add(M, [0, 0.01, 0.0]), ra: 0.0145, rb: 0.0135, k: 0.01 });
|
|
480
|
-
m.cone({ tag: 'tendon', bone: 'metacarpus' + S, group: g, a: add(W, [0, -0.015, -0.012]), b: add(M, [0, 0.015, -0.013]), ra: 0.009, rb: 0.011, k: 0.01 });
|
|
481
|
-
ellY({ tag: 'fetlock', bone: 'metacarpus' + S, group: g, c: add(M, [0, 0.0, -0.004]), ydir: [0, 1, 0.3], lateral: lat, r: [0.019, 0.02, 0.021], k: 0.01 });
|
|
482
|
-
sculptDewclaws(m, 'fpaw' + S, g, M, s);
|
|
483
|
-
m.cone({ tag: 'pastern', bone: 'fpaw' + S, group: g, a: M, b: C, ra: 0.0155, rb: 0.0165, k: 0.01 });
|
|
484
|
-
sculptHoof(m, 'fhoof' + S, g, C, T, 1.0, s);
|
|
485
|
-
|
|
486
|
-
const h = 'H' + S;
|
|
487
|
-
const Hp = J['hip' + S], K = J['knee' + S], Hk = J['hock' + S], Mt = J['mtp' + S], Ch = J['hcoffin' + S], Tt = J['htoe' + S];
|
|
488
|
-
ellY({ tag: 'thigh', bone: 'femur' + S, group: h, c: add(lerp(Hp, K, 0.42), sx([0.02, 0, -0.025])), ydir: sub(K, Hp), lateral: lat, r: [0.032 * lk, 0.13, 0.095], k: 0.06 });
|
|
489
|
-
m.cone({ tag: 'thighfront', bone: 'femur' + S, group: h, a: sx([0.075, 0.66, -0.3 + HQ]), b: add(K, sx([0.0, 0.03, 0.02])), ra: 0.048, rb: 0.03, k: 0.05 });
|
|
490
|
-
m.cone({ tag: 'hamstring', bone: 'femur' + S, group: h, a: sx([0.045, 0.64, -0.345]), b: add(lerp(K, Hk, 0.3), [0, 0, -0.04]), ra: 0.045, rb: 0.026, k: 0.04 });
|
|
491
|
-
ellY({ tag: 'flankfold', bone: 'femur' + S, group: h, c: sx([0.09, 0.47, -0.2 + HQ]), ydir: [-0.1, 0.3, 0.12], lateral: lat, r: [0.024, 0.07, 0.038], k: 0.05 });
|
|
492
|
-
m.sphere({ tag: 'stifle', bone: 'tibia' + S, group: h, c: add(K, sx([0.004, 0.005, 0.014])), rad: 0.027, k: 0.035 });
|
|
493
|
-
// (a broad gaskin, side2: the lower thigh read thin next to the barrel)
|
|
494
|
-
ellY({ tag: 'gaskin', bone: 'tibia' + S, group: h, c: add(lerp(K, Hk, 0.3), sx([0.004, 0, -0.024])), ydir: sub(Hk, K), lateral: lat, r: [0.037 * lk, 0.085, 0.048], k: 0.03 });
|
|
495
|
-
m.cone({ tag: 'shin', bone: 'tibia' + S, group: h, a: lerp(K, Hk, 0.1), b: Hk, ra: 0.026, rb: 0.018, k: 0.025 });
|
|
496
|
-
m.cone({ tag: 'achilles', bone: 'tibia' + S, group: h, a: add(lerp(K, Hk, 0.45), [0, 0, -0.035]), b: add(Hk, [0, 0.03, -0.03]), ra: 0.012, rb: 0.01, k: 0.015 });
|
|
497
|
-
m.sphere({ tag: 'calcaneus', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.028, -0.028]), rad: 0.014, k: 0.012 });
|
|
498
|
-
ellY({ tag: 'hock', bone: 'metatarsus' + S, group: h, c: add(Hk, [0, 0.004, -0.002]), ydir: [0, 1, 0.25], lateral: lat, r: [0.02, 0.03, 0.022], k: 0.015 });
|
|
499
|
-
m.cone({ tag: 'cannon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.016, 0.002]), b: add(Mt, [0, 0.01, 0.0]), ra: 0.015, rb: 0.0135, k: 0.01 });
|
|
500
|
-
m.cone({ tag: 'tendon', bone: 'metatarsus' + S, group: h, a: add(Hk, [0, -0.016, -0.013]), b: add(Mt, [0, 0.015, -0.013]), ra: 0.009, rb: 0.011, k: 0.01 });
|
|
501
|
-
ellY({ tag: 'fetlock', bone: 'metatarsus' + S, group: h, c: add(Mt, [0, 0.0, -0.004]), ydir: [0, 1, 0.3], lateral: lat, r: [0.019, 0.02, 0.021], k: 0.01 });
|
|
502
|
-
sculptDewclaws(m, 'hpaw' + S, h, Mt, s);
|
|
503
|
-
m.cone({ tag: 'pastern', bone: 'hpaw' + S, group: h, a: Mt, b: Ch, ra: 0.015, rb: 0.016, k: 0.01 });
|
|
504
|
-
sculptHoof(m, 'hhoof' + S, h, Ch, Tt, 0.95, s);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// ---------------------------------------------------------------- TAIL: short, flat, triangular, carried up
|
|
508
|
-
// a thin tail with a flat brush of hair that widens to the tip (side2, walk1)
|
|
509
|
-
// (the hair pieces overlap along the tail and blend over 2 cm: five short separate ones left knobs between
|
|
510
|
-
// them, a caterpillar)
|
|
511
|
-
for (let i = 0; i < TAIL_SEGS; i++) {
|
|
512
|
-
const t0 = i / TAIL_SEGS, t1 = (i + 1) / TAIL_SEGS;
|
|
513
|
-
const a = J['tail' + i], b = J['tail' + (i + 1)];
|
|
514
|
-
const w0 = 0.024 - 0.016 * t0, w1 = 0.024 - 0.016 * t1;
|
|
515
|
-
m.cone({ tag: i === 0 ? 'tailhead' : 'tail', bone: 'tail' + i, a, b, ra: w0 * 0.72, rb: w1 * 0.72, k: i === 0 ? 0.03 : 0.012 });
|
|
516
|
-
const d = norm(sub(b, a)), tm = (t0 + t1) * 0.5, L = Math.hypot(...sub(b, a));
|
|
517
|
-
m.ell({ tag: 'tailhair', bone: 'tail' + i, c: lerp(a, b, 0.55), r: [0.015 + 0.01 * tm, 0.0095 + 0.002 * tm, L * 0.95], axis: d, up: norm(cross(d, X)), k: 0.02 });
|
|
518
|
-
}
|
|
519
|
-
void kid;
|
|
520
|
-
return m;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Dewclaws: two small horny knobs behind the fetlock
|
|
524
|
-
function sculptDewclaws(m, bone, group, M, s) {
|
|
525
|
-
for (const k of [1, -1]) m.sphere({ tag: 'dewclaw', bone, group, c: add(M, [0.009 * k * s, -0.006, -0.021]), rad: 0.0055, k: 0.004 });
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Cloven hoof: two claws (digits III and IV) with a cleft between them, the front wall parallel to
|
|
529
|
-
// the pastern, heel bulbs behind, flat on the ground.
|
|
530
|
-
function sculptHoof(m, bone, group, C, T, w, s) {
|
|
531
|
-
const zc = (C[2] + T[2]) * 0.5;
|
|
532
|
-
for (const k of [1, -1]) {
|
|
533
|
-
const x = C[0] + 0.0105 * k * w;
|
|
534
|
-
const top = [x, C[1] + 0.008, C[2] - 0.004];
|
|
535
|
-
const toe = [x - 0.002 * k, 0.006, T[2] - 0.004];
|
|
536
|
-
const heel = [x, 0.008, C[2] - 0.016];
|
|
537
|
-
m.cone({ tag: 'hoof', bone, group, a: top, b: toe, ra: 0.0115 * w, rb: 0.0055 * w, k: 0.006 });
|
|
538
|
-
m.cone({ tag: 'hoof', bone, group, a: heel, b: [toe[0], 0.006, zc + 0.004], ra: 0.011 * w, rb: 0.009 * w, k: 0.008 });
|
|
539
|
-
m.sphere({ tag: 'heelbulb', bone, group, c: [x, 0.013, C[2] - 0.02], rad: 0.0105 * w, k: 0.008 });
|
|
540
|
-
}
|
|
541
|
-
m.cone({ tag: 'coronet', bone, group, a: add(C, [0, 0.012, -0.01]), b: add(C, [0, 0.006, 0.008]), ra: 0.017 * w, rb: 0.017 * w, k: 0.008 });
|
|
542
|
-
// the cleft between the claws, open at the toe
|
|
543
|
-
m.ell({ tag: 'cleft', bone, group, c: [C[0], 0.006, zc + 0.012], r: [0.0022, 0.02, 0.03], k: 0.002, carve: true });
|
|
544
|
-
// flat sole
|
|
545
|
-
m.ell({ tag: 'sole', bone, group, c: [C[0], -0.1 + 0.001, zc], r: [0.1, 0.1, 0.1], k: 0.002, carve: true });
|
|
546
|
-
void s;
|
|
547
|
-
}
|