Yo @bghgary … Can you look over my new Submesh And Multi Material Extension.
Its working beautiful… but i had to guess at the promise chain for creating materials in a loop synchronously and pushing promises in a loop to load the material properties for each loaded material asynchronously… Thats in my new _loadMultiMaterialAsync function. Can you look that over and see if im using promise chain API as intended ???
I also will include my COPY lof a custom _loadMeshPrimitiveAsync function I HAD TO COPY to get to call my _loadMultiMaterialAsync if the metadata has a multimaterial property and after you apply the mesh geometry i create the submeshes…
Again is working, but f you can look it over for me… as you are the GLTF Guru 
public _loadMeshPrimitiveAsync(context: string, name: string, node: BABYLON.GLTF2.INode, mesh: BABYLON.GLTF2.IMesh, primitive: BABYLON.GLTF2.IMeshPrimitive, assign: (babylonMesh: BABYLON.AbstractMesh) => void): Promise<BABYLON.AbstractMesh> {
const loader:any = this._loader;
//console.warn("CVTOOLS: LoadMeshPrimitiveAsync: " + name);
loader.logOpen(`${context}`);
const canInstance = (node.skin == undefined && !mesh.primitives[0].targets);
let babylonAbstractMesh: BABYLON.AbstractMesh;
let promise: Promise<any>;
const instanceData = (<any>primitive)._instanceData;
if (canInstance && instanceData) {
babylonAbstractMesh = instanceData.babylonSourceMesh.createInstance(name);
promise = instanceData.promise;
}
else {
const promises = new Array<Promise<any>>();
const babylonMesh = new BABYLON.Mesh(name, loader._babylonScene);
loader._createMorphTargets(context, node, mesh, primitive, babylonMesh);
promises.push(loader._loadVertexDataAsync(context, primitive, babylonMesh).then((babylonGeometry) => {
return loader._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(() => {
babylonGeometry.applyToMesh(babylonMesh);
if (primitive.extras != null && primitive.extras.metadata != null && primitive.extras.metadata.multimaterial != null && primitive.extras.metadata.submeshes != null) {
//////////////////////////////////////////////////////
// Mackey Primitives Modifications
//////////////////////////////////////////////////////
const submeshes:any = primitive.extras.metadata.submeshes;
babylonMesh.subMeshes = [];
for (let subIndex = 0; subIndex < submeshes.length; subIndex++) {
const parsedSubMesh = submeshes[subIndex];
BABYLON.SubMesh.AddToMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, <BABYLON.AbstractMesh>babylonMesh);
}
//////////////////////////////////////////////////////
}
});
}));
const babylonDrawMode = (<any>BABYLON.GLTF2.GLTFLoader)._GetDrawMode(context, primitive.mode);
if (primitive.extras != null && primitive.extras.metadata != null && primitive.extras.metadata.multimaterial != null && primitive.extras.metadata.submeshes != null) {
//////////////////////////////////////////////////////
// Mackey Primitives Modifications
//////////////////////////////////////////////////////
const multimaterial:any = primitive.extras.metadata.multimaterial;
const materialids:string[] = multimaterial.materials;
const materials:BABYLON.GLTF2.IMaterial[] = [];
const matid = multimaterial.id || (mesh.name + "_multi");
const matname = multimaterial.name || (mesh.name + "_multi");
const multimat = new BABYLON.MultiMaterial(matname, loader._babylonScene);
multimat.id = matid;
materialids.forEach((materialId) => {
const material:any = BABYLON.GLTF2.ArrayItem.Get(`${context}/material`, loader._gltf.materials, parseInt(materialId));
materials.push(material);
});
promises.push(this._loadMultiMaterialAsync(`/materials/${matname}`, materials, babylonMesh, babylonDrawMode, (babylonMaterials) => {
multimat.subMaterials = babylonMaterials;
babylonMesh.material = multimat;
}));
//////////////////////////////////////////////////////
} else if (primitive.material == undefined) {
let babylonMaterial = loader._defaultBabylonMaterialData[babylonDrawMode];
if (!babylonMaterial) {
babylonMaterial = loader._createDefaultMaterial("__GLTFLoader._default", babylonDrawMode);
loader._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
loader._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
}
babylonMesh.material = babylonMaterial;
} else {
const material:any = BABYLON.GLTF2.ArrayItem.Get(`${context}/material`, loader._gltf.materials, primitive.material);
promises.push(loader._loadMaterialAsync(`/materials/${material.index}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
babylonMesh.material = babylonMaterial;
}));
}
promise = Promise.all(promises);
if (canInstance) {
(<any>primitive)._instanceData = {
babylonSourceMesh: babylonMesh,
promise: promise
};
}
babylonAbstractMesh = babylonMesh;
}
BABYLON.GLTF2.GLTFLoader.AddPointerMetadata(babylonAbstractMesh, context);
loader._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
assign(babylonAbstractMesh);
loader.logClose();
return promise.then(() => {
return babylonAbstractMesh;
});
}
private _loadMultiMaterialAsync(context: string, materials: BABYLON.GLTF2.IMaterial[], babylonMesh: BABYLON.Mesh, babylonDrawMode: number, assign: (babylonMaterials: BABYLON.Material[]) => void = () => { }): Promise<BABYLON.Material[]> {
const loader:any = this._loader;
const xmaterials:any = materials;
//console.warn("CVTOOLS: LoadMultiMaterialAsync: " + babylonMesh.name);
xmaterials._data = xmaterials._data || {};
let babylonData = xmaterials._data[babylonDrawMode];
if (!babylonData) {
loader.logOpen(`${context} ${babylonMesh.name || ""}`);
const babylonMaterials:BABYLON.Material[] = [];
const promises = new Array<Promise<any>>();
materials.forEach((material) => {
const babylonMaterial = loader.createMaterial(context, material, babylonDrawMode);
BABYLON.GLTF2.GLTFLoader.AddPointerMetadata(babylonMaterial, context);
loader._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
promises.push(loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
babylonMaterials.push(babylonMaterial);
});
babylonData = {
babylonMaterials: babylonMaterials,
babylonMeshes: [],
promises: promises
};
xmaterials._data[babylonDrawMode] = babylonData;
loader.logClose();
}
babylonData.babylonMeshes.push(babylonMesh);
babylonMesh.onDisposeObservable.addOnce(() => {
const index = babylonData.babylonMeshes.indexOf(babylonMesh);
if (index !== -1) {
babylonData.babylonMeshes.splice(index, 1);
}
});
assign(babylonData.babylonMaterials);
return Promise.all(babylonData.promises).then(() => {
return babylonData.babylonMaterials;
});
}