aboutsummaryrefslogtreecommitdiffhomepage
path: root/bluebey-studio/src/face.js
diff options
context:
space:
mode:
Diffstat (limited to 'bluebey-studio/src/face.js')
-rw-r--r--bluebey-studio/src/face.js92
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;
}
}