puppet opacity

This commit is contained in:
2026-08-29 19:11:49 +08:00
parent 55ed1f9d32
commit 7be507bff2
10 changed files with 131 additions and 27 deletions
+4 -3
View File
@@ -9,12 +9,12 @@
let {
puppets,
frozenPuppetId,
selectedPuppetId,
onBoundsChange,
skinHashes,
}: {
puppets: readonly PuppetState[];
frozenPuppetId: string | null;
selectedPuppetId: string | null;
onBoundsChange: (bounds: PuppetScreenBounds[]) => void;
skinHashes: ReadonlyMap<string, string | null>;
} = $props();
@@ -38,7 +38,8 @@
puppetManager.update(
puppets,
$sceneConfiguration.puppetScale,
frozenPuppetId,
$sceneConfiguration.puppetOpacity,
selectedPuppetId,
clock.getDelta(),
clock.getElapsed(),
skinHashes,
@@ -54,8 +54,10 @@ export class Puppet {
deltaSeconds: number,
elapsedSeconds: number,
skinHash: string | null,
opacity: number,
) {
this.skin.update(skinHash);
this.skin.setOpacity(opacity);
if (frozen) {
this.animation.pause();
return;
@@ -77,11 +79,12 @@ export class Puppet {
dispose() {
this.skin.dispose();
const geometries = new Set<THREE.BufferGeometry>();
this.root.traverse((object) => {
if (!(object instanceof THREE.Mesh)) return;
object.geometry.dispose();
geometries.add(object.geometry);
});
for (const geometry of geometries) geometry.dispose();
}
private createRig(): PuppetRig {
@@ -11,7 +11,8 @@ export class PuppetManager {
update(
states: readonly PuppetState[],
scale: number,
frozenPuppetId: string | null,
idleOpacity: number,
selectedPuppetId: string | null,
deltaSeconds: number,
elapsedSeconds: number,
skinHashes: ReadonlyMap<string, string | null>,
@@ -24,10 +25,11 @@ export class PuppetManager {
?.update(
state,
this.world,
state.id === frozenPuppetId,
state.id === selectedPuppetId,
deltaSeconds,
elapsedSeconds,
skinHashes.get(state.id) ?? null,
state.id === selectedPuppetId ? 1 : idleOpacity,
);
}
}
@@ -118,6 +118,7 @@ function loadTexture(url: string) {
export class PuppetSkin {
private readonly material: THREE.MeshStandardMaterial;
private readonly depthMaterial: THREE.MeshBasicMaterial;
private texture: THREE.Texture | null = null;
private requestedHash: string | null | undefined;
private requestId = 0;
@@ -130,6 +131,14 @@ export class PuppetSkin {
color: "white",
transparent: true,
alphaTest: 0.5,
depthWrite: false,
depthFunc: THREE.EqualDepth,
});
this.depthMaterial = new THREE.MeshBasicMaterial({
colorWrite: false,
depthWrite: true,
depthTest: true,
alphaTest: 0.5,
});
const parts: SkinPart[] = [
{ mesh: rig.head, faces: HEAD },
@@ -142,6 +151,11 @@ export class PuppetSkin {
for (const part of parts) {
part.mesh.material = this.material;
applyUvs(part.mesh.geometry, part.faces);
// Opaque-phase depth establishes the nearest rig surface before the
// translucent parent mesh contributes color.
const depthMesh = new THREE.Mesh(part.mesh.geometry, this.depthMaterial);
part.mesh.add(depthMesh);
}
}
@@ -161,16 +175,25 @@ export class PuppetSkin {
this.texture?.dispose();
this.texture = texture;
this.material.map = texture;
this.depthMaterial.map = texture;
this.material.needsUpdate = true;
this.depthMaterial.needsUpdate = true;
})
.catch((error) =>
console.error(`Failed to load skin for ${this.userId}`, error),
);
}
setOpacity(opacity: number) {
if (this.material.opacity === opacity) return;
this.material.opacity = opacity;
this.material.alphaTest = opacity * 0.5;
}
dispose() {
this.requestId++;
this.texture?.dispose();
this.material.dispose();
this.depthMaterial.dispose();
}
}