main
John Lauer Fusion 360 viewer: load embedded textures as data URLs 61aaf20 10d ago
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Buck in Fusion 360, 3D</title>
<script>
  // the wiki's readme frame is an opaque origin: localStorage throws, so shim it before the viewer loads
  try { localStorage } catch (e) { var _m = {}; Object.defineProperty(window, 'localStorage', { value: { getItem: k => _m[k] ?? null, setItem: (k, v) => { _m[k] = String(v) }, removeItem: k => { delete _m[k] }, clear: () => { _m = {} } } }) }
  window.process = window.process || { env: { NODE_ENV: 'production' } };
  window.global = window.global || window;
  addEventListener('unhandledrejection', e => console.warn(e.reason));
  // the render route forbids blob: images, and the glTF loader hands embedded textures over as blob URLs:
  // remember each Blob's bytes and give out a data: URL instead (img-src allows data:)
  (function () {
    const _Blob = window.Blob;
    function B(parts, opts) { const b = new _Blob(parts || [], opts); try { b.__parts = parts; b.__type = (opts && opts.type) || ''; } catch (e) {} return b; }
    B.prototype = _Blob.prototype; Object.setPrototypeOf(B, _Blob); window.Blob = B;
    const _create = URL.createObjectURL.bind(URL);
    URL.createObjectURL = function (o) {
      if (!o || !o.__parts) return _create(o);
      const chunks = o.__parts.map(p => typeof p === 'string' ? new TextEncoder().encode(p) : p instanceof ArrayBuffer ? new Uint8Array(p) : ArrayBuffer.isView(p) ? new Uint8Array(p.buffer, p.byteOffset, p.byteLength) : new Uint8Array(0));
      let s = ''; for (const c of chunks) for (let i = 0; i < c.length; i += 0x8000) s += String.fromCharCode.apply(null, c.subarray(i, i + 0x8000));
      return 'data:' + (o.__type || 'application/octet-stream') + ';base64,' + btoa(s);
    };
    const _revoke = URL.revokeObjectURL.bind(URL);
    URL.revokeObjectURL = u => { if (typeof u === 'string' && u.startsWith('blob:')) _revoke(u); };
  })();
</script>
<style>
  :root { color-scheme: dark; }
  html, body { margin: 0; height: 100%; background: linear-gradient(180deg, #1e2a3d 0%, #1e2a3d 20%, #0a121f 100%); font-family: 'Satoshi', -apple-system, 'Segoe UI', sans-serif; color: #e6edf3; }
  #host { position: absolute; inset: 0; }
  #poster { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: center / cover no-repeat url('img/19-3d-board.jpg'); }
  #poster button { font: 600 15px/1 inherit; padding: 12px 20px; border-radius: 8px; border: 1px solid #00b8b1; background: rgba(13, 17, 23, 0.82); color: #00b8b1; cursor: pointer; }
  #poster button:hover { background: #00b8b1; color: #0d1117; }
  #poster button:focus-visible { outline: 2px solid #00b8b1; outline-offset: 3px; }
  #note { position: absolute; left: 12px; bottom: 10px; font-size: 12px; color: #8b949e; pointer-events: none; }
</style>
</head>
<body>
<div id="host"></div>
<div id="poster"><button id="load" type="button">Load 3D model</button></div>
<div id="note">The final board from Autodesk Fusion 360, cloud version 23</div>
<script type="module">
  const btn = document.getElementById('load');
  btn.addEventListener('click', async () => {
    btn.textContent = 'Loading...'; btn.disabled = true;
    try {
      await import('https://wiki.adom.inc/static/vendor/adom-3d-viewer-babylon9/adom-3d-viewer-babylon9.esm.js');
      const viewer = window.Adom3DViewerBabylon9.init(document.getElementById('host'), { zUp: true, showViewCube: true, modelUrl: new URL('buck-fusion-360.glb', location.href).href });
      const report = await viewer.whenReady();
      window.__report = report;
      if (!report.model || !report.model.loaded) throw new Error((report.model && report.model.error) || 'model not loaded');
      document.getElementById('poster').remove();
      window.__viewer = viewer;
    } catch (e) {
      console.warn(e); btn.disabled = false; btn.textContent = 'Could not load the model. Try again';
    }
  });
</script>
</body>
</html>