"asset.ready" firing when asset isn't loaded yet?

Hello everyone.

I’ve been fiddling with the AssetRegistry for a project I’ve been tasked with, but I can’t get the “load” event to work as intended.

It’s basicly a 3D model showcase demo: I pre-load a mesh-only scene at start and anything else is loaded later via app.assets.load(asset) calls. Some textures are relatively high-res, so I’m trying to implement a sort of LOD system.

This is how things are executed as of now.
[EDIT: 04/21]
Do note that this is just a rough run-down snippet of the current logic I implemented for this feature. It’s not the actual code. Things like asset availability checks (through either asset.loaded or asset.resource) are still done, of course

var canLoadNewMaterial = false;

var loadMaterialAsset = function (myAssetName) {
    var materialAsset = this.app.assets.find(myAssetName, "material");
    materialAsset.ready(onMaterialAssetLoaded);
    canLoadNewMaterial = true;
    this.app.assets.load(materialAsset);
}

var onMaterialAssetLoaded = function (asset) {
    //This is executed BEFORE the asset's HTML GET request is actually completed
    targetScript.entity.meshInstances[0].material = asset.resource;
    canLoadNewMaterial = false;
}

//targetScript is the PlayCanvas script attached to the entity whose mesh and materials need to be updated/loaded on-demand

This leaves behind a HTML GET request per each LOD level which never gets shown, only consuming bandwidth and memory. The only one showing is, of course, the last, highest level one, as soon as its own HTML request completes.

I’ve seen a couple of other open projects floating around the PlayCanvas portal and they seem to work around the matter by creating multiple copies of the model entities with pre-assigned materials, but I can’t do this, unfortunately. The model being showcased has to be almost fully customizable, and it’d mean creating hundreds of combinations between the various pieces, not to mention it’d make the project a maintenance hell.

Is there any way to actually tell the script to switch the Material only AFTER the request is completed?
Did I miss something or do something wrong?

Thanks in advance

pc.Material will be created for asset.resource for actual asset.data, and not loading it from any external file. Because all material data is already available, calling assets.load(materialAsset) - will simply create that material.
Loading all related textures - will be handled by material loader, but material will be created before that, as all necessary properties are already available.

It is better to do this:

var asset = this.app.assets.find(assetName, 'material');

var onMaterialAvailable = function() {
    // asset.resource - is available
};

if (asset.resource) {
    onMaterialAvailable();
} else {
    asset.once('load', onMaterialAvailable);
    this.app.assets.load(asset);
}

This makes it more clear of what is going on. But with async nature of loading assets, any other assets you reference should be treated with assumption that they can be not yet loaded. So when you refer to a model to assign material, you always should check if model is loaded too, if not, then subscribe to event of model loading, and assign only then.

1 Like

I tried changing my code with your suggestion, but I still can’t get the desired effect out of it.
In any case, if I’m getting right what you said, I’m targeting the wrong asset type.
I should’ve also clarified that the code above was just a rough run-down of how the current logic is working (I will edit it after this reply)

  • I already check that any asset I load is available before assigning its reference via asset.resource
  • I was checking for asset.loaded rather than asset.resource, which actually sounds clearer as you’ve said, so I’ve switched those two

The issue still lies within how PlayCanvas loads materials. If it gives me a reference to a material before the actual textures are loaded, since this “material loader” handles all textures separately and asynchronously, then the only way I can do this is by actually loading and waiting on texture assets. Once all of them are loaded for a given material, I can load the material itself.
Perhaps I’m just overthinking stuff, although, so please do tell me if I’m still on the wrong track :slight_smile:

Some devs need material before textures loaded, some need it after. If you need it after, then you can iterate through data fields which point to textures, and check their loading status, and react when all of them (or none needed) to be loaded, and then use material once they all loaded.

This is very similar to the way web pages work - you load HTML, and get it running, before all images on the page loaded, if you need to know when all images are loaded - you need to handle it manually by tracking their loading status.

Ok, I wasn’t off-track then. It’s basicly the same conclusion I ended up with.
I’ll look into it and eventually write back if I have any more questions or my solution to share with others.

Thank you.

1 Like