Skip to content

Commit cb9a60e

Browse files
slimbuckDonovan Hutchence
andauthored
Cleanup loadFromUrl, construct relative asset URLs correctly (playcanvas#2087)
* construct urls correctly, fix unloading of assets with multiple resources Co-authored-by: Donovan Hutchence <dhutchence@snapchat.com>
1 parent ca4d739 commit cb9a60e

File tree

5 files changed

+114
-172
lines changed

5 files changed

+114
-172
lines changed

src/asset/asset-registry.js

Lines changed: 88 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -339,9 +339,25 @@ Object.assign(pc, function () {
339339
return;
340340
}
341341

342-
var load = !!asset.file;
343-
344342
var file = asset.getPreferredFile();
343+
var load = !!file;
344+
345+
// open has completed on the resource
346+
var _opened = function (resource) {
347+
if (resource instanceof Array) {
348+
asset.resources = resource;
349+
} else {
350+
asset.resource = resource;
351+
}
352+
353+
self._loader.patch(asset, self);
354+
355+
self.fire("load", asset);
356+
self.fire("load:" + asset.id, asset);
357+
if (file && file.url)
358+
self.fire("load:url:" + file.url, asset);
359+
asset.fire("load", asset);
360+
};
345361

346362
var _load = function () {
347363
var url = asset.getFileUrl();
@@ -358,11 +374,6 @@ Object.assign(pc, function () {
358374
asset.fire("error", err, asset);
359375
return;
360376
}
361-
if (resource instanceof Array) {
362-
asset.resources = resource;
363-
} else {
364-
asset.resource = resource;
365-
}
366377

367378
if (!pc.script.legacy && asset.type === 'script') {
368379
var loader = self._loader.getHandler('script');
@@ -375,32 +386,14 @@ Object.assign(pc, function () {
375386
loader._cache[asset.id] = extra;
376387
}
377388

378-
self._loader.patch(asset, self);
379-
380-
self.fire("load", asset);
381-
self.fire("load:" + asset.id, asset);
382-
if (file && file.url)
383-
self.fire("load:url:" + file.url, asset);
384-
asset.fire("load", asset);
389+
_opened(resource);
385390
}, asset);
386391
};
387392

388393
var _open = function () {
389394
var resource = self._loader.open(asset.type, asset.data);
390-
if (resource instanceof Array) {
391-
asset.resources = resource;
392-
} else {
393-
asset.resource = resource;
394-
}
395395
asset.loaded = true;
396-
397-
self._loader.patch(asset, self);
398-
399-
self.fire("load", asset);
400-
self.fire("load:" + asset.id, asset);
401-
if (file && file.url)
402-
self.fire("load:url:" + file.url, asset);
403-
asset.fire("load", asset);
396+
_opened(resource);
404397
};
405398

406399
// check for special case for cubemaps
@@ -434,7 +427,6 @@ Object.assign(pc, function () {
434427
} else if (load) {
435428
this.fire("load:start", asset);
436429
this.fire("load:" + asset.id + ":start", asset);
437-
438430
_load();
439431
}
440432
},
@@ -480,184 +472,127 @@ Object.assign(pc, function () {
480472
filename: filename || name,
481473
url: url
482474
};
483-
var data = {};
484475

485476
var asset = self.getByUrl(url);
486477
if (!asset) {
487-
asset = new pc.Asset(name, type, file, data);
478+
asset = new pc.Asset(name, type, file);
488479
self.add(asset);
489480
}
490481

491-
var onLoadAsset = function (loadedAsset) {
492-
if (type === 'material') {
493-
self._loadTextures([loadedAsset], function (err, textures) {
494-
if (err) {
495-
callback(err);
496-
} else {
497-
callback(null, loadedAsset);
498-
}
499-
});
500-
} else {
482+
var startLoad = function (asset) {
483+
asset.once("load", function (loadedAsset) {
501484
callback(null, loadedAsset);
502-
}
485+
});
486+
asset.once("error", function (err) {
487+
callback(err);
488+
});
489+
self.load(asset);
503490
};
504491

505492
if (asset.resource) {
506-
onLoadAsset(asset);
507-
return;
508-
}
509-
510-
if (type === 'model') {
511-
self._loadModel(asset, callback);
512-
return;
493+
callback(null, asset);
494+
} else if (type === 'model') {
495+
self._loadModel(asset, startLoad);
496+
} else {
497+
startLoad(asset);
513498
}
514-
515-
asset.once("load", onLoadAsset);
516-
asset.once("error", function (err) {
517-
callback(err);
518-
});
519-
self.load(asset);
520499
},
521500

522501
// private method used for engine-only loading of model data
523-
_loadModel: function (asset, callback) {
502+
_loadModel: function (modelAsset, continuation) {
524503
var self = this;
525504

526-
var url = asset.getFileUrl();
527-
var dir = pc.path.getDirectory(url);
528-
var basename = pc.path.getBasename(url);
505+
var url = modelAsset.getFileUrl();
529506
var ext = pc.path.getExtension(url);
530507

531-
var _loadAsset = function (assetToLoad) {
532-
asset.once("load", function (loadedAsset) {
533-
callback(null, loadedAsset);
534-
});
535-
asset.once("error", function (err) {
536-
callback(err);
537-
});
538-
self.load(assetToLoad);
539-
};
540-
541508
if (ext === '.json' || ext === '.glb') {
509+
var dir = pc.path.getDirectory(url);
510+
var basename = pc.path.getBasename(url);
511+
542512
// playcanvas model format supports material mapping file
543513
var mappingUrl = pc.path.join(dir, basename.replace(ext, ".mapping.json"));
544514
this._loader.load(mappingUrl, 'json', function (err, data) {
545515
if (err) {
546-
asset.data = { mapping: [] };
547-
_loadAsset(asset);
548-
return;
516+
modelAsset.data = { mapping: [] };
517+
continuation(modelAsset);
518+
} else {
519+
self._loadMaterials(modelAsset, data, function (e, materials) {
520+
modelAsset.data = data;
521+
continuation(modelAsset);
522+
});
549523
}
550-
551-
self._loadMaterials(dir, data, function (e, materials) {
552-
asset.data = data;
553-
_loadAsset(asset);
554-
});
555524
});
556525
} else {
557526
// other model format (e.g. obj)
558-
_loadAsset(asset);
527+
continuation(modelAsset);
559528
}
560529
},
561530

562-
// private method used for engine-only loading of model data
563-
_loadMaterials: function (dir, mapping, callback) {
564-
if (dir) {
565-
// dir is generated from a call to pc.path.getDirectory which never returns
566-
// a path ending in a forward slash so add one here
567-
dir += '/';
568-
if (this.prefix && dir.startsWith(this.prefix)) {
569-
dir = dir.slice(this.prefix.length);
570-
}
571-
}
572-
531+
// private method used for engine-only loading of model materials
532+
_loadMaterials: function (modelAsset, mapping, callback) {
573533
var self = this;
574-
var i;
575-
var count = mapping.mapping.length;
576534
var materials = [];
535+
var count = 0;
577536

578-
if (count === 0) {
579-
callback(null, materials);
580-
}
581-
582-
var onLoadAsset = function (err, asset) {
583-
materials.push(asset);
584-
count--;
585-
if (count === 0)
586-
callback(null, materials);
537+
var onMaterialLoaded = function (err, materialAsset) {
538+
// load dependent textures
539+
self._loadTextures(materialAsset, function (err, textures) {
540+
materials.push(materialAsset);
541+
if (materials.length === count) {
542+
callback(null, materials);
543+
}
544+
});
587545
};
588546

589-
for (i = 0; i < mapping.mapping.length; i++) {
547+
for (var i = 0; i < mapping.mapping.length; i++) {
590548
var path = mapping.mapping[i].path;
591549
if (path) {
592-
path = pc.path.join(dir, path);
593-
self.loadFromUrl(path, "material", onLoadAsset);
594-
} else {
595-
count--;
550+
count++;
551+
self.loadFromUrl(modelAsset.getAbsoluteUrl(path), "material", onMaterialLoaded);
596552
}
597553
}
554+
555+
if (count === 0) {
556+
callback(null, materials);
557+
}
598558
},
599559

600-
// private method used for engine-only loading of model data
601-
_loadTextures: function (materialAssets, callback) {
560+
// private method used for engine-only loading of the textures referenced by
561+
// the material asset
562+
_loadTextures: function (materialAsset, callback) {
602563
var self = this;
603-
var i;
604-
var used = {}; // prevent duplicate urls
605-
var urls = [];
606564
var textures = [];
607565
var count = 0;
608-
for (i = 0; i < materialAssets.length; i++) {
609-
var materialData = materialAssets[i].data;
610-
611-
if (materialData.mappingFormat !== 'path') {
612-
console.warn('Skipping: ' + materialAssets[i].name + ', material files must be mappingFormat: "path" to be loaded from URL');
613-
continue;
614-
}
615-
616-
var url = materialAssets[i].getFileUrl();
617-
var dir = pc.path.getDirectory(url);
618-
if (dir) {
619-
// pc.path.getDirectory never returns a path ending in a forward slash so add one
620-
dir += '/';
621-
622-
if (this.prefix && dir.startsWith(this.prefix)) {
623-
dir = dir.slice(this.prefix.length);
624-
}
625-
}
626-
627-
var textureUrl;
628-
629-
for (var pi = 0; pi < pc.StandardMaterial.TEXTURE_PARAMETERS.length; pi++) {
630-
var paramName = pc.StandardMaterial.TEXTURE_PARAMETERS[pi];
631-
632-
if (materialData[paramName] && typeof(materialData[paramName]) === 'string') {
633-
var texturePath = materialData[paramName];
634-
textureUrl = pc.path.join(dir, texturePath);
635-
if (!used[textureUrl]) {
636-
used[textureUrl] = true;
637-
urls.push(textureUrl);
638-
count++;
639-
}
640-
}
641-
}
642-
}
643566

644-
if (!count) {
567+
var data = materialAsset.data;
568+
if (data.mappingFormat !== 'path') {
569+
// #ifdef DEBUG
570+
console.warn('Skipping: ' + materialAsset.name + ', material files must be mappingFormat: "path" to be loaded from URL');
571+
// #endif
645572
callback(null, textures);
646573
return;
647574
}
648575

649-
var onLoadAsset = function (err, texture) {
650-
textures.push(texture);
651-
count--;
652-
576+
var onTextureLoaded = function (err, texture) {
653577
if (err) console.error(err);
654-
655-
if (count === 0)
578+
textures.push(texture);
579+
if (textures.length === count) {
656580
callback(null, textures);
581+
}
657582
};
658583

659-
for (i = 0; i < urls.length; i++)
660-
self.loadFromUrl(urls[i], "texture", onLoadAsset);
584+
var texParams = pc.StandardMaterial.TEXTURE_PARAMETERS;
585+
for (var i = 0; i < texParams.length; i++) {
586+
var path = data[texParams[i]];
587+
if (path && typeof(path) === 'string') {
588+
count++;
589+
self.loadFromUrl(materialAsset.getAbsoluteUrl(path), "texture", onTextureLoaded);
590+
}
591+
}
592+
593+
if (count === 0) {
594+
callback(null, textures);
595+
}
661596
},
662597

663598
/**

src/asset/asset.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Object.assign(pc, function () {
6767
* @property {string} [file.filename] The filename of the resource file
6868
* @property {number} [file.size] The size of the resource file
6969
* @property {string} [file.hash] The MD5 hash of the resource file data and the Asset data field
70-
* @property {object} data JSON data that contains either the complete resource data (e.g. in the case of a material) or additional data (e.g. in the case of a model it contains mappings from mesh to material)
70+
* @property {object} [data] Optional JSON data that contains either the complete resource data (e.g. in the case of a material) or additional data (e.g. in the case of a model it contains mappings from mesh to material)
7171
* @property {object} resource A reference to the resource when the asset is loaded. e.g. a {@link pc.Texture} or a {@link pc.Model}
7272
* @property {Array} resources A reference to the resources of the asset when it's loaded. An asset can hold more runtime resources than one e.g. cubemaps
7373
* @property {boolean} preload If true the asset will be loaded during the preload phase of application set up.
@@ -223,6 +223,19 @@ Object.assign(pc, function () {
223223
return this.file;
224224
},
225225

226+
/**
227+
* @private
228+
* @function
229+
* @name pcAsset#getAbsoluteUrl
230+
* @description Construct an asset URL from this asset's location and a relative path
231+
* @param {string} relativePath - The relative path to be concatenated to this asset's base url
232+
* @returns {string} Resulting URL of the asset
233+
*/
234+
getAbsoluteUrl: function (relativePath) {
235+
var base = pc.path.getDirectory(this.file.url);
236+
return pc.path.join(base, relativePath);
237+
},
238+
226239
/**
227240
* @private
228241
* @function
@@ -314,16 +327,20 @@ Object.assign(pc, function () {
314327
* // asset.resource is null
315328
*/
316329
unload: function () {
317-
if (!this.loaded && !this.resource)
330+
if (!this.loaded && this._resources.length === 0)
318331
return;
319332

320333
this.fire('unload', this);
321334
this.registry.fire('unload:' + this.id, this);
322335

323-
if (this.resource && this.resource.destroy)
324-
this.resource.destroy();
336+
for (var i = 0; i < this._resources.length; ++i) {
337+
var resource = this._resources[i];
338+
if (resource && resource.destroy) {
339+
resource.destroy();
340+
}
341+
}
325342

326-
this.resource = null;
343+
this.resources = [];
327344
this.loaded = false;
328345

329346
if (this.file) {

0 commit comments

Comments
 (0)