import * as THREE from "three"; // Each viewer owns its textures. Browser HTTP caching handles repeat downloads; // keeping GPU resources in a global cache leaks every visited map and signed URL. export async function loadPanoramaTextures( urls: string[], loader: Pick = new THREE.TextureLoader(), timeoutMs = 30000, ): Promise { const textures: THREE.Texture[] = []; let failed = false; let timer: ReturnType | undefined; try { return await Promise.race([ Promise.all( urls.map(async (url) => { const texture = await loader.loadAsync(url); if (failed) { texture.dispose(); throw new Error("Panorama loading cancelled"); } texture.colorSpace = THREE.SRGBColorSpace; textures.push(texture); return texture; }), ), new Promise((_, reject) => { timer = setTimeout( () => reject( new Error("Panorama loading timed out. Reconnect to retry."), ), timeoutMs, ); }), ]); } catch (error) { failed = true; textures.forEach((texture) => texture.dispose()); throw error; } finally { clearTimeout(timer); } }