Is there a better way to load a bunch of objects from the server?

I’ve been building a game for a while now, and am starting to get closer to release. During development I’ve had all assets (3d models, images, materials, etc.) on the client. But I’ve now moved these to the server, and my strategy is as follows:

On the client, I have a list of all the URLs for the assets. I then use the fetch API to get every .obj, .mtl, .png, and .json file. (I use cache: ‘force-cache’, so the client doesn’t need to download each time). After they are downloaded (cached locally), I let the garbage collector delete themfrom memory, as I simply want to them to be cached client-side at this point. This way, the files are cached locally and can be quickly imported on the fly during gameplay, when entering a new area, level, etc.) Essentially, I am ‘downloading’ the entire game at the start.

It works, but in total there are about 150 assets to get, and I don’t want to call fetch that many times. Most of the assets are .obj and .mtl files.

I understand a single .babylon file can hold all the mesh files, but again I don’t need to load every single object at once, so I can’t use a babylon file to represent a ‘scene’ in my game.

Are there any tips anyone can give?

Thanks

Hey! There is no absolutely perfect strategy here.
It ultimately depend on how your server is setup. Multiple fetches can be really cool as they will load data in parallel so faster than one unique load

Hey, thanks for the quick reply. Here is the actual code I’m using:

await Promise.all(MeshUrls.getFullUrls().map((meshUrl : string) => {
    fetch(meshUrl, { 
        method: 'GET', 
        cache: 'force-cache', 
        headers: new Headers({'content-type': 'text/plain'}) 
    })
}));

I’m just wondering how much of an impact it’ll be on the server. The size isn’t an issue, all together the JS bundle is 16MB, and in total the assets are about another 3MB.

The files are getting cached after fetching them (subsequent page reloads shows the .obj files are being served from disk cache), however when Babylon tries to import them, they are not being loaded from cache, and instead they are being downloaded again. I’m using Express for my nodejs server. The code I’m using for adding response headers is:

    this._app.use(function(req : any, res : any, next : any) {
        res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
        res.header('Access-Control-Allow-Methods', 'GET');
        res.header('Access-Control-Allow-Headers', 'Content-Type');
        res.header('Cache-Control', 'max-age=1y, public');
        
        next();
    });

In fact, Babylon seems to be inconsistent on using the cached file or not.

As you can see, sometimes it reads from the cache, and other times it doesn’t (two subsequent page refreshes seen below)

cacheproblems

Also, I’m using SceneLoader.ImportMeshAsync() to import meshes as they are needed.

Well this is not something we control :slight_smile:
We are just issuing a XHR to the URL

Hmm, ok. I guess a fallback strategy is to save the meshes & images as blobs/base64 in IndexedDB after fetching them, then load them from object/data URLs. I will update it and see how it goes

I have an error when trying to import a .glb from an object URL:

SyntaxError: Unexpected token g in JSON at position 0
    at JSON.parse (<anonymous>)
    at Object.importMesh (babylon.js:16)
    at babylon.js:16
    at f (babylon.js:16)
    at XMLHttpRequest.p (babylon.js:16)

g is the first character in the file. I am importing this way:

await SceneLoader.ImportMeshAsync('', objectUrl, '', scene);

Using an object url, should I be loading it a different way? Seems like it shouldn’t be calling JSON.parse

It should work but you need to give us a repro to let us help :slight_smile:

Seems like I just forgot to include the plugin extension parameter “.glb”. Fixed the error, but the mesh isn’t in the scene. I’ll fiddle around with it a bit more to see what’s happening.

Also, I wish I read up on the .glb file type earlier. Now I’ll just convert my .obj files to .glb, this way I won’t need to download a .mtl file for each mesh, which will certainly help on reducing the number of network requests.

Edit: here is a repro, with regards to being unable to see the .glb mesh in my scene:

https://www.babylonjs-playground.com/#46AZYT#1

it is here but REALLY small:

1 Like

Oh wow, you’re right haha. I exported it from 3D Builder, and must’ve used mm instead of meter (for OBJ I had to use mm settings for some reason)

Also, I notice that it actually creates two meshes (console.log(data.meshes)). Do you know why that is the case?

@Sme, if you really want to get vested in caching resources, Service Workers are the way to go. Caching there has virtually unlimited complexity, since you write the rules in JavaScript.

I use them. I recommend them. They are relatively new but they have a lot of support across browsers. It does lend a new dimension (complexity) though.