@ikijs/editor 0.12.2 → 0.14.0
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/README.md +7 -8
- package/dist/index.d.mts +16 -20
- package/dist/index.d.ts +16 -20
- package/dist/index.js +152 -116
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,23 +57,22 @@ leaves the document untouched.
|
|
|
57
57
|
## Auto-rig
|
|
58
58
|
|
|
59
59
|
`generateIkiFromLayerSet` turns role-named layers (`face`, `eye_L`, `eye_R`,
|
|
60
|
-
`mouth`, plus optional `iris_*`, `brow_*`, `lash_*`, `hair_front`, `hair_back`,
|
|
60
|
+
`mouth`, plus optional `iris_*`, `brow_*`, `lash_*`, `lash_lower_*`, `hair_front`, `hair_back`,
|
|
61
61
|
…) into a rigged model that blinks, gazes, opens its mouth, turns its head over
|
|
62
62
|
a torso that breathes, and emotes with its brows — including a
|
|
63
63
|
hair-sway physics rig when a `hair_front` layer is present.
|
|
64
64
|
|
|
65
|
-
The rig
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(the nose most, then the brows, eyes and mouth), the eyes foreshortening mildly
|
|
65
|
+
The rig turns the head by parallax, not a reshaped face, with values of our
|
|
66
|
+
own picked by eye (`auto-rig/profile.ts`, `AUTO-RIG.md`). The face plate
|
|
67
|
+
translates, the chin leading it; the features lead the plate by their depth
|
|
68
|
+
(the nose most, then the eyes, brows and mouth), the eyes foreshortening mildly
|
|
70
69
|
about their own centres; the front hair rides the face where it lies over it and holds the head's
|
|
71
70
|
outline where it draws it — though on its cap the turn never carries its outer
|
|
72
71
|
edge further outside the back hair painted behind it than the nod alone does —
|
|
73
72
|
and the back hair stays behind; the neck and the
|
|
74
73
|
ears the face layer paints are cut off into islands of the plate's own mesh,
|
|
75
74
|
drawn behind the head — the neck stays (only the chin's shade slides across
|
|
76
|
-
it), the ears lag the face, the far one narrowing. AngleZ rolls the head
|
|
75
|
+
it), the ears lag the face, the far one narrowing. AngleZ rolls the head 14° about the chin; the torso only
|
|
77
76
|
breathes.
|
|
78
77
|
|
|
79
78
|
On a layer set with a `nose`, `options.turnTargets` — the cues a 30° reference
|
|
@@ -82,7 +81,7 @@ whether the silhouette holds) — are **fitted** each by the one knob it reads:
|
|
|
82
81
|
the turn's amount, the eyes' foreshortening, the hair's width, a feature's own
|
|
83
82
|
shift. With none given the profile itself renders (`DEFAULT_TURN_TARGETS`
|
|
84
83
|
expresses it in the cues' units). `options.style` tunes a character from the
|
|
85
|
-
profile
|
|
84
|
+
profile: the whole turn, the
|
|
86
85
|
features' lead, the front hair's follow and its outline's, the blink and the
|
|
87
86
|
hair sway. The turn
|
|
88
87
|
is bounded by the art: the far eye stays inside the plate's outline and, given
|
package/dist/index.d.mts
CHANGED
|
@@ -657,21 +657,18 @@ interface AlphaBbox {
|
|
|
657
657
|
declare function detectAlphaBbox(rgba: ArrayLike<number>, width: number, height: number): AlphaBbox | null;
|
|
658
658
|
|
|
659
659
|
/**
|
|
660
|
-
* Per-character tuning, starting from the profile
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* replace its value.
|
|
660
|
+
* Per-character tuning, starting from the profile. `turn`, `featureLead` and
|
|
661
|
+
* `sway` multiply the profile (1 = the profile); `hairFollow`,
|
|
662
|
+
* `outlineFollow` and `blink` replace its value. `AUTO-RIG.md` lists the
|
|
663
|
+
* ranges that read well.
|
|
665
664
|
*/
|
|
666
665
|
interface RigStyle {
|
|
667
|
-
/** The whole turn — plate, features and hair
|
|
668
|
-
*
|
|
669
|
-
* fits it instead. */
|
|
666
|
+
/** The whole turn — plate, features and hair. Ignored when a
|
|
667
|
+
* `turnTargets.eyeShift` is given, which fits it instead. */
|
|
670
668
|
turn?: number;
|
|
671
|
-
/** How far the features lead the plate
|
|
669
|
+
/** How far the features lead the plate. */
|
|
672
670
|
featureLead?: number;
|
|
673
|
-
/** The front hair's share of the face's turn
|
|
674
|
-
* profile 1.1). */
|
|
671
|
+
/** The front hair's share of the face's turn. */
|
|
675
672
|
hairFollow?: number;
|
|
676
673
|
/** Its outer edge's share, where the front hair draws the head's outline
|
|
677
674
|
* and no back hair paints behind that edge (profile 0: the outline holds;
|
|
@@ -683,9 +680,9 @@ interface RigStyle {
|
|
|
683
680
|
* otherwise carry it further outside its back hair than the nod alone
|
|
684
681
|
* does. */
|
|
685
682
|
outlineFollow?: number;
|
|
686
|
-
/** How far the upper lid comes down, over the eye's height
|
|
683
|
+
/** How far the upper lid comes down, over the eye's height. */
|
|
687
684
|
blink?: number;
|
|
688
|
-
/** Hair sway amplitude
|
|
685
|
+
/** Hair sway amplitude. */
|
|
689
686
|
sway?: number;
|
|
690
687
|
}
|
|
691
688
|
|
|
@@ -765,12 +762,11 @@ declare class TurnTargetError extends Error {
|
|
|
765
762
|
constructor(message: string);
|
|
766
763
|
}
|
|
767
764
|
/**
|
|
768
|
-
* The
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
772
|
-
*
|
|
773
|
-
* widens to 1.085, and the cue is their ratio, 0.78. silhouetteRatio: the
|
|
765
|
+
* The profile's own turn (`profile.ts`), in the cues' units — what a rig with
|
|
766
|
+
* no `turnTargets` renders on a head whose silhouette at the eye row is back
|
|
767
|
+
* hair about 1 hh (eye row → chin) wide. eyeShift: the eye pair's mean shift
|
|
768
|
+
* at ±30 less the back hair's drift, over a 1 hh head half-width.
|
|
769
|
+
* farEyeRatio: the far eye's width over the near one's. silhouetteRatio: the
|
|
774
770
|
* silhouette holds its width.
|
|
775
771
|
*
|
|
776
772
|
* With no target given the rig renders the profile itself, whatever the cues
|
|
@@ -858,7 +854,7 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
858
854
|
/**
|
|
859
855
|
* `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
|
|
860
856
|
*
|
|
861
|
-
* The rig's shape
|
|
857
|
+
* The rig's shape is a 2D head rig's usual one (`profile.ts`): a torso that
|
|
862
858
|
* only breathes; a head deformer that rolls about the chin and breathes; one
|
|
863
859
|
* small warp grid per feature (each eye, each brow, the nose, the mouth),
|
|
864
860
|
* translating and foreshortening it by its own lead over the plate; and the
|
package/dist/index.d.ts
CHANGED
|
@@ -657,21 +657,18 @@ interface AlphaBbox {
|
|
|
657
657
|
declare function detectAlphaBbox(rgba: ArrayLike<number>, width: number, height: number): AlphaBbox | null;
|
|
658
658
|
|
|
659
659
|
/**
|
|
660
|
-
* Per-character tuning, starting from the profile
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
664
|
-
* replace its value.
|
|
660
|
+
* Per-character tuning, starting from the profile. `turn`, `featureLead` and
|
|
661
|
+
* `sway` multiply the profile (1 = the profile); `hairFollow`,
|
|
662
|
+
* `outlineFollow` and `blink` replace its value. `AUTO-RIG.md` lists the
|
|
663
|
+
* ranges that read well.
|
|
665
664
|
*/
|
|
666
665
|
interface RigStyle {
|
|
667
|
-
/** The whole turn — plate, features and hair
|
|
668
|
-
*
|
|
669
|
-
* fits it instead. */
|
|
666
|
+
/** The whole turn — plate, features and hair. Ignored when a
|
|
667
|
+
* `turnTargets.eyeShift` is given, which fits it instead. */
|
|
670
668
|
turn?: number;
|
|
671
|
-
/** How far the features lead the plate
|
|
669
|
+
/** How far the features lead the plate. */
|
|
672
670
|
featureLead?: number;
|
|
673
|
-
/** The front hair's share of the face's turn
|
|
674
|
-
* profile 1.1). */
|
|
671
|
+
/** The front hair's share of the face's turn. */
|
|
675
672
|
hairFollow?: number;
|
|
676
673
|
/** Its outer edge's share, where the front hair draws the head's outline
|
|
677
674
|
* and no back hair paints behind that edge (profile 0: the outline holds;
|
|
@@ -683,9 +680,9 @@ interface RigStyle {
|
|
|
683
680
|
* otherwise carry it further outside its back hair than the nod alone
|
|
684
681
|
* does. */
|
|
685
682
|
outlineFollow?: number;
|
|
686
|
-
/** How far the upper lid comes down, over the eye's height
|
|
683
|
+
/** How far the upper lid comes down, over the eye's height. */
|
|
687
684
|
blink?: number;
|
|
688
|
-
/** Hair sway amplitude
|
|
685
|
+
/** Hair sway amplitude. */
|
|
689
686
|
sway?: number;
|
|
690
687
|
}
|
|
691
688
|
|
|
@@ -765,12 +762,11 @@ declare class TurnTargetError extends Error {
|
|
|
765
762
|
constructor(message: string);
|
|
766
763
|
}
|
|
767
764
|
/**
|
|
768
|
-
* The
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
*
|
|
772
|
-
*
|
|
773
|
-
* widens to 1.085, and the cue is their ratio, 0.78. silhouetteRatio: the
|
|
765
|
+
* The profile's own turn (`profile.ts`), in the cues' units — what a rig with
|
|
766
|
+
* no `turnTargets` renders on a head whose silhouette at the eye row is back
|
|
767
|
+
* hair about 1 hh (eye row → chin) wide. eyeShift: the eye pair's mean shift
|
|
768
|
+
* at ±30 less the back hair's drift, over a 1 hh head half-width.
|
|
769
|
+
* farEyeRatio: the far eye's width over the near one's. silhouetteRatio: the
|
|
774
770
|
* silhouette holds its width.
|
|
775
771
|
*
|
|
776
772
|
* With no target given the rig renders the profile itself, whatever the cues
|
|
@@ -858,7 +854,7 @@ declare function parseLayerRoles(fileNames: string[]): {
|
|
|
858
854
|
/**
|
|
859
855
|
* `generateIkiFromLayerSet`: role layers in, a rigged `.iki` out.
|
|
860
856
|
*
|
|
861
|
-
* The rig's shape
|
|
857
|
+
* The rig's shape is a 2D head rig's usual one (`profile.ts`): a torso that
|
|
862
858
|
* only breathes; a head deformer that rolls about the chin and breathes; one
|
|
863
859
|
* small warp grid per feature (each eye, each brow, the nose, the mouth),
|
|
864
860
|
* translating and foreshortening it by its own lead over the plate; and the
|
package/dist/index.js
CHANGED
|
@@ -1963,14 +1963,155 @@ function createDefaultWarpDeformer(model) {
|
|
|
1963
1963
|
};
|
|
1964
1964
|
}
|
|
1965
1965
|
|
|
1966
|
+
// src/auto-rig/profile.ts
|
|
1967
|
+
var PROFILE_VALUES = {
|
|
1968
|
+
slide: 0.12,
|
|
1969
|
+
lead: 0.1,
|
|
1970
|
+
noseDepth: 2,
|
|
1971
|
+
hairFollow: 1,
|
|
1972
|
+
fore: 0.2,
|
|
1973
|
+
backShare: 0.1,
|
|
1974
|
+
earFar: 0.5,
|
|
1975
|
+
nodDown: 0.18,
|
|
1976
|
+
nodUp: 0.1,
|
|
1977
|
+
nodLead: 0.06,
|
|
1978
|
+
chinShare: 0.75,
|
|
1979
|
+
rollDeg: 14,
|
|
1980
|
+
gaze: 0.2,
|
|
1981
|
+
blink: 0.6,
|
|
1982
|
+
mouthOpen: 0.8,
|
|
1983
|
+
mouthWiden: 1.2,
|
|
1984
|
+
brow: 0.15,
|
|
1985
|
+
breath: 0.03,
|
|
1986
|
+
sway: 0.08
|
|
1987
|
+
};
|
|
1988
|
+
function deriveProfile(v) {
|
|
1989
|
+
const farLead = v.lead * (1 - v.fore / 2);
|
|
1990
|
+
const nearLead = v.lead * (1 + v.fore / 2);
|
|
1991
|
+
const turn = {
|
|
1992
|
+
/** The face plate's translation. */
|
|
1993
|
+
face: v.slide,
|
|
1994
|
+
/** The chin tip's lead over the plate. */
|
|
1995
|
+
chinLead: v.lead,
|
|
1996
|
+
/** The plate's width at full turn: eye and cheek rows, then the jaw. The
|
|
1997
|
+
* plate translates; it does not narrow. */
|
|
1998
|
+
widthUpper: 1,
|
|
1999
|
+
widthJaw: 1,
|
|
2000
|
+
eyeFar: v.slide + farLead,
|
|
2001
|
+
eyeNear: v.slide + nearLead,
|
|
2002
|
+
/** Each eye's width at full turn, about its own centre. */
|
|
2003
|
+
eyeFarScale: 1 - v.fore,
|
|
2004
|
+
eyeNearScale: 1 + v.fore / 2,
|
|
2005
|
+
browFar: v.slide + farLead,
|
|
2006
|
+
browNear: v.slide + nearLead,
|
|
2007
|
+
nose: v.slide + v.noseDepth * v.lead,
|
|
2008
|
+
mouth: v.slide + v.lead,
|
|
2009
|
+
mouthWidth: 1,
|
|
2010
|
+
/** The mouth's far corner rises (its near end drops) by this much: it
|
|
2011
|
+
* stays level. */
|
|
2012
|
+
mouthTiltDeg: 0,
|
|
2013
|
+
/** The front hair (bangs and side locks) rides the face at this share of
|
|
2014
|
+
* its translation. */
|
|
2015
|
+
hairFollow: v.hairFollow,
|
|
2016
|
+
/** The head's outline at the eye row holds: where the front hair draws
|
|
2017
|
+
* it, its outer edge follows the face this much on a row where no back
|
|
2018
|
+
* hair paints behind that edge. Where back hair does, the edge rides
|
|
2019
|
+
* further, as far as that back hair stays behind it through the turn and
|
|
2020
|
+
* the nod, up to the face's follow there (`hairFollow`; over the far
|
|
2021
|
+
* eye's rows, as far as that eye's outer corner goes). On the cap (the
|
|
2022
|
+
* rows above the eyes) it follows less where the turn would otherwise
|
|
2023
|
+
* carry it further outside its back hair than the nod alone does. */
|
|
2024
|
+
outlineFollow: 0,
|
|
2025
|
+
/** The back hair's slight counter-motion. */
|
|
2026
|
+
hairBack: -v.backShare * v.slide,
|
|
2027
|
+
/** The ears lag the plate: the far one's outer edge moves this share of
|
|
2028
|
+
* its slide; the near one rides the head. */
|
|
2029
|
+
earFar: v.earFar,
|
|
2030
|
+
earNear: 1,
|
|
2031
|
+
/** The far ear's width at full turn, about its outer edge, where the head
|
|
2032
|
+
* leaves it room; less where it does not. */
|
|
2033
|
+
earFarScale: 1 - v.fore
|
|
2034
|
+
};
|
|
2035
|
+
const mid = v.nodDown * (1 + v.chinShare) / 2;
|
|
2036
|
+
const feature = (depth) => ({
|
|
2037
|
+
up: -(v.nodUp + depth * v.nodLead),
|
|
2038
|
+
down: mid + depth * v.nodLead
|
|
2039
|
+
});
|
|
2040
|
+
const nod = {
|
|
2041
|
+
faceTop: { up: -v.nodUp, down: v.nodDown },
|
|
2042
|
+
chin: { up: -v.nodUp, down: v.chinShare * v.nodDown },
|
|
2043
|
+
eye: feature(1),
|
|
2044
|
+
/** Eye height at the extreme, about the eye's centre: it holds. */
|
|
2045
|
+
eyeHeight: { up: 1, down: 1 },
|
|
2046
|
+
brow: feature(1),
|
|
2047
|
+
nose: feature(v.noseDepth),
|
|
2048
|
+
mouth: feature(1),
|
|
2049
|
+
hairFront: { up: -v.hairFollow * v.nodUp, down: v.hairFollow * v.nodDown },
|
|
2050
|
+
/** The front hair's cap top: half the plate's rise looking up; looking
|
|
2051
|
+
* down the plate's top, which the rig slides only as far as the back
|
|
2052
|
+
* hair covers the crown behind it. */
|
|
2053
|
+
hairFrontTop: { up: -v.nodUp / 2, down: v.nodDown },
|
|
2054
|
+
hairBack: { up: -v.backShare * v.nodUp, down: v.backShare * v.nodDown }
|
|
2055
|
+
};
|
|
2056
|
+
const amplitude = {
|
|
2057
|
+
/** Iris travel, in iris widths. */
|
|
2058
|
+
gazeX: v.gaze,
|
|
2059
|
+
gazeY: v.gaze / 2,
|
|
2060
|
+
/** How far the upper lid comes down, over the eye's height. */
|
|
2061
|
+
blink: v.blink,
|
|
2062
|
+
/** A single mouth drawing opens to this height over its rest width. */
|
|
2063
|
+
mouthOpenHeight: v.mouthOpen,
|
|
2064
|
+
/** The mouth widens this much open. */
|
|
2065
|
+
mouthOpenWidth: v.mouthWiden,
|
|
2066
|
+
/** Brow raise / lower, hh. */
|
|
2067
|
+
brow: v.brow,
|
|
2068
|
+
/** Breath lifts the shoulders and the head, hh. */
|
|
2069
|
+
breathBody: v.breath,
|
|
2070
|
+
breathHead: v.breath * 2 / 3,
|
|
2071
|
+
/** Hair tip travel at full sway (±20), hh. */
|
|
2072
|
+
sway: v.sway,
|
|
2073
|
+
/** The neck under the face reaches this far above the jaw, hh — what the
|
|
2074
|
+
* chin uncovers as it slides off it: half again the chin's full-turn
|
|
2075
|
+
* slide, to the next 0.05. */
|
|
2076
|
+
hiddenNeck: Math.ceil(30 * (v.slide + v.lead) - 1e-9) / 20
|
|
2077
|
+
};
|
|
2078
|
+
return { TURN: turn, NOD: nod, ROLL_DEG: v.rollDeg, AMPLITUDE: amplitude };
|
|
2079
|
+
}
|
|
2080
|
+
var { TURN, NOD, ROLL_DEG, AMPLITUDE } = deriveProfile(PROFILE_VALUES);
|
|
2081
|
+
var STYLE_KNOBS = [
|
|
2082
|
+
"turn",
|
|
2083
|
+
"featureLead",
|
|
2084
|
+
"hairFollow",
|
|
2085
|
+
"outlineFollow",
|
|
2086
|
+
"blink",
|
|
2087
|
+
"sway"
|
|
2088
|
+
];
|
|
2089
|
+
function resolveStyle(style, check) {
|
|
2090
|
+
const s = style ?? {};
|
|
2091
|
+
const get = (key, def, lo, hi) => s[key] === void 0 ? def : check(`style.${key}`, s[key], lo, hi);
|
|
2092
|
+
return {
|
|
2093
|
+
turn: get("turn", 1, 0, 3),
|
|
2094
|
+
featureLead: get("featureLead", 1, 0, 3),
|
|
2095
|
+
hairFollow: get("hairFollow", TURN.hairFollow, 0, 2),
|
|
2096
|
+
outlineFollow: get("outlineFollow", TURN.outlineFollow, 0, 2),
|
|
2097
|
+
blink: get("blink", AMPLITUDE.blink, 0.1, 1),
|
|
2098
|
+
sway: get("sway", 1, 0, 5)
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
|
|
1966
2102
|
// src/auto-rig/types.ts
|
|
2103
|
+
var round2 = (v) => Math.round(v * 100) / 100;
|
|
1967
2104
|
var TurnTargetError = class extends Error {
|
|
1968
2105
|
constructor(message) {
|
|
1969
2106
|
super(message);
|
|
1970
2107
|
this.name = "TurnTargetError";
|
|
1971
2108
|
}
|
|
1972
2109
|
};
|
|
1973
|
-
var DEFAULT_TURN_TARGETS = Object.freeze({
|
|
2110
|
+
var DEFAULT_TURN_TARGETS = Object.freeze({
|
|
2111
|
+
eyeShift: round2((TURN.eyeFar + TURN.eyeNear) / 2 - TURN.hairBack),
|
|
2112
|
+
farEyeRatio: round2(TURN.eyeFarScale / TURN.eyeNearScale),
|
|
2113
|
+
silhouetteRatio: 1
|
|
2114
|
+
});
|
|
1974
2115
|
|
|
1975
2116
|
// src/auto-rig/roles.ts
|
|
1976
2117
|
var ROLE_TABLE = [
|
|
@@ -1990,6 +2131,8 @@ var ROLE_TABLE = [
|
|
|
1990
2131
|
{ role: "pupil_R", family: "eye_R" },
|
|
1991
2132
|
{ role: "highlight_L", family: "eye_L" },
|
|
1992
2133
|
{ role: "highlight_R", family: "eye_R" },
|
|
2134
|
+
{ role: "lash_lower_L", family: "eye_L" },
|
|
2135
|
+
{ role: "lash_lower_R", family: "eye_R" },
|
|
1993
2136
|
{ role: "lash_L", family: "eye_L" },
|
|
1994
2137
|
{ role: "lash_R", family: "eye_R" },
|
|
1995
2138
|
{ role: "brow_L", family: "brow_L" },
|
|
@@ -2427,117 +2570,6 @@ function land(l, offsets, x, y) {
|
|
|
2427
2570
|
return [topX + (botX - topX) * t, topY + (botY - topY) * t];
|
|
2428
2571
|
}
|
|
2429
2572
|
|
|
2430
|
-
// src/auto-rig/profile.ts
|
|
2431
|
-
var HH_PER_EYE_TO_CHIN = 1.017;
|
|
2432
|
-
var TURN = {
|
|
2433
|
-
/** The face plate's translation (its cheek edges, 0.118 / 0.126). */
|
|
2434
|
-
face: 0.118,
|
|
2435
|
-
/** The chin tip's lead over the plate (it moves 0.194). */
|
|
2436
|
-
chinLead: 0.076,
|
|
2437
|
-
/** The plate's width at full turn: eye and cheek rows, then the jaw. */
|
|
2438
|
-
widthUpper: 0.985,
|
|
2439
|
-
widthJaw: 0.96,
|
|
2440
|
-
eyeFar: 0.192,
|
|
2441
|
-
eyeNear: 0.23,
|
|
2442
|
-
/** Each eye's width at full turn, about its own centre. */
|
|
2443
|
-
eyeFarScale: 0.85,
|
|
2444
|
-
eyeNearScale: 1.085,
|
|
2445
|
-
browFar: 0.213,
|
|
2446
|
-
browNear: 0.243,
|
|
2447
|
-
nose: 0.301,
|
|
2448
|
-
mouth: 0.213,
|
|
2449
|
-
mouthWidth: 0.975,
|
|
2450
|
-
/** The mouth's far corner rises (its near end drops) by this much. */
|
|
2451
|
-
mouthTiltDeg: 4.4,
|
|
2452
|
-
/** The front hair (bangs and side locks) rides the face at this share of
|
|
2453
|
-
* its translation (bangs 1.18×, side locks at the eye row ≈ 1.0×). */
|
|
2454
|
-
hairFollow: 1.1,
|
|
2455
|
-
/** The head's outline at the eye row holds (−0.014 / −0.004 hh): where the
|
|
2456
|
-
* front hair draws it, its outer edge follows the face this much on a row
|
|
2457
|
-
* where no back hair paints behind that edge. Where back hair does, the
|
|
2458
|
-
* edge rides further, as far as that back hair stays behind it through
|
|
2459
|
-
* the turn and the nod, up to the face's follow there (`hairFollow`;
|
|
2460
|
-
* over the far eye's rows, as far as that eye's outer corner goes). On
|
|
2461
|
-
* the cap (the rows above the eyes) it follows less where the turn would
|
|
2462
|
-
* otherwise carry it further outside its back hair than the nod alone
|
|
2463
|
-
* does. */
|
|
2464
|
-
outlineFollow: 0,
|
|
2465
|
-
/** The back hair's slight counter-motion. */
|
|
2466
|
-
hairBack: -0.027,
|
|
2467
|
-
/** The ears lag the plate: the far one's outer edge moves this share of
|
|
2468
|
-
* its slide, the near one this at its widest reach, its root riding the
|
|
2469
|
-
* head (the samples' parallax ratios, 0.44 and 0.87). */
|
|
2470
|
-
earFar: 0.44,
|
|
2471
|
-
earNear: 0.87,
|
|
2472
|
-
/** The far ear's width at full turn, about its outer edge, where the head
|
|
2473
|
-
* leaves it room (the samples' median: Haru 0.81 / 0.91, Hiyori 0.90 /
|
|
2474
|
-
* 0.85, Mao 0.77 / 0.79; none fades); less where it does not. */
|
|
2475
|
-
earFarScale: 0.83
|
|
2476
|
-
};
|
|
2477
|
-
var NOD = {
|
|
2478
|
-
faceTop: { up: -0.081, down: 0.178 },
|
|
2479
|
-
chin: { up: -0.0875, down: 0.109 },
|
|
2480
|
-
eye: { up: -0.167, down: 0.202 },
|
|
2481
|
-
/** Eye height at the extreme, about the eye's centre. */
|
|
2482
|
-
eyeHeight: { up: 0.994, down: 0.96 },
|
|
2483
|
-
brow: { up: -0.167, down: 0.223 },
|
|
2484
|
-
nose: { up: -0.207, down: 0.193 },
|
|
2485
|
-
mouth: { up: -0.172, down: 0.177 },
|
|
2486
|
-
hairFront: { up: -0.11, down: 0.189 },
|
|
2487
|
-
/** The front hair's cap top. The samples' cap top moves 1.215× their face's
|
|
2488
|
-
* centroid looking down (Mao 1.236, Haru 1.626, Hiyori 1.194, Natori
|
|
2489
|
-
* 0.730) and 0.415× looking up (0.385, 0.430, 0.400, 0.487); each ratio
|
|
2490
|
-
* times the plate's own nod halfway between its top and the chin (0.1435
|
|
2491
|
-
* down, 0.084 up). Looking down, the rig slides it only as far as the
|
|
2492
|
-
* back hair covers the crown behind it. */
|
|
2493
|
-
hairFrontTop: { up: -0.035, down: 0.174 },
|
|
2494
|
-
hairBack: { up: -0.011, down: 0.025 }
|
|
2495
|
-
};
|
|
2496
|
-
var ROLL_DEG = 10;
|
|
2497
|
-
var AMPLITUDE = {
|
|
2498
|
-
/** Iris travel, in iris widths (X 0.045 hh, Y 0.031 hh on the samples). */
|
|
2499
|
-
gazeX: 0.17,
|
|
2500
|
-
gazeY: 0.11,
|
|
2501
|
-
/** How far the upper lid comes down, over the eye's height (Haru 0.30,
|
|
2502
|
-
* Mao 0.58, Hiyori 0.67). */
|
|
2503
|
-
blink: 0.58,
|
|
2504
|
-
/** A single mouth drawing opens to this height over its rest width. */
|
|
2505
|
-
mouthOpenHeight: 0.77,
|
|
2506
|
-
/** The mouth widens this much open. */
|
|
2507
|
-
mouthOpenWidth: 1.15,
|
|
2508
|
-
/** Brow raise / lower, hh. */
|
|
2509
|
-
brow: 0.089,
|
|
2510
|
-
/** Breath lifts the shoulders and the head, hh. */
|
|
2511
|
-
breathBody: 0.025,
|
|
2512
|
-
breathHead: 0.016,
|
|
2513
|
-
/** Hair tip travel at full sway (±20), hh (the keyform ranges at ±1 have a
|
|
2514
|
-
* median of 0.05–0.06 hh). */
|
|
2515
|
-
sway: 0.06,
|
|
2516
|
-
/** The neck under the face reaches this far above the jaw, hh — what the
|
|
2517
|
-
* chin uncovers as it slides off it. */
|
|
2518
|
-
hiddenNeck: 0.3
|
|
2519
|
-
};
|
|
2520
|
-
var STYLE_KNOBS = [
|
|
2521
|
-
"turn",
|
|
2522
|
-
"featureLead",
|
|
2523
|
-
"hairFollow",
|
|
2524
|
-
"outlineFollow",
|
|
2525
|
-
"blink",
|
|
2526
|
-
"sway"
|
|
2527
|
-
];
|
|
2528
|
-
function resolveStyle(style, check) {
|
|
2529
|
-
const s = style ?? {};
|
|
2530
|
-
const get = (key, def, lo, hi) => s[key] === void 0 ? def : check(`style.${key}`, s[key], lo, hi);
|
|
2531
|
-
return {
|
|
2532
|
-
turn: get("turn", 1, 0, 3),
|
|
2533
|
-
featureLead: get("featureLead", 1, 0, 3),
|
|
2534
|
-
hairFollow: get("hairFollow", TURN.hairFollow, 0, 2),
|
|
2535
|
-
outlineFollow: get("outlineFollow", TURN.outlineFollow, 0, 2),
|
|
2536
|
-
blink: get("blink", AMPLITUDE.blink, 0.1, 1),
|
|
2537
|
-
sway: get("sway", 1, 0, 5)
|
|
2538
|
-
};
|
|
2539
|
-
}
|
|
2540
|
-
|
|
2541
2573
|
// src/auto-rig/head.ts
|
|
2542
2574
|
function outerEdge(runs, axisX, side) {
|
|
2543
2575
|
if (runs.length === 0) return void 0;
|
|
@@ -2750,7 +2782,7 @@ function buildHeadFrame(layers, opts) {
|
|
|
2750
2782
|
return face.y1;
|
|
2751
2783
|
};
|
|
2752
2784
|
const chin = chinY;
|
|
2753
|
-
const shade = JAW_SHADE *
|
|
2785
|
+
const shade = JAW_SHADE * Math.max(1, eyeY - chin);
|
|
2754
2786
|
const reach = opts.chinSlide === void 0 ? nk.waist : clamp(
|
|
2755
2787
|
nk.waist - opts.chinSlide - CUT_MARGIN - (CUT_LOW + 1),
|
|
2756
2788
|
0,
|
|
@@ -2799,7 +2831,7 @@ function buildHeadFrame(layers, opts) {
|
|
|
2799
2831
|
}
|
|
2800
2832
|
}
|
|
2801
2833
|
}
|
|
2802
|
-
const hh =
|
|
2834
|
+
const hh = Math.max(1, eyeY - chinY);
|
|
2803
2835
|
const headHalf = opts.headHalfWidth;
|
|
2804
2836
|
const hairShell = headHalf !== void 0;
|
|
2805
2837
|
let shellLeft = axisX - plateHalf;
|
|
@@ -4428,12 +4460,12 @@ function motionReach(warps, mesh, box, extra = [0, 0]) {
|
|
|
4428
4460
|
}
|
|
4429
4461
|
return out;
|
|
4430
4462
|
}
|
|
4431
|
-
function blinkFold(param, mesh,
|
|
4463
|
+
function blinkFold(param, mesh, box, travel, eye = box) {
|
|
4432
4464
|
const yc = eye.y1 - travel * bh(eye);
|
|
4433
4465
|
return localWarp(
|
|
4434
4466
|
param,
|
|
4435
4467
|
mesh,
|
|
4436
|
-
|
|
4468
|
+
box,
|
|
4437
4469
|
[0, 1],
|
|
4438
4470
|
([, y], v) => v === 1 ? [0, 0] : [0, yc - y]
|
|
4439
4471
|
);
|
|
@@ -4782,6 +4814,10 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
4782
4814
|
case "eye_R":
|
|
4783
4815
|
warps.push(blinkFold(eyeOpen, mesh, b, style.blink));
|
|
4784
4816
|
break;
|
|
4817
|
+
case "lash_lower_L":
|
|
4818
|
+
case "lash_lower_R":
|
|
4819
|
+
warps.push(blinkFold(eyeOpen, mesh, b, style.blink, box(`eye_${side}`)));
|
|
4820
|
+
break;
|
|
4785
4821
|
case "lash_L":
|
|
4786
4822
|
case "lash_R":
|
|
4787
4823
|
warps.push(lashFold(eyeOpen, mesh, b, box(`eye_${side}`), style.blink));
|