Preventing duplication of materials and textures on a library of GLTF models

We’re working on building a library of 3d assets, and wondering what the best strategy in Babylon is for re-using textures as much as possible.

Our 3D assets share a common set of textures. Let’s say there are around 100 textures in the game, and all of our GLTF models share them. Exporting from Blender and loading the GLTFs in the game results in each unique model loading a unique copy of the textures it uses, and I’m guessing that’s going to hurt performance and burden low-end devices as our scenes get more complex over time.

I’m thinking the best way to prevent this might be to turn off image export in Blender’s GLTF exporter, make sure Blender material names match a matching library created in Babylon, then apply the materials defined in Babylon to matching names on GLTF files after load? That seems like a huge pain, since it would require that artists maintain parity at all times.

Is there a more formal way of dealing with this?

Good news is that by default the shaders will be shared (as well as textures if they come from the same url).

On your specific question your answer is the right one and I would encourage you to do it just because that way your glb files will be smaller. Perf wise as I mentioned earlier I’m not sure the gain will be huge

1 Like

Have a look at this thread - The glTF optimization process and how to change the glTF image URI at runtime
Still I think you need a set of materials, not a set of textures.

1 Like

The other advantage to having a dynamically loaded and assigned material/texture library, is that you can more easily target a range of devices and network bandwidths e.g. have “low”, “mid” and “high” resolution versions of your textures.

So my preference would be to not export any textures at all, not even placeholder textures, then just dynamically assign materials by mesh name after loading.

But yes, the process/workflow is more convoluted and less artist friendly.

Compartmentalising things like this can also help if you’re concerned about IP/assets being stolen. This won’t prevent it, but makes it more painful for people to have to reassemble.

  1. Load the 2 gltf with glTF-Transform
  2. merge models
  3. merge scenes
        let scenes = doc.getRoot().listScenes();
        let defaultScene = scenes[0];
        doc.getRoot().setDefaultScene(defaultScene);
        // merge scene
        for (let j = 1; j < scenes.length; j++) {
            let children = scenes[j].listChildren();
            for (let k = 0; k < children.length; k++) {
                let node = children[k];
                scenes[j].removeChild(node);
                defaultScene.addChild(node);
            }
            scenes[j].dispose();
        }
  1. unpartition and dedup
  2. Export it and load the merged glb

What if there are several dozens of GLB and some of them should be moving?

I believe prune would be also useful here.

1 Like

Yes, but prune seems kind of aggressive in recent versions

1 Like

Well there is another issue if you dont do this , regardless of babylon sharing textures in shaders… the other issue is that materials with the same name are not resolved to a single material by the engine.

eg , if you have 10 glb models , each of them having a material named “mat1” , if you load all these models into the scene , you will have 10 materials named mat1. This becomes a huge problem when you are doing dynamic color changes or texture replacements on a material. Since your changes would only affect one of the mat1 materials.

I have personally had to deal with this issue building configurators. I resorted to having to check all the materials on a loaded model vs what already exists in the scene. Then switch any matching marerials for the existing ones. This made sure all models with a same named material actually shared a same single material.

In regards to artists and a more complex workflow, this is the nature of the beast when wanting to actually build more professional software. I can assure you in any bigger studio involving asset pipelines , artists would have to follow conventions to make sure the assets are ready for pipeline processing, so this is kind of an expected aspect of a more complex system.

In regards to including textures , I have to agree with others here in saying after doing this for a while I found not including textures is the most flexible manner to do it. The process of mapping these and loading them can be made easier by automating some pipeline procedures with code.

I personally use custom python scripts to bake textures , save them and export glb models to specific folders , then build up a json file mapping textures to materials , even material types ( for custom shaders ) etc. Obviously my project is built to understand the pipelines output directories and the json files.

Once you have a system in place , the asset pipeline actually becomes easier and faster.

2 Likes

Thank you all!

@Deltakosh I’m trying to demonstrate locally that materials are shared when coming from the same URI.

In the following playground, I load the exact same GLTF three times, and I get three copies of the material. Is that expected behavior, or are you referring to creating mesh instances?

This does not seem to be the correct PG. I see it loaded only once

Here is small and very simple example of removing material duplicates by name after loading - https://playground.babylonjs.com/#WGZLGJ#10502

1 Like

And as you can see (thanks @labris), the internal texture are the same even without replacing the materials:
glTF Loader Demo | Babylon.js Playground (babylonjs.com)

The texture object is just a shell around the real data which is shared

1 Like

Sorry, forgot to save before copying the URL.

@deltakosh I thought you were saying that Babylon should automatically detect duplicate materials and textures with this statement “by default the shaders will be shared (as well as textures if they come from the same url).”

I just took the sandbox that @labris sent over and I see four copies of the BoomBox material in the inspector, and four copies of each texture:

I thought that meant it would automatically detect the duplicates, but it looks like you have to do that manually.

Thanks all for your help.

In my PG there is only one material :slight_smile: - https://playground.babylonjs.com/#WGZLGJ#10502

Interesting, maybe there’s a bug here? I’m using Chrome, MacOS 14.6.1

What I’m seeing:

Is there perhaps a Chrome setting I might have fiddled with that would result in this?
Otherwise, we are looking at the exact same playground.

This is Deltakosh PG - https://playground.babylonjs.com/#WGZLGJ#10503
My PG has another URL - https://playground.babylonjs.com/#WGZLGJ#10502 :slight_smile:
The code is a bit different.

OK, sorry. Sounds like it’s working as expected, then.

1 Like

Because its material has been dispose.

1 Like

Seems like you have some solutions already, but in regards to a common texture directory (this is separate from the duplicate material question ):

I export .gltf (binary) instead of .glb and export the texture directory as a relative path (“../textures” ).

I made a tweak to the loader to allow relative paths during texture loading. Unfortunately, the validation method wasn’t public, so I had to tell Typescript to ignore my “hack”.

More discussion in an old thread here:

IIRC, the final code needed was significantly less complicated than was originally discussed. If you want to pursue this option and need a PG, I can probably throw something together.

1 Like

Thanks for that tip! I committed to GLB so long ago that it didn’t occur to me to look at the GLTF exporter options.