diff options
Diffstat (limited to 'public/bluebey-studio/src/face.js')
| -rw-r--r-- | public/bluebey-studio/src/face.js | 35 |
1 files changed, 31 insertions, 4 deletions
diff --git a/public/bluebey-studio/src/face.js b/public/bluebey-studio/src/face.js index 0813c54..de8a31c 100644 --- a/public/bluebey-studio/src/face.js +++ b/public/bluebey-studio/src/face.js @@ -1,5 +1,5 @@ import * as THREE from 'three'; -import { drawEyes, drawMouth, canvasLayout } from './faceArt.js'; +import { drawEyes, drawMouth, drawHeadCoverWrap, canvasLayout } from './faceArt.js'; /** * Owns the two procedural textures (eyes and mouth) and keeps them in sync with @@ -16,14 +16,19 @@ import { drawEyes, drawMouth, canvasLayout } from './faceArt.js'; * that ships inside the GLB. */ export class Face { - constructor({ eyeMesh, mouthMesh, originals }) { + 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. + this.hairMesh = hairMesh; // 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 @@ -43,11 +48,14 @@ export class Face { this.beardImages = {}; this.eyeCanvas = makeCanvas(this.eyeLayout); this.mouthCanvas = makeCanvas(this.mouthLayout); + this.hairCanvas = this.hairLayout ? makeCanvas(this.hairLayout) : 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'; @@ -64,7 +72,8 @@ export class Face { // artwork the way they should - it only ever hid the old planes because // those did not reach far enough down the head. const orders = new Map([[eyeMesh, 2], [mouthMesh, 1]]); - for (const mesh of [eyeMesh, mouthMesh]) { + if (hairMesh) orders.set(hairMesh, 3); + for (const mesh of [eyeMesh, mouthMesh, ...(hairMesh ? [hairMesh] : [])]) { const material = mesh.material; material.polygonOffset = true; material.polygonOffsetFactor = -1; @@ -106,6 +115,10 @@ 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; + } } /** @@ -210,7 +223,10 @@ export class Face { // ほっぺ 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, - headMark: p.eyes.headMark, + // 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, @@ -242,6 +258,17 @@ export class Face { this.mouthTexture.needsUpdate = true; } + if (this.hairMesh) { + const layout = this.hairLayout; + const ctx = this.hairCtx; + ctx.clearRect(0, 0, layout.width, layout.height); + ctx.save(); + ctx.translate(layout.offsetX, layout.offsetY); + drawHeadCoverWrap(ctx, p.eyes.headMark ?? {}, layout); + ctx.restore(); + this.hairTexture.needsUpdate = true; + } + this.dirty = false; } } |
