Skip to content

Commit 1414aa8

Browse files
Support browserless environment (playcanvas#3381)
* wrap calls to window and navigator in typeof tests * http returns Buffer not ArrayBuffer * skip creating blobs when loading glb embedded textures * handle buffer in glb parser * handle arraybuffer in basis and img loading * hdr support asset.file.contents * disable dp lighting for now * add platform.global object * add AssetFile * comment * handle file contents in all image handlers * sort dual paraboloid code * extract ReadStream and use for both ktx2 and hdr * Apply suggestions from code review Co-authored-by: Will Eastcott <will@playcanvas.com>
1 parent 1786fdb commit 1414aa8

31 files changed

+438
-467
lines changed

src/asset/asset-file.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @private
3+
* @class
4+
* @name AssetFile
5+
* @classdesc Wraps a source of asset data.
6+
*/
7+
class AssetFile {
8+
constructor(url, filename, hash, size, opt, contents) {
9+
this.url = url || '';
10+
this.filename = filename || '';
11+
this.hash = (hash === undefined) ? null : hash;
12+
this.size = (size === undefined) ? null : size;
13+
this.opt = (opt === undefined) ? null : opt;
14+
this.contents = contents || null;
15+
}
16+
17+
// Compare this AssetFile with another. Returns true if they have the same data
18+
// and false otherwise.
19+
equals(other) {
20+
return this.url === other.url &&
21+
this.filename === other.filename &&
22+
this.hash === other.hash &&
23+
this.size === other.size &&
24+
this.opt === other.opt &&
25+
this.contents === other.contents;
26+
}
27+
}
28+
29+
export { AssetFile };

src/asset/asset-registry.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ class AssetRegistry extends EventHandler {
340340
}
341341

342342
var self = this;
343-
var file = asset.getPreferredFile();
343+
var file = asset.file;
344344

345345
// open has completed on the resource
346346
var _opened = function (resource) {

src/asset/asset-variants.js

Lines changed: 0 additions & 54 deletions
This file was deleted.

src/asset/asset.js

Lines changed: 62 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import { EventHandler } from '../core/event-handler.js';
66
import { findAvailableLocale } from '../i18n/utils.js';
77

88
import { ABSOLUTE_URL } from './constants.js';
9-
import { AssetVariants } from './asset-variants.js';
9+
import { AssetFile } from './asset-file.js';
1010
import { getApplication } from '../framework/globals.js';
11+
import { http } from '../net/http.js';
1112

1213
// auto incrementing number for asset ids
1314
var assetIdCounter = -1;
@@ -82,8 +83,6 @@ class Asset extends EventHandler {
8283
this.tags = new Tags(this);
8384
this._preload = false;
8485

85-
this.variants = new AssetVariants(this);
86-
8786
this._file = null;
8887
this._data = data || { };
8988
this.options = options || { };
@@ -169,7 +168,7 @@ class Asset extends EventHandler {
169168
* var img = "&lt;img src='" + assets[0].getFileUrl() + "'&gt;";
170169
*/
171170
getFileUrl() {
172-
var file = this.getPreferredFile();
171+
var file = this.file;
173172

174173
if (!file || !file.url)
175174
return null;
@@ -188,44 +187,6 @@ class Asset extends EventHandler {
188187
return url;
189188
}
190189

191-
getPreferredFile() {
192-
if (!this.file)
193-
return null;
194-
195-
if (this.type === 'texture' || this.type === 'textureatlas' || this.type === 'bundle') {
196-
var app = this.registry?._loader?._app || getApplication();
197-
var device = app?.graphicsDevice;
198-
if (device) {
199-
for (var i = 0, len = VARIANT_DEFAULT_PRIORITY.length; i < len; i++) {
200-
var variant = VARIANT_DEFAULT_PRIORITY[i];
201-
// if the device supports the variant
202-
if (! device[VARIANT_SUPPORT[variant]]) continue;
203-
204-
// if the variant exists in the asset then just return it
205-
if (this.file.variants[variant]) {
206-
return this.file.variants[variant];
207-
}
208-
209-
// if the variant does not exist but the asset is in a bundle
210-
// and the bundle contain assets with this variant then return the default
211-
// file for the asset
212-
if (app.enableBundles) {
213-
var bundles = app.bundles.listBundlesForAsset(this);
214-
if (! bundles) continue;
215-
216-
for (var j = 0, len2 = bundles.length; j < len2; j++) {
217-
if (bundles[j].file && bundles[j].file.variants && bundles[j].file.variants[variant]) {
218-
return this.file;
219-
}
220-
}
221-
}
222-
}
223-
}
224-
}
225-
226-
return this.file;
227-
}
228-
229190
/**
230191
* @private
231192
* @function
@@ -369,54 +330,42 @@ class Asset extends EventHandler {
369330
}
370331

371332
set file(value) {
372-
// fire change event when the file changes
373-
// so that we reload it if necessary
374-
// set/unset file property of file hash been changed
375-
var key;
376-
var valueAsBool = !!value;
377-
var fileAsBool = !!this._file;
378-
if (valueAsBool !== fileAsBool || (value && this._file && value.hash !== this._file)) {
379-
if (value) {
380-
if (!this._file)
381-
this._file = { };
382-
383-
this._file.url = value.url;
384-
this._file.filename = value.filename;
385-
this._file.hash = value.hash;
386-
this._file.size = value.size;
387-
this._file.variants = this.variants;
388-
this._file.contents = value.contents;
389-
390-
if (value.hasOwnProperty('variants')) {
391-
this.variants.clear();
392-
393-
if (value.variants) {
394-
for (key in value.variants) {
395-
if (!value.variants[key])
396-
continue;
397-
398-
this.variants[key] = value.variants[key];
333+
// if value contains variants, choose the correct variant first
334+
if (value && value.variants && ['texture', 'textureatlas', 'bundle'].indexOf(this.type) !== -1) {
335+
// search for active variant
336+
const app = this.registry?._loader?._app || getApplication();
337+
const device = app?.graphicsDevice;
338+
if (device) {
339+
for (let i = 0, len = VARIANT_DEFAULT_PRIORITY.length; i < len; i++) {
340+
const variant = VARIANT_DEFAULT_PRIORITY[i];
341+
// if the device supports the variant
342+
if (value.variants[variant] && device[VARIANT_SUPPORT[variant]]) {
343+
value = value.variants[variant];
344+
break;
345+
}
346+
347+
// if the variant does not exist but the asset is in a bundle
348+
// and the bundle contain assets with this variant then return the default
349+
// file for the asset
350+
if (app.enableBundles) {
351+
const bundles = app.bundles.listBundlesForAsset(this);
352+
if (bundles && bundles.find((b) => {
353+
return b?.file?.variants[variant];
354+
})) {
355+
break;
399356
}
400357
}
401358
}
402-
403-
this.fire('change', this, 'file', this._file, this._file);
404-
this.reload();
405-
} else {
406-
this._file = null;
407-
this.variants.clear();
408359
}
409-
} else if (value && this._file && value.hasOwnProperty('variants')) {
410-
this.variants.clear();
360+
}
411361

412-
if (value.variants) {
413-
for (key in value.variants) {
414-
if (!value.variants[key])
415-
continue;
362+
const oldFile = this._file;
363+
const newFile = value ? new AssetFile(value.url, value.filename, value.hash, value.size, value.opt, value.contents) : null;
416364

417-
this.variants[key] = value.variants[key];
418-
}
419-
}
365+
if (!!newFile !== !!oldFile || (newFile && !newFile.equals(oldFile))) {
366+
this._file = newFile;
367+
this.fire('change', this, 'file', newFile, oldFile);
368+
this.reload();
420369
}
421370
}
422371

@@ -487,6 +436,35 @@ class Asset extends EventHandler {
487436
this.registry._loader.patch(this, this.registry);
488437
}
489438
}
439+
440+
/**
441+
* @private
442+
* @function
443+
* @name Asset#fetchArrayBuffer
444+
* @description Helper function to resolve asset file data and return the contents as an
445+
* ArrayBuffer. If the asset file contents are present, that is returned. Otherwise the file
446+
* data is be downloaded via http.
447+
* @param {string} loadUrl - The URL as passed into the handler
448+
* @param {callbacks.ResourceLoader} callback - The callback function to receive results.
449+
* @param {Asset} [asset] - The asset
450+
* @param {number} maxRetries - Number of retries if http download is required
451+
*/
452+
static fetchArrayBuffer(loadUrl, callback, asset, maxRetries = 0) {
453+
if (asset?.file?.contents) {
454+
// asset file contents were provided
455+
setTimeout(() => {
456+
callback(null, asset.file.contents);
457+
});
458+
} else {
459+
// asset contents must be downloaded
460+
http.get(loadUrl, {
461+
cache: true,
462+
responseType: 'arraybuffer',
463+
retry: maxRetries > 0,
464+
maxRetries: maxRetries
465+
}, callback);
466+
}
467+
}
490468
}
491469

492470
export { Asset };

src/core/platform.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ if (typeof navigator !== 'undefined') {
5252
} catch (e) {}
5353
}
5454

55+
// detect browser/node environment
56+
const environment = (typeof window !== 'undefined') ? 'browser' : 'node';
57+
5558
/**
5659
* @namespace
5760
* @name platform
@@ -62,6 +65,34 @@ if (typeof navigator !== 'undefined') {
6265
* }
6366
*/
6467
const platform = {
68+
/**
69+
* @static
70+
* @readonly
71+
* @type {string}
72+
* @name platform.environment
73+
* @description String identifying the current runtime environment. Either 'browser' or 'node'.
74+
*/
75+
environment: environment,
76+
77+
/**
78+
* @static
79+
* @readonly
80+
* @type {object}
81+
* @name platform.global
82+
* @description The global object. This will be the window object when running in a browser and
83+
* the global object when running in nodejs.
84+
*/
85+
global: (environment === 'browser') ? window : global,
86+
87+
/**
88+
* @static
89+
* @readonly
90+
* @type {boolean}
91+
* @name platform.isBrowser
92+
* @description Convenience boolean indicating whether we're running in the browser.
93+
*/
94+
browser: environment === 'browser',
95+
6596
/**
6697
* @static
6798
* @readonly

0 commit comments

Comments
 (0)