소스 검색

Range request unit test and force compile fixes

Gary Hsu 5 년 전
부모
커밋
f25b9ac834

+ 4 - 1
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -66,6 +66,8 @@ export class MSFT_lod implements IGLTFLoaderExtension {
 
     /** @hidden */
     public dispose() {
+        this._disposeUnusedMaterials();
+
         delete this._loader;
 
         this._nodeIndexLOD = null;
@@ -340,7 +342,8 @@ export class MSFT_lod implements IGLTFLoaderExtension {
                 if (material._data) {
                     for (const drawMode in material._data) {
                         const data = material._data[drawMode];
-                        if (data.babylonMeshes.length === 0) {
+                        if (data.babylonMeshes.every((babylonMesh) => babylonMesh.material !== data.babylonMaterial)) {
+                            // TODO: check if texture is in use instead of force disposing textures
                             data.babylonMaterial.dispose(false, true);
                             delete material._data[drawMode];
                         }

+ 16 - 1
loaders/src/glTF/2.0/glTFLoader.ts

@@ -379,7 +379,20 @@ export class GLTFLoader implements IGLTFLoader {
         this._gltf = data.json as IGLTF;
         this._setupData();
 
-        this._bin = data.bin;
+        if (data.bin) {
+            const buffers = this._gltf.buffers;
+            if (buffers && buffers[0] && !buffers[0].uri) {
+                const binaryBuffer = buffers[0];
+                if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
+                    Tools.Warn(`Binary buffer length (${binaryBuffer.byteLength}) from JSON does not match chunk length (${data.bin.byteLength})`);
+                }
+
+                this._bin = data.bin;
+            }
+            else {
+                Tools.Warn("Unexpected BIN chunk");
+            }
+        }
     }
 
     private _setupData(): void {
@@ -2169,8 +2182,10 @@ export class GLTFLoader implements IGLTFLoader {
 
                             const babylonMaterial = babylonData.babylonMaterial;
                             promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
+                            promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { useInstances: true }));
                             if (this._parent.useClipPlane) {
                                 promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
+                                promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true, useInstances: true }));
                             }
                         }
                     }

+ 1 - 3
loaders/src/glTF/glTFFileLoader.ts

@@ -480,7 +480,6 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
                     Logger.Warn("glTF validation is not supported when range requests are enabled");
                 }
 
-                let firstWebRequest: WebRequest | undefined;
                 const fileRequests = new Array<IFileRequest>();
                 const aggregatedFileRequest: IFileRequest = {
                     abort: () => fileRequests.forEach((fileRequest) => fileRequest.abort()),
@@ -491,7 +490,6 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
                     readAsync: (byteOffset: number, byteLength: number) => {
                         return new Promise<ArrayBufferView>((resolve, reject) => {
                             fileRequests.push(scene._requestFile(url, (data, webRequest) => {
-                                firstWebRequest = firstWebRequest || webRequest;
                                 dataBuffer.byteLength = Number(webRequest!.getResponseHeader("Content-Range")!.split("/")[1]);
                                 resolve(new Uint8Array(data as ArrayBuffer));
                             }, onProgress, true, true, (error) => {
@@ -506,7 +504,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
 
                 this._unpackBinaryAsync(new DataReader(dataBuffer)).then((loaderData) => {
                     aggregatedFileRequest.onCompleteObservable.notifyObservers(aggregatedFileRequest);
-                    onSuccess(loaderData, firstWebRequest);
+                    onSuccess(loaderData);
                 }, onError);
 
                 return aggregatedFileRequest;

+ 5 - 5
src/Loading/sceneLoader.ts

@@ -406,9 +406,9 @@ export class SceneLoader {
             return plugin;
         }
 
-        let useArrayBuffer = registeredPlugin.isBinary;
+        const useArrayBuffer = registeredPlugin.isBinary;
 
-        let dataCallback = (data: any, responseURL?: string) => {
+        const dataCallback = (data: any, responseURL?: string) => {
             if (scene.isDisposed) {
                 onError("Scene has been disposed");
                 return;
@@ -419,7 +419,7 @@ export class SceneLoader {
 
         let request: Nullable<IFileRequest> = null;
         let pluginDisposed = false;
-        let onDisposeObservable = (plugin as any).onDisposeObservable as Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
+        const onDisposeObservable = (plugin as any).onDisposeObservable as Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
         if (onDisposeObservable) {
             onDisposeObservable.add(() => {
                 pluginDisposed = true;
@@ -437,7 +437,7 @@ export class SceneLoader {
             onProgress(SceneLoaderProgressEvent.FromProgressEvent(event));
         } : undefined;
 
-        let manifestChecked = () => {
+        const manifestChecked = () => {
             if (pluginDisposed) {
                 return;
             }
@@ -458,7 +458,7 @@ export class SceneLoader {
         const file = fileInfo.file || FilesInputStore.FilesToLoad[fileInfo.name.toLowerCase()];
 
         if (fileInfo.rootUrl.indexOf("file:") === -1 || (fileInfo.rootUrl.indexOf("file:") !== -1 && !file)) {
-            let engine = scene.getEngine();
+            const engine = scene.getEngine();
             let canUseOfflineSupport = engine.enableOfflineSupport;
             if (canUseOfflineSupport) {
                 // Also check for exceptions

+ 7 - 5
src/Materials/PBR/pbrBaseMaterial.ts

@@ -21,7 +21,7 @@ import { Color3, TmpColors } from '../../Maths/math.color';
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, IEffectCreationOptions } from "../../Materials/effect";
-import { Material } from "../../Materials/material";
+import { Material, IMaterialCompilationOptions } from "../../Materials/material";
 import { MaterialDefines } from "../../Materials/materialDefines";
 import { PushMaterial } from "../../Materials/pushMaterial";
 import { MaterialHelper } from "../../Materials/materialHelper";
@@ -1356,7 +1356,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             defines.USEIRRADIANCEMAP = true;
                             defines.USESPHERICALFROMREFLECTIONMAP = false;
                         }
-                        else if (reflectionTexture.sphericalPolynomial) {
+                        // Assume using spherical polynomial if the reflection texture is a cube map
+                        else if (reflectionTexture.isCube) {
                             defines.USESPHERICALFROMREFLECTIONMAP = true;
                             defines.USEIRRADIANCEMAP = false;
                             if (this._forceIrradianceInFragment || scene.getEngine().getCaps().maxVaryingVectors <= 8) {
@@ -1536,14 +1537,15 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     /**
      * Force shader compilation
      */
-    public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<{ clipPlane: boolean }>): void {
-        let localOptions = {
+    public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<IMaterialCompilationOptions>): void {
+        const localOptions = {
             clipPlane: false,
+            useInstances: false,
             ...options
         };
 
         const defines = new PBRMaterialDefines();
-        const effect = this._prepareEffect(mesh, defines, undefined, undefined, undefined, localOptions.clipPlane)!;
+        const effect = this._prepareEffect(mesh, defines, undefined, undefined, localOptions.useInstances, localOptions.clipPlane)!;
         if (effect.isReady()) {
             if (onCompiled) {
                 onCompiled(this);

+ 9 - 7
src/Materials/Textures/baseTexture.polynomial.ts

@@ -17,16 +17,18 @@ declare module "./baseTexture" {
 
 Object.defineProperty(BaseTexture.prototype, "sphericalPolynomial", {
     get: function(this: BaseTexture) {
-        if (!this._texture || !CubeMapToSphericalPolynomialTools || !this.isReady()) {
-            return null;
-        }
+        if (this._texture) {
+            if (this._texture._sphericalPolynomial) {
+                return this._texture._sphericalPolynomial;
+            }
 
-        if (!this._texture._sphericalPolynomial) {
-            this._texture._sphericalPolynomial =
-                CubeMapToSphericalPolynomialTools.ConvertCubeMapTextureToSphericalPolynomial(this);
+            if (this._texture.isReady) {
+                this._texture._sphericalPolynomial =
+                    CubeMapToSphericalPolynomialTools.ConvertCubeMapTextureToSphericalPolynomial(this);
+            }
         }
 
-        return this._texture._sphericalPolynomial;
+        return null;
     },
     set: function(this: BaseTexture, value: Nullable<SphericalPolynomial>) {
         if (this._texture) {

+ 9 - 3
src/Materials/material.ts

@@ -26,6 +26,11 @@ declare type InstancedMesh = import('../Meshes/instancedMesh').InstancedMesh;
 
 declare var BABYLON: any;
 
+export interface IMaterialCompilationOptions {
+    clipPlane: boolean;
+    useInstances: boolean;
+}
+
 /**
  * Base class for the main features of a material in Babylon.js
  */
@@ -854,9 +859,10 @@ export class Material implements IAnimatable {
      * @param options defines the options to configure the compilation
      * @param onError defines a function to execute if the material fails compiling
      */
-    public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<{ clipPlane: boolean }>, onError?: (reason: string) => void): void {
+    public forceCompilation(mesh: AbstractMesh, onCompiled?: (material: Material) => void, options?: Partial<IMaterialCompilationOptions>, onError?: (reason: string) => void): void {
         let localOptions = {
             clipPlane: false,
+            useInstances: false,
             ...options
         };
 
@@ -879,7 +885,7 @@ export class Material implements IAnimatable {
             }
 
             if (this._storeEffectOnSubMeshes) {
-                if (this.isReadyForSubMesh(mesh, subMesh)) {
+                if (this.isReadyForSubMesh(mesh, subMesh, localOptions.useInstances)) {
                     if (onCompiled) {
                         onCompiled(this);
                     }
@@ -918,7 +924,7 @@ export class Material implements IAnimatable {
      * @param options defines additional options for compiling the shaders
      * @returns a promise that resolves when the compilation completes
      */
-    public forceCompilationAsync(mesh: AbstractMesh, options?: Partial<{ clipPlane: boolean }>): Promise<void> {
+    public forceCompilationAsync(mesh: AbstractMesh, options?: Partial<IMaterialCompilationOptions>): Promise<void> {
         return new Promise((resolve, reject) => {
             this.forceCompilation(mesh, () => {
                 resolve();

+ 22 - 20
src/Misc/rgbdTextureTools.ts

@@ -21,32 +21,34 @@ export class RGBDTextureTools {
             return;
         }
 
-        texture.onLoadObservable.addOnce(() => {
-            // Gets everything ready.
-            let engine = internalTexture.getEngine() as Engine;
-            let caps = engine.getCaps();
-            let expandTexture = false;
+        // Gets everything ready.
+        const engine = internalTexture.getEngine() as Engine;
+        const caps = engine.getCaps();
+        let expandTexture = false;
 
-            // If half float available we can uncompress the texture
-            if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
-                expandTexture = true;
-                internalTexture.type = Constants.TEXTURETYPE_HALF_FLOAT;
-            }
-            // If full float available we can uncompress the texture
-            else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
-                expandTexture = true;
-                internalTexture.type = Constants.TEXTURETYPE_FLOAT;
-            }
+        // If half float available we can uncompress the texture
+        if (caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering) {
+            expandTexture = true;
+            internalTexture.type = Constants.TEXTURETYPE_HALF_FLOAT;
+        }
+        // If full float available we can uncompress the texture
+        else if (caps.textureFloatRender && caps.textureFloatLinearFiltering) {
+            expandTexture = true;
+            internalTexture.type = Constants.TEXTURETYPE_FLOAT;
+        }
 
+        if (expandTexture) {
+            // Do not use during decode.
+            internalTexture.isReady = false;
+            internalTexture._isRGBD = false;
+            internalTexture.invertY = false;
+        }
+
+        texture.onLoadObservable.addOnce(() => {
             // Expand the texture if possible
             if (expandTexture) {
-                // Do not use during decode.
-                internalTexture.isReady = false;
-
                 // Simply run through the decode PP.
                 const rgbdPostProcess = new PostProcess("rgbdDecode", "rgbdDecode", null, null, 1, null, Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, engine, false, undefined, internalTexture.type, undefined, null, false);
-                internalTexture._isRGBD = false;
-                internalTexture.invertY = false;
 
                 // Hold the output of the decoding.
                 const expandedTexture = engine.createRenderTargetTexture(internalTexture.width, {

+ 56 - 47
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -276,7 +276,7 @@ describe('Babylon Scene Loader', function() {
             });
         });
 
-        it('Load TwoQuads with LODs', () => {
+        it('Load LevelOfDetail', () => {
             const scene = new BABYLON.Scene(subject);
             const promises = new Array<Promise<void>>();
 
@@ -297,56 +297,50 @@ describe('Babylon Scene Loader', function() {
                         scene.getMeshByName("node1")
                     ];
 
-                    expect(meshes[0].material.name, "Material for node 0").to.equal("LOD0");
-                    expect(meshes[1].material.name, "Material for node 1").to.equal("LOD0");
+                    expect(meshes[0].material.name, "Material for node 0").to.equal("High");
+                    expect(meshes[1].material.name, "Material for node 1").to.equal("High");
 
                     expect(scene.materials, "scene.materials").to.have.lengthOf(1);
-                    const materials = [
-                        scene.getMaterialByName("LOD0")
-                    ];
-
-                    expect(materials[0].isReady(meshes[0]), "Material of LOD 0 is ready for node 0").to.be.true;
-                    expect(materials[0].isReady(meshes[1]), "Material of LOD 0 is ready for node 1").to.be.true;
                 }));
             });
 
-            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/LevelOfDetail/", `LevelOfDetail.gltf`, scene).then(() => {
                 const meshes = [
                     scene.getMeshByName("node0"),
                     scene.getMeshByName("node1")
                 ];
 
-                expect(meshes[0].material.name, "Material for node 0").to.equal("LOD2");
-                expect(meshes[1].material.name, "Material for node 1").to.equal("LOD2");
+                expect(meshes[0].material.name, "Material for node 0").to.equal("Low");
+                expect(meshes[1].material.name, "Material for node 1").to.equal("Low");
 
                 expect(scene.materials, "scene.materials").to.have.lengthOf(3);
-                const materials = [
-                    scene.getMaterialByName("LOD0"),
-                    scene.getMaterialByName("LOD1"),
-                    scene.getMaterialByName("LOD2")
-                ];
-
-                expect(materials[0].isReady(meshes[0]), "Material of LOD 0 is ready for node 0").to.be.false;
-                expect(materials[0].isReady(meshes[1]), "Material of LOD 0 is ready for node 1").to.be.false;
-                expect(materials[1].isReady(meshes[0]), "Material of LOD 1 is ready for node 0").to.be.false;
-                expect(materials[1].isReady(meshes[1]), "Material of LOD 1 is ready for node 1").to.be.false;
-                expect(materials[2].isReady(meshes[0]), "Material of LOD 2 is ready for node 0").to.be.true;
-                expect(materials[2].isReady(meshes[1]), "Material of LOD 2 is ready for node 1").to.be.true;
+                const materialLow = scene.getMaterialByName("Low");
+                const materialMedium = scene.getMaterialByName("Medium");
+                const materialHigh = scene.getMaterialByName("High");
+
+                expect(materialLow.isReady(meshes[0]), "Material 'Low' is ready for node 0").to.be.true;
+                expect(materialLow.isReady(meshes[1]), "Material 'Low' is ready for node 1").to.be.true;
+                expect(materialMedium.isReady(meshes[0]), "Material 'Medium' is ready for node 0").to.be.true;
+                expect(materialMedium.isReady(meshes[1]), "Material 'Medium' is ready for node 1").to.be.true;
+                expect(materialHigh.isReady(meshes[0]), "Material 'High' is ready for node 0").to.be.true;
+                expect(materialHigh.isReady(meshes[1]), "Material 'High' is ready for node 1").to.be.true;
             }));
 
             return Promise.all(promises);
         });
 
-        it('Load TwoQuads with LODs and onMaterialLODsLoadedObservable', () => {
+        it('Load LevelOfDetail with onMaterialLODsLoadedObservable', () => {
             const scene = new BABYLON.Scene(subject);
             const promises = new Array<Promise<void>>();
 
+            const materialNames = [ "Low", "Medium", "High" ];
+
             BABYLON.SceneLoader.OnPluginActivatedObservable.addOnce((loader: BABYLON.GLTFFileLoader) => {
                 const observer = loader.onExtensionLoadedObservable.add((extension) => {
                     if (extension instanceof BABYLON.GLTF2.Loader.Extensions.MSFT_lod) {
                         loader.onExtensionLoadedObservable.remove(observer);
                         extension.onMaterialLODsLoadedObservable.add((indexLOD) => {
-                            const expectedMaterialName = `LOD${2 - indexLOD}`;
+                            const expectedMaterialName = materialNames[indexLOD];
                             expect(scene.getMeshByName("node0").material.name, "Material for node 0").to.equal(expectedMaterialName);
                             expect(scene.getMeshByName("node1").material.name, "Material for node 1").to.equal(expectedMaterialName);
                         });
@@ -356,14 +350,14 @@ describe('Babylon Scene Loader', function() {
                 promises.push(loader.whenCompleteAsync());
             });
 
-            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/LevelOfDetail/", "LevelOfDetail.gltf", scene).then(() => {
                 // do nothing
             }));
 
             return Promise.all(promises);
         });
 
-        it('Load TwoQuads with LODs and dispose when onMaterialLODsLoadedObservable', () => {
+        it('Load LevelOfDetail with dispose when onMaterialLODsLoadedObservable', () => {
             const scene = new BABYLON.Scene(subject);
             const promises = new Array<Promise<void>>();
 
@@ -385,14 +379,30 @@ describe('Babylon Scene Loader', function() {
                 }));
             });
 
-            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuads.gltf", scene).then(() => {
+            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/LevelOfDetail/", "LevelOfDetail.gltf", scene).then(() => {
                 // do nothing
             }));
 
             return Promise.all(promises);
         });
 
-        it('Load TwoQuadsNoTextures with LODs', () => {
+        it('Load LevelOfDetail with useRangeRequests', () => {
+            const scene = new BABYLON.Scene(subject);
+            const promises = new Array<Promise<void>>();
+
+            BABYLON.SceneLoader.OnPluginActivatedObservable.addOnce((loader: BABYLON.GLTFFileLoader) => {
+                loader.useRangeRequests = true;
+                promises.push(loader.whenCompleteAsync());
+            });
+
+            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/LevelOfDetail/", "LevelOfDetail.glb", scene).then(() => {
+                // do nothing
+            }));
+
+            return Promise.all(promises);
+        });
+
+        it('Load LevelOfDetailNoTextures', () => {
             const scene = new BABYLON.Scene(subject);
 
             const promises = new Array<Promise<any>>();
@@ -401,14 +411,14 @@ describe('Babylon Scene Loader', function() {
                 promises.push(loader.whenCompleteAsync());
             });
 
-            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/TwoQuads/", "TwoQuadsNoTextures.gltf", scene));
+            promises.push(BABYLON.SceneLoader.AppendAsync("http://models.babylonjs.com/Tests/LevelOfDetail/", "LevelOfDetailNoTextures.gltf", scene));
 
             return Promise.all(promises);
         });
 
         it('Load MultiPrimitive', () => {
             const scene = new BABYLON.Scene(subject);
-            return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then(result => {
+            return BABYLON.SceneLoader.ImportMeshAsync(null, "http://models.babylonjs.com/Tests/MultiPrimitive/", "MultiPrimitive.gltf", scene).then((result) => {
                 expect(result.meshes, "meshes").to.have.lengthOf(3);
 
                 const node = scene.getNodeByName("node");
@@ -531,13 +541,12 @@ describe('Babylon Scene Loader', function() {
                 expect(mesh.metadata.gltf, "Camera metadata.gltf").to.exist;
                 expect(mesh.metadata.gltf.extras, "Camera metadata.gltf.extras").to.exist;
                 expect(camera.metadata.gltf.extras.custom, "Camera extras.custom").to.equal("cameraProp");
-                const material = scene.getMaterialByName("01___Default")
+                const material = scene.getMaterialByName("01___Default");
                 expect(material, "Material").to.exist;
                 expect(material.metadata, "Material metadata").to.exist;
                 expect(mesh.metadata.gltf, "Material metadata.gltf").to.exist;
                 expect(mesh.metadata.gltf.extras, "Material metadata.gltf.extras").to.exist;
                 expect(material.metadata.gltf.extras.custom, "Material extras.custom").to.equal("materialProp");
-
             });
         });
 
@@ -552,14 +561,14 @@ describe('Babylon Scene Loader', function() {
      */
     describe('#OBJ', () => {
         it('should load a tetrahedron (without colors)', () => {
-            var fileContents = `               
+            var fileContents = `
                 g tetrahedron
-                
+
                 v 1.00 1.00 1.00 0.666 0 0
                 v 2.00 1.00 1.00 0.666 0 0
                 v 1.00 2.00 1.00 0.666 0 0
                 v 1.00 1.00 2.00 0.666 0 0
-                
+
                 f 1 3 2
                 f 1 4 3
                 f 1 2 4
@@ -567,7 +576,7 @@ describe('Babylon Scene Loader', function() {
             `;
 
             var scene = new BABYLON.Scene(subject);
-            return BABYLON.SceneLoader.LoadAssetContainerAsync('', 'data:' + fileContents, scene, () => { }, ".obj").then(container => {
+            return BABYLON.SceneLoader.LoadAssetContainerAsync('', 'data:' + fileContents, scene, () => { }, ".obj").then((container) => {
                 expect(container.meshes.length).to.eq(1);
                 let tetrahedron = container.meshes[0];
 
@@ -575,14 +584,14 @@ describe('Babylon Scene Loader', function() {
                 var colors: BABYLON.FloatArray = tetrahedron.getVerticesData(BABYLON.VertexBuffer.ColorKind);
 
                 expect(positions).to.deep.equal([1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 2]);
-                assert.isNull(colors, 'expecting colors vertex buffer to be null')
-            })
-        })
+                assert.isNull(colors, 'expecting colors vertex buffer to be null');
+            });
+        });
 
         it('should parse leniently allowing extra spaces with vertex definitions', () => {
-            var fileContents = `               
+            var fileContents = `
                 g tetrahedron
-                
+
                 v  1.00 1.00 1.00 0.666 0 0
                 v  2.00 1.00 1.00 0.666 0 0
                 v  1.00 2.00 1.00 0.666 0 0
@@ -597,16 +606,16 @@ describe('Babylon Scene Loader', function() {
             `;
 
             var scene = new BABYLON.Scene(subject);
-            return BABYLON.SceneLoader.LoadAssetContainerAsync('', 'data:' + fileContents, scene, () => { }, ".obj").then(container => {
+            return BABYLON.SceneLoader.LoadAssetContainerAsync('', 'data:' + fileContents, scene, () => { }, ".obj").then((container) => {
                 expect(container.meshes.length).to.eq(1);
                 let tetrahedron = container.meshes[0];
 
                 var positions: BABYLON.FloatArray = tetrahedron.getVerticesData(BABYLON.VertexBuffer.PositionKind);
 
                 expect(positions).to.deep.equal([1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 2]);
-            })
-        })
-    })
+            });
+        });
+    });
 
     describe('#AssetContainer', () => {
         it('should be loaded from BoomBox GLTF', () => {