Using `scene.createDefaultEnvironment` removes textures from `scene`

Calling scene.createDefaultEnvironment(); removes most textures from scene.textures:

const createScene = async function () {
    const scene = new BABYLON.Scene(engine);

    function onTextureAdded(texture) {
        console.warn('==================================================================================================================');
        console.warn('Texture added ➕:', texture.uniqueId);

        const allTextures = scene.textures.map(x => x.uniqueId);
        console.warn('All textures in scene:', allTextures);
    }

    scene.onNewTextureAddedObservable.add(onTextureAdded);

    const urls = {
        babylon: 'https://raw.githubusercontent.com/CombeeMike/babylonjs-test-files/main/cup/cup.full.babylon'
    }
    
    const assetContainer = await BABYLON.SceneLoader.LoadAssetContainerAsync(urls.babylon, '', scene);
    assetContainer.meshes.forEach(x => scene.addMesh(x));
    scene.createDefaultCameraOrLight(true, true, true);
    console.warn('==================================================================================================================');
    console.warn('Adding default env');
    scene.createDefaultEnvironment();

    return scene;
};

Is this somehow expected behavior or a bug?

Actually, createDefaultEnvironment does not remove textures, it creates new ones!

If you add an event observer for “texture removed” you will see that the list of textures holds only a single texture before createDefaultEnvironment is called and has 4 after:

1 Like

Interesting, so I draw the incorrect conclusion that the removal was because of createDefaultEnvironment whilst it simply happens “right before” the call.

So this changes the question:
What is causing the textures from being removed in the first place? Is this somehow by design that I don’t understand?

I don’t really know but I would guess those are temporary textures that are created for some reasons and that are removed when not used anymore.

Ok, does someone else have an idea why that happens?

Those textures are created since they’re referenced by materials defined inside the loaded babylon file. They are shown in the “final” scene and are not disposed (actively) at any point.

I just realized that the materials, which reference them are at some point also removed from scene.materials and are therefore not shown in the inspector neither :astonished:

The definition of those materials inside the loaded babylon file is rather straight forward and looks like this (1 of them):

{
  "name": "mat_porcelan",
  "id": "mat_porcelan",
  "customType": "BABYLON.PBRMaterial",
  "emissive": [ 0, 0, 0 ],
  "reflectivity": [ 0.5091, 0.5091, 0.5091 ],
  "roughness": 0.5528,
  "alpha": 1,
  "transparencyMode": 0,
  "alphaCutOff": 0.4,
  "metallic": 0,
  "useAlphaFromAlbedoTexture": false,
  "albedoTexture": {
    "url": "cup_smooth_bc.jpg",
    "uAng": 3.141592653589793,
    "name": "cup_smooth_bc.jpg",
    "invertY": false
  },
  "bumpTexture": {
    "url": "cup_smooth_norm.jpg",
    "uAng": 3.141592653589793,
    "name": "cup_smooth_norm.jpg",
    "invertY": false
  },
  "metallicTexture": {
    "url": "cup_smooth_orm.jpg",
    "uAng": 3.141592653589793,
    "name": "cup_smooth_orm.jpg",
    "invertY": false
  },
  "useRoughnessFromMetallicTextureAlpha": false,
  "useRoughnessFromMetallicTextureGreen": true,
  "useMetallnessFromMetallicTextureBlue": true,
  "lightmapTexture": null,
  "useAmbientOcclusionFromMetallicTextureRed": true
}

They probably are part of the asset container your loaded but not in the scene as you manually only copy meshes in :wink:

Also, if I recall correctly, if a material is defined in a gltf file but not used by any mesh, it is not added to the scene (or maybe it is added during loading but removed in an after pass). I think there’s a flag in the gltf loader that let’s you keep the materials (and the associated ressources) from a gltf even if they are not actively used.

[…] Try the loadAllMaterials flag: by default it is set to false.

[EDIT] Ah, you are loading a .babylon file…

@sebavan

Ok, it’s probably getting a little embarrassing right now but how do I correctly load the “complete” asset container with all its materials & textures into the scene when using LoadAssetContainerAsync :thinking:?

A little background might be helpful at this point:

We’re building our own wrapper around BJS, which is mostly meant to be used inside configurators of our platform Combeenation. This wrapper provides some APIs in a way which is “tailer-made” for the specific use cases of our configurators. Since this “BJS based” viewer of ours is used in many different projects, it is implemented in a rather generic way.

One such use case is that most of our configurators would like to show a load mask above the 3d model whenever a texture is loading in the background and we therefore added 2 events “texture load start” & “texture load end” which can be used to show & hide those load masks.

Those 2 events are basically built around scene.onNewTextureAddedObservable, texture.onLoadObservable and scene.textures which is why I’m currently asking so many questions regarding those topics (i.e. this one and this one).

Long story short:
Our viewer is currently using LoadAssetContainerAsync and we probably can’t simply replace it with something else like SceneLoader.Append (which probably automatically adds the materials etc. as well), so I’d need to know how to add all those assets correctly when using LoadAssetContainerAsync :sweat_smile:

This would be the simplest Asset Containers | Babylon.js Documentation

If you do manually, you need to do it for all asset types.