aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/bluebey-studio/src/model.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/bluebey-studio/src/model.js')
-rw-r--r--public/bluebey-studio/src/model.js15
1 files changed, 13 insertions, 2 deletions
diff --git a/public/bluebey-studio/src/model.js b/public/bluebey-studio/src/model.js
index 083c745..68f1255 100644
--- a/public/bluebey-studio/src/model.js
+++ b/public/bluebey-studio/src/model.js
@@ -11,6 +11,7 @@ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
* body parts Body, Foots, Leaf, Vein, LeftHand, RightHand, Nose
* overlays eyes-* (eight hand-drawn variants, one texture each) and Mouth
* face plates eyes-plate / mouth-plate, the big shells the studio draws on
+ * hair plate hair-plate, a shell that wraps right round the head (optional)
*
* The two face plates are what the artwork is painted onto. They are the front
* half of the body, so wherever the drawing lands there is a surface to carry
@@ -60,7 +61,7 @@ const normalizeBoneName = (name) => (name ?? '').replace(/[.\s]/g, '').toLowerCa
* the ring of leaves around the base, otherwise a low or frowning mouth gets
* swallowed by them.
*/
-const OVERLAY_INFLATE = { eyes: 0.004, mouth: 0.004 };
+const OVERLAY_INFLATE = { eyes: 0.004, mouth: 0.004, hair: 0.008 };
export async function loadModel(source, { onProgress } = {}) {
const loader = new GLTFLoader();
@@ -90,6 +91,8 @@ export async function loadModel(source, { onProgress } = {}) {
/** The big shells the artwork is drawn onto, if the file has them. */
let eyePlate = null;
let mouthPlate = null;
+ /** The shell that wraps round the head, so hair shows from behind (optional). */
+ let hairPlate = null;
root.traverse((object) => {
if (!object.isMesh) return;
@@ -105,6 +108,10 @@ export async function loadModel(source, { onProgress } = {}) {
mouthPlate = object;
return;
}
+ if (materialName === 'hair-plate') {
+ hairPlate = object;
+ return;
+ }
if (materialName.startsWith('eyes-')) {
eyes.set(materialName.slice('eyes-'.length), object);
return;
@@ -125,6 +132,8 @@ export async function loadModel(source, { onProgress } = {}) {
// The drawing surface: the big plate when present, otherwise the 2022 plane.
const eyeMesh = eyePlate ?? eyes.get('opened') ?? eyes.values().next().value;
const mouth = mouthPlate ?? mouthOriginal;
+ // The wrapping hair shell, if the model carries one (`hair-plate`).
+ const hairMesh = hairPlate;
if (!eyeMesh) throw new Error('eye mesh not found in the GLB');
if (!mouth) throw new Error('mouth mesh not found in the GLB');
@@ -154,6 +163,7 @@ export async function loadModel(source, { onProgress } = {}) {
.getCenter(new THREE.Vector3());
inflateOverlay(eyeMesh, headCentre, OVERLAY_INFLATE.eyes);
inflateOverlay(mouth, headCentre, OVERLAY_INFLATE.mouth);
+ if (hairMesh) inflateOverlay(hairMesh, headCentre, OVERLAY_INFLATE.hair);
// Recentre: put the character on the origin with its feet on the ground so the
// camera maths stays trivial.
@@ -196,9 +206,10 @@ export async function loadModel(source, { onProgress } = {}) {
body,
kinds,
leaves,
- overlays: [eyeMesh, mouth],
+ overlays: [eyeMesh, mouth, ...(hairMesh ? [hairMesh] : [])],
eyeMesh,
mouthMesh: mouth,
+ hairMesh,
noseMesh,
hasFacePlates: Boolean(eyePlate && mouthPlate),
},