|
4 | 4 | * One line to render a 3D model: |
5 | 5 | * SceneView.modelViewer("canvas", "model.glb") |
6 | 6 | * |
7 | | - * Prerequisites: load Filament.js CDN via <script> BEFORE this file: |
8 | | - * <script src="https://cdn.jsdelivr.net/npm/filament@1.52.3/filament.js"></script> |
| 7 | + * No prerequisites — sceneview.js loads Filament.js CDN automatically. |
| 8 | + * Just include one script: |
9 | 9 | * <script src="js/sceneview.js"></script> |
10 | 10 | * |
11 | 11 | * Powered by Filament.js (Google's PBR renderer, WASM). |
12 | 12 | * https://sceneview.github.io |
13 | 13 | * |
14 | | - * @version 2.0.0 |
| 14 | + * @version 1.1.0 |
15 | 15 | * @license MIT |
16 | 16 | */ |
17 | 17 | (function(global) { |
18 | 18 | 'use strict'; |
19 | 19 |
|
| 20 | + var FILAMENT_CDN = 'https://cdn.jsdelivr.net/npm/filament@1.52.3/filament.js'; |
| 21 | + |
| 22 | + /** |
| 23 | + * Load Filament.js CDN dynamically if not already present. |
| 24 | + * Returns a Promise that resolves when the Filament global is available. |
| 25 | + */ |
| 26 | + function _ensureFilament() { |
| 27 | + return new Promise(function(resolve, reject) { |
| 28 | + // Already loaded |
| 29 | + if (typeof Filament !== 'undefined') { |
| 30 | + resolve(); |
| 31 | + return; |
| 32 | + } |
| 33 | + // Check if script tag already exists but hasn't finished loading |
| 34 | + var existing = document.querySelector('script[src*="filament"]'); |
| 35 | + if (existing) { |
| 36 | + existing.addEventListener('load', function() { resolve(); }); |
| 37 | + existing.addEventListener('error', function() { reject(new Error('SceneView: Failed to load Filament.js from CDN')); }); |
| 38 | + return; |
| 39 | + } |
| 40 | + // Inject script tag |
| 41 | + var script = document.createElement('script'); |
| 42 | + script.src = FILAMENT_CDN; |
| 43 | + script.onload = function() { resolve(); }; |
| 44 | + script.onerror = function() { reject(new Error('SceneView: Failed to load Filament.js from CDN (' + FILAMENT_CDN + ')')); }; |
| 45 | + document.head.appendChild(script); |
| 46 | + }); |
| 47 | + } |
| 48 | + |
20 | 49 | /** |
21 | 50 | * SceneView instance — wraps Filament engine, scene, camera, renderer. |
22 | 51 | */ |
|
59 | 88 | } |
60 | 89 | return; |
61 | 90 | } |
62 | | - // Fetch via Filament.init (handles WASM + fetch) |
| 91 | + // Fetch via Filament.init with asset — this always fires the callback |
| 92 | + // because it needs to fetch the asset even if WASM is already loaded |
63 | 93 | Filament.init([url], function() { |
64 | 94 | try { |
65 | 95 | self._showModel(url); |
|
291 | 321 |
|
292 | 322 | /** |
293 | 323 | * Create an empty SceneView on a canvas. |
294 | | - * Filament.js must be loaded via <script> before calling this. |
| 324 | + * Filament.js is loaded automatically from CDN if not already present. |
295 | 325 | * |
296 | 326 | * @param {string|HTMLCanvasElement} canvasOrId - Canvas element or its ID |
297 | 327 | * @param {Object} [options] - Configuration options |
298 | 328 | * @returns {Promise<SceneViewInstance>} |
299 | 329 | */ |
300 | 330 | function create(canvasOrId, options) { |
301 | | - return new Promise(function(resolve, reject) { |
302 | | - if (typeof Filament === 'undefined') { |
303 | | - reject(new Error('SceneView: Filament.js not loaded. Add <script src="https://cdn.jsdelivr.net/npm/filament@1.52.3/filament.js"></script> before sceneview.js')); |
304 | | - return; |
305 | | - } |
306 | | - Filament.init([], function() { |
307 | | - try { |
308 | | - resolve(_createEngine(canvasOrId, options)); |
309 | | - } catch (e) { |
310 | | - reject(e); |
| 331 | + return _ensureFilament().then(function() { |
| 332 | + return new Promise(function(resolve, reject) { |
| 333 | + // If WASM is already initialized (Engine exists), skip Filament.init |
| 334 | + if (typeof Filament.Engine !== 'undefined') { |
| 335 | + try { |
| 336 | + resolve(_createEngine(canvasOrId, options)); |
| 337 | + } catch (e) { |
| 338 | + reject(e); |
| 339 | + } |
| 340 | + return; |
311 | 341 | } |
| 342 | + // First time: initialize WASM |
| 343 | + Filament.init([], function() { |
| 344 | + try { |
| 345 | + resolve(_createEngine(canvasOrId, options)); |
| 346 | + } catch (e) { |
| 347 | + reject(e); |
| 348 | + } |
| 349 | + }); |
312 | 350 | }); |
313 | 351 | }); |
314 | 352 | } |
315 | 353 |
|
316 | 354 | /** |
317 | 355 | * One-liner: create viewer and load a model. |
318 | | - * Filament.js must be loaded via <script> before calling this. |
| 356 | + * Filament.js is loaded automatically from CDN if not already present. |
319 | 357 | * |
320 | 358 | * @param {string|HTMLCanvasElement} canvasOrId |
321 | 359 | * @param {string} modelUrl - URL to .glb/.gltf model |
322 | 360 | * @param {Object} [options] |
323 | 361 | * @returns {Promise<SceneViewInstance>} |
324 | 362 | */ |
325 | 363 | function modelViewer(canvasOrId, modelUrl, options) { |
326 | | - return new Promise(function(resolve, reject) { |
327 | | - if (typeof Filament === 'undefined') { |
328 | | - reject(new Error('SceneView: Filament.js not loaded. Add <script src="https://cdn.jsdelivr.net/npm/filament@1.52.3/filament.js"></script> before sceneview.js')); |
329 | | - return; |
330 | | - } |
331 | | - // Pre-fetch model AND initialize WASM in one call |
332 | | - Filament.init([modelUrl], function() { |
333 | | - try { |
334 | | - var instance = _createEngine(canvasOrId, options); |
335 | | - instance._showModel(modelUrl); |
336 | | - resolve(instance); |
337 | | - } catch (e) { |
338 | | - reject(e); |
339 | | - } |
| 364 | + return _ensureFilament().then(function() { |
| 365 | + return new Promise(function(resolve, reject) { |
| 366 | + // Always use Filament.init with the model URL in the assets array. |
| 367 | + // This works whether WASM is already loaded or not, because Filament |
| 368 | + // needs to fetch the model asset and will call back when done. |
| 369 | + Filament.init([modelUrl], function() { |
| 370 | + try { |
| 371 | + var instance = _createEngine(canvasOrId, options); |
| 372 | + instance._showModel(modelUrl); |
| 373 | + resolve(instance); |
| 374 | + } catch (e) { |
| 375 | + reject(e); |
| 376 | + } |
| 377 | + }); |
340 | 378 | }); |
341 | 379 | }); |
342 | 380 | } |
343 | 381 |
|
344 | 382 | // Public API |
345 | 383 | global.SceneView = { |
346 | | - version: '2.0.0', |
| 384 | + version: '1.1.0', |
347 | 385 | create: create, |
348 | 386 | modelViewer: modelViewer |
349 | 387 | }; |
|
0 commit comments