d217c3376b
Several mitigations against the WebGL context lost that fires when hand tracking starts on a loaded scene: - Canvas: fixed DPR [1,1], antialias off, scoped id="game-canvas", context-lost handler releases MediaPipe and logs GPU memory counters - optimizeGLTFScene: cap anisotropy at 2 and stop forcing mipmaps / needsUpdate on every pass — avoids massive texture re-uploads - MediaPipe: force CPU delegate (HAND_TRACKING_BROWSER_DELEGATE), cache the landmarker instance, and expose releaseBrowserHandLandmarker - useBrowserHandTracking / useRemoteHandTracking: idempotent cleanup guarded by a cleanedUp flag, try/catch around the detect loop, and release of the landmarker on stop - World: mount HandTrackingGlove only when the matching hand is actually present in the snapshot (status connected + hands.length > 0) - HandTrackingGlove: drop the eager useGLTF.preload that was running at startup whether or not hand tracking was used Does not yet absorb the React StrictMode double-mount — that is the follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
70 lines
1.6 KiB
TypeScript
70 lines
1.6 KiB
TypeScript
import * as THREE from "three";
|
|
|
|
const TEXTURE_KEYS = [
|
|
"map",
|
|
"alphaMap",
|
|
"aoMap",
|
|
"bumpMap",
|
|
"displacementMap",
|
|
"emissiveMap",
|
|
"envMap",
|
|
"lightMap",
|
|
"metalnessMap",
|
|
"normalMap",
|
|
"roughnessMap",
|
|
] as const;
|
|
|
|
type TextureKey = (typeof TEXTURE_KEYS)[number];
|
|
type TexturedMaterial = THREE.Material &
|
|
Partial<Record<TextureKey, THREE.Texture>>;
|
|
|
|
const optimizedTextures = new WeakSet<THREE.Texture>();
|
|
const MAX_GLTF_TEXTURE_ANISOTROPY = 2;
|
|
|
|
function optimizeTexture(texture: THREE.Texture, maxAnisotropy: number): void {
|
|
if (optimizedTextures.has(texture)) return;
|
|
|
|
optimizedTextures.add(texture);
|
|
const nextAnisotropy = Math.min(
|
|
MAX_GLTF_TEXTURE_ANISOTROPY,
|
|
Math.max(1, maxAnisotropy),
|
|
);
|
|
|
|
if (texture.anisotropy > nextAnisotropy) {
|
|
texture.anisotropy = nextAnisotropy;
|
|
texture.needsUpdate = true;
|
|
}
|
|
}
|
|
|
|
function optimizeMaterialTextures(
|
|
material: THREE.Material,
|
|
maxAnisotropy: number,
|
|
): void {
|
|
const texturedMaterial = material as TexturedMaterial;
|
|
|
|
for (const key of TEXTURE_KEYS) {
|
|
const texture = texturedMaterial[key];
|
|
if (texture instanceof THREE.Texture) {
|
|
optimizeTexture(texture, maxAnisotropy);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function optimizeGLTFSceneTextures(
|
|
scene: THREE.Object3D,
|
|
maxAnisotropy: number,
|
|
): void {
|
|
scene.traverse((child) => {
|
|
if (!(child instanceof THREE.Mesh)) return;
|
|
|
|
if (Array.isArray(child.material)) {
|
|
for (const material of child.material) {
|
|
optimizeMaterialTextures(material, maxAnisotropy);
|
|
}
|
|
return;
|
|
}
|
|
|
|
optimizeMaterialTextures(child.material, maxAnisotropy);
|
|
});
|
|
}
|