From c6d1df09c66bcfe8388651866d26ed5d05b4d2d2 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Sat, 3 Oct 2026 14:56:41 +0900 Subject: public/bluebey-studio: 鼻ちょうちんの追従とひげを修正 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 鼻ちょうちんの位置が、カメラ移動やポーズ変更のあとに飛ぶ問題を修正。位置はボーン局所座標で持ち、スライダーのオフセットは体の向きに追従させた。寝ている動きでは鼻ちょうちんを自動で表示する。 ひげは「猫」に左右の間隔スライダーを追加し、太さを変えずに大きくできるようにした。カイゼルは先をダリ風のカールにした。 --- public/bluebey-studio/src/main.js | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'public/bluebey-studio/src/main.js') diff --git a/public/bluebey-studio/src/main.js b/public/bluebey-studio/src/main.js index 1f6ad6e..c7e6501 100644 --- a/public/bluebey-studio/src/main.js +++ b/public/bluebey-studio/src/main.js @@ -275,13 +275,15 @@ async function init() { if (!group) return; group.visible = snot?.enabled === true; if (!group.visible) return; + // The offsets are applied in the *bone's* frame, along the world axes measured + // once at rest, so the bubble travels with the body. Recomputing them against + // the live world matrix made it snap back whenever the body had moved. const k = 0.01; // artwork px -> world units, enough to nudge the bubble - const target = group.userData.baseWorld.clone().add(new THREE.Vector3( - (snot.offsetX ?? 0) * k, - (snot.offsetY ?? 0) * k, - (snot.offsetZ ?? 0) * k, - )); - group.position.copy(group.userData.bone.worldToLocal(target)); + const { localBase, axes } = group.userData; + group.position.copy(localBase) + .addScaledVector(axes.x, (snot.offsetX ?? 0) * k) + .addScaledVector(axes.y, (snot.offsetY ?? 0) * k) + .addScaledVector(axes.z, (snot.offsetZ ?? 0) * k); group.userData.baseScale = Number.isFinite(snot.size) ? snot.size : 1; group.scale.setScalar(group.userData.baseScale); group.userData.material.color.set(snot.color ?? '#dfe8ff'); @@ -326,8 +328,20 @@ async function init() { group.add(bubble); const bone = master.bone; const baseWorld = centre.clone().add(new THREE.Vector3(radius * 1.9, -radius * 0.85, radius * 0.7)); - group.position.copy(bone.worldToLocal(baseWorld.clone())); - group.userData = { bubble, material, bone, baseWorld, baseScale: 1 }; + const localBase = bone.worldToLocal(baseWorld.clone()); + // World-axis directions expressed in the bone's frame, captured at rest: the + // sliders keep meaning "right / up / towards the viewer" while the bubble rides + // with the body. + const boneQuat = new THREE.Quaternion(); + bone.getWorldQuaternion(boneQuat); + const invQuat = boneQuat.clone().invert(); + const axes = { + x: new THREE.Vector3(1, 0, 0).applyQuaternion(invQuat), + y: new THREE.Vector3(0, 1, 0).applyQuaternion(invQuat), + z: new THREE.Vector3(0, 0, 1).applyQuaternion(invQuat), + }; + group.position.copy(localBase); + group.userData = { bubble, material, bone, localBase, axes, baseScale: 1 }; group.visible = false; bone.add(group); return group; -- cgit v1.3.1