// These are the default values for the settings, which are overridden below import {ungzip} from "pako"; import {b64UrlDecode} from "../tools/b64.ts"; import {retrieveFile} from "../tools/upload-file.ts"; const firstTimeNames: Array = []; // Needed for array values, which clear the array when overridden export const settings = (async () => { let settings = { preload: [ // @ts-ignore // new URL('../../assets/fox.glb', import.meta.url).href, // @ts-ignore // new URL('../../assets/logo_build/base.glb', import.meta.url).href, // @ts-ignore // new URL('../../assets/logo_build/location.glb', import.meta.url).href, // @ts-ignore // new URL('../../assets/logo_build/img.glb', import.meta.url).href, // Websocket URLs automatically listen for new models from the python backend '', // Get the default preload URL if not overridden ], loadHelpers: true, edgeWidth: 0, /* The default line size for edges, set to 0 to use basic gl.LINEs */ displayLoadingEveryMs: 1000, /* How often to display partially loaded models */ monitorEveryMs: 100, monitorOpenTimeoutMs: 1000, // ModelViewer settings autoplay: true, // Global animation toggle arModes: 'webxr scene-viewer quick-look', zoomSensitivity: 0.25, orbitSensitivity: 1, panSensitivity: 1, exposure: 1, shadowIntensity: 0, // Nice low-res outdoor/high-contrast HDRI image (CC0 licensed) for lighting environment: new URL('../../assets/qwantani_afternoon_1k_hdr.jpg', import.meta.url).href, environmentIntensity: 1.0, // Uniform (1x1 pixel) medium gray background for visibility (following dark/light mode) skybox: (window.matchMedia("(prefers-color-scheme: dark)").matches ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEU4ODiyn42XAAAACklEQVQI" + "12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEW6urpaLVq8AAAACklEQVQI" + "12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="), // Playground settings pg_code: "", // Automatically loaded and executed code for the playground pg_opacity_loading: -1, // Opacity of the code during first load and run (< 0 is 0.0 if preload and 0.9 if not) pg_opacity_loaded: 0.9, // Opacity of the code after it has been run for the first time }; // Auto-override any settings from the URL (either GET parameters or hash) const url = new URL(window.location.href); url.searchParams.forEach((value, key) => { if (key in settings) (settings as any)[key] = parseSetting(key, value, settings); }) if (url.hash.length > 0) { // Hash has bigger limits as it is not sent to the server const hash = url.hash.slice(1); const hashParams = new URLSearchParams(hash); hashParams.forEach((value, key) => { if (key in settings) (settings as any)[key] = parseSetting(key, value, settings); }); } // Get the default preload URL if not overridden (requires a fetch that is avoided if possible) for (let i = 0; i < settings.preload.length; i++) { let url = settings.preload[i]; // Ignore empty preload URLs to allow overriding default auto behavior if (url === '') { settings.preload = settings.preload.slice(0, i).concat(settings.preload.slice(i + 1)); continue; // Skip this preload URL } // Handle special preload URL if (url === '') { if (settings.pg_code != "") { // means no preload URL if code is set settings.preload = settings.preload.slice(0, i).concat(settings.preload.slice(i + 1)); continue; // Skip this preload URL } const possibleBackend = new URL("./?api_updates=true", window.location.href) await fetch(possibleBackend, {method: "HEAD"}).then((response) => { if (response.ok && response.headers.get("Content-Type") === "text/event-stream") { // Frontend served by the backend: default to this URL for updates url = "dev+" + possibleBackend.href; } }).catch((error) => console.error("Failed to check for backend:", error)); if (url === '') { // Fallback to the default preload URL of localhost url = "dev+http://localhost:32323"; } } settings.preload[i] = url ?? ""; } // Auto-decompress the code and other playground settings if (settings.pg_code.length > 0) { // pg_code has a few possible formats: URL, base64url+gzipped, or raw code (try them in that order) try { new URL(settings.pg_code); // Check if it's a valid absolute URL settings.pg_code = await (await retrieveFile(settings.pg_code)).text(); } catch (error1) { // Not a valid URL, try base64url+gzipped try { settings.pg_code = ungzip(b64UrlDecode(settings.pg_code), {to: 'string'}); } catch (error2) { // Not base64url+gzipped, assume it's raw code console.log("pg_code is not a URL (", error1, ") or base64url+gzipped (", error2, "), using it as raw code:", settings.pg_code); } } if (settings.pg_opacity_loading < 0) { // If the opacity is not set, use 0.0 if preload is set, otherwise 0.9 settings.pg_opacity_loading = settings.preload.length > 0 ? 0.0 : 0.9; } } return settings; })() function parseSetting(name: string, value: string, settings: any): any { let arrayElem = name.endsWith(".0") if (arrayElem) name = name.slice(0, -2); let prevValue = (settings as any)[name]; if (prevValue === undefined) throw new Error(`Unknown setting: ${name}`); if (Array.isArray(prevValue)) { if (!arrayElem) { let toExtend = [] if (!firstTimeNames.includes(name)) { firstTimeNames.push(name); } else { toExtend = prevValue; } toExtend.push(parseSetting(name + ".0", value, settings)); return toExtend; } else { prevValue = prevValue[0]; } } switch (typeof prevValue) { case 'boolean': return value === 'true'; case 'number': return Number(value); case 'string': return value; default: throw new Error(`Unknown setting type: ${typeof prevValue} -- ${prevValue}`); } }