@ikijs/editor 0.18.0 → 0.20.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 +20 -16
- package/dist/index.d.mts +106 -34
- package/dist/index.d.ts +106 -34
- package/dist/index.js +376 -132
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +375 -130
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1565,7 +1565,13 @@ var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
|
|
|
1565
1565
|
"hair_back",
|
|
1566
1566
|
"body",
|
|
1567
1567
|
"arm_L",
|
|
1568
|
-
"arm_R"
|
|
1568
|
+
"arm_R",
|
|
1569
|
+
"forearm_pose_L",
|
|
1570
|
+
"forearm_pose_R"
|
|
1571
|
+
]);
|
|
1572
|
+
var REST_HIDDEN_ROLES = /* @__PURE__ */ new Set([
|
|
1573
|
+
"forearm_pose_L",
|
|
1574
|
+
"forearm_pose_R"
|
|
1569
1575
|
]);
|
|
1570
1576
|
function createLayerSetMeasurer(canvas) {
|
|
1571
1577
|
for (const field of ["width", "height"]) {
|
|
@@ -1596,6 +1602,7 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1596
1602
|
if (bbox === null) return null;
|
|
1597
1603
|
const rowLeft = new Int32Array(canvasH).fill(canvasW);
|
|
1598
1604
|
const rowRight = new Int32Array(canvasH).fill(-1);
|
|
1605
|
+
const hidden = REST_HIDDEN_ROLES.has(role);
|
|
1599
1606
|
const rowRuns = ROW_RUN_ROLES.has(role) ? [] : void 0;
|
|
1600
1607
|
for (let y = 0; y < canvasH; y++) {
|
|
1601
1608
|
let runs;
|
|
@@ -1611,14 +1618,14 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1611
1618
|
runs.push(x);
|
|
1612
1619
|
inRun = on;
|
|
1613
1620
|
}
|
|
1614
|
-
if (!on) continue;
|
|
1621
|
+
if (!on || hidden) continue;
|
|
1615
1622
|
opaque[p] = 1;
|
|
1616
1623
|
if (x < rowLeft[y]) rowLeft[y] = x;
|
|
1617
1624
|
if (x > rowRight[y]) rowRight[y] = x;
|
|
1618
1625
|
}
|
|
1619
1626
|
if (runs !== void 0 && inRun) runs.push(canvasW);
|
|
1620
1627
|
}
|
|
1621
|
-
rowSpansByRole.push({ role, rowLeft, rowRight });
|
|
1628
|
+
if (!hidden) rowSpansByRole.push({ role, rowLeft, rowRight });
|
|
1622
1629
|
const layer = {
|
|
1623
1630
|
role,
|
|
1624
1631
|
fileName,
|
|
@@ -2684,7 +2691,11 @@ function headOwnBreath(hasBody) {
|
|
|
2684
2691
|
var ELBOW_AT = 0.42;
|
|
2685
2692
|
var CAP_ROWS = [0.1, 0.3];
|
|
2686
2693
|
var CAP_SEGMENTS = 12;
|
|
2687
|
-
var
|
|
2694
|
+
var CAP_INSET = 3;
|
|
2695
|
+
var capInset = (i) => Math.round(CAP_INSET * Math.min(1, Math.min(i, CAP_SEGMENTS - i) / 2));
|
|
2696
|
+
var CAP_RIM = 0.25;
|
|
2697
|
+
var ARM_RANGE = [-8, 32];
|
|
2698
|
+
var ELBOW_RANGE = [-10, 90];
|
|
2688
2699
|
var ARM_PARAMS = {
|
|
2689
2700
|
arm_L: { arm: P3.ArmLeft, elbow: P3.ElbowLeft },
|
|
2690
2701
|
arm_R: { arm: P3.ArmRight, elbow: P3.ElbowRight }
|
|
@@ -2692,49 +2703,67 @@ var ARM_PARAMS = {
|
|
|
2692
2703
|
var ARM_IDS = {
|
|
2693
2704
|
arm_L: {
|
|
2694
2705
|
forearm: "forearm_L",
|
|
2706
|
+
cap: "elbow_L",
|
|
2695
2707
|
armDeformer: "armDeformer_L",
|
|
2696
2708
|
forearmDeformer: "forearmDeformer_L"
|
|
2697
2709
|
},
|
|
2698
2710
|
arm_R: {
|
|
2699
2711
|
forearm: "forearm_R",
|
|
2712
|
+
cap: "elbow_R",
|
|
2700
2713
|
armDeformer: "armDeformer_R",
|
|
2701
2714
|
forearmDeformer: "forearmDeformer_R"
|
|
2702
2715
|
}
|
|
2703
2716
|
};
|
|
2704
|
-
function
|
|
2717
|
+
function endPivot(layer, end) {
|
|
2705
2718
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2706
|
-
const
|
|
2707
|
-
const
|
|
2719
|
+
const span = runSpan(layer);
|
|
2720
|
+
const from = (k) => end === "top" ? k : bbox.h - 1 - k;
|
|
2721
|
+
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2722
|
+
const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
|
|
2723
|
+
const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
|
|
2724
|
+
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2725
|
+
const widths = [];
|
|
2726
|
+
for (let k = lo; k <= hi; k++) {
|
|
2727
|
+
const [s2, e2] = span(from(k));
|
|
2728
|
+
widths.push(e2 - s2);
|
|
2729
|
+
}
|
|
2730
|
+
widths.sort((p, q) => p - q);
|
|
2731
|
+
const radius = widths[widths.length >> 1] / 2;
|
|
2732
|
+
const ks = end === "top" ? firstK + radius : firstK - radius;
|
|
2733
|
+
const [s, e] = span(Math.round(ks));
|
|
2734
|
+
return {
|
|
2735
|
+
point: {
|
|
2736
|
+
x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
|
|
2737
|
+
y: roundTo(canvasH / 2 - (bbox.y + ks + 0.5), 0.01)
|
|
2738
|
+
},
|
|
2739
|
+
radius,
|
|
2740
|
+
row: ks
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
function runSpan(layer) {
|
|
2744
|
+
const { bbox, rowRuns } = layer;
|
|
2745
|
+
return (k) => {
|
|
2708
2746
|
const runs = rowRuns?.[k] ?? [];
|
|
2709
2747
|
if (runs.length === 0) return [bbox.x, bbox.x + bbox.w];
|
|
2710
|
-
let [
|
|
2748
|
+
let [s, e] = [runs[0], runs[1]];
|
|
2711
2749
|
for (let i = 2; i < runs.length; i += 2) {
|
|
2712
|
-
if (runs[i + 1] - runs[i] >
|
|
2750
|
+
if (runs[i + 1] - runs[i] > e - s) [s, e] = [runs[i], runs[i + 1]];
|
|
2713
2751
|
}
|
|
2714
|
-
return [
|
|
2752
|
+
return [s, e];
|
|
2715
2753
|
};
|
|
2754
|
+
}
|
|
2755
|
+
function armGeometry(layer, bodyAxisX) {
|
|
2756
|
+
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2757
|
+
const b = boxOfLayer(layer);
|
|
2758
|
+
const span = runSpan(layer);
|
|
2716
2759
|
const centreX = (k) => {
|
|
2717
2760
|
const [s2, e2] = span(k);
|
|
2718
2761
|
return (s2 + e2) / 2 - canvasW / 2;
|
|
2719
2762
|
};
|
|
2720
2763
|
const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
|
|
2721
2764
|
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2722
|
-
const first = painted[0] ?? 0;
|
|
2723
2765
|
const last = painted.at(-1) ?? bbox.h - 1;
|
|
2724
|
-
const
|
|
2725
|
-
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2726
|
-
const widths = [];
|
|
2727
|
-
for (let k = lo; k <= hi; k++) {
|
|
2728
|
-
const [s2, e2] = span(k);
|
|
2729
|
-
widths.push(e2 - s2);
|
|
2730
|
-
}
|
|
2731
|
-
widths.sort((p, q) => p - q);
|
|
2732
|
-
const ru = widths[widths.length >> 1] / 2;
|
|
2733
|
-
const ks = first + ru;
|
|
2734
|
-
const shoulder = {
|
|
2735
|
-
x: roundTo(centreX(Math.round(ks)), 0.01),
|
|
2736
|
-
y: roundTo(rowY(ks), 0.01)
|
|
2737
|
-
};
|
|
2766
|
+
const { point: shoulder, radius: ru, row: ks } = endPivot(layer, "top");
|
|
2738
2767
|
const ke = Math.round(ks + ELBOW_AT * (last - ks));
|
|
2739
2768
|
if (ke <= ks) {
|
|
2740
2769
|
throw new LayerGeometryError(
|
|
@@ -2749,11 +2778,30 @@ function armGeometry(layer, bodyAxisX) {
|
|
|
2749
2778
|
b.x1 - elbow.x,
|
|
2750
2779
|
b.y1 - elbow.y
|
|
2751
2780
|
);
|
|
2781
|
+
const inside = (x, y) => {
|
|
2782
|
+
const k = Math.floor(y) - bbox.y;
|
|
2783
|
+
if (k < 0 || k >= bbox.h) return false;
|
|
2784
|
+
const [rs, re] = span(k);
|
|
2785
|
+
return rs < x && x < re;
|
|
2786
|
+
};
|
|
2787
|
+
const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
|
|
2788
|
+
const a = Math.PI * i / CAP_SEGMENTS;
|
|
2789
|
+
let t = 1;
|
|
2790
|
+
while (t <= capRadius + CAP_INSET && inside(
|
|
2791
|
+
elbow.x + canvasW / 2 + t * Math.cos(a),
|
|
2792
|
+
canvasH / 2 - elbow.y - t * Math.sin(a)
|
|
2793
|
+
)) {
|
|
2794
|
+
t++;
|
|
2795
|
+
}
|
|
2796
|
+
return Math.min(capRadius, Math.max(1, t - capInset(i)));
|
|
2797
|
+
});
|
|
2752
2798
|
return {
|
|
2753
2799
|
shoulder,
|
|
2754
2800
|
shoulderRadius: ru,
|
|
2755
2801
|
elbow,
|
|
2756
2802
|
capRadius,
|
|
2803
|
+
capRays,
|
|
2804
|
+
seamRun: [s - bbox.x, e - bbox.x],
|
|
2757
2805
|
side: shoulder.x > bodyAxisX ? 1 : -1
|
|
2758
2806
|
};
|
|
2759
2807
|
}
|
|
@@ -2763,84 +2811,190 @@ function meshOf(vertices, indices) {
|
|
|
2763
2811
|
);
|
|
2764
2812
|
return { vertices, uvs, indices };
|
|
2765
2813
|
}
|
|
2766
|
-
function armParts(role, layer, g, order) {
|
|
2814
|
+
function armParts(role, layer, g, order, poseSwitch) {
|
|
2767
2815
|
const b = boxOfLayer(layer);
|
|
2768
2816
|
const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
|
|
2769
2817
|
const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
|
|
2770
2818
|
const seam = ly(g.elbow.y);
|
|
2771
|
-
const band = (
|
|
2819
|
+
const band = (top2, bottom) => [
|
|
2772
2820
|
-0.5,
|
|
2773
|
-
|
|
2821
|
+
top2,
|
|
2774
2822
|
0.5,
|
|
2775
|
-
|
|
2823
|
+
top2,
|
|
2776
2824
|
-0.5,
|
|
2777
2825
|
bottom,
|
|
2778
2826
|
0.5,
|
|
2779
2827
|
bottom
|
|
2780
2828
|
];
|
|
2781
2829
|
const quad = [2, 3, 0, 0, 3, 1];
|
|
2782
|
-
const
|
|
2830
|
+
const rays = g.capRays.length;
|
|
2831
|
+
const top = (rays - 1) / 2;
|
|
2832
|
+
const at = (i, r) => {
|
|
2833
|
+
const a = Math.PI * i / (rays - 1);
|
|
2834
|
+
return [lx(g.elbow.x + r * Math.cos(a)), ly(g.elbow.y + r * Math.sin(a))];
|
|
2835
|
+
};
|
|
2836
|
+
const width = (i) => Math.min(CAP_RIM * g.capRadius, g.capRays[i] / 2);
|
|
2837
|
+
const ringRays = Array.from({ length: rays }, (_, i) => i);
|
|
2838
|
+
ringRays.splice(top, 0, top);
|
|
2839
|
+
const vertices = [lx(g.elbow.x), seam];
|
|
2840
|
+
for (let i = 0; i < rays; i++) {
|
|
2841
|
+
vertices.push(...at(i, g.capRays[i] - width(i)));
|
|
2842
|
+
}
|
|
2843
|
+
const ringInner = vertices.length / 2;
|
|
2844
|
+
for (const i of ringRays) vertices.push(...at(i, g.capRays[i] - width(i)));
|
|
2845
|
+
const ringOuter = vertices.length / 2;
|
|
2846
|
+
for (const i of ringRays) vertices.push(...at(i, g.capRays[i]));
|
|
2847
|
+
const { uvs } = meshOf(vertices, []);
|
|
2848
|
+
const col = (c) => roundTo(Math.min(Math.max(c + 0.5, 0.5), bw(b) - 0.5) / bw(b), 1e-5);
|
|
2849
|
+
const v = roundTo(0.5 - seam, 1e-5);
|
|
2850
|
+
ringRays.forEach((i, j) => {
|
|
2851
|
+
const right = j <= top;
|
|
2852
|
+
const outer = right ? g.seamRun[1] : g.seamRun[0] - 1;
|
|
2853
|
+
const step = right ? -width(i) : width(i);
|
|
2854
|
+
uvs[(ringInner + j) * 2] = col(outer + step);
|
|
2855
|
+
uvs[(ringInner + j) * 2 + 1] = v;
|
|
2856
|
+
uvs[(ringOuter + j) * 2] = col(outer);
|
|
2857
|
+
uvs[(ringOuter + j) * 2 + 1] = v;
|
|
2858
|
+
});
|
|
2783
2859
|
const fan = [];
|
|
2784
|
-
for (let i = 0; i
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
);
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
const part = (id, deformer, at, mesh) => ({
|
|
2860
|
+
for (let i = 0; i < rays - 1; i++) fan.push(0, 1 + i, 2 + i);
|
|
2861
|
+
const ring = [];
|
|
2862
|
+
for (let j = 0; j < ringRays.length - 1; j++) {
|
|
2863
|
+
if (j === top) continue;
|
|
2864
|
+
const [p, q] = [ringInner + j, ringOuter + j];
|
|
2865
|
+
ring.push(p, q, q + 1, p, q + 1, p + 1);
|
|
2866
|
+
}
|
|
2867
|
+
const part = (id, deformer, z, mesh, swaps = false) => ({
|
|
2793
2868
|
id,
|
|
2794
2869
|
color: [1, 1, 1, 1],
|
|
2795
2870
|
width: bw(b),
|
|
2796
2871
|
height: bh(b),
|
|
2797
2872
|
transform: { x: cx(b), y: cy(b) },
|
|
2798
|
-
order:
|
|
2873
|
+
order: z,
|
|
2799
2874
|
deformer,
|
|
2800
|
-
mesh
|
|
2875
|
+
mesh,
|
|
2876
|
+
...swaps && poseSwitch !== void 0 ? {
|
|
2877
|
+
bindings: [
|
|
2878
|
+
{
|
|
2879
|
+
parameter: poseSwitch,
|
|
2880
|
+
channel: "opacity",
|
|
2881
|
+
from: 1,
|
|
2882
|
+
to: 0
|
|
2883
|
+
}
|
|
2884
|
+
]
|
|
2885
|
+
} : {}
|
|
2801
2886
|
});
|
|
2802
2887
|
const ids = ARM_IDS[role];
|
|
2803
2888
|
return [
|
|
2804
|
-
part(
|
|
2889
|
+
part(
|
|
2890
|
+
ids.cap,
|
|
2891
|
+
ids.forearmDeformer,
|
|
2892
|
+
order,
|
|
2893
|
+
{
|
|
2894
|
+
vertices,
|
|
2895
|
+
uvs,
|
|
2896
|
+
indices: [...fan, ...ring]
|
|
2897
|
+
},
|
|
2898
|
+
true
|
|
2899
|
+
),
|
|
2900
|
+
part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
|
|
2805
2901
|
part(
|
|
2806
2902
|
ids.forearm,
|
|
2807
2903
|
ids.forearmDeformer,
|
|
2808
|
-
order +
|
|
2809
|
-
meshOf(
|
|
2904
|
+
order + 2,
|
|
2905
|
+
meshOf(band(seam, -0.5), quad),
|
|
2906
|
+
true
|
|
2810
2907
|
)
|
|
2811
2908
|
];
|
|
2812
2909
|
}
|
|
2910
|
+
var rotateBinding = (parameter, range, sign) => ({
|
|
2911
|
+
parameter,
|
|
2912
|
+
channel: "rotate",
|
|
2913
|
+
from: sign * range[0],
|
|
2914
|
+
to: sign * range[1]
|
|
2915
|
+
});
|
|
2813
2916
|
function armDeformers(role, g) {
|
|
2814
2917
|
const ids = ARM_IDS[role];
|
|
2815
2918
|
const params = ARM_PARAMS[role];
|
|
2816
|
-
const rotate = (parameter) => ({
|
|
2817
|
-
parameter,
|
|
2818
|
-
channel: "rotate",
|
|
2819
|
-
from: g.side * ARM_RANGE[0],
|
|
2820
|
-
to: g.side * ARM_RANGE[1]
|
|
2821
|
-
});
|
|
2822
2919
|
return [
|
|
2823
2920
|
{
|
|
2824
2921
|
id: ids.armDeformer,
|
|
2825
2922
|
parent: BODY_WARP_ID,
|
|
2826
2923
|
pivot: { ...g.shoulder },
|
|
2827
|
-
bindings: [
|
|
2924
|
+
bindings: [rotateBinding(params.arm, ARM_RANGE, g.side)]
|
|
2828
2925
|
},
|
|
2829
2926
|
{
|
|
2830
2927
|
id: ids.forearmDeformer,
|
|
2831
2928
|
parent: ids.armDeformer,
|
|
2832
2929
|
pivot: { ...g.elbow },
|
|
2833
|
-
bindings: [
|
|
2930
|
+
bindings: [rotateBinding(params.elbow, ELBOW_RANGE, -g.side)]
|
|
2834
2931
|
}
|
|
2835
2932
|
];
|
|
2836
2933
|
}
|
|
2837
2934
|
|
|
2935
|
+
// src/auto-rig/forearm-pose.ts
|
|
2936
|
+
import {
|
|
2937
|
+
StandardParameter as P4
|
|
2938
|
+
} from "@ikijs/format";
|
|
2939
|
+
var ARM_OF_POSE = {
|
|
2940
|
+
forearm_pose_L: "arm_L",
|
|
2941
|
+
forearm_pose_R: "arm_R"
|
|
2942
|
+
};
|
|
2943
|
+
var POSE_OF_ARM = {
|
|
2944
|
+
arm_L: "forearm_pose_L",
|
|
2945
|
+
arm_R: "forearm_pose_R"
|
|
2946
|
+
};
|
|
2947
|
+
var POSE_PARAMS = {
|
|
2948
|
+
forearm_pose_L: { pose: P4.ArmPoseLeft, angle: P4.ArmPoseAngleLeft },
|
|
2949
|
+
forearm_pose_R: { pose: P4.ArmPoseRight, angle: P4.ArmPoseAngleRight }
|
|
2950
|
+
};
|
|
2951
|
+
var POSE_IDS = {
|
|
2952
|
+
forearm_pose_L: "armPoseDeformer_L",
|
|
2953
|
+
forearm_pose_R: "armPoseDeformer_R"
|
|
2954
|
+
};
|
|
2955
|
+
var POSE_ANGLE_RANGE = [-15, 15];
|
|
2956
|
+
function forearmPoseGeometry(layer) {
|
|
2957
|
+
const { point, radius } = endPivot(layer, "bottom");
|
|
2958
|
+
const first = layer.rowRuns?.findIndex((runs) => runs.length > 0) ?? -1;
|
|
2959
|
+
const k = first < 0 ? 0 : first;
|
|
2960
|
+
return {
|
|
2961
|
+
pivot: point,
|
|
2962
|
+
radius,
|
|
2963
|
+
top: roundTo(layer.canvasH / 2 - (layer.bbox.y + k + 0.5), 0.01)
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
function forearmPosePart(role, layer, order) {
|
|
2967
|
+
const b = boxOfLayer(layer);
|
|
2968
|
+
return {
|
|
2969
|
+
id: role,
|
|
2970
|
+
color: [1, 1, 1, 1],
|
|
2971
|
+
width: bw(b),
|
|
2972
|
+
height: bh(b),
|
|
2973
|
+
transform: { x: cx(b), y: cy(b) },
|
|
2974
|
+
order,
|
|
2975
|
+
deformer: POSE_IDS[role],
|
|
2976
|
+
bindings: [
|
|
2977
|
+
{ parameter: POSE_PARAMS[role].pose, channel: "opacity", from: 0, to: 1 }
|
|
2978
|
+
]
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
function forearmPoseDeformer(role, arm) {
|
|
2982
|
+
return {
|
|
2983
|
+
id: POSE_IDS[role],
|
|
2984
|
+
parent: ARM_IDS[ARM_OF_POSE[role]].armDeformer,
|
|
2985
|
+
pivot: { ...arm.elbow },
|
|
2986
|
+
bindings: [
|
|
2987
|
+
rotateBinding(POSE_PARAMS[role].angle, POSE_ANGLE_RANGE, -arm.side)
|
|
2988
|
+
]
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2838
2992
|
// src/auto-rig/roles.ts
|
|
2839
2993
|
var ROLE_TABLE = [
|
|
2840
2994
|
{ role: "hair_back", family: "hair_back" },
|
|
2841
2995
|
{ role: "body", family: "body" },
|
|
2842
|
-
{ role: "arm_L", family: "arm", parts: ["arm_L", "forearm_L"] },
|
|
2843
|
-
{ role: "arm_R", family: "arm", parts: ["arm_R", "forearm_R"] },
|
|
2996
|
+
{ role: "arm_L", family: "arm", parts: ["elbow_L", "arm_L", "forearm_L"] },
|
|
2997
|
+
{ role: "arm_R", family: "arm", parts: ["elbow_R", "arm_R", "forearm_R"] },
|
|
2844
2998
|
{ role: "face", family: "face", required: true },
|
|
2845
2999
|
{ role: "blush_L", family: "face" },
|
|
2846
3000
|
{ role: "blush_R", family: "face" },
|
|
@@ -2861,7 +3015,10 @@ var ROLE_TABLE = [
|
|
|
2861
3015
|
{ role: "lash_R", family: "eye_R" },
|
|
2862
3016
|
{ role: "brow_L", family: "brow_L" },
|
|
2863
3017
|
{ role: "brow_R", family: "brow_R" },
|
|
2864
|
-
{ role: "hair_front", family: "hair_front" }
|
|
3018
|
+
{ role: "hair_front", family: "hair_front" },
|
|
3019
|
+
// Drawn above the face and the hair: a raised hand passes them.
|
|
3020
|
+
{ role: "forearm_pose_L", family: "forearm_pose" },
|
|
3021
|
+
{ role: "forearm_pose_R", family: "forearm_pose" }
|
|
2865
3022
|
];
|
|
2866
3023
|
var REQUIRED_ROLES = ROLE_TABLE.filter(
|
|
2867
3024
|
(r) => r.required
|
|
@@ -2901,6 +3058,16 @@ function checkArmsHaveBody(roles) {
|
|
|
2901
3058
|
);
|
|
2902
3059
|
}
|
|
2903
3060
|
}
|
|
3061
|
+
function checkPosesHaveArms(roles) {
|
|
3062
|
+
const has = new Set(roles);
|
|
3063
|
+
for (const side of ["L", "R"]) {
|
|
3064
|
+
if (has.has(`forearm_pose_${side}`) && !has.has(`arm_${side}`)) {
|
|
3065
|
+
throw new Error(
|
|
3066
|
+
`auto-rig: forearm_pose_${side} needs an arm_${side} layer (a pose forearm hangs from its arm's elbow)`
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
2904
3071
|
function parseLayerRoles(fileNames) {
|
|
2905
3072
|
const seen = /* @__PURE__ */ new Map();
|
|
2906
3073
|
const out = [];
|
|
@@ -2927,42 +3094,43 @@ function parseLayerRoles(fileNames) {
|
|
|
2927
3094
|
);
|
|
2928
3095
|
}
|
|
2929
3096
|
checkArmsHaveBody(seen.keys());
|
|
3097
|
+
checkPosesHaveArms(seen.keys());
|
|
2930
3098
|
return out;
|
|
2931
3099
|
}
|
|
2932
3100
|
|
|
2933
3101
|
// src/auto-rig/generate.ts
|
|
2934
3102
|
import {
|
|
2935
3103
|
IKI_FORMAT_VERSION as IKI_FORMAT_VERSION2,
|
|
2936
|
-
StandardParameter as
|
|
3104
|
+
StandardParameter as P7,
|
|
2937
3105
|
parseIkiModel as parseIkiModel3
|
|
2938
3106
|
} from "@ikijs/format";
|
|
2939
3107
|
|
|
2940
3108
|
// src/auto-rig/animations.ts
|
|
2941
3109
|
import {
|
|
2942
|
-
StandardParameter as
|
|
3110
|
+
StandardParameter as P5
|
|
2943
3111
|
} from "@ikijs/format";
|
|
2944
3112
|
function browsY(y) {
|
|
2945
3113
|
return [
|
|
2946
|
-
{ parameter:
|
|
2947
|
-
{ parameter:
|
|
3114
|
+
{ parameter: P5.BrowLeftY, value: y, blend: "add" },
|
|
3115
|
+
{ parameter: P5.BrowRightY, value: y, blend: "add" }
|
|
2948
3116
|
];
|
|
2949
3117
|
}
|
|
2950
3118
|
function browsTilt(innerDown) {
|
|
2951
3119
|
return [
|
|
2952
|
-
{ parameter:
|
|
2953
|
-
{ parameter:
|
|
3120
|
+
{ parameter: P5.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3121
|
+
{ parameter: P5.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
2954
3122
|
];
|
|
2955
3123
|
}
|
|
2956
3124
|
function cheek(value) {
|
|
2957
|
-
return { parameter:
|
|
3125
|
+
return { parameter: P5.Cheek, value, blend: "add" };
|
|
2958
3126
|
}
|
|
2959
|
-
var BROWS = [
|
|
3127
|
+
var BROWS = [P5.BrowLeftY, P5.BrowRightY, P5.BrowLeftAngle, P5.BrowRightAngle];
|
|
2960
3128
|
var EXPRESSIONS = [
|
|
2961
3129
|
{
|
|
2962
3130
|
id: "smile",
|
|
2963
3131
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
2964
3132
|
parameters: [
|
|
2965
|
-
{ parameter:
|
|
3133
|
+
{ parameter: P5.MouthForm, value: 1, blend: "add" },
|
|
2966
3134
|
...browsY(0.25),
|
|
2967
3135
|
cheek(0.4)
|
|
2968
3136
|
]
|
|
@@ -2971,10 +3139,10 @@ var EXPRESSIONS = [
|
|
|
2971
3139
|
id: "laugh",
|
|
2972
3140
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
2973
3141
|
parameters: [
|
|
2974
|
-
{ parameter:
|
|
2975
|
-
{ parameter:
|
|
2976
|
-
{ parameter:
|
|
2977
|
-
{ parameter:
|
|
3142
|
+
{ parameter: P5.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3143
|
+
{ parameter: P5.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3144
|
+
{ parameter: P5.MouthForm, value: 1, blend: "add" },
|
|
3145
|
+
{ parameter: P5.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
2978
3146
|
...browsY(0.4),
|
|
2979
3147
|
cheek(0.65)
|
|
2980
3148
|
]
|
|
@@ -2986,7 +3154,7 @@ var EXPRESSIONS = [
|
|
|
2986
3154
|
parameters: [
|
|
2987
3155
|
...browsY(-0.5),
|
|
2988
3156
|
...browsTilt(0.8),
|
|
2989
|
-
{ parameter:
|
|
3157
|
+
{ parameter: P5.MouthForm, value: -0.9, blend: "add" }
|
|
2990
3158
|
]
|
|
2991
3159
|
},
|
|
2992
3160
|
{
|
|
@@ -2996,7 +3164,7 @@ var EXPRESSIONS = [
|
|
|
2996
3164
|
parameters: [
|
|
2997
3165
|
...browsY(0.35),
|
|
2998
3166
|
...browsTilt(-0.8),
|
|
2999
|
-
{ parameter:
|
|
3167
|
+
{ parameter: P5.MouthForm, value: -0.8, blend: "add" }
|
|
3000
3168
|
]
|
|
3001
3169
|
},
|
|
3002
3170
|
{
|
|
@@ -3004,18 +3172,18 @@ var EXPRESSIONS = [
|
|
|
3004
3172
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3005
3173
|
parameters: [
|
|
3006
3174
|
...browsY(0.8),
|
|
3007
|
-
{ parameter:
|
|
3175
|
+
{ parameter: P5.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3008
3176
|
]
|
|
3009
3177
|
},
|
|
3010
3178
|
{
|
|
3011
3179
|
id: "shy",
|
|
3012
3180
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3013
|
-
needs: [
|
|
3181
|
+
needs: [P5.Cheek],
|
|
3014
3182
|
parameters: [
|
|
3015
3183
|
cheek(1),
|
|
3016
3184
|
// Gaze down.
|
|
3017
|
-
{ parameter:
|
|
3018
|
-
{ parameter:
|
|
3185
|
+
{ parameter: P5.EyeballY, value: -0.65, blend: "add" },
|
|
3186
|
+
{ parameter: P5.MouthForm, value: 0.4, blend: "add" },
|
|
3019
3187
|
...browsTilt(-0.25)
|
|
3020
3188
|
]
|
|
3021
3189
|
}
|
|
@@ -3028,14 +3196,14 @@ function defaultExpressions(declared) {
|
|
|
3028
3196
|
parameters: e.parameters.filter((t) => declared.has(t.parameter))
|
|
3029
3197
|
}));
|
|
3030
3198
|
}
|
|
3031
|
-
var
|
|
3199
|
+
var HEAD_MOTIONS = {
|
|
3032
3200
|
Nod: [
|
|
3033
3201
|
{
|
|
3034
3202
|
description: "Nods yes: agreement, acknowledgement, understanding.",
|
|
3035
3203
|
duration: 1,
|
|
3036
3204
|
curves: [
|
|
3037
3205
|
{
|
|
3038
|
-
parameter:
|
|
3206
|
+
parameter: P5.AngleY,
|
|
3039
3207
|
keys: [
|
|
3040
3208
|
[0, 0],
|
|
3041
3209
|
[0.3, -18],
|
|
@@ -3056,7 +3224,7 @@ var DEFAULT_MOTIONS = {
|
|
|
3056
3224
|
fadeIn: 0.15,
|
|
3057
3225
|
curves: [
|
|
3058
3226
|
{
|
|
3059
|
-
parameter:
|
|
3227
|
+
parameter: P5.AngleX,
|
|
3060
3228
|
keys: [
|
|
3061
3229
|
[0, 0],
|
|
3062
3230
|
[0.2, -18],
|
|
@@ -3075,7 +3243,7 @@ var DEFAULT_MOTIONS = {
|
|
|
3075
3243
|
duration: 1.4,
|
|
3076
3244
|
curves: [
|
|
3077
3245
|
{
|
|
3078
|
-
parameter:
|
|
3246
|
+
parameter: P5.AngleZ,
|
|
3079
3247
|
keys: [
|
|
3080
3248
|
[0, 0],
|
|
3081
3249
|
[0.35, 20],
|
|
@@ -3087,6 +3255,55 @@ var DEFAULT_MOTIONS = {
|
|
|
3087
3255
|
}
|
|
3088
3256
|
]
|
|
3089
3257
|
};
|
|
3258
|
+
var WAVE = {
|
|
3259
|
+
Wave: [
|
|
3260
|
+
{
|
|
3261
|
+
description: "Waves hello with the right hand: greeting, goodbye, getting attention.",
|
|
3262
|
+
duration: 2.2,
|
|
3263
|
+
fadeIn: 0.15,
|
|
3264
|
+
fadeOut: 0.15,
|
|
3265
|
+
curves: [
|
|
3266
|
+
{
|
|
3267
|
+
parameter: P5.ArmPoseRight,
|
|
3268
|
+
interpolation: "linear",
|
|
3269
|
+
keys: [
|
|
3270
|
+
[0, 0],
|
|
3271
|
+
[0.15, 1],
|
|
3272
|
+
[2.05, 1],
|
|
3273
|
+
[2.2, 0]
|
|
3274
|
+
]
|
|
3275
|
+
},
|
|
3276
|
+
{
|
|
3277
|
+
parameter: P5.ArmRight,
|
|
3278
|
+
keys: [
|
|
3279
|
+
[0, 0],
|
|
3280
|
+
[0.4, 10],
|
|
3281
|
+
[1.8, 10],
|
|
3282
|
+
[2.2, 0]
|
|
3283
|
+
]
|
|
3284
|
+
},
|
|
3285
|
+
{
|
|
3286
|
+
parameter: P5.ArmPoseAngleRight,
|
|
3287
|
+
keys: [
|
|
3288
|
+
[0, 0],
|
|
3289
|
+
[0.5, 0],
|
|
3290
|
+
[0.7, 12],
|
|
3291
|
+
[1, -12],
|
|
3292
|
+
[1.3, 12],
|
|
3293
|
+
[1.7, 0],
|
|
3294
|
+
[2.2, 0]
|
|
3295
|
+
]
|
|
3296
|
+
}
|
|
3297
|
+
]
|
|
3298
|
+
}
|
|
3299
|
+
]
|
|
3300
|
+
};
|
|
3301
|
+
function defaultMotions(declared) {
|
|
3302
|
+
const waves = [P5.ArmRight, P5.ArmPoseRight, P5.ArmPoseAngleRight].every(
|
|
3303
|
+
(id) => declared.has(id)
|
|
3304
|
+
);
|
|
3305
|
+
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
3306
|
+
}
|
|
3090
3307
|
|
|
3091
3308
|
// src/auto-rig/checks.ts
|
|
3092
3309
|
var finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
@@ -3192,6 +3409,7 @@ function checkLayers(layers, canvas) {
|
|
|
3192
3409
|
throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
|
|
3193
3410
|
}
|
|
3194
3411
|
checkArmsHaveBody(seen);
|
|
3412
|
+
checkPosesHaveArms(seen);
|
|
3195
3413
|
}
|
|
3196
3414
|
function checkHeadEdges(edges, byRole) {
|
|
3197
3415
|
if (edges === void 0) return;
|
|
@@ -3309,7 +3527,7 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
3309
3527
|
}
|
|
3310
3528
|
|
|
3311
3529
|
// src/auto-rig/context.ts
|
|
3312
|
-
import { StandardParameter as
|
|
3530
|
+
import { StandardParameter as P6 } from "@ikijs/format";
|
|
3313
3531
|
|
|
3314
3532
|
// src/auto-rig/head.ts
|
|
3315
3533
|
function outerEdge(runs, axisX, side) {
|
|
@@ -4341,6 +4559,7 @@ function familyField(m, family) {
|
|
|
4341
4559
|
return hairField(m, m.hairBack, NOD.hairBack);
|
|
4342
4560
|
case "body":
|
|
4343
4561
|
case "arm":
|
|
4562
|
+
case "forearm_pose":
|
|
4344
4563
|
return () => [0, 0];
|
|
4345
4564
|
}
|
|
4346
4565
|
}
|
|
@@ -4440,7 +4659,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
4440
4659
|
const family = roleSpec(role).family;
|
|
4441
4660
|
const part = parts.find((p) => p.id === role);
|
|
4442
4661
|
const g = grids.get(family);
|
|
4443
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
4662
|
+
const warp = part?.warps?.find((w) => w.parameter === P6.AngleX);
|
|
4444
4663
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
4445
4664
|
return x + field(role)(x, y, ax, 0)[0];
|
|
4446
4665
|
}
|
|
@@ -5176,6 +5395,7 @@ var WARP_ID = {
|
|
|
5176
5395
|
};
|
|
5177
5396
|
var isGridFamily = (f) => f in WARP_ID;
|
|
5178
5397
|
var isArm = (spec) => spec.family === "arm";
|
|
5398
|
+
var isPose = (spec) => spec.family === "forearm_pose";
|
|
5179
5399
|
var GRID_PAD = 6;
|
|
5180
5400
|
function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
5181
5401
|
checkLayers(layers, canvas);
|
|
@@ -5244,11 +5464,22 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5244
5464
|
const layer = byRole.get(spec.role);
|
|
5245
5465
|
if (layer === void 0) continue;
|
|
5246
5466
|
if (isArm(spec)) {
|
|
5467
|
+
const pose = POSE_OF_ARM[spec.role];
|
|
5247
5468
|
parts.push(
|
|
5248
|
-
...armParts(
|
|
5469
|
+
...armParts(
|
|
5470
|
+
spec.role,
|
|
5471
|
+
layer,
|
|
5472
|
+
arms.get(spec.role),
|
|
5473
|
+
parts.length,
|
|
5474
|
+
has(pose) ? POSE_PARAMS[pose].pose : void 0
|
|
5475
|
+
)
|
|
5249
5476
|
);
|
|
5250
5477
|
continue;
|
|
5251
5478
|
}
|
|
5479
|
+
if (isPose(spec)) {
|
|
5480
|
+
parts.push(forearmPosePart(spec.role, layer, parts.length));
|
|
5481
|
+
continue;
|
|
5482
|
+
}
|
|
5252
5483
|
const built = buildPart(
|
|
5253
5484
|
spec,
|
|
5254
5485
|
boxOfLayer(layer),
|
|
@@ -5301,7 +5532,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5301
5532
|
part.mesh = bodyWarp.mesh;
|
|
5302
5533
|
continue;
|
|
5303
5534
|
}
|
|
5304
|
-
if (family === "arm") continue;
|
|
5535
|
+
if (family === "arm" || family === "forearm_pose") continue;
|
|
5305
5536
|
part.deformer = "headDeformer";
|
|
5306
5537
|
if (part.mesh === void 0) continue;
|
|
5307
5538
|
const own = familyField(turn, family);
|
|
@@ -5318,10 +5549,10 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5318
5549
|
canvas: { width: canvas.width, height: canvas.height },
|
|
5319
5550
|
parameters,
|
|
5320
5551
|
parts,
|
|
5321
|
-
deformers: deformers(frame, grids, bodyWarp?.deformer, arms),
|
|
5552
|
+
deformers: deformers(frame, grids, bodyWarp?.deformer, arms, byRole),
|
|
5322
5553
|
...has("hair_front") ? { physics: hairPhysics() } : {},
|
|
5323
5554
|
expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
|
|
5324
|
-
motions:
|
|
5555
|
+
motions: defaultMotions(new Set(parameters.map((p) => p.id)))
|
|
5325
5556
|
};
|
|
5326
5557
|
const valid = parseIkiModel3(model);
|
|
5327
5558
|
if (hasNose && options.onTurnSolved) {
|
|
@@ -5338,14 +5569,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5338
5569
|
function turnWarps(mesh, b, fieldOf) {
|
|
5339
5570
|
return [
|
|
5340
5571
|
localWarp(
|
|
5341
|
-
|
|
5572
|
+
P7.AngleX,
|
|
5342
5573
|
mesh,
|
|
5343
5574
|
b,
|
|
5344
5575
|
X_STOPS,
|
|
5345
5576
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
5346
5577
|
),
|
|
5347
5578
|
localWarp(
|
|
5348
|
-
|
|
5579
|
+
P7.AngleY,
|
|
5349
5580
|
mesh,
|
|
5350
5581
|
b,
|
|
5351
5582
|
Y_STOPS,
|
|
@@ -5393,7 +5624,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5393
5624
|
}
|
|
5394
5625
|
if (mesh !== void 0) part.mesh = mesh;
|
|
5395
5626
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
5396
|
-
const eyeOpen = side === "L" ?
|
|
5627
|
+
const eyeOpen = side === "L" ? P7.EyeOpenLeft : P7.EyeOpenRight;
|
|
5397
5628
|
const hh = frame.hh;
|
|
5398
5629
|
switch (spec.role) {
|
|
5399
5630
|
case "face":
|
|
@@ -5408,7 +5639,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5408
5639
|
headOwnRoll(byRole.has("body"))
|
|
5409
5640
|
),
|
|
5410
5641
|
localWarp(
|
|
5411
|
-
|
|
5642
|
+
P7.Breath,
|
|
5412
5643
|
mesh,
|
|
5413
5644
|
b,
|
|
5414
5645
|
[0, 1],
|
|
@@ -5455,8 +5686,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5455
5686
|
if (byRole.has("mouth_open")) {
|
|
5456
5687
|
warps.push(mouthWiden(mesh, b));
|
|
5457
5688
|
bindings.push(
|
|
5458
|
-
{ parameter:
|
|
5459
|
-
{ parameter:
|
|
5689
|
+
{ parameter: P7.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
5690
|
+
{ parameter: P7.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
5460
5691
|
);
|
|
5461
5692
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5462
5693
|
} else {
|
|
@@ -5466,19 +5697,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5466
5697
|
);
|
|
5467
5698
|
bindings.push(
|
|
5468
5699
|
{
|
|
5469
|
-
parameter:
|
|
5700
|
+
parameter: P7.MouthOpen,
|
|
5470
5701
|
channel: "scaleY",
|
|
5471
5702
|
from: 0,
|
|
5472
5703
|
to: roundTo(grow, 0.01)
|
|
5473
5704
|
},
|
|
5474
5705
|
{
|
|
5475
|
-
parameter:
|
|
5706
|
+
parameter: P7.MouthOpen,
|
|
5476
5707
|
channel: "scaleX",
|
|
5477
5708
|
from: 0,
|
|
5478
5709
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
5479
5710
|
},
|
|
5480
5711
|
{
|
|
5481
|
-
parameter:
|
|
5712
|
+
parameter: P7.MouthOpen,
|
|
5482
5713
|
channel: "translateY",
|
|
5483
5714
|
from: 0,
|
|
5484
5715
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -5490,7 +5721,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5490
5721
|
case "mouth_open":
|
|
5491
5722
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
5492
5723
|
bindings.push({
|
|
5493
|
-
parameter:
|
|
5724
|
+
parameter: P7.MouthOpen,
|
|
5494
5725
|
channel: "opacity",
|
|
5495
5726
|
from: 0,
|
|
5496
5727
|
to: 1
|
|
@@ -5500,7 +5731,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5500
5731
|
case "blush_L":
|
|
5501
5732
|
case "blush_R":
|
|
5502
5733
|
bindings.push({
|
|
5503
|
-
parameter:
|
|
5734
|
+
parameter: P7.Cheek,
|
|
5504
5735
|
channel: "opacity",
|
|
5505
5736
|
from: AMPLITUDE.blushRest,
|
|
5506
5737
|
to: 1
|
|
@@ -5511,8 +5742,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5511
5742
|
if (byRole.has("hair_front")) {
|
|
5512
5743
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
5513
5744
|
warps.push(
|
|
5514
|
-
hairSway(
|
|
5515
|
-
hairSway(
|
|
5745
|
+
hairSway(P7.HairSwayX, mesh, b, amp),
|
|
5746
|
+
hairSway(P7.HairSwayZ, mesh, b, amp)
|
|
5516
5747
|
);
|
|
5517
5748
|
}
|
|
5518
5749
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -5585,10 +5816,14 @@ function meshScale(role, frame) {
|
|
|
5585
5816
|
return Infinity;
|
|
5586
5817
|
}
|
|
5587
5818
|
}
|
|
5588
|
-
function deformers(frame, grids, bodyWarp, arms) {
|
|
5819
|
+
function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
5589
5820
|
const out = [];
|
|
5590
5821
|
if (bodyWarp !== void 0) out.push(bodyWarp);
|
|
5591
5822
|
for (const [role, g] of arms) out.push(...armDeformers(role, g));
|
|
5823
|
+
for (const [role, arm] of arms) {
|
|
5824
|
+
const pose = POSE_OF_ARM[role];
|
|
5825
|
+
if (byRole.has(pose)) out.push(forearmPoseDeformer(pose, arm));
|
|
5826
|
+
}
|
|
5592
5827
|
const hasBody = bodyWarp !== void 0;
|
|
5593
5828
|
const roll = headOwnRoll(hasBody);
|
|
5594
5829
|
out.push({
|
|
@@ -5597,9 +5832,9 @@ function deformers(frame, grids, bodyWarp, arms) {
|
|
|
5597
5832
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
5598
5833
|
bindings: [
|
|
5599
5834
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
5600
|
-
{ parameter:
|
|
5835
|
+
{ parameter: P7.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
5601
5836
|
{
|
|
5602
|
-
parameter:
|
|
5837
|
+
parameter: P7.Breath,
|
|
5603
5838
|
channel: "translateY",
|
|
5604
5839
|
from: 0,
|
|
5605
5840
|
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
@@ -5617,8 +5852,8 @@ function deformers(frame, grids, bodyWarp, arms) {
|
|
|
5617
5852
|
points: g.lattice.points
|
|
5618
5853
|
},
|
|
5619
5854
|
warp2d: {
|
|
5620
|
-
parameter:
|
|
5621
|
-
parameterY:
|
|
5855
|
+
parameter: P7.AngleX,
|
|
5856
|
+
parameterY: P7.AngleY,
|
|
5622
5857
|
valuesX: X_STOPS,
|
|
5623
5858
|
valuesY: Y_STOPS,
|
|
5624
5859
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -5631,16 +5866,16 @@ function hairPhysics() {
|
|
|
5631
5866
|
return [
|
|
5632
5867
|
{
|
|
5633
5868
|
id: "hairSway",
|
|
5634
|
-
input: { parameter:
|
|
5635
|
-
output: { parameter:
|
|
5869
|
+
input: { parameter: P7.AngleX, weight: 1 },
|
|
5870
|
+
output: { parameter: P7.HairSwayX, scale: 5 },
|
|
5636
5871
|
mass: 1,
|
|
5637
5872
|
stiffness: 30,
|
|
5638
5873
|
damping: 3
|
|
5639
5874
|
},
|
|
5640
5875
|
{
|
|
5641
5876
|
id: "hairTilt",
|
|
5642
|
-
input: { parameter:
|
|
5643
|
-
output: { parameter:
|
|
5877
|
+
input: { parameter: P7.AngleZ, weight: 1 },
|
|
5878
|
+
output: { parameter: P7.HairSwayZ, scale: 8 },
|
|
5644
5879
|
mass: 1,
|
|
5645
5880
|
stiffness: 25,
|
|
5646
5881
|
damping: 2.5
|
|
@@ -5650,46 +5885,55 @@ function hairPhysics() {
|
|
|
5650
5885
|
function declareParameters(roles) {
|
|
5651
5886
|
const out = [];
|
|
5652
5887
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
5653
|
-
add(
|
|
5654
|
-
add(
|
|
5655
|
-
add(
|
|
5888
|
+
add(P7.AngleX, "Head Angle", -30, 30, 0);
|
|
5889
|
+
add(P7.AngleY, "Head Angle Y", -30, 30, 0);
|
|
5890
|
+
add(P7.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
5656
5891
|
if (roles.has("body")) {
|
|
5657
|
-
add(
|
|
5658
|
-
add(
|
|
5659
|
-
add(
|
|
5892
|
+
add(P7.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
5893
|
+
add(P7.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
5894
|
+
add(P7.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
5660
5895
|
}
|
|
5896
|
+
const addPose = (side, pose, angle) => {
|
|
5897
|
+
add(pose, `Arm Pose ${side}`, 0, 1, 0);
|
|
5898
|
+
add(angle, `Arm Pose Angle ${side}`, ...POSE_ANGLE_RANGE, 0);
|
|
5899
|
+
};
|
|
5661
5900
|
const [armMin, armMax] = ARM_RANGE;
|
|
5901
|
+
const [elbowMin, elbowMax] = ELBOW_RANGE;
|
|
5662
5902
|
if (roles.has("arm_L")) {
|
|
5663
|
-
add(
|
|
5664
|
-
add(
|
|
5903
|
+
add(P7.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
5904
|
+
add(P7.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
|
|
5905
|
+
if (roles.has("forearm_pose_L"))
|
|
5906
|
+
addPose("L", P7.ArmPoseLeft, P7.ArmPoseAngleLeft);
|
|
5665
5907
|
}
|
|
5666
5908
|
if (roles.has("arm_R")) {
|
|
5667
|
-
add(
|
|
5668
|
-
add(
|
|
5909
|
+
add(P7.ArmRight, "Arm R", armMin, armMax, 0);
|
|
5910
|
+
add(P7.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
|
|
5911
|
+
if (roles.has("forearm_pose_R"))
|
|
5912
|
+
addPose("R", P7.ArmPoseRight, P7.ArmPoseAngleRight);
|
|
5669
5913
|
}
|
|
5670
|
-
add(
|
|
5671
|
-
add(
|
|
5914
|
+
add(P7.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
5915
|
+
add(P7.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
5672
5916
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
5673
|
-
add(
|
|
5674
|
-
add(
|
|
5917
|
+
add(P7.EyeballX, "Gaze X", -1, 1, 0);
|
|
5918
|
+
add(P7.EyeballY, "Gaze Y", -1, 1, 0);
|
|
5675
5919
|
}
|
|
5676
|
-
add(
|
|
5677
|
-
add(
|
|
5920
|
+
add(P7.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
5921
|
+
add(P7.MouthForm, "Mouth Form", -1, 1, 0);
|
|
5678
5922
|
if (roles.has("brow_L")) {
|
|
5679
|
-
add(
|
|
5680
|
-
add(
|
|
5923
|
+
add(P7.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
5924
|
+
add(P7.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
5681
5925
|
}
|
|
5682
5926
|
if (roles.has("brow_R")) {
|
|
5683
|
-
add(
|
|
5684
|
-
add(
|
|
5927
|
+
add(P7.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
5928
|
+
add(P7.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
5685
5929
|
}
|
|
5686
5930
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
5687
|
-
add(
|
|
5931
|
+
add(P7.Cheek, "Cheek", 0, 1, 0);
|
|
5688
5932
|
}
|
|
5689
|
-
add(
|
|
5933
|
+
add(P7.Breath, "Breath", 0, 1, 0);
|
|
5690
5934
|
if (roles.has("hair_front")) {
|
|
5691
|
-
add(
|
|
5692
|
-
add(
|
|
5935
|
+
add(P7.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
5936
|
+
add(P7.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
5693
5937
|
}
|
|
5694
5938
|
return out;
|
|
5695
5939
|
}
|
|
@@ -5736,6 +5980,7 @@ export {
|
|
|
5736
5980
|
createLayerSetMeasurer,
|
|
5737
5981
|
denseCoreOf,
|
|
5738
5982
|
detectAlphaBbox,
|
|
5983
|
+
forearmPoseGeometry,
|
|
5739
5984
|
foregroundSpan,
|
|
5740
5985
|
generateIkiFromLayerSet,
|
|
5741
5986
|
headHalfOf,
|