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.
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
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.
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();
}
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.
@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?
@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.
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.