From bc2821adb11a30244fc4663f4fafb756877d9508 Mon Sep 17 00:00:00 2001 From: Yasutake Yohei <61961825+yasutakeyohei@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:09:40 +0900 Subject: bluebey-studio: public/ の外へ移動し非公開化 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/bluebey-studio/src/clip.js | 202 -------------------------------------- 1 file changed, 202 deletions(-) delete mode 100644 public/bluebey-studio/src/clip.js (limited to 'public/bluebey-studio/src/clip.js') diff --git a/public/bluebey-studio/src/clip.js b/public/bluebey-studio/src/clip.js deleted file mode 100644 index 4fdcfd9..0000000 --- a/public/bluebey-studio/src/clip.js +++ /dev/null @@ -1,202 +0,0 @@ -import * as THREE from 'three'; - -/** - * 見えない壁 (the invisible wall): a clipping plane that hides whatever falls - * behind it, so the character can be buried in the wall and only the rest of the - * body shows. - * - * The wall is a *finite* rectangle: an invisible quad that writes depth and no - * colour, drawn before everything else in the opaque pass, so the character - * behind it is culled by the depth test. That is what a real wall does, and it - * is the only way to bound the effect - a `renderer.clippingPlanes` entry is an - * infinite half-space, so it can never be limited to a rectangle. - * - * Nothing needs to agree per-material: the depth buffer does the work, so the - * body, the outline hulls, the face plates, the ink pass and the offscreen - * renders an export uses are all hidden by the same wall. - * - * `wallPlane` is pure (no three.js maths), so the geometry can be unit-tested - * without a renderer. - */ - -const DEG = Math.PI / 180; - -/** - * A hair toward the kept side. The guide sits exactly on the cut, and a plane - * on its own boundary is half inside the discarded half - nudging it along the - * normal keeps all of it on the visible side. - */ -const GUIDE_EPSILON = 0.002; - -/** - * The plane a wall setting lies in. - * - * The wall is a *finite* quad, so this no longer decides which half of space is - * hidden - the quad's own depth does that. What `apply` needs from here is the - * normal, which is the direction the quad faces (and so the direction the wall - * lies along), plus the constant of the plane through the point it sits on. - * - * By default the wall lies in the XY plane through `(x, y, z)`; `yaw` turns it - * about Y and `tilt` leans it about X afterwards. - * - * @param {{x?:number,y?:number,z?:number,yaw?:number,tilt?:number}} [wall] - * @returns {{normal:[number,number,number], constant:number}} - */ -export function wallPlane(wall = {}) { - const yaw = (wall.yaw ?? 0) * DEG; - const tilt = (wall.tilt ?? 0) * DEG; - const cosYaw = Math.cos(yaw); - const sinYaw = Math.sin(yaw); - const cosTilt = Math.cos(tilt); - const sinTilt = Math.sin(tilt); - - // Start from +Z, turn about Y, then lean about the (already turned) X axis. - let nx = sinYaw; - let ny = -cosYaw * sinTilt; - let nz = cosYaw * cosTilt; - - const length = Math.hypot(nx, ny, nz) || 1; - nx /= length; - ny /= length; - nz /= length; - - // Constant so the plane passes through (x, y, z): dot(n, p) + c = 0. - const constant = -(nx * (wall.x ?? 0) + ny * (wall.y ?? 0) + nz * (wall.z ?? 0)); - return { normal: [nx, ny, nz], constant }; -} - -/** The faint plane shown while placing the wall. Hidden from every export. */ -/** - * The wall itself: an invisible, *finite* quad that writes depth but no colour. - * - * It is drawn before everything else in the opaque pass (`renderOrder`), so the - * character behind it is culled by the depth test. That is what a real wall does, - * and - unlike a clip plane, which is an infinite half-space - it only hides what - * the rectangle actually covers. So the size sliders are the wall's real size. - */ -function makeWall() { - const material = new THREE.MeshBasicMaterial({ - colorWrite: false, - side: THREE.DoubleSide, - toneMapped: false, - }); - const mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material); - mesh.name = 'wall'; - // Before everything, including the silhouette-only hulls (renderOrder -1), so - // its depth hides a nose buried in the wall (see styles.js hullMaterialFor). - mesh.renderOrder = -2; - mesh.visible = false; - mesh.castShadow = false; - mesh.receiveShadow = false; - return mesh; -} - -function makeGuide() { - const geometry = new THREE.PlaneGeometry(1, 1); - const material = new THREE.MeshBasicMaterial({ - color: 0x8a4fe0, - transparent: true, - opacity: 0.16, - side: THREE.DoubleSide, - depthWrite: false, - toneMapped: false, - }); - const mesh = new THREE.Mesh(geometry, material); - mesh.name = 'wall-guide'; - mesh.userData.isHelper = true; - mesh.visible = false; - - // An edge, so the plane's extent is legible even where the fill is faint. - const edge = new THREE.LineSegments( - new THREE.EdgesGeometry(geometry), - new THREE.LineBasicMaterial({ color: 0x8a4fe0, transparent: true, opacity: 0.55, toneMapped: false }), - ); - edge.userData.isHelper = true; - mesh.add(edge); - return mesh; -} - -export function createClipper({ scene, renderer, model }) { - // A little larger than the character: big enough to read as a wall, small - // enough not to cover the whole viewport. - const guideSpan = Math.max(model.size.x, model.size.y, model.size.z) * 1.5; - const FROM = new THREE.Vector3(0, 0, 1); // the plane the quad starts in - - function makeSlot() { - const occluder = makeWall(); - const guide = makeGuide(); - scene.add(guide); - scene.add(occluder); - return { occluder, guide, normal: new THREE.Vector3(0, 0, 1) }; - } - // Two walls. Each slot keeps its own rectangle, so the two are independent. - const slots = [makeSlot(), makeSlot()]; - - /** - * @param {Array} walls up to two `state.render.wall` settings, in slot - * order. - * @param {{x?:number,y?:number,z?:number}} [offset] the character's own - * translation. A wall's position is stored *relative to the character*, so - * adding this makes the walls travel with the body when it is moved. - * @param {number} [yaw] the character's own turn about Y, in radians. The stored - * position is rotated by it (and added to the wall's own `yaw`), so a wall - * stays glued to the body when the character is turned. - * - * The guide is drawn only when `wall.guide` is on, which is also when the wall - * can be grabbed in the viewport. Off, the wall is invisible: the cut still - * applies, so you can see the character half-hidden with nothing in the way. - */ - function apply(walls, offset, yaw = 0) { - const list = Array.isArray(walls) ? walls : [walls]; - const ox = offset?.x ?? 0; - const oy = offset?.y ?? 0; - const oz = offset?.z ?? 0; - const cos = Math.cos(yaw); - const sin = Math.sin(yaw); - const yawDeg = (yaw * 180) / Math.PI; - slots.forEach((slot, index) => { - const wall = list[index]; - if (!wall || wall.on !== true) { - slot.occluder.visible = false; - slot.guide.visible = false; - return; - } - // The stored position is relative to the character, so it turns with the - // body: rotate (x, z) about Y by the character's yaw, then add the offset. - const lx = wall.x ?? 0; - const lz = wall.z ?? 0; - const x = cos * lx + sin * lz + ox; - const y = (wall.y ?? 0) + oy; - const z = -sin * lx + cos * lz + oz; - const { normal } = wallPlane({ yaw: (wall.yaw ?? 0) + yawDeg, tilt: wall.tilt, x, y, z }); - slot.normal.set(normal[0], normal[1], normal[2]); - const span = guideSpan * Math.max(0.05, wall.size ?? 1); - // The wall and its guide are the same rectangle, turned to face along the - // plane's normal: the wall is the occluder, the guide is the tinted copy - // that is shown only while placing. - for (const mesh of [slot.occluder, slot.guide]) { - mesh.quaternion.setFromUnitVectors(FROM, slot.normal); - mesh.scale.setScalar(span); - } - slot.occluder.position.set(x, y, z); - slot.occluder.visible = true; - slot.guide.position.set(x, y, z).addScaledVector(slot.normal, GUIDE_EPSILON); - slot.guide.visible = wall.guide === true; - }); - } - - return { - guides: slots.map((slot) => slot.guide), - occluders: slots.map((slot) => slot.occluder), - apply, - dispose() { - for (const slot of slots) { - for (const mesh of [slot.guide, slot.occluder]) { - mesh.geometry.dispose(); - mesh.material.dispose(); - mesh.removeFromParent(); - } - } - }, - }; -} -- cgit v1.3.1