aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/bluebey-studio/src/main.js
diff options
context:
space:
mode:
authorYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-05 14:42:08 +0900
committerYasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com>2026-10-05 14:42:08 +0900
commit1bf7ee115176bc366315a90ab31740e6cd968744 (patch)
tree752b2db6126700c920f7e8b98bc167c95035341a /public/bluebey-studio/src/main.js
parent88010ce24f3a863518071c4bd5dd5d351d1bad26 (diff)
bluebey-studio: ハートの白目内クリップ・まつげ追従・まばたき修正、帽子の線画と位置、手描き目UI削除
指定場所を見るときもハートが白目内に収まるようクリップ。まつげを上まぶたに連動。閉じ目はまばたきせず、ハート選択後のまばたきで3の目/わらうが一瞬出る不具合を修正。帽子をランナンスタイル登録して線画対応、横・奥行き追加。手描きの目UIを削除。
Diffstat (limited to 'public/bluebey-studio/src/main.js')
-rw-r--r--public/bluebey-studio/src/main.js32
1 files changed, 27 insertions, 5 deletions
diff --git a/public/bluebey-studio/src/main.js b/public/bluebey-studio/src/main.js
index 7fdc97f..849d3d1 100644
--- a/public/bluebey-studio/src/main.js
+++ b/public/bluebey-studio/src/main.js
@@ -382,14 +382,18 @@ async function init() {
let hatObject = null;
const hatMeshes = [];
/** Last transform applied, so the render loop only wakes when it changes. */
- const hatXform = { height: null, tiltX: null, tiltZ: null };
+ const hatXform = { x: null, z: null, height: null, tiltX: null, tiltZ: null };
/** Set once the outline pass exists, so a hat change can refresh the exclude set. */
let refreshOutlineExclusion = null;
+ /** Set once the style system exists, so a hat can register for styles + outline. */
+ let hatStyleRegister = null;
+ let hatStyleUnregister = null;
function applyHat(kind) {
if (!hatMount) return;
if (kind !== hatKindCurrent) {
hatKindCurrent = kind;
if (hatObject) {
+ hatObject.traverse((o) => { if (o.isMesh) hatStyleUnregister?.(o); });
hatMount.remove(hatObject);
hatObject.traverse((o) => {
o.geometry?.dispose();
@@ -403,7 +407,11 @@ async function init() {
if (built) {
hatMount.add(built);
hatObject = built;
- built.traverse((o) => { if (o.isMesh) hatMeshes.push(o); });
+ built.traverse((o) => {
+ if (!o.isMesh) return;
+ hatMeshes.push(o);
+ hatStyleRegister?.(o);
+ });
}
refreshOutlineExclusion?.();
}
@@ -412,14 +420,18 @@ async function init() {
// character's front and +x its right: a plain translation and rotation read
// the way the sliders say.
const hat = state.face.hat ?? {};
+ const x = hat.x ?? 0;
+ const z = hat.z ?? 0;
const height = hat.height ?? 0;
const tiltX = hat.tiltX ?? 0;
const tiltZ = hat.tiltZ ?? 0;
if (hatObject) {
- hatObject.position.y = height;
+ hatObject.position.set(x, height, z);
hatObject.rotation.set((tiltX * Math.PI) / 180, 0, (tiltZ * Math.PI) / 180);
}
- if (height !== hatXform.height || tiltX !== hatXform.tiltX || tiltZ !== hatXform.tiltZ) {
+ if (x !== hatXform.x || z !== hatXform.z || height !== hatXform.height || tiltX !== hatXform.tiltX || tiltZ !== hatXform.tiltZ) {
+ hatXform.x = x;
+ hatXform.z = z;
hatXform.height = height;
hatXform.tiltX = tiltX;
hatXform.tiltZ = tiltZ;
@@ -454,6 +466,12 @@ async function init() {
// ------------------------------------------------------------- controllers
const styles = new Styles({ meshes: model.parts.body });
+ // Hats are built at runtime, so they are not in the GLB's mesh list. Register
+ // them with the style system (and the current hat) so they follow flat/line art
+ // and get an outline hull like every other part.
+ hatStyleRegister = (mesh) => styles.addMesh(mesh);
+ hatStyleUnregister = (mesh) => styles.removeMesh(mesh);
+ for (const mesh of hatMeshes) styles.addMesh(mesh);
const face = new Face({
eyeMesh: model.parts.eyeMesh,
mouthMesh: model.parts.mouthMesh,
@@ -1626,11 +1644,15 @@ async function init() {
const eyeFor = (key) => {
const own = eyes[key];
+ // A shut artwork (1〜3線 / 3の目 / わらう) does not blink: its lid stays where
+ // the user put it, so the blink animation must not scale its `open`.
+ const shutArt = own.shape === 'three' || own.shape === 'arch'
+ || (typeof own.shape === 'string' && own.shape.startsWith('line'));
return {
// Which artwork this eye shows (ふつう / 1〜3線 / 3の目 / わらう / ハート).
// The renderer chooses the shut artwork from this, so it must travel.
shape: own.shape,
- open: clamp01(own.open * openScale),
+ open: clamp01(own.open * (shutArt ? 1 : openScale)),
lookX: clamp(own.lookX + drift.x + aimOffset.x, -1, 1),
lookY: clamp(own.lookY + drift.y + aimOffset.y, -1, 1),
closed: own.closed,