Load mesh without adding to scene

I’m building a small POC game, and I’m looking at the loader, but I can only find methods that immediately add the loaded obj to the scene. How can I “preload” an obj file and then use the loaded data to create multiple objects with the same model during the game?

2 Likes

One thing you might do is actually fully load it, but disable it. This way the shader will already be compiled. When you enable it, it will not have a frame studder.

I understand that you might think just downloading in advance might give better initial load time, but coming in the very near future, is the ability to compile shaders on worker threads (BabylonJS is already pre-coded for this in 4.0). Think this kind of tips the balance for disabled loading.

The Blender exporter, at least, can export mesh already disabled. Do not know about the others, but it is not hard to do.

1 Like

For the multiples part of your question, Once you have a mesh, you can create either instances or clones.

Clones share geometry, but can have different materials. They require a separate draw call. If you share the same material, you can make an instance. These draw in the same call on most hardware. Both still have the same cpu overhead.

If none of them ever change position, rotation, or scale ( like trees ), consider cloning, then merging. Now they will happen in one draw, but have the cpu overhead of only one mesh. This increases GPU memory, but most of the time this is not that much of a limiting factor.

1 Like

You can take a look at this post as I guess you are looking for the AssetContainer:

2 Likes

@JCPalmer Thanks for the great explanation, it helped me understand a bit more what’s going on (wrt compiling the shaders and clones vs instances).

I think having my meshes fully loaded and ready to go would be ideal. I just don’t need a lot of them at the start of the game but they do need to spawn in later during the game, so disabling the until they’re needed sounds like the way to go.

And here I was gonna ask how to do that but @sebavan was one step ahead, AssetContainer seems to be piece of the puzzle that I was missing. Thanks for the help! :slight_smile:

So I’ve been trying to implement the AssetContainer, but it only has addAllToScene(). So how would I go about having a “root” mesh not in the scene and then create instances of it in my scene?

you can get your mesh from assetsContainer.meshes

I got that, but how do I add an instance of the mesh to my scene, without adding the original mesh as well?

you should just be able to clone it

Do you have an example of how to do this? Cause I don’t seem to be able to get it working.

What I’m trying to do:

const container = await SceneLoader.LoadAssetContainerAsync('res://', 'filename.obj');
container.createRootMesh();

function get() : InstancedMesh {
    const rootMesh = container.meshes[0] as Mesh;
    const instance = rootMesh.createInstance(`name`);
    return instance.clone(`id`, scene.rootNodes[0]);
}

Here’s some code from a game I have that loads a few dozen meshes, hides them all, and then I make instances of those meshes when players spawn later. I skipped a lot but I hope it can still be useful. I was using assetsManager, here’s the link: ( Babylon.js Documentation )

const Atlas = {
  gunMeshes = new Map(),
  skinMeshes = new Map(),
  // etc, lots
}

const guns = [
    'ak.obj',
    'smg.obj',
    'sniper.obj' // etc
]
// skipped code that creates assetsManager

// queue the guns to be loaded...
guns.forEach(gun => {
    const path = `${basePath}models/`
    const name = `load ${path}${gun}`
    const meshTask = assetsManager.addMeshTask(name, '', path, gun)

    meshTask.onSuccess = (task) => {
        // disable the original mesh and store it in the Atlas
        task.loadedMeshes[0].setEnabled(false)
        Atlas.gunMeshes.set(gun, task.loadedMeshes[0])
    }
})
const skins = [...]
// etc for like 30 other meshes
// skipped code that tells assetsManager to load all of the stuff queued above

And then eventually there is some code that reuses these meshes:

function equipWeapon(player, weaponString) {
    player.weapon = Atlas.gunMeshes.get(weaponString).createInstance()
    player.weapon.setParent(player.model.rightHand)    
}
...
equipWeapon(player4, 'ak.obj') // this is an instance of ak, not the original

A quick explanation is that I maintain an object named Atlas which stores a disabled copy of every mesh that I need. Atlas is a made up thing in my own code, and not a babylon concept. I’m careful to use the AssetsManager and program in an async way to get everything loaded prior to creating any objects that need one of these meshes. The meshes are stored by the name of their file such as ak.obj. To create a mesh I access the original copy from the Atlas, and I use createInstance to create a copy of it.

5 Likes

You cannot clone an instance. Either you clone or you instanciate a mesh buy you cannot do both
Sonin your case I would call assets container[0].createInstance() or .clone()

Your example code helped a lot to see how the asset manager is supposed to work. Some of my objects have multiple meshes so I had a little more work to get it up and running, and it turns out I need to use clone() after all for those objects cause createInstance() doesn’t include the child meshes, but it’s all working now. Thanks for the help! :smile:

Wonderful:)

Hello, woubuc. now I have the same needs as you. Can you find the best solution? Can you share the implementation or code? thank you very much

I know the topic is old, but Google led me here looking for an answer for how to load a mesh without adding it to the scene immediately. Since the thread did not provide a copy/paste ready answer, I thought I contribute my solution to it. This works for me.

// load the mesh  
var container = await BABYLON.SceneLoader.LoadAssetContainerAsync(
    "https://models.babylonjs.com/TrailMeshSpell/spellDisk.glb"
  );
// whenever you want to add the mesh to the scene
scene.addMesh(container.meshes[0], true);
3 Likes