diff options
Diffstat (limited to 'bluebey-studio/src/face.js')
| -rw-r--r-- | bluebey-studio/src/face.js | 92 |
1 files changed, 7 insertions, 85 deletions
diff --git a/bluebey-studio/src/face.js b/bluebey-studio/src/face.js index cec7619..f42b823 100644 --- a/bluebey-studio/src/face.js +++ b/bluebey-studio/src/face.js @@ -1,11 +1,5 @@ import * as THREE from 'three'; -import { drawEyes, drawMouth, drawHeadCoverWrap, canvasLayout } from './faceArt.js'; - -// The hair plate's texture is drawn at this multiple of the plate's natural UV -// size. The hairline crosses the head over only a fraction of that height, so at -// 1x its edge comes out visibly jagged; doubling it (with the finer curve -// subdivision in `drawHeadCoverWrap`) keeps the edge smooth. -const HAIR_SCALE = 2; +import { drawEyes, drawMouth, canvasLayout } from './faceArt.js'; /** * Owns the two procedural textures (eyes and mouth) and keeps them in sync with @@ -15,8 +9,8 @@ const HAIR_SCALE = 2; * of the *whole* front half of the body, so their UVs cover far more than the * 0..1 window the drawing lives in. `remapPlate` rewrites each plate's UVs onto * 0..1 and the canvas is enlarged to match, which is what removes the - * clamp-to-edge smear at the border and what gives a moved tear, brow or mouth - * room to move (see `canvasLayout` in src/faceArt.js). + * clamp-to-edge smear at the border and what gives a moved tear or mouth room to + * move (see `canvasLayout` in src/faceArt.js). * * Both parts can independently fall back to the original hand-drawn texture * that ships inside the GLB. @@ -25,16 +19,16 @@ export class Face { constructor({ eyeMesh, mouthMesh, hairMesh = null, originals }) { this.eyeMesh = eyeMesh; this.mouthMesh = mouthMesh; - // 頭の模様 can be carried by a plate that wraps right round the head (the - // `hair-plate`), so hair is visible from behind as well. Older models have no - // such plate and draw the covering on the front one instead. + // The `hair-plate`, if the model has one, is never drawn: ぶるべー has no + // hair. Hiding the mesh outright (rather than showing an empty texture) keeps + // it from tinting the head on platforms where transparent textures do. this.hairMesh = hairMesh; + if (hairMesh) hairMesh.visible = false; // Rewrite each plate's UVs so its own range fills 0..1, and work out the // canvas that keeps the artwork exactly where the 2022 textures put it. this.eyeLayout = remapPlate(eyeMesh); this.mouthLayout = remapPlate(mouthMesh); - this.hairLayout = hairMesh ? remapPlate(hairMesh) : null; // The hand-drawn eye textures bake the face colour into their background, // which is invisible in the lit "real" style but shows up as a flat purple @@ -49,21 +43,13 @@ export class Face { }; this.customNames = new Map(); - // Drawings supplied for a beard kind (assets/beards/<shape>.png). Loaded by the - // app; a kind without one falls back to the built-in drawing (see drawBeard). - this.beardImages = {}; - // Drawings for the brows (assets/brows/<kind>.png), loaded the same way. - this.browImages = {}; this.eyeCanvas = makeCanvas(this.eyeLayout); this.mouthCanvas = makeCanvas(this.mouthLayout); - this.hairCanvas = this.hairLayout ? makeCanvas(this.hairLayout, HAIR_SCALE) : null; this.eyeCtx = this.eyeCanvas.getContext('2d'); this.mouthCtx = this.mouthCanvas.getContext('2d'); - this.hairCtx = this.hairCanvas ? this.hairCanvas.getContext('2d') : null; this.eyeTexture = makeTexture(this.eyeCanvas); this.mouthTexture = makeTexture(this.mouthCanvas); - this.hairTexture = this.hairCanvas ? makeTexture(this.hairCanvas) : null; this.params = null; this.styleMode = 'paint'; @@ -123,10 +109,6 @@ export class Face { this.mouthMesh.material.map = mouthMap; this.mouthMesh.material.needsUpdate = true; } - if (this.hairMesh && this.hairMesh.material.map !== this.hairTexture) { - this.hairMesh.material.map = this.hairTexture; - this.hairMesh.material.needsUpdate = true; - } } /** @@ -155,25 +137,6 @@ export class Face { return 'original'; } - /** - * Set the drawings used for the beards, keyed by beard kind (`scotch`, `kaiser`, - * `apron`, `cat`). Called by the app once the files are loaded; the next redraw - * picks them up. - */ - setBeardImages(map) { - this.beardImages = map ?? {}; - this.dirty = true; - } - - /** - * Set the drawings used for the brows, keyed by brow kind (`gol`). Called by the - * app once the files are loaded; the next redraw picks them up. - */ - setBrowImages(map) { - this.browImages = map ?? {}; - this.dirty = true; - } - /** `kind` is `'eyes'` or `'mouth'`; `source` is `'parametric'` or a variant key. */ setSource(kind, source) { if (kind === 'eyes') { @@ -248,19 +211,6 @@ export class Face { lidShape: p.eyes.lidShape, lidTilt: p.eyes.lidTilt, lashes: p.eyes.lashes, - brow: p.eyes.brow, - // ほっぺ and 頭の模様 are shared by the whole face, so they travel with - // the other shared eye fields (see `drawEyes` in src/faceArt.js). - cheeks: p.eyes.cheeks, - // When the model has a wrapping hair plate the covering is drawn there - // instead (below), so it is switched off on the front plate to keep it - // from being drawn twice. - headMark: this.hairMesh ? { shape: 'off' } : p.eyes.headMark, - // ひげ is shared by the whole face as well. (鼻ちょうちん is a real 3D - // object hung off the nose - see src/main.js - so it is not drawn here.) - beard: p.eyes.beard, - beards: this.beardImages, - brows: this.browImages, // 眼鏡 / サングラス are shared by both eyes and drawn into this same // texture, so they travel with the other shared eye fields. glasses: p.eyes.glasses, @@ -287,34 +237,6 @@ export class Face { this.mouthTexture.needsUpdate = true; } - if (this.hairMesh) { - const hairShape = p.eyes.headMark?.shape ?? 'off'; - // Shapes that draw nothing (the retired front-plate ones, or なし). A fully - // transparent texture still tinted the head on iOS, so the plate is hidden - // outright when there is no hair rather than left showing an empty texture. - const hairShown = !(hairShape === 'off' || hairShape === 'none' - || hairShape === 'split' || hairShape === 'wave' || hairShape === 'side'); - this.hairMesh.visible = hairShown; - if (hairShown) { - // `overEyes` decides the draw order: the hair plate sits in front of the - // eyes (bangs over the face) or behind them. - this.hairMesh.renderOrder = p.eyes.headMark?.overEyes === false ? 0 : 3; - // The colour lives on the material (the texture is a white mask), so it - // follows the same colour management as the body on every platform. - this.hairMesh.material.color.set(p.eyes.headMark?.color ?? '#8a4fe0'); - const layout = this.hairLayout; - const ctx = this.hairCtx; - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.clearRect(0, 0, layout.width * HAIR_SCALE, layout.height * HAIR_SCALE); - ctx.scale(HAIR_SCALE, HAIR_SCALE); - // `drawHeadCoverWrap` works in canvas coordinates (its x axis is the UV - // angle already normalised), so it is not shifted by the window offset. - drawHeadCoverWrap(ctx, p.eyes.headMark ?? {}, layout); - ctx.setTransform(1, 0, 0, 1, 0, 0); - this.hairTexture.needsUpdate = true; - } - } - this.dirty = false; } } |
