Handling Material and Texture copies

Hi guys,

So I’ve run into a problem that I don’t fully understand. I’m importing glTFs into my scene and each file is using same materials. Yet the mesh is always different. Some meshes don’t use all materials, all of them share at least ONE the same material.

And what I noticed that when you import glTF asset it creates some instance of a material. And that would be ok, because I think Unity does exact same thing (that each asset has an instance of the material). However what worries me more is that each material also instantiates the shared texture…

I’m using just few assets for now, however in the future I will have thousands or more… So that would mean that for each glTF has an instanced material and each has a separate texture instance?

I’ve tried to collect all the ‘shared’ materials and textures and then reassigning to the assets that use same materials(by name). And in the end destroying the duplicates with material.dispose() in the onReadyObservable yet with insignifficant results…

image

Hello! Do you know all of the materials that will be loaded in the scene? If so, you could load all those materials at once: GLTFFileLoader | Babylon.js Documentation (babylonjs.com), keep a reference to the materials and then skip the material loading on subsequent files: GLTFFileLoader | Babylon.js Documentation (babylonjs.com). This won’t automatically assign the already existing materials to the subsequent meshes, but you can control this through code instead.

1 Like

Here’s an example of how this could be done :slight_smile: Loading materials | Babylon.js Playground (babylonjs.com) I accidentally had two materials with the same name here, but it should work if all materials have different names xD

1 Like

Thank you so much! This helped a lot.

First of all I was using SceneLoader.Append which in success callback returns just a scene, which was super annoying. With SceneLoader.ImportMesh I can at least get what’s been imported. Also I’m confused that material.dispose() doesn’t always dispose properly, and I have to force the dispose with scene.materials.splice(i, 1).

But then in the end I’m still using onReadyObservable hook, as this seems to have a proper result for me:

    const uniqueMatNames = []
    const uniqueMats = []
    const assets = ['HomeLayout', 'Bedroom', 'SpareRoom', 'Kitchen', 'OfficeRoom', 'Bathroom', 'Restroom']
    assets.forEach(asset => {
      SceneLoader.OnPluginActivatedObservable.addOnce((plugin) => {
        if (plugin instanceof GLTFFileLoader) {
          plugin.loadAllMaterials = true
          plugin.loadOnlyMaterials = true
        }
      })

      SceneLoader.ImportMesh('', '/assets/', `${asset}.gltf`, scene, (asset) => {
        const newMats = scene.materials
        for (const mat of newMats) {
          if (uniqueMatNames.indexOf(mat.name) === -1 && mat.name !== 'BackgroundPlaneMaterial' && mat.name !== 'BackgroundSkyboxMaterial' && mat.name !== 'default material') {
            uniqueMatNames.push(mat.name)
            uniqueMats.push(mat)
          }
        }
      })
    })

    scene.onReadyObservable.add(function () {
      for (let i = scene.materials.length - 1; i >= 0; i--) {
        if (uniqueMats.indexOf(scene.materials[i]) !== -1 || scene.materials[i].name === 'BackgroundPlaneMaterial' || scene.materials[i].name === 'BackgroundSkyboxMaterial' || scene.materials[i].name === 'default material') {
          console.log('Keeping:', scene.materials[i].name)
        } else {
          console.log('Disposing:', scene.materials[i].name)
          scene.materials[i].albedoTexture.dispose()
          scene.materials.splice(i, 1)
        }
      }
    })

Ofcourse this only loads unique materials and no meshes yet, but it’s a good start!

Hmmm that’s a bit weird, only dispose should have been enough… How did you notice that the materials weren’t disposed?

Because in the inspector some of the materials were still duplicated.

However when now I’m trying material.dispose() they are all disposed properly, no duplicates. Even though I haven’t changed anything in the code since then…

Hmmmm that might be a inspector bug, not being updated when the material is disposed of. I’ll dig into it and see what I find :thinking:

Hey @Jozef_Plata just checking, did you encounter the materials on inspector after dispose again? I tried reproducing it here but with no success :thinking:

Hi @carolhmj so I cannot reproduce the issue I had before. And the meterial.dispose() works as expected.

However I’m still using onReadyObservable hook to dispose all the spare materials and reassign them.

Here’s an example that quite works (last asset is not loading properly, but at least it’s not assigning incorrect materials) https://playground.babylonjs.com/#CCVH4D#30