|
@@ -33,6 +33,11 @@ module BABYLON.GLTF2 {
|
|
|
readonly BYTES_PER_ELEMENT: number;
|
|
|
}
|
|
|
|
|
|
+ interface GLTFLoaderRequest extends XMLHttpRequest {
|
|
|
+ _loaded: Nullable<number>;
|
|
|
+ _total: Nullable<number>;
|
|
|
+ }
|
|
|
+
|
|
|
export class GLTFLoader implements IGLTFLoader {
|
|
|
public _gltf: IGLTF;
|
|
|
public _babylonScene: Scene;
|
|
@@ -46,7 +51,7 @@ module BABYLON.GLTF2 {
|
|
|
private _progressCallback: (event: ProgressEvent) => void;
|
|
|
private _errorCallback: (message: string) => void;
|
|
|
private _renderReady = false;
|
|
|
- private _requests = new Array<XMLHttpRequest>();
|
|
|
+ private _requests = new Array<GLTFLoaderRequest>();
|
|
|
|
|
|
private _renderReadyObservable = new Observable<GLTFLoader>();
|
|
|
|
|
@@ -127,10 +132,27 @@ module BABYLON.GLTF2 {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- private _onProgress(event: ProgressEvent): void {
|
|
|
- if (this._progressCallback) {
|
|
|
- this._progressCallback(event);
|
|
|
+ private _onProgress(): void {
|
|
|
+ if (!this._progressCallback) {
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ let loaded = 0;
|
|
|
+ let total = 0;
|
|
|
+ for (let request of this._requests) {
|
|
|
+ if (!request._loaded || !request._total) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ loaded += request._loaded;
|
|
|
+ total += request._total;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._progressCallback(new ProgressEvent("GLTFLoaderProgress", {
|
|
|
+ lengthComputable: true,
|
|
|
+ loaded: loaded,
|
|
|
+ total: total
|
|
|
+ }));
|
|
|
}
|
|
|
|
|
|
public _executeWhenRenderReady(func: () => void): void {
|
|
@@ -276,6 +298,10 @@ module BABYLON.GLTF2 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (this._parent.onMeshLoaded) {
|
|
|
+ this._parent.onMeshLoaded(this._rootNode.babylonMesh);
|
|
|
+ }
|
|
|
+
|
|
|
let nodeIndices = scene.nodes;
|
|
|
|
|
|
this._traverseNodes(context, nodeIndices, (node, parentNode) => {
|
|
@@ -361,6 +387,10 @@ module BABYLON.GLTF2 {
|
|
|
this._loadNode("#/nodes/" + index, childNode);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (this._parent.onMeshLoaded) {
|
|
|
+ this._parent.onMeshLoaded(node.babylonMesh);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
|
|
@@ -417,17 +447,7 @@ module BABYLON.GLTF2 {
|
|
|
this._parent.onMaterialLoaded(babylonMaterial);
|
|
|
}
|
|
|
|
|
|
- if (this._parent.onBeforeMaterialReadyAsync) {
|
|
|
- this._addLoaderPendingData(material);
|
|
|
- this._parent.onBeforeMaterialReadyAsync(babylonMaterial, node.babylonMesh, subMaterials[index] != null, () => {
|
|
|
- this._tryCatchOnError(() => {
|
|
|
- subMaterials[index] = babylonMaterial;
|
|
|
- this._removeLoaderPendingData(material);
|
|
|
- });
|
|
|
- });
|
|
|
- } else {
|
|
|
- subMaterials[index] = babylonMaterial;
|
|
|
- }
|
|
|
+ subMaterials[index] = babylonMaterial;
|
|
|
});
|
|
|
}
|
|
|
};
|
|
@@ -1078,26 +1098,17 @@ module BABYLON.GLTF2 {
|
|
|
throw new Error(context + ": Uri is missing");
|
|
|
}
|
|
|
|
|
|
- if (GLTFUtils.IsBase64(buffer.uri)) {
|
|
|
- const data = GLTFUtils.DecodeBase64(buffer.uri);
|
|
|
- buffer.loadedData = new Uint8Array(data);
|
|
|
- onSuccess(buffer.loadedData);
|
|
|
+ buffer.loadedObservable = new Observable<IGLTFBuffer>();
|
|
|
+ buffer.loadedObservable.add(buffer => {
|
|
|
+ onSuccess(buffer.loadedData!);
|
|
|
this._removePendingData(buffer);
|
|
|
- }
|
|
|
- else {
|
|
|
-
|
|
|
- buffer.loadedObservable = new Observable<IGLTFBuffer>();
|
|
|
- buffer.loadedObservable.add(buffer => {
|
|
|
- onSuccess(buffer.loadedData!);
|
|
|
- this._removePendingData(buffer);
|
|
|
- });
|
|
|
+ });
|
|
|
|
|
|
- this._loadUri(context, buffer.uri, data => {
|
|
|
- buffer.loadedData = data;
|
|
|
- buffer.loadedObservable!.notifyObservers(buffer);
|
|
|
- buffer.loadedObservable = undefined;
|
|
|
- });
|
|
|
- }
|
|
|
+ this._loadUriAsync(context, buffer.uri, data => {
|
|
|
+ buffer.loadedData = data;
|
|
|
+ buffer.loadedObservable!.notifyObservers(buffer);
|
|
|
+ buffer.loadedObservable = undefined;
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1217,8 +1228,14 @@ module BABYLON.GLTF2 {
|
|
|
public _removePendingData(data: any): void {
|
|
|
if (!this._renderReady) {
|
|
|
if (--this._renderPendingCount === 0) {
|
|
|
- this._renderReady = true;
|
|
|
- this._onRenderReady();
|
|
|
+ this._addLoaderPendingData(this);
|
|
|
+ this._compileMaterialsAsync(() => {
|
|
|
+ this._compileShadowGeneratorsAsync(() => {
|
|
|
+ this._removeLoaderPendingData(this);
|
|
|
+ this._renderReady = true;
|
|
|
+ this._onRenderReady();
|
|
|
+ });
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1252,7 +1269,7 @@ module BABYLON.GLTF2 {
|
|
|
this._loaderTrackers.push(tracker);
|
|
|
|
|
|
this._addLoaderPendingData(tracker);
|
|
|
-
|
|
|
+
|
|
|
action();
|
|
|
|
|
|
this._removeLoaderPendingData(tracker);
|
|
@@ -1469,7 +1486,7 @@ module BABYLON.GLTF2 {
|
|
|
throw new Error(context + ": Failed to find source " + texture.source);
|
|
|
}
|
|
|
|
|
|
- this._loadImage("#/images/" + image.index, image, data => {
|
|
|
+ this._loadImageAsync("#/images/" + image.index, image, data => {
|
|
|
texture.url = URL.createObjectURL(new Blob([data], { type: image.mimeType }));
|
|
|
texture.dataReadyObservable!.notifyObservers(texture);
|
|
|
texture.dataReadyObservable = undefined;
|
|
@@ -1488,14 +1505,9 @@ module BABYLON.GLTF2 {
|
|
|
return babylonTexture;
|
|
|
}
|
|
|
|
|
|
- private _loadImage(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
|
|
|
+ private _loadImageAsync(context: string, image: IGLTFImage, onSuccess: (data: ArrayBufferView) => void): void {
|
|
|
if (image.uri) {
|
|
|
- if (GLTFUtils.IsBase64(image.uri)) {
|
|
|
- onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(image.uri)));
|
|
|
- }
|
|
|
- else {
|
|
|
- this._loadUri(context, image.uri, onSuccess);
|
|
|
- }
|
|
|
+ this._loadUriAsync(context, image.uri, onSuccess);
|
|
|
}
|
|
|
else {
|
|
|
const bufferView = GLTFLoader._GetProperty(this._gltf.bufferViews, image.bufferView);
|
|
@@ -1507,7 +1519,12 @@ module BABYLON.GLTF2 {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public _loadUri(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
|
|
|
+ public _loadUriAsync(context: string, uri: string, onSuccess: (data: ArrayBufferView) => void): void {
|
|
|
+ if (GLTFUtils.IsBase64(uri)) {
|
|
|
+ onSuccess(new Uint8Array(GLTFUtils.DecodeBase64(uri)));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if (!GLTFUtils.ValidateUri(uri)) {
|
|
|
throw new Error(context + ": Uri '" + uri + "' is invalid");
|
|
|
}
|
|
@@ -1518,15 +1535,21 @@ module BABYLON.GLTF2 {
|
|
|
});
|
|
|
}, event => {
|
|
|
this._tryCatchOnError(() => {
|
|
|
- this._onProgress(event);
|
|
|
+ if (request && !this._renderReady) {
|
|
|
+ request._loaded = event.loaded;
|
|
|
+ request._total = event.total;
|
|
|
+ this._onProgress();
|
|
|
+ }
|
|
|
});
|
|
|
}, this._babylonScene.database, true, request => {
|
|
|
this._tryCatchOnError(() => {
|
|
|
throw new Error(context + ": Failed to load '" + uri + "'" + (request ? ": " + request.status + " " + request.statusText : ""));
|
|
|
});
|
|
|
- });
|
|
|
+ }) as GLTFLoaderRequest;
|
|
|
|
|
|
if (request) {
|
|
|
+ request._loaded = null;
|
|
|
+ request._total = null;
|
|
|
this._requests.push(request);
|
|
|
}
|
|
|
}
|
|
@@ -1630,6 +1653,98 @@ module BABYLON.GLTF2 {
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+ private _compileMaterialAsync(babylonMaterial: Material, babylonMesh: AbstractMesh, onSuccess: () => void): void {
|
|
|
+ if (!this._parent.compileMaterials) {
|
|
|
+ onSuccess();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this._parent.useClipPlane) {
|
|
|
+ babylonMaterial.forceCompilation(babylonMesh, () => {
|
|
|
+ babylonMaterial.forceCompilation(babylonMesh, () => {
|
|
|
+ this._tryCatchOnError(onSuccess);
|
|
|
+ }, { clipPlane: true });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ babylonMaterial.forceCompilation(babylonMesh, () => {
|
|
|
+ this._tryCatchOnError(onSuccess);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _compileMaterialsAsync(onSuccess: () => void): void {
|
|
|
+ if (!this._parent.compileMaterials || !this._gltf.materials) {
|
|
|
+ onSuccess();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let meshes = this._getMeshes();
|
|
|
+
|
|
|
+ let remaining = 0;
|
|
|
+ for (let mesh of meshes) {
|
|
|
+ if (mesh.material instanceof MultiMaterial) {
|
|
|
+ for (let subMaterial of mesh.material.subMaterials) {
|
|
|
+ if (subMaterial) {
|
|
|
+ remaining++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (remaining === 0) {
|
|
|
+ onSuccess();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let mesh of meshes) {
|
|
|
+ if (mesh.material instanceof MultiMaterial) {
|
|
|
+ for (let subMaterial of mesh.material.subMaterials) {
|
|
|
+ if (subMaterial) {
|
|
|
+ this._compileMaterialAsync(subMaterial, mesh, () => {
|
|
|
+ if (--remaining === 0) {
|
|
|
+ onSuccess();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _compileShadowGeneratorsAsync(onSuccess: () => void): void {
|
|
|
+ if (!this._parent.compileShadowGenerators) {
|
|
|
+ onSuccess();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let lights = this._babylonScene.lights;
|
|
|
+
|
|
|
+ let remaining = 0;
|
|
|
+ for (let light of lights) {
|
|
|
+ let generator = light.getShadowGenerator();
|
|
|
+ if (generator) {
|
|
|
+ remaining++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (remaining === 0) {
|
|
|
+ onSuccess();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (let light of lights) {
|
|
|
+ let generator = light.getShadowGenerator();
|
|
|
+ if (generator) {
|
|
|
+ generator.forceCompilation(() => {
|
|
|
+ if (--remaining === 0) {
|
|
|
+ this._tryCatchOnError(onSuccess);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
BABYLON.GLTFFileLoader.CreateGLTFLoaderV2 = parent => new GLTFLoader(parent);
|