UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

120 lines 5.03 kB
import { createLoaders } from "@needle-tools/gltf-progressive"; import { CubeUVReflectionMapping, EquirectangularRefractionMapping, SRGBColorSpace, TextureLoader } from "three"; import { EXRLoader } from "three/examples/jsm/loaders/EXRLoader.js"; import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js"; const running = new Map(); // #region api /** * Loads a PMREM texture from the given URL. This also supports the ultra-fast preprocessed environment maps (PMREM) format. * @param url The URL of the PMREM texture to load. * @param renderer The WebGLRenderer to use for loading the texture. * @returns A promise that resolves to the loaded texture or null if loading failed. */ export function loadPMREM(url, renderer) { if (running.has(url)) { return running.get(url); } const actualUrl = new URL(url, window.location.href); const promise = internalLoadPMREM(actualUrl, renderer); running.set(url, promise); promise.finally(() => { running.delete(url); }); return promise; } // #region loading async function internalLoadPMREM(url, renderer) { if (!url) return Promise.resolve(null); const pathname = url.pathname; const isPMREM_URL = url.toString().toLowerCase().includes("pmrem") || url.searchParams.get("pmrem") != null; let isEXR = pathname.endsWith(".exr"); let isHdr = pathname.endsWith(".hdr"); let isKtx2 = pathname.endsWith(".ktx2"); // For URLs that don't carry a recognizable extension (e.g. `blob:` URLs // from dropped local files, or signed CDN URLs that elide the filename), // sniff Content-Type and a few magic bytes to pick the right loader. // EXRLoader / RGBELoader / KTX2Loader vs TextureLoader is not interchangeable — // routing EXR binary data through TextureLoader fails outright. if (!isEXR && !isHdr && !isKtx2) { try { const probe = await fetch(url.toString(), { method: "GET", headers: { range: "bytes=0-15" }, }); const contentType = probe.headers.get("content-type")?.toLowerCase() ?? ""; // Content-Type wins when it's specific — skip reading the body. if (contentType === "image/x-exr" || contentType === "image/aces") { isEXR = true; } else if (contentType === "image/vnd.radiance" || contentType === "image/x-hdr") { isHdr = true; } else if (contentType === "image/ktx2") { isKtx2 = true; } // Otherwise fall back to magic-byte sniffing. else { const bytes = new Uint8Array(await probe.arrayBuffer()); if (bytes.length >= 4 && bytes[0] === 0x76 && bytes[1] === 0x2f && bytes[2] === 0x31 && bytes[3] === 0x01) { // EXR magic: 0x76 0x2f 0x31 0x01 isEXR = true; } else if (bytes.length >= 12 && bytes[0] === 0xAB && bytes[1] === 0x4B && bytes[2] === 0x54 && bytes[3] === 0x58 && bytes[4] === 0x20 && bytes[5] === 0x32 && bytes[6] === 0x30 && bytes[7] === 0xBB) { // KTX2 magic: «KTX 20»\r\n\x1A\n isKtx2 = true; } else if (bytes.length >= 10 && bytes[0] === 0x23 && bytes[1] === 0x3F // "#?" && bytes[2] === 0x52 && bytes[3] === 0x41 && bytes[4] === 0x44 && bytes[5] === 0x49 // "RADI" && bytes[6] === 0x41 && bytes[7] === 0x4E && bytes[8] === 0x43 && bytes[9] === 0x45) { // "ANCE" // Radiance HDR header: "#?RADIANCE" isHdr = true; } } } catch (err) { console.warn("loadPMREM: failed to probe URL for format detection", url, err); } } let loader; if (isEXR) { loader = new EXRLoader(); } else if (isHdr) { loader = new RGBELoader(); } else if (isKtx2) { const { ktx2Loader } = createLoaders(renderer); loader = ktx2Loader; } else { loader = new TextureLoader(); } const str = url.toString(); const promise = loader.loadAsync(str) .then(tex => { if (tex) { const nameIndex = pathname.lastIndexOf("/"); tex.name = pathname.substring(nameIndex >= 0 ? nameIndex + 1 : 0); if (isPMREM_URL) { tex.mapping = CubeUVReflectionMapping; } else { tex.mapping = EquirectangularRefractionMapping; } if (loader instanceof TextureLoader) { tex.colorSpace = SRGBColorSpace; } } return tex; }); const texture = await promise.catch(_err => { console.warn("Failed to load texture from url:", url); return null; }); return texture; } //# sourceMappingURL=engine_pmrem.js.map