Load GLTF into AssetContainer from FilePicker window/File API object

I’m currently working on a project where I want to be able to load and position various .gltf files that I have locally. The current method I have of loading them relies on having them bundled with the build and loading them with a relative path, but this doesn’t allow new models to be tested.

The API docs say that LoadAssetContainerAsync() should work with a File object, but fails with both a .glb and .gltf when tried using showFilePicker().

Is there any way to go from a file picker File to an AssetContainer?

1 Like

You should be able to do this, though you may need to specify some combination of the correct file extension, mime type, or pluginExtension option of LoadAssetContainerAsync. Here are all of the options in action, just to demonstrate:

const glbFile = new File([glbData], "myFile.glb", { type: "model/gltf-binary" });
const gltfFile = new File([gltfData], "myFile.gltf", { type: "model/gltf+json" });
const glbResult = await LoadAssetContainerAsync(glbFile, scene, { pluginExtension: ".glb" });
const gltfResult = await LoadAssetContainerAsync(gltfFile, scene, { pluginExtension: ".gltf" });

If none of this helps, could you share a repro in the playground?

And welcome to the forum!! :slight_smile:

For the [glbData], is that going through FileReader, and getting an arrayBuffer or dataURL? I didn’t expect it to work as is, but I get a TypeError: path.lastIndexOf is not a function.

Doing roughly this after getting a full directory of files with showDirectoryPicker()

if (entry.name.includes(".gltf") || entry.name.includes(".glb"))
{
    let fileData = await entry.getFile();
    let dataURL = URL.createObjectURL(fileData);
    const glbFile = new File([dataURL], "myFile.glb", { type: "model/gltf-binary" });
    let newModel = await BABYLON.SceneLoader.LoadAssetContainerAsync(glbFile, null, { pluginExtension: ".glb" });
    return;
}

You should just use the File directly instead of converting it into an object URL. See this playground for more info:
https://playground.babylonjs.com/#9ZRSPR

This is fine for GLBs. For GLTFs, there’s probably more to it, since it can reference external files (like images, binaries) that will also need to be loaded alongside the GLTF.

Thank you for the playground, that actually cleared a few things up. Not sure if I was on an older version, or the LoadAssetContainerAsync was being referenced incorrectly, but it’s handling the Files properly now.

Any chance of knowing how properly loading the multiple parts of the GLTF would work? It has to be possible, as the model viewer playground lets you multiselect all the parts of the GLTF from the file picker and that properly displays, but the code is minified enough I can’t figure out what it’s actually doing.

Got this working as I needed it now, on /core@8.49.6 and /loader@8.49.6

This is a use case for loading a single GLTF, as there’s some pre-processing that needs to be done with selecting all the parts of multiple GLTF files, or selecting a directory and parsing through the whole thing:

var tempscene = new BABYLON.Scene(yourEngineReference);
var gltfList = [gltfFile, binFile, pngFile]; //each of these are the FileSystemFileHandle.getFile from your directoryPicker/filePicker

const fi = new BABYLON.FilesInput(yourEngineReference, tempscene, containerCallback, null, null, null, null, null, null, true);

let newTransfer = { dataTransfer: { files: gltfList } };

async function containerCallback(file, loadedScene)
{
	let newAssetContainer = new BABYLON.AssetContainer();
	loadedScene.rootNodes.forEach(node =>
	{
		newAssetContainer.addAllAssetsToContainer(node);
	});

	newAssetContainer.removeAllFromScene();
	tempscene.dispose();
}

fi.dispose();
fi.loadFiles(newTransfer);

I was a little too focused on having it work exactly like LoadAssetContainerAsync(), and the solution was loading into a temporary scene, transferring into a assetContainer, and then disposing the temp scene, which changes all the mesh/material/geometry/whatever references to look the same in the new assetContainer as it would loading directly into one.

1 Like