aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/bluebey-studio
diff options
context:
space:
mode:
Diffstat (limited to 'public/bluebey-studio')
-rw-r--r--public/bluebey-studio/src/main.js4
-rw-r--r--public/bluebey-studio/src/panel.js89
2 files changed, 45 insertions, 48 deletions
diff --git a/public/bluebey-studio/src/main.js b/public/bluebey-studio/src/main.js
index d4866c7..e282e9d 100644
--- a/public/bluebey-studio/src/main.js
+++ b/public/bluebey-studio/src/main.js
@@ -1584,6 +1584,10 @@ async function init() {
closedLines: own.closedLines,
irisShape: own.irisShape,
threeFlip: own.threeFlip === true,
+ // 白目 / 光彩 are per eye and tri-state: `null` means "the usual" (open
+ // eyes show their white; the shared 光彩 switch decides the glint).
+ white: own.white ?? null,
+ highlight: own.highlight ?? null,
// The tears are per eye, so one eye can cry on its own.
tear: own.tear ?? 0,
tearY: own.tearY ?? 0,
diff --git a/public/bluebey-studio/src/panel.js b/public/bluebey-studio/src/panel.js
index 6cf8c40..e6f6446 100644
--- a/public/bluebey-studio/src/panel.js
+++ b/public/bluebey-studio/src/panel.js
@@ -578,6 +578,34 @@ export function buildPanel(app, root) {
center: 'eye',
onChange: ({ x, y }) => { write((e) => { e.lookX = x; e.lookY = y; }); touched(); },
});
+ // 涙: per eye. Linked, one set drives both - size and height match, while the
+ // sideways position and the tilt mirror so the pair stays symmetric.
+ const writeTear = (patch) => {
+ Object.assign(state.face.eyes[primary], patch);
+ if (mode === 'both') {
+ const mirrored = { ...patch };
+ if (patch.tearX != null) mirrored.tearX = -patch.tearX;
+ if (patch.tearTilt != null) mirrored.tearTilt = -patch.tearTilt;
+ Object.assign(state.face.eyes[primary === 'left' ? 'right' : 'left'], mirrored);
+ }
+ touched();
+ };
+ const tear = slider({
+ label: '涙', min: 0, max: 1.6, step: 0.01, value: 0, format: (v) => v.toFixed(2),
+ onInput: (v) => writeTear({ tear: v }),
+ });
+ const tearY = slider({
+ label: '涙の高さ', min: -80, max: 140, step: 1, value: 0, format: (v) => `${Math.round(v)}`,
+ onInput: (v) => writeTear({ tearY: v }),
+ });
+ const tearX = slider({
+ label: '涙のよこ位置', min: -150, max: 150, step: 1, value: 0, format: (v) => `${Math.round(v)}`,
+ onInput: (v) => writeTear({ tearX: v }),
+ });
+ const tearTilt = slider({
+ label: '涙の傾き', min: -60, max: 60, step: 1, value: 0, format: (v) => `${Math.round(v)}°`,
+ onInput: (v) => writeTear({ tearTilt: v }),
+ });
group.add(open);
group.add(shape);
@@ -588,6 +616,10 @@ export function buildPanel(app, root) {
group.add(eyeX);
group.add(controlRow('目線', h('div', { class: 'pad-wrap' }, pad.el,
h('p', { class: 'hint', text: 'ドラッグで目線。ダブルクリックで正面に戻ります。' }))));
+ group.add(tear);
+ group.add(tearY);
+ group.add(tearX);
+ group.add(tearTilt);
const syncOne = () => {
const e = state.face.eyes[primary];
@@ -602,6 +634,10 @@ export function buildPanel(app, root) {
highlight.set(eyeHighlightOf(e));
eyeX.set(e.eyeX ?? 0);
pad.set({ x: e.lookX ?? 0, y: e.lookY ?? 0 });
+ tear.set(e.tear ?? 0);
+ tearY.set(e.tearY ?? 0);
+ tearX.set(e.tearX ?? 0);
+ tearTilt.set(e.tearTilt ?? 0);
};
return { group, sync: syncOne };
};
@@ -615,53 +651,16 @@ export function buildPanel(app, root) {
faceSection.add(leftControls.group);
faceSection.add(rightControls.group);
- // 下まぶた・涙・目線スライダー (per eye) live in こまかい設定; the tears mirror
- // onto the other eye while the two are linked.
+ // 下まぶた (per eye) lives in こまかい設定.
const eyeAdvancedWidgets = {};
for (const key of ['left', 'right']) {
const label = key === 'left' ? '左の目' : '右の目';
- const otherKey = key === 'left' ? 'right' : 'left';
- const setTear = (patch) => {
- Object.assign(state.face.eyes[key], patch);
- if (state.face.eyes.linked) {
- const mirrored = { ...patch };
- if (patch.tearX != null) mirrored.tearX = -patch.tearX;
- if (patch.tearTilt != null) mirrored.tearTilt = -patch.tearTilt;
- Object.assign(state.face.eyes[otherKey], mirrored);
- }
- app.actions.refresh('face');
- sync();
- };
- const tear = slider({
- label: `${label}の涙`, min: 0, max: 1.6, step: 0.01, value: 0, format: (v) => v.toFixed(2),
- onInput: (value) => setTear({ tear: value }),
- });
- const tearY = slider({
- label: `${label}の涙の高さ`, min: -80, max: 140, step: 1, value: 0, format: (v) => `${Math.round(v)}`,
- onInput: (value) => setTear({ tearY: value }),
- });
- const tearX = slider({
- label: `${label}の涙のよこ位置`, min: -150, max: 150, step: 1, value: 0, format: (v) => `${Math.round(v)}`,
- onInput: (value) => setTear({ tearX: value }),
- });
- const tearTilt = slider({
- label: `${label}の涙の傾き`, min: -60, max: 60, step: 1, value: 0, format: (v) => `${Math.round(v)}°`,
- onInput: (value) => setTear({ tearTilt: value }),
- });
const lowerLid = slider({
label: `${label}の下まぶた`, min: 0, max: 1, step: 0.01, value: 0, format: (v) => v.toFixed(2),
onInput: (value) => { state.face.eyes[key].lowerLid = value; app.actions.refresh('face'); },
});
- const lookX = slider({
- label: `${label}の目線(よこ)`, min: -1, max: 1, step: 0.01, value: 0, format: (v) => v.toFixed(2),
- onInput: (value) => { state.face.eyes[key].lookX = value; app.actions.refresh('face'); },
- });
- const lookY = slider({
- label: `${label}の目線(たて)`, min: -1, max: 1, step: 0.01, value: 0, format: (v) => v.toFixed(2),
- onInput: (value) => { state.face.eyes[key].lookY = value; app.actions.refresh('face'); },
- });
- eyeAdvancedWidgets[key] = { tear, tearY, tearX, tearTilt, lowerLid, lookX, lookY };
- tearWidgets.push(lowerLid.el, lookX.el, lookY.el, tear.el, tearY.el, tearX.el, tearTilt.el);
+ eyeAdvancedWidgets[key] = { lowerLid };
+ tearWidgets.push(lowerLid.el);
}
// One button to put every eye setting back to the artwork's defaults. The
@@ -762,8 +761,8 @@ export function buildPanel(app, root) {
const eyeColorBox = details(faceSection.body, '色(白目・瞳・まぶたの線)');
for (const widget of Object.values(eyeColors)) eyeColorBox.add(widget.el);
- // 目線・下まぶた・涙 (all per eye) live here with the other extras.
- const tearBox = details(eyeAdvanced.body, '目線・下まぶた・涙(左右べつ)');
+ // 下まぶた (per eye) lives here with the other extras.
+ const tearBox = details(eyeAdvanced.body, '下まぶた(左右べつ)');
for (const el of tearWidgets) tearBox.add(el);
const eyeSource = selectField({
@@ -2748,13 +2747,7 @@ export function buildPanel(app, root) {
for (const key of ['left', 'right']) {
const eye = state.face.eyes[key];
const w = eyeAdvancedWidgets[key];
- w.tear.set(eye.tear ?? 0);
- w.tearY.set(eye.tearY ?? 0);
- w.tearX.set(eye.tearX ?? 0);
- w.tearTilt.set(eye.tearTilt ?? 0);
w.lowerLid.set(eye.lowerLid ?? state.face.eyes.lowerLid ?? 0);
- w.lookX.set(eye.lookX ?? 0);
- w.lookY.set(eye.lookY ?? 0);
}
irisScaleSlider.set(state.face.eyes.irisScale);