浏览代码

Merge pull request #3922 from bghgary/primitive-modes

Add support for primitive modes to glTF loader
David Catuhe 7 年之前
父节点
当前提交
70d9fc4238

+ 1 - 0
dist/preview release/what's new.md

@@ -95,6 +95,7 @@
 - Tests for sharpen, chromatic aberration, default pipeline and enable/disable post processes ([trevordev](https://github.com/trevordev))
 - Tests for sharpen, chromatic aberration, default pipeline and enable/disable post processes ([trevordev](https://github.com/trevordev))
 - onPointer* callbacks have now the event type as a 3rd variable ([RaananW](https://github.com/RaananW))
 - onPointer* callbacks have now the event type as a 3rd variable ([RaananW](https://github.com/RaananW))
 - Lightmap texture in PBR material follow the gammaSpace Flag of the texture ([sebavan](https://github.com/sebavan))
 - Lightmap texture in PBR material follow the gammaSpace Flag of the texture ([sebavan](https://github.com/sebavan))
+- Added support for primitive modes to glTF 2.0 loader. ([bghgary](https://github.com/bghgary)]
 
 
 ## Bug fixes
 ## Bug fixes
 
 

+ 22 - 16
loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts

@@ -16,34 +16,40 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
     export class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         public readonly name = NAME;
         public readonly name = NAME;
 
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (context, extension) => {
             return this._loadExtensionAsync<IKHRMaterialsPbrSpecularGlossiness>(context, material, (context, extension) => {
-                material._babylonMeshes = material._babylonMeshes || [];
-                material._babylonMeshes.push(babylonMesh);
-
-                if (!material._loaded) {
+                material._babylonData = material._babylonData || {};
+                let babylonData = material._babylonData[babylonDrawMode];
+                if (!babylonData) {
                     const promises = new Array<Promise<void>>();
                     const promises = new Array<Promise<void>>();
 
 
-                    const babylonMaterial = this._loader._createMaterial(material);
-                    material._babylonMaterial = babylonMaterial;
+                    const name = material.name || `materialSG_${material._index}`;
+                    const babylonMaterial = this._loader._createMaterial(PBRMaterial, name, babylonDrawMode);
 
 
-                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material));
-                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension));
+                    promises.push(this._loader._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+                    promises.push(this._loadSpecularGlossinessPropertiesAsync(context, material, extension, babylonMaterial));
 
 
                     this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                     this._loader.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                    material._loaded = Promise.all(promises).then(() => {});
+
+                    babylonData = {
+                        material: babylonMaterial,
+                        meshes: [],
+                        loaded: Promise.all(promises).then(() => {})
+                    };
+
+                    material._babylonData[babylonDrawMode] = babylonData;
                 }
                 }
 
 
-                assign(material._babylonMaterial!);
-                return material._loaded;
+                babylonData.meshes.push(babylonMesh);
+
+                assign(babylonData.material);
+                return babylonData.loaded;
             });
             });
         }
         }
 
 
-        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness): Promise<void> {
+        private _loadSpecularGlossinessPropertiesAsync(context: string, material: ILoaderMaterial, properties: IKHRMaterialsPbrSpecularGlossiness, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             if (properties.diffuseFactor) {
             if (properties.diffuseFactor) {
                 babylonMaterial.albedoColor = Color3.FromArray(properties.diffuseFactor);
                 babylonMaterial.albedoColor = Color3.FromArray(properties.diffuseFactor);
                 babylonMaterial.alpha = properties.diffuseFactor[3];
                 babylonMaterial.alpha = properties.diffuseFactor[3];
@@ -70,7 +76,7 @@ module BABYLON.GLTF2.Extensions {
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
                 babylonMaterial.useMicroSurfaceFromReflectivityMapAlpha = true;
             }
             }
 
 
-            this._loader._loadMaterialAlphaProperties(context, material);
+            this._loader._loadMaterialAlphaProperties(context, material, babylonMaterial);
 
 
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }

+ 8 - 7
loaders/src/glTF/2.0/Extensions/MSFT_lod.ts

@@ -71,7 +71,7 @@ module BABYLON.GLTF2.Extensions {
             });
             });
         }
         }
 
 
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
             // Don't load material LODs if already loading a node LOD.
             // Don't load material LODs if already loading a node LOD.
             if (this._loadingNodeLOD) {
             if (this._loadingNodeLOD) {
                 return null;
                 return null;
@@ -92,14 +92,15 @@ module BABYLON.GLTF2.Extensions {
                         }
                         }
                     }
                     }
 
 
-                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, indexLOD === 0 ? assign : () => {}).then(() => {
+                    const promise = this._loader._loadMaterialAsync(`#/materials/${materialLOD._index}`, materialLOD, babylonMesh, babylonDrawMode, indexLOD === 0 ? assign : () => {}).then(() => {
                         if (indexLOD !== 0) {
                         if (indexLOD !== 0) {
-                            assign(materialLOD._babylonMaterial!);
+                            const babylonDataLOD = materialLOD._babylonData!;
+                            assign(babylonDataLOD[babylonDrawMode].material);
 
 
-                            const previousMaterialLOD = materialLODs[indexLOD - 1];
-                            if (previousMaterialLOD._babylonMaterial) {
-                                previousMaterialLOD._babylonMaterial.dispose();
-                                delete previousMaterialLOD._babylonMaterial;
+                            const previousBabylonDataLOD = materialLODs[indexLOD - 1]._babylonData!;
+                            if (previousBabylonDataLOD[babylonDrawMode]) {
+                                previousBabylonDataLOD[babylonDrawMode].material.dispose();
+                                delete previousBabylonDataLOD[babylonDrawMode];
                             }
                             }
                         }
                         }
 
 

+ 71 - 50
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -16,6 +16,11 @@ module BABYLON.GLTF2 {
         readonly BYTES_PER_ELEMENT: number;
         readonly BYTES_PER_ELEMENT: number;
     }
     }
 
 
+    export interface MaterialConstructor<T extends Material> {
+        readonly prototype: T;
+        new(name: string, scene: Scene): T;
+    }
+
     export class GLTFLoader implements IGLTFLoader {
     export class GLTFLoader implements IGLTFLoader {
         public _gltf: ILoaderGLTF;
         public _gltf: ILoaderGLTF;
         public _babylonScene: Scene;
         public _babylonScene: Scene;
@@ -27,6 +32,7 @@ module BABYLON.GLTF2 {
         private _rootUrl: string;
         private _rootUrl: string;
         private _rootBabylonMesh: Mesh;
         private _rootBabylonMesh: Mesh;
         private _defaultSampler = {} as ILoaderSampler;
         private _defaultSampler = {} as ILoaderSampler;
+        private _defaultBabylonMaterials: { [drawMode: number]: PBRMaterial } = {};
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
         private _requests = new Array<IFileRequestInfo>();
         private _requests = new Array<IFileRequestInfo>();
 
 
@@ -315,7 +321,7 @@ module BABYLON.GLTF2 {
             }
             }
             else {
             else {
                 callback(node._babylonMesh!);
                 callback(node._babylonMesh!);
-        }
+            }
         }
         }
 
 
         private _getMeshes(): Mesh[] {
         private _getMeshes(): Mesh[] {
@@ -329,12 +335,12 @@ module BABYLON.GLTF2 {
                 for (const node of nodes) {
                 for (const node of nodes) {
                     if (node._babylonMesh) {
                     if (node._babylonMesh) {
                         meshes.push(node._babylonMesh);
                         meshes.push(node._babylonMesh);
-                }
+                    }
 
 
                     if (node._primitiveBabylonMeshes) {
                     if (node._primitiveBabylonMeshes) {
                         for (const babylonMesh of node._primitiveBabylonMeshes) {
                         for (const babylonMesh of node._primitiveBabylonMeshes) {
                             meshes.push(babylonMesh);
                             meshes.push(babylonMesh);
-            }
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -470,12 +476,13 @@ module BABYLON.GLTF2 {
                 return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
                 return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonVertexData);
             }));
             }));
 
 
+            const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
             if (primitive.material == undefined) {
             if (primitive.material == undefined) {
-                babylonMesh.material = this._getDefaultMaterial();
+                babylonMesh.material = this._getDefaultMaterial(babylonDrawMode);
             }
             }
             else {
             else {
                 const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
                 const material = GLTFLoader._GetProperty(`${context}/material}`, this._gltf.materials, primitive.material);
-                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonMaterial => {
+                promises.push(this._loadMaterialAsync(`#/materials/${material._index}`, material, babylonMesh, babylonDrawMode, babylonMaterial => {
                     babylonMesh.material = babylonMaterial;
                     babylonMesh.material = babylonMaterial;
                 }));
                 }));
             }
             }
@@ -494,11 +501,6 @@ module BABYLON.GLTF2 {
                 throw new Error(`${context}: Attributes are missing`);
                 throw new Error(`${context}: Attributes are missing`);
             }
             }
 
 
-            if (primitive.mode != undefined && primitive.mode !== MeshPrimitiveMode.TRIANGLES) {
-                // TODO: handle other primitive modes
-                throw new Error(`${context}: Mode (${primitive.mode}) is not currently supported`);
-            }
-
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
             const babylonVertexData = new VertexData();
             const babylonVertexData = new VertexData();
@@ -1141,13 +1143,11 @@ module BABYLON.GLTF2 {
             return targetBuffer;
             return targetBuffer;
         }
         }
 
 
-        private _getDefaultMaterial(): Material {
-            const id = "__gltf_default";
-            let babylonMaterial = <PBRMaterial>this._babylonScene.getMaterialByName(id);
+        private _getDefaultMaterial(drawMode: number): Material {
+            let babylonMaterial = this._defaultBabylonMaterials[drawMode];
             if (!babylonMaterial) {
             if (!babylonMaterial) {
-                babylonMaterial = new PBRMaterial(id, this._babylonScene);
+                babylonMaterial = this._createMaterial(PBRMaterial, "__gltf_default", drawMode);
                 babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE;
                 babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE;
-                babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
                 babylonMaterial.metallic = 1;
                 babylonMaterial.metallic = 1;
                 babylonMaterial.roughness = 1;
                 babylonMaterial.roughness = 1;
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
@@ -1156,11 +1156,9 @@ module BABYLON.GLTF2 {
             return babylonMaterial;
             return babylonMaterial;
         }
         }
 
 
-        private _loadMaterialMetallicRoughnessPropertiesAsync(context: string, material: ILoaderMaterial): Promise<void> {
+        private _loadMaterialMetallicRoughnessPropertiesAsync(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             // Ensure metallic workflow
             // Ensure metallic workflow
             babylonMaterial.metallic = 1;
             babylonMaterial.metallic = 1;
             babylonMaterial.roughness = 1;
             babylonMaterial.roughness = 1;
@@ -1195,48 +1193,55 @@ module BABYLON.GLTF2 {
                 }
                 }
             }
             }
 
 
-            this._loadMaterialAlphaProperties(context, material);
+            this._loadMaterialAlphaProperties(context, material, babylonMaterial);
 
 
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
-        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Promise<void> {
-            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, assign);
+        public _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Promise<void> {
+            const promise = GLTFLoaderExtension._LoadMaterialAsync(this, context, material, babylonMesh, babylonDrawMode, assign);
             if (promise) {
             if (promise) {
                 return promise;
                 return promise;
             }
             }
 
 
-            material._babylonMeshes = material._babylonMeshes || [];
-            material._babylonMeshes.push(babylonMesh);
-
-            if (!material._loaded) {
+            material._babylonData = material._babylonData || {};
+            let babylonData = material._babylonData[babylonDrawMode];
+            if (!babylonData) {
                 const promises = new Array<Promise<void>>();
                 const promises = new Array<Promise<void>>();
 
 
-                const babylonMaterial = this._createMaterial(material);
-                material._babylonMaterial = babylonMaterial;
+                const name = material.name || `materialSG_${material._index}`;
+                const babylonMaterial = this._createMaterial(PBRMaterial, name, babylonDrawMode);
 
 
-                promises.push(this._loadMaterialBasePropertiesAsync(context, material));
-                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material));
+                promises.push(this._loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
+                promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(context, material, babylonMaterial));
 
 
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
                 this.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
-                material._loaded = Promise.all(promises).then(() => {});
+
+                babylonData = {
+                    material: babylonMaterial,
+                    meshes: [],
+                    loaded: Promise.all(promises).then(() => {})
+                };
+
+                material._babylonData[babylonDrawMode] = babylonData;
             }
             }
 
 
-            assign(material._babylonMaterial!);
-            return material._loaded;
+            babylonData.meshes.push(babylonMesh);
+
+            assign(babylonData.material);
+            return babylonData.loaded;
         }
         }
 
 
-        public _createMaterial(material: ILoaderMaterial): PBRMaterial {
-            const babylonMaterial = new PBRMaterial(material.name || `material${material._index}`, this._babylonScene);
+        public _createMaterial<T extends Material>(type: MaterialConstructor<T>, name: string, drawMode: number): T {
+            const babylonMaterial = new type(name, this._babylonScene);
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
             babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
+            babylonMaterial.fillMode = drawMode;
             return babylonMaterial;
             return babylonMaterial;
         }
         }
 
 
-        public _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial): Promise<void> {
+        public _loadMaterialBasePropertiesAsync(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): Promise<void> {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
             babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
             babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
             if (material.doubleSided) {
             if (material.doubleSided) {
                 babylonMaterial.backFaceCulling = false;
                 babylonMaterial.backFaceCulling = false;
@@ -1275,9 +1280,7 @@ module BABYLON.GLTF2 {
             return Promise.all(promises).then(() => {});
             return Promise.all(promises).then(() => {});
         }
         }
 
 
-        public _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial): void {
-            const babylonMaterial = material._babylonMaterial as PBRMaterial;
-
+        public _loadMaterialAlphaProperties(context: string, material: ILoaderMaterial, babylonMaterial: PBRMaterial): void {
             const alphaMode = material.alphaMode || MaterialAlphaMode.OPAQUE;
             const alphaMode = material.alphaMode || MaterialAlphaMode.OPAQUE;
             switch (alphaMode) {
             switch (alphaMode) {
                 case MaterialAlphaMode.OPAQUE: {
                 case MaterialAlphaMode.OPAQUE: {
@@ -1532,21 +1535,39 @@ module BABYLON.GLTF2 {
             return (Tools.IsBase64(uri) || uri.indexOf("..") === -1);
             return (Tools.IsBase64(uri) || uri.indexOf("..") === -1);
         }
         }
 
 
+        private static _GetDrawMode(context: string, mode: number | undefined): number {
+            mode = mode || MeshPrimitiveMode.TRIANGLES;
+
+            switch (mode) {
+                case MeshPrimitiveMode.POINTS: return Material.PointListDrawMode;
+                case MeshPrimitiveMode.LINES: return Material.LineListDrawMode;
+                case MeshPrimitiveMode.LINE_LOOP: return Material.LineLoopDrawMode;
+                case MeshPrimitiveMode.LINE_STRIP: return Material.LineStripDrawMode;
+                case MeshPrimitiveMode.TRIANGLES: return Material.TriangleFillMode;
+                case MeshPrimitiveMode.TRIANGLE_STRIP: return Material.TriangleStripDrawMode;
+                case MeshPrimitiveMode.TRIANGLE_FAN: return Material.TriangleFanDrawMode;
+            }
+
+            throw new Error(`${context}: Invalid mesh primitive mode (${mode})`);
+        }
+
         private _compileMaterialsAsync(): Promise<void> {
         private _compileMaterialsAsync(): Promise<void> {
             const promises = new Array<Promise<void>>();
             const promises = new Array<Promise<void>>();
 
 
             if (this._gltf.materials) {
             if (this._gltf.materials) {
                 for (const material of this._gltf.materials) {
                 for (const material of this._gltf.materials) {
-                    const babylonMaterial = material._babylonMaterial;
-                    const babylonMeshes = material._babylonMeshes;
-                    if (babylonMaterial && babylonMeshes) {
-                        for (const babylonMesh of babylonMeshes) {
-                            // Ensure nonUniformScaling is set if necessary.
-                            babylonMesh.computeWorldMatrix(true);
-
-                            promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
-                            if (this.useClipPlane) {
-                                promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
+                    if (material._babylonData) {
+                        for (const babylonDrawMode in material._babylonData) {
+                            const babylonData = material._babylonData[babylonDrawMode];
+                            for (const babylonMesh of babylonData.meshes) {
+                                // Ensure nonUniformScaling is set if necessary.
+                                babylonMesh.computeWorldMatrix(true);
+
+                                const babylonMaterial = babylonData.material;
+                                promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
+                                if (this.useClipPlane) {
+                                    promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
+                                }
                             }
                             }
                         }
                         }
                     }
                     }

+ 3 - 3
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -27,7 +27,7 @@ module BABYLON.GLTF2 {
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> { return null; }
 
 
         /** Override this method to modify the default behavior for loading materials. */
         /** Override this method to modify the default behavior for loading materials. */
-        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
+        protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> { return null; }
 
 
         /** Override this method to modify the default behavior for loading uris. */
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> { return null; }
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>> { return null; }
@@ -75,8 +75,8 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         /** Helper method called by the loader to allow extensions to override loading materials. */
         /** Helper method called by the loader to allow extensions to override loading materials. */
-        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
-            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, assign));
+        public static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh, babylonDrawMode: number, assign: (babylonMaterial: Material) => void): Nullable<Promise<void>> {
+            return loader._applyExtensions(extension => extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
         }
         }
 
 
         /** Helper method called by the loader to allow extensions to override loading uris. */
         /** Helper method called by the loader to allow extensions to override loading uris. */

+ 9 - 6
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -7,7 +7,6 @@ module BABYLON.GLTF2 {
     }
     }
 
 
     export interface ILoaderAnimationChannel extends IAnimationChannel, IArrayItem {
     export interface ILoaderAnimationChannel extends IAnimationChannel, IArrayItem {
-        _babylonAnimationGroup: AnimationGroup;
     }
     }
 
 
     export interface ILoaderAnimationSamplerData {
     export interface ILoaderAnimationSamplerData {
@@ -24,7 +23,7 @@ module BABYLON.GLTF2 {
         channels: ILoaderAnimationChannel[];
         channels: ILoaderAnimationChannel[];
         samplers: ILoaderAnimationSampler[];
         samplers: ILoaderAnimationSampler[];
 
 
-        _babylonAnimationGroup: Nullable<AnimationGroup>;
+        _babylonAnimationGroup?: AnimationGroup;
     }
     }
 
 
     export interface ILoaderBuffer extends IBuffer, IArrayItem {
     export interface ILoaderBuffer extends IBuffer, IArrayItem {
@@ -43,9 +42,13 @@ module BABYLON.GLTF2 {
     }
     }
 
 
     export interface ILoaderMaterial extends IMaterial, IArrayItem {
     export interface ILoaderMaterial extends IMaterial, IArrayItem {
-        _babylonMaterial?: Material;
-        _babylonMeshes?: AbstractMesh[];
-        _loaded?: Promise<void>;
+        _babylonData?: {
+            [drawMode: number]: {
+                material: Material;
+                meshes: AbstractMesh[];
+                loaded: Promise<void>;
+            }
+        };
     }
     }
 
 
     export interface ILoaderMesh extends IMesh, IArrayItem {
     export interface ILoaderMesh extends IMesh, IArrayItem {
@@ -78,7 +81,7 @@ module BABYLON.GLTF2 {
     }
     }
 
 
     export interface ILoaderSkin extends ISkin, IArrayItem {
     export interface ILoaderSkin extends ISkin, IArrayItem {
-        _babylonSkeleton: Nullable<Skeleton>;
+        _babylonSkeleton?: Skeleton;
         _loaded?: Promise<void>;
         _loaded?: Promise<void>;
     }
     }
 
 

+ 15 - 15
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -132,6 +132,8 @@
 
 
         public FORCENORMALFORWARD = false;
         public FORCENORMALFORWARD = false;
 
 
+        public UNLIT = false;
+
         /**
         /**
          * Initializes the PBR Material defines.
          * Initializes the PBR Material defines.
          */
          */
@@ -533,6 +535,11 @@
         private _useLogarithmicDepth: boolean;
         private _useLogarithmicDepth: boolean;
 
 
         /**
         /**
+         * If set to true, no lighting calculations will be applied.
+         */
+        private _unlit = false;
+
+        /**
          * Instantiates a new PBRMaterial instance.
          * Instantiates a new PBRMaterial instance.
          * 
          * 
          * @param name The material name
          * @param name The material name
@@ -787,20 +794,10 @@
                 }
                 }
             }
             }
 
 
-            if (!engine.getCaps().standardDerivatives) {
-                let bufferMesh = null;
-                if (mesh.getClassName() === "InstancedMesh") {
-                    bufferMesh = (mesh as InstancedMesh).sourceMesh;
-                }
-                else if (mesh.getClassName() === "Mesh") {
-                    bufferMesh = mesh as Mesh;
-                }
-
-                if (bufferMesh && bufferMesh.geometry && bufferMesh.geometry.isReady() && !bufferMesh.geometry.isVerticesDataPresent(VertexBuffer.NormalKind)) {
-                    bufferMesh.createNormals(true);
-                    Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + bufferMesh.name);
+            if (!engine.getCaps().standardDerivatives && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
+                mesh.createNormals(true);
+                Tools.Warn("PBRMaterial: Normals have been created for the mesh: " + mesh.name);
                 }
                 }
-            }
 
 
             const effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
             const effect = this._prepareEffect(mesh, defines, this.onCompiled, this.onError, useInstances);
             if (effect) {
             if (effect) {
@@ -999,7 +996,7 @@
 
 
             // Textures
             // Textures
             defines.METALLICWORKFLOW = this.isMetallicWorkflow();
             defines.METALLICWORKFLOW = this.isMetallicWorkflow();
-            if (defines._areTexturesDirty) {     
+            if (defines._areTexturesDirty) {
                 defines._needUVs = false;
                 defines._needUVs = false;
                 if (scene.texturesEnabled) {
                 if (scene.texturesEnabled) {
                     if (scene.getEngine().getCaps().textureLOD) {
                     if (scene.getEngine().getCaps().textureLOD) {
@@ -1217,7 +1214,10 @@
             defines.HORIZONOCCLUSION = this._useHorizonOcclusion;
             defines.HORIZONOCCLUSION = this._useHorizonOcclusion;
 
 
             // Misc.
             // Misc.
-            MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh) || this._forceAlphaTest, defines);
+            if (defines._areMiscDirty) {
+                MaterialHelper.PrepareDefinesForMisc(mesh, scene, this._useLogarithmicDepth, this.pointsCloud, this.fogEnabled, this._shouldTurnAlphaTestOn(mesh) || this._forceAlphaTest, defines);
+                defines.UNLIT = this._unlit || ((this.pointsCloud || this.wireframe) && !mesh.isVerticesDataPresent(VertexBuffer.NormalKind));
+            }
 
 
             // Values that need to be evaluated on every frame
             // Values that need to be evaluated on every frame
             MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false, useClipPlane);
             MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false, useClipPlane);

+ 7 - 0
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -475,6 +475,13 @@
         public useRadianceOcclusion = true;
         public useRadianceOcclusion = true;
 
 
         /**
         /**
+         * If set to true, no lighting calculations will be applied.
+         */
+        @serialize()
+        @expandToProperty("_markAllSubMeshesAsLightsDirty")
+        public unlit = false;
+
+        /**
          * Gets the image processing configuration used either in this material.
          * Gets the image processing configuration used either in this material.
          */
          */
         public get imageProcessingConfiguration(): ImageProcessingConfiguration {
         public get imageProcessingConfiguration(): ImageProcessingConfiguration {

+ 19 - 5
src/Materials/babylon.material.ts

@@ -638,14 +638,22 @@
          */
          */
         @serialize()
         @serialize()
         public get wireframe(): boolean {
         public get wireframe(): boolean {
-            return this._fillMode === Material.WireFrameFillMode;
+            switch (this._fillMode) {
+                case Material.WireFrameFillMode:
+                case Material.LineListDrawMode:
+                case Material.LineLoopDrawMode:
+                case Material.LineStripDrawMode:
+                    return true;
+            }
+
+            return this._scene.forceWireframe;
         }
         }
 
 
         /**
         /**
          * Sets the state of wireframe mode.
          * Sets the state of wireframe mode.
          */
          */
         public set wireframe(value: boolean) {
         public set wireframe(value: boolean) {
-            this._fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
+            this.fillMode = (value ? Material.WireFrameFillMode : Material.TriangleFillMode);
         }
         }
 
 
         /**
         /**
@@ -653,14 +661,20 @@
          */
          */
         @serialize()
         @serialize()
         public get pointsCloud(): boolean {
         public get pointsCloud(): boolean {
-            return this._fillMode === Material.PointFillMode;
+            switch (this._fillMode) {
+                case Material.PointFillMode:
+                case Material.PointListDrawMode:
+                    return true;
+            }
+
+            return this._scene.forcePointsCloud;
         }
         }
 
 
         /**
         /**
          * Sets the state of point cloud mode.
          * Sets the state of point cloud mode.
          */
          */
         public set pointsCloud(value: boolean) {
         public set pointsCloud(value: boolean) {
-            this._fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
+            this.fillMode = (value ? Material.PointFillMode : Material.TriangleFillMode);
         }
         }
 
 
         /**
         /**
@@ -1202,7 +1216,7 @@
                 defines.markAsTexturesDirty();
                 defines.markAsTexturesDirty();
                 defines.markAsMiscDirty();
                 defines.markAsMiscDirty();
             });
             });
-        }        
+        }
 
 
         /**
         /**
          * Disposes the material.
          * Disposes the material.

+ 1 - 1
src/Materials/babylon.materialHelper.ts

@@ -71,7 +71,7 @@ module BABYLON {
         public static PrepareDefinesForMisc(mesh: AbstractMesh, scene: Scene, useLogarithmicDepth: boolean, pointsCloud: boolean, fogEnabled: boolean, alphaTest: boolean, defines: any): void {
         public static PrepareDefinesForMisc(mesh: AbstractMesh, scene: Scene, useLogarithmicDepth: boolean, pointsCloud: boolean, fogEnabled: boolean, alphaTest: boolean, defines: any): void {
             if (defines._areMiscDirty) {
             if (defines._areMiscDirty) {
                 defines["LOGARITHMICDEPTH"] = useLogarithmicDepth;
                 defines["LOGARITHMICDEPTH"] = useLogarithmicDepth;
-                defines["POINTSIZE"] = (pointsCloud || scene.forcePointsCloud);
+                defines["POINTSIZE"] = pointsCloud;
                 defines["FOG"] = (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && fogEnabled);
                 defines["FOG"] = (scene.fogEnabled && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && fogEnabled);
                 defines["NONUNIFORMSCALING"] = mesh.nonUniformScaling;
                 defines["NONUNIFORMSCALING"] = mesh.nonUniformScaling;
                 defines["ALPHATEST"] = alphaTest;
                 defines["ALPHATEST"] = alphaTest;

+ 15 - 0
src/Mesh/babylon.subMesh.ts

@@ -247,6 +247,21 @@
         public intersects(ray: Ray, positions: Vector3[], indices: IndicesArray, fastCheck?: boolean): Nullable<IntersectionInfo> {
         public intersects(ray: Ray, positions: Vector3[], indices: IndicesArray, fastCheck?: boolean): Nullable<IntersectionInfo> {
             var intersectInfo: Nullable<IntersectionInfo> = null;
             var intersectInfo: Nullable<IntersectionInfo> = null;
 
 
+            const material = this.getMaterial();
+            if (!material) {
+                return null;
+            }
+
+            switch (material.fillMode) {
+                case Material.PointListDrawMode:
+                case Material.LineListDrawMode:
+                case Material.LineLoopDrawMode:
+                case Material.LineStripDrawMode:
+                case Material.TriangleFanDrawMode:
+                case Material.TriangleStripDrawMode:
+                    return null;
+            }
+
             // LineMesh first as it's also a Mesh...
             // LineMesh first as it's also a Mesh...
             if (LinesMesh && this._mesh instanceof LinesMesh) {
             if (LinesMesh && this._mesh instanceof LinesMesh) {
                 var lineMesh = <LinesMesh>this._mesh;
                 var lineMesh = <LinesMesh>this._mesh;

+ 386 - 379
src/Shaders/pbr.fragment.fx

@@ -304,431 +304,451 @@ void main(void) {
 	ambientOcclusionColor = mix(ambientOcclusionColor, ambientOcclusionColorMap, vAmbientInfos.z);
 	ambientOcclusionColor = mix(ambientOcclusionColor, ambientOcclusionColorMap, vAmbientInfos.z);
 #endif
 #endif
 
 
-// _____________________________ Reflectivity Info _______________________________
-	float microSurface = vReflectivityColor.a;
-	vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
+#ifdef UNLIT
+	vec3 diffuseBase = vec3(1., 1., 1.);
+#else
+	// _____________________________ Reflectivity Info _______________________________
+		float microSurface = vReflectivityColor.a;
+		vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
 
 
-#ifdef METALLICWORKFLOW
-	vec2 metallicRoughness = surfaceReflectivityColor.rg;
+	#ifdef METALLICWORKFLOW
+		vec2 metallicRoughness = surfaceReflectivityColor.rg;
 
 
-	#ifdef REFLECTIVITY
-		vec4 surfaceMetallicColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+		#ifdef REFLECTIVITY
+			vec4 surfaceMetallicColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
 
 
-		#ifdef AOSTOREINMETALMAPRED
-			vec3 aoStoreInMetalMap = vec3(surfaceMetallicColorMap.r, surfaceMetallicColorMap.r, surfaceMetallicColorMap.r);
-			ambientOcclusionColor = mix(ambientOcclusionColor, aoStoreInMetalMap, vReflectivityInfos.z);
-		#endif
+			#ifdef AOSTOREINMETALMAPRED
+				vec3 aoStoreInMetalMap = vec3(surfaceMetallicColorMap.r, surfaceMetallicColorMap.r, surfaceMetallicColorMap.r);
+				ambientOcclusionColor = mix(ambientOcclusionColor, aoStoreInMetalMap, vReflectivityInfos.z);
+			#endif
 
 
-		#ifdef METALLNESSSTOREINMETALMAPBLUE
-			metallicRoughness.r *= surfaceMetallicColorMap.b;
-		#else
-			metallicRoughness.r *= surfaceMetallicColorMap.r;
-		#endif
+			#ifdef METALLNESSSTOREINMETALMAPBLUE
+				metallicRoughness.r *= surfaceMetallicColorMap.b;
+			#else
+				metallicRoughness.r *= surfaceMetallicColorMap.r;
+			#endif
 
 
-		#ifdef ROUGHNESSSTOREINMETALMAPALPHA
-			metallicRoughness.g *= surfaceMetallicColorMap.a;
-		#else
-			#ifdef ROUGHNESSSTOREINMETALMAPGREEN
-				metallicRoughness.g *= surfaceMetallicColorMap.g;
+			#ifdef ROUGHNESSSTOREINMETALMAPALPHA
+				metallicRoughness.g *= surfaceMetallicColorMap.a;
+			#else
+				#ifdef ROUGHNESSSTOREINMETALMAPGREEN
+					metallicRoughness.g *= surfaceMetallicColorMap.g;
+				#endif
 			#endif
 			#endif
 		#endif
 		#endif
-	#endif
 
 
-	#ifdef MICROSURFACEMAP
-		vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
-		metallicRoughness.g *= microSurfaceTexel.r;
-	#endif
-
-	// Compute microsurface form roughness.
-	microSurface = 1.0 - metallicRoughness.g;
+		#ifdef MICROSURFACEMAP
+			vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
+			metallicRoughness.g *= microSurfaceTexel.r;
+		#endif
 
 
-	// Diffuse is used as the base of the reflectivity.
-	vec3 baseColor = surfaceAlbedo;
+		// Compute microsurface form roughness.
+		microSurface = 1.0 - metallicRoughness.g;
 
 
-	// Default specular reflectance at normal incidence.
-	// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
-	const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
+		// Diffuse is used as the base of the reflectivity.
+		vec3 baseColor = surfaceAlbedo;
 
 
-	// Compute the converted diffuse.
-	surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
+		// Default specular reflectance at normal incidence.
+		// 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
+		const vec3 DefaultSpecularReflectanceDielectric = vec3(0.04, 0.04, 0.04);
 
 
-	// Compute the converted reflectivity.
-	surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
-#else
-	#ifdef REFLECTIVITY
-		vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
-		surfaceReflectivityColor *= toLinearSpace(surfaceReflectivityColorMap.rgb);
-		surfaceReflectivityColor *= vReflectivityInfos.y;
-
-		#ifdef MICROSURFACEFROMREFLECTIVITYMAP
-			microSurface *= surfaceReflectivityColorMap.a;
-			microSurface *= vReflectivityInfos.z;
-		#else
-			#ifdef MICROSURFACEAUTOMATIC
-				microSurface *= computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
-			#endif
+		// Compute the converted diffuse.
+		surfaceAlbedo = mix(baseColor.rgb * (1.0 - DefaultSpecularReflectanceDielectric.r), vec3(0., 0., 0.), metallicRoughness.r);
 
 
-			#ifdef MICROSURFACEMAP
-				vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
-				microSurface *= microSurfaceTexel.r;
+		// Compute the converted reflectivity.
+		surfaceReflectivityColor = mix(DefaultSpecularReflectanceDielectric, baseColor, metallicRoughness.r);
+	#else
+		#ifdef REFLECTIVITY
+			vec4 surfaceReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+			surfaceReflectivityColor *= toLinearSpace(surfaceReflectivityColorMap.rgb);
+			surfaceReflectivityColor *= vReflectivityInfos.y;
+
+			#ifdef MICROSURFACEFROMREFLECTIVITYMAP
+				microSurface *= surfaceReflectivityColorMap.a;
+				microSurface *= vReflectivityInfos.z;
+			#else
+				#ifdef MICROSURFACEAUTOMATIC
+					microSurface *= computeDefaultMicroSurface(microSurface, surfaceReflectivityColor);
+				#endif
+
+				#ifdef MICROSURFACEMAP
+					vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
+					microSurface *= microSurfaceTexel.r;
+				#endif
 			#endif
 			#endif
 		#endif
 		#endif
 	#endif
 	#endif
-#endif
 
 
-	// Adapt microSurface.
-	microSurface = clamp(microSurface, 0., 1.);
-	// Compute roughness.
-	float roughness = 1. - microSurface;
-
-// _____________________________ Alpha Fresnel ___________________________________
-#ifdef ALPHAFRESNEL
-	#if defined(ALPHATEST) || defined(ALPHABLEND)
-		// Convert approximate perceptual opacity (gamma-encoded opacity) to linear opacity (absorptance, or inverse transmission)
-		// for use with the linear HDR render target. The final composition will be converted back to gamma encoded values for eventual display.
-		// Uses power 2.0 rather than 2.2 for simplicity/efficiency, and because the mapping does not need to map the gamma applied to RGB.
-		float opacityPerceptual = alpha;
-
-		#ifdef LINEARALPHAFRESNEL
-			float opacity0 = opacityPerceptual;
-		#else
-			float opacity0 = opacityPerceptual * opacityPerceptual;
-		#endif
-		float opacity90 = fresnelGrazingReflectance(opacity0);
+		// Adapt microSurface.
+		microSurface = clamp(microSurface, 0., 1.);
+		// Compute roughness.
+		float roughness = 1. - microSurface;
 
 
-		vec3 normalForward = faceforward(normalW, -viewDirectionW, normalW);
+	// _____________________________ Alpha Fresnel ___________________________________
+	#ifdef ALPHAFRESNEL
+		#if defined(ALPHATEST) || defined(ALPHABLEND)
+			// Convert approximate perceptual opacity (gamma-encoded opacity) to linear opacity (absorptance, or inverse transmission)
+			// for use with the linear HDR render target. The final composition will be converted back to gamma encoded values for eventual display.
+			// Uses power 2.0 rather than 2.2 for simplicity/efficiency, and because the mapping does not need to map the gamma applied to RGB.
+			float opacityPerceptual = alpha;
 
 
-		// Calculate the appropriate linear opacity for the current viewing angle (formally, this quantity is the "directional absorptance").
-		alpha = fresnelSchlickEnvironmentGGX(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
-		
-		#ifdef ALPHATEST
-			if (alpha <= ALPHATESTVALUE)
-				discard;
+			#ifdef LINEARALPHAFRESNEL
+				float opacity0 = opacityPerceptual;
+			#else
+				float opacity0 = opacityPerceptual * opacityPerceptual;
+			#endif
+			float opacity90 = fresnelGrazingReflectance(opacity0);
 
 
-			#ifndef ALPHABLEND
-				// Prevent to blend with the canvas.
-				alpha = 1.0;
+			vec3 normalForward = faceforward(normalW, -viewDirectionW, normalW);
+
+			// Calculate the appropriate linear opacity for the current viewing angle (formally, this quantity is the "directional absorptance").
+			alpha = fresnelSchlickEnvironmentGGX(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
+
+			#ifdef ALPHATEST
+				if (alpha <= ALPHATESTVALUE)
+					discard;
+
+				#ifndef ALPHABLEND
+					// Prevent to blend with the canvas.
+					alpha = 1.0;
+				#endif
 			#endif
 			#endif
 		#endif
 		#endif
 	#endif
 	#endif
-#endif
 
 
-// _____________________________ Compute LODs Fetch ____________________________________
-	// Compute N dot V.
-	float NdotVUnclamped = dot(normalW, viewDirectionW);
-	float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
-	float alphaG = convertRoughnessToAverageSlope(roughness);
+	// _____________________________ Compute LODs Fetch ____________________________________
+		// Compute N dot V.
+		float NdotVUnclamped = dot(normalW, viewDirectionW);
+		float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
+		float alphaG = convertRoughnessToAverageSlope(roughness);
 
 
-// _____________________________ Refraction Info _______________________________________
-#ifdef REFRACTION
-	vec3 environmentRefraction = vec3(0., 0., 0.);
-	
-	vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
-	#ifdef REFRACTIONMAP_OPPOSITEZ
-		refractionVector.z *= -1.0;
-	#endif
+	// _____________________________ Refraction Info _______________________________________
+	#ifdef REFRACTION
+		vec3 environmentRefraction = vec3(0., 0., 0.);
 
 
-	// _____________________________ 2D vs 3D Maps ________________________________
-	#ifdef REFRACTIONMAP_3D
-		refractionVector.y = refractionVector.y * vRefractionInfos.w;
-		vec3 refractionCoords = refractionVector;
-		refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
-	#else
-		vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
-		vec2 refractionCoords = vRefractionUVW.xy / vRefractionUVW.z;
-		refractionCoords.y = 1.0 - refractionCoords.y;
-	#endif
+		vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
+		#ifdef REFRACTIONMAP_OPPOSITEZ
+			refractionVector.z *= -1.0;
+		#endif
 
 
-	#ifdef LODINREFRACTIONALPHA
-		float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
-	#else
-		float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, 1.0);
-	#endif
-	
-	#ifdef LODBASEDMICROSFURACE
-		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
-		refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
+		// _____________________________ 2D vs 3D Maps ________________________________
+		#ifdef REFRACTIONMAP_3D
+			refractionVector.y = refractionVector.y * vRefractionInfos.w;
+			vec3 refractionCoords = refractionVector;
+			refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
+		#else
+			vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
+			vec2 refractionCoords = vRefractionUVW.xy / vRefractionUVW.z;
+			refractionCoords.y = 1.0 - refractionCoords.y;
+		#endif
 
 
 		#ifdef LODINREFRACTIONALPHA
 		#ifdef LODINREFRACTIONALPHA
-			// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection 
-			// is constrained to appropriate LOD levels in order to prevent aliasing.
-			// The environment map is first sampled without custom LOD selection to determine
-			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
-			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
-			// where the normal is varying rapidly).
-
-			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
-			// manual calculation via derivatives is also possible, but for simplicity we use the 
-			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
-			float automaticRefractionLOD = UNPACK_LOD(sampleRefraction(refractionSampler, refractionCoords).a);
-			float requestedRefractionLOD = max(automaticRefractionLOD, refractionLOD);
+			float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
 		#else
 		#else
-			float requestedRefractionLOD = refractionLOD;
+			float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, 1.0);
 		#endif
 		#endif
 
 
-		environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
-	#else
-		float lodRefractionNormalized = clamp(refractionLOD / log2(vRefractionMicrosurfaceInfos.x), 0., 1.);
-		float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
-
-		vec3 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords).rgb;
-		if(lodRefractionNormalizedDoubled < 1.0){
-			environmentRefraction = mix(
-				sampleRefraction(refractionSamplerHigh, refractionCoords).rgb,
-				environmentRefractionMid,
-				lodRefractionNormalizedDoubled
-			);
-		}else{
-			environmentRefraction = mix(
-				environmentRefractionMid,
-				sampleRefraction(refractionSamplerLow, refractionCoords).rgb,
-				lodRefractionNormalizedDoubled - 1.0
-			);
-		}
-	#endif
-
-	#ifdef GAMMAREFRACTION
-		environmentRefraction = toLinearSpace(environmentRefraction.rgb);
-	#endif
-
-	// _____________________________ Levels _____________________________________
-	environmentRefraction *= vRefractionInfos.x;
-#endif
+		#ifdef LODBASEDMICROSFURACE
+			// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+			refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
+
+			#ifdef LODINREFRACTIONALPHA
+				// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
+				// is constrained to appropriate LOD levels in order to prevent aliasing.
+				// The environment map is first sampled without custom LOD selection to determine
+				// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+				// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry
+				// where the normal is varying rapidly).
+
+				// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+				// manual calculation via derivatives is also possible, but for simplicity we use the 
+				// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+				float automaticRefractionLOD = UNPACK_LOD(sampleRefraction(refractionSampler, refractionCoords).a);
+				float requestedRefractionLOD = max(automaticRefractionLOD, refractionLOD);
+			#else
+				float requestedRefractionLOD = refractionLOD;
+			#endif
 
 
-// _____________________________ Reflection Info _______________________________________
-#ifdef REFLECTION
-	vec3 environmentRadiance = vec3(0., 0., 0.);
-	vec3 environmentIrradiance = vec3(0., 0., 0.);
+			environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
+		#else
+			float lodRefractionNormalized = clamp(refractionLOD / log2(vRefractionMicrosurfaceInfos.x), 0., 1.);
+			float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
+
+			vec3 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords).rgb;
+			if(lodRefractionNormalizedDoubled < 1.0){
+				environmentRefraction = mix(
+					sampleRefraction(refractionSamplerHigh, refractionCoords).rgb,
+					environmentRefractionMid,
+					lodRefractionNormalizedDoubled
+				);
+			}else{
+				environmentRefraction = mix(
+					environmentRefractionMid,
+					sampleRefraction(refractionSamplerLow, refractionCoords).rgb,
+					lodRefractionNormalizedDoubled - 1.0
+				);
+			}
+		#endif
+
+		#ifdef GAMMAREFRACTION
+			environmentRefraction = toLinearSpace(environmentRefraction.rgb);
+		#endif
 
 
-	vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
-	#ifdef REFLECTIONMAP_OPPOSITEZ
-		reflectionVector.z *= -1.0;
+		// _____________________________ Levels _____________________________________
+		environmentRefraction *= vRefractionInfos.x;
 	#endif
 	#endif
 
 
-	// _____________________________ 2D vs 3D Maps ________________________________
-	#ifdef REFLECTIONMAP_3D
-		vec3 reflectionCoords = reflectionVector;
-	#else
-		vec2 reflectionCoords = reflectionVector.xy;
-		#ifdef REFLECTIONMAP_PROJECTION
-			reflectionCoords /= reflectionVector.z;
+	// _____________________________ Reflection Info _______________________________________
+	#ifdef REFLECTION
+		vec3 environmentRadiance = vec3(0., 0., 0.);
+		vec3 environmentIrradiance = vec3(0., 0., 0.);
+
+		vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
+		#ifdef REFLECTIONMAP_OPPOSITEZ
+			reflectionVector.z *= -1.0;
 		#endif
 		#endif
-		reflectionCoords.y = 1.0 - reflectionCoords.y;
-	#endif
-	
-	#if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
-		float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
-	#else
-		float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, 1.);
-	#endif
-
-	#ifdef LODBASEDMICROSFURACE
-		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
-		reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
-
-		#ifdef LODINREFLECTIONALPHA
-			// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection 
-			// is constrained to appropriate LOD levels in order to prevent aliasing.
-			// The environment map is first sampled without custom LOD selection to determine
-			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
-			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
-			// where the normal is varying rapidly).
-
-			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
-			// manual calculation via derivatives is also possible, but for simplicity we use the 
-			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
-			float automaticReflectionLOD = UNPACK_LOD(sampleReflection(reflectionSampler, reflectionCoords).a);
-			float requestedReflectionLOD = max(automaticReflectionLOD, reflectionLOD);
+
+		// _____________________________ 2D vs 3D Maps ________________________________
+		#ifdef REFLECTIONMAP_3D
+			vec3 reflectionCoords = reflectionVector;
+		#else
+			vec2 reflectionCoords = reflectionVector.xy;
+			#ifdef REFLECTIONMAP_PROJECTION
+				reflectionCoords /= reflectionVector.z;
+			#endif
+			reflectionCoords.y = 1.0 - reflectionCoords.y;
+		#endif
+		
+		#if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
+			float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
 		#else
 		#else
-			float requestedReflectionLOD = reflectionLOD;
+			float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, 1.);
 		#endif
 		#endif
 
 
-		environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
-	#else
-		float lodReflectionNormalized = clamp(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
-		float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
-
-		vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
-		if(lodReflectionNormalizedDoubled < 1.0){
-			environmentRadiance = mix(
-				sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
-				environmentSpecularMid,
-				lodReflectionNormalizedDoubled
-			);
-		}else{
-			environmentRadiance = mix(
-				environmentSpecularMid,
-				sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
-				lodReflectionNormalizedDoubled - 1.0
-			);
-		}
-	#endif
-
-	#ifdef GAMMAREFLECTION
-		environmentRadiance = toLinearSpace(environmentRadiance.rgb);
-	#endif
-
-	// _____________________________ Irradiance ________________________________
-	#ifdef USESPHERICALFROMREFLECTIONMAP
-		#if defined(NORMAL) && defined(USESPHERICALINVERTEX)
-			environmentIrradiance = vEnvironmentIrradiance;
+		#ifdef LODBASEDMICROSFURACE
+			// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+			reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
+
+			#ifdef LODINREFLECTIONALPHA
+				// Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
+				// is constrained to appropriate LOD levels in order to prevent aliasing.
+				// The environment map is first sampled without custom LOD selection to determine
+				// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+				// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry
+				// where the normal is varying rapidly).
+
+				// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+				// manual calculation via derivatives is also possible, but for simplicity we use the
+				// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+				float automaticReflectionLOD = UNPACK_LOD(sampleReflection(reflectionSampler, reflectionCoords).a);
+				float requestedReflectionLOD = max(automaticReflectionLOD, reflectionLOD);
+			#else
+				float requestedReflectionLOD = reflectionLOD;
+			#endif
+
+			environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
 		#else
 		#else
-			vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
-			#ifdef REFLECTIONMAP_OPPOSITEZ
-				irradianceVector.z *= -1.0;
+			float lodReflectionNormalized = clamp(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
+			float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
+
+			vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
+			if(lodReflectionNormalizedDoubled < 1.0){
+				environmentRadiance = mix(
+					sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
+					environmentSpecularMid,
+					lodReflectionNormalizedDoubled
+				);
+			}else{
+				environmentRadiance = mix(
+					environmentSpecularMid,
+					sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
+					lodReflectionNormalizedDoubled - 1.0
+				);
+			}
+		#endif
+
+		#ifdef GAMMAREFLECTION
+			environmentRadiance = toLinearSpace(environmentRadiance.rgb);
+		#endif
+
+		// _____________________________ Irradiance ________________________________
+		#ifdef USESPHERICALFROMREFLECTIONMAP
+			#if defined(NORMAL) && defined(USESPHERICALINVERTEX)
+				environmentIrradiance = vEnvironmentIrradiance;
+			#else
+				vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+				#ifdef REFLECTIONMAP_OPPOSITEZ
+					irradianceVector.z *= -1.0;
+				#endif
+				environmentIrradiance = environmentIrradianceJones(irradianceVector);
 			#endif
 			#endif
-			environmentIrradiance = environmentIrradianceJones(irradianceVector);
 		#endif
 		#endif
+
+		// _____________________________ Levels _____________________________________
+		environmentRadiance *= vReflectionInfos.x;
+		environmentRadiance *= vReflectionColor.rgb;
+		environmentIrradiance *= vReflectionColor.rgb;
 	#endif
 	#endif
 
 
-	// _____________________________ Levels _____________________________________
-	environmentRadiance *= vReflectionInfos.x;
-	environmentRadiance *= vReflectionColor.rgb;
-	environmentIrradiance *= vReflectionColor.rgb;
-#endif
+	// ____________________________________________________________________________________
+	// _____________________________ Direct Lighting Param ________________________________
+		// Compute reflectance.
+		float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
+		float reflectance90 = fresnelGrazingReflectance(reflectance);
+		vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
+		vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
 
-// ____________________________________________________________________________________
-// _____________________________ Direct Lighting Param ________________________________
-	// Compute reflectance.
-	float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
-	float reflectance90 = fresnelGrazingReflectance(reflectance);
-	vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
-	vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
-
-// _____________________________ Direct Lighting Info __________________________________
-	vec3 diffuseBase = vec3(0., 0., 0.);
-#ifdef SPECULARTERM
-	vec3 specularBase = vec3(0., 0., 0.);
-#endif
+	// _____________________________ Direct Lighting Info __________________________________
+		vec3 diffuseBase = vec3(0., 0., 0.);
+	#ifdef SPECULARTERM
+		vec3 specularBase = vec3(0., 0., 0.);
+	#endif
 
 
-#ifdef LIGHTMAP
-	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb);
-	#ifdef GAMMALIGHTMAP
-		lightmapColor = toLinearSpace(lightmapColor);
+	#ifdef LIGHTMAP
+		vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb);
+		#ifdef GAMMALIGHTMAP
+			lightmapColor = toLinearSpace(lightmapColor);
+		#endif
+		lightmapColor *= vLightmapInfos.y
 	#endif
 	#endif
-	lightmapColor *= vLightmapInfos.y
-#endif
 
 
-	lightingInfo info;
-	float shadow = 1.; // 1 - shadowLevel
-	float NdotL = -1.;
+		lightingInfo info;
+		float shadow = 1.; // 1 - shadowLevel
+		float NdotL = -1.;
 
 
-#include<lightFragment>[0..maxSimultaneousLights]
+	#include<lightFragment>[0..maxSimultaneousLights]
 
 
-// _________________________ Specular Environment Oclusion __________________________
-#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
-	// Indexed on cos(theta) and roughness
-	vec2 brdfSamplerUV = vec2(NdotV, roughness);
-	
-	// We can find the scale and offset to apply to the specular value.
-	vec4 environmentBrdf = texture2D(environmentBrdfSampler, brdfSamplerUV);
+	// _________________________ Specular Environment Oclusion __________________________
+	#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
+		// Indexed on cos(theta) and roughness
+		vec2 brdfSamplerUV = vec2(NdotV, roughness);
+		
+		// We can find the scale and offset to apply to the specular value.
+		vec4 environmentBrdf = texture2D(environmentBrdfSampler, brdfSamplerUV);
 
 
-	vec3 specularEnvironmentReflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
+		vec3 specularEnvironmentReflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
 
 
-	#ifdef RADIANCEOCCLUSION
-		#ifdef AMBIENTINGRAYSCALE
-			float ambientMonochrome = ambientOcclusionColor.r;
-		#else
-			float ambientMonochrome = getLuminance(ambientOcclusionColor);
-		#endif
+		#ifdef RADIANCEOCCLUSION
+			#ifdef AMBIENTINGRAYSCALE
+				float ambientMonochrome = ambientOcclusionColor.r;
+			#else
+				float ambientMonochrome = getLuminance(ambientOcclusionColor);
+			#endif
 
 
-		float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
-		specularEnvironmentReflectance *= seo;
-	#endif
+			float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
+			specularEnvironmentReflectance *= seo;
+		#endif
 
 
-	#ifdef HORIZONOCCLUSION
-		#ifdef BUMP
-			#ifdef REFLECTIONMAP_3D
-				float eho = environmentHorizonOcclusion(reflectionCoords, normalW);
-				specularEnvironmentReflectance *= eho;
+		#ifdef HORIZONOCCLUSION
+			#ifdef BUMP
+				#ifdef REFLECTIONMAP_3D
+					float eho = environmentHorizonOcclusion(reflectionCoords, normalW);
+					specularEnvironmentReflectance *= eho;
+				#endif
 			#endif
 			#endif
 		#endif
 		#endif
+	#else
+		// Jones implementation of a well balanced fast analytical solution.
+		vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
 	#endif
 	#endif
-#else
-	// Jones implementation of a well balanced fast analytical solution.
-	vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
-#endif
 
 
-// _____________________________ Refractance+Tint ________________________________
-#ifdef REFRACTION
-	vec3 refractance = vec3(0.0, 0.0, 0.0);
-	vec3 transmission = vec3(1.0, 1.0, 1.0);
-	#ifdef LINKREFRACTIONTOTRANSPARENCY
-		// Transmission based on alpha.
-		transmission *= (1.0 - alpha);
+	// _____________________________ Refractance+Tint ________________________________
+	#ifdef REFRACTION
+		vec3 refractance = vec3(0.0, 0.0, 0.0);
+		vec3 transmission = vec3(1.0, 1.0, 1.0);
+		#ifdef LINKREFRACTIONTOTRANSPARENCY
+			// Transmission based on alpha.
+			transmission *= (1.0 - alpha);
 
 
-		// Tint the material with albedo.
-		// TODO. PBR Tinting.
-		vec3 mixedAlbedo = surfaceAlbedo;
-		float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
-		vec3 tint = clamp(maxChannel * mixedAlbedo, 0.0, 1.0);
+			// Tint the material with albedo.
+			// TODO. PBR Tinting.
+			vec3 mixedAlbedo = surfaceAlbedo;
+			float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
+			vec3 tint = clamp(maxChannel * mixedAlbedo, 0.0, 1.0);
 
 
-		// Decrease Albedo Contribution
-		surfaceAlbedo *= alpha;
+			// Decrease Albedo Contribution
+			surfaceAlbedo *= alpha;
 
 
-		// Decrease irradiance Contribution
-		environmentIrradiance *= alpha;
+			// Decrease irradiance Contribution
+			environmentIrradiance *= alpha;
 
 
-		// Tint reflectance
-		environmentRefraction *= tint;
+			// Tint reflectance
+			environmentRefraction *= tint;
 
 
-		// Put alpha back to 1;
-		alpha = 1.0;
-	#endif
+			// Put alpha back to 1;
+			alpha = 1.0;
+		#endif
 
 
-	// Add Multiple internal bounces.
-	vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
-	specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
+		// Add Multiple internal bounces.
+		vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
+		specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
 
 
-	// In theory T = 1 - R.
-	transmission *= 1.0 - specularEnvironmentReflectance;
+		// In theory T = 1 - R.
+		transmission *= 1.0 - specularEnvironmentReflectance;
 
 
-	// Should baked in diffuse.
-	refractance = transmission;
-#endif
+		// Should baked in diffuse.
+		refractance = transmission;
+	#endif
 
 
-// ______________________________________________________________________________
-// _____________________________ Energy Conservation  ___________________________
+	// ______________________________________________________________________________
+	// _____________________________ Energy Conservation  ___________________________
 	// Apply Energy Conservation taking in account the environment level only if 
 	// Apply Energy Conservation taking in account the environment level only if 
 	// the environment is present.
 	// the environment is present.
 	surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
 	surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
 
 
-// _____________________________ Diffuse ________________________________________
-	vec3 finalDiffuse = diffuseBase;
-	finalDiffuse.rgb += vAmbientColor;
-	finalDiffuse *= surfaceAlbedo.rgb;
-	finalDiffuse = max(finalDiffuse, 0.0);
+	// _____________________________ Irradiance ______________________________________
+	#ifdef REFLECTION
+		vec3 finalIrradiance = environmentIrradiance;
+		finalIrradiance *= surfaceAlbedo.rgb;
+	#endif
 
 
-// _____________________________ Irradiance ______________________________________
-#ifdef REFLECTION
-	vec3 finalIrradiance = environmentIrradiance;
-	finalIrradiance *= surfaceAlbedo.rgb;
-#endif
+	// _____________________________ Specular ________________________________________
+	#ifdef SPECULARTERM
+		vec3 finalSpecular = specularBase;
+		finalSpecular = max(finalSpecular, 0.0);
 
 
-// _____________________________ Specular ________________________________________
-#ifdef SPECULARTERM
-	vec3 finalSpecular = specularBase;
-	finalSpecular = max(finalSpecular, 0.0);
+		// Full value needed for alpha.
+		vec3 finalSpecularScaled = finalSpecular * vLightingIntensity.x * vLightingIntensity.w;
+	#endif
 
 
-	// Full value needed for alpha.
-	vec3 finalSpecularScaled = finalSpecular * vLightingIntensity.x * vLightingIntensity.w;
-#endif
+	// _____________________________ Radiance ________________________________________
+	#ifdef REFLECTION
+		vec3 finalRadiance = environmentRadiance;
+		finalRadiance *= specularEnvironmentReflectance;
 
 
-// _____________________________ Radiance_________________________________________
-#ifdef REFLECTION
-	vec3 finalRadiance = environmentRadiance;
-	finalRadiance *= specularEnvironmentReflectance;
+		// Full value needed for alpha. 
+		vec3 finalRadianceScaled = finalRadiance * vLightingIntensity.z;
+	#endif
 
 
-	// Full value needed for alpha. 
-	vec3 finalRadianceScaled = finalRadiance * vLightingIntensity.z;
-#endif
+	// _____________________________ Refraction ______________________________________
+	#ifdef REFRACTION
+		vec3 finalRefraction = environmentRefraction;
+		finalRefraction *= refractance;
+	#endif
 
 
-// _____________________________ Refraction ______________________________________
-#ifdef REFRACTION
-	vec3 finalRefraction = environmentRefraction;
-	finalRefraction *= refractance;
+	// _____________________________ Highlights on Alpha _____________________________
+	#ifdef ALPHABLEND
+		float luminanceOverAlpha = 0.0;
+		#if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
+			luminanceOverAlpha += getLuminance(finalRadianceScaled);
+		#endif
+
+		#if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
+			luminanceOverAlpha += getLuminance(finalSpecularScaled);
+		#endif
+
+		#if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
+			alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
+		#endif
+	#endif
 #endif
 #endif
 
 
+// _____________________________ Diffuse ________________________________________
+	vec3 finalDiffuse = diffuseBase;
+	finalDiffuse.rgb += vAmbientColor;
+	finalDiffuse *= surfaceAlbedo.rgb;
+	finalDiffuse = max(finalDiffuse, 0.0);
+
 // _____________________________ Emissive ________________________________________
 // _____________________________ Emissive ________________________________________
 	vec3 finalEmissive = vEmissiveColor;
 	vec3 finalEmissive = vEmissiveColor;
 #ifdef EMISSIVE
 #ifdef EMISSIVE
@@ -737,44 +757,31 @@ void main(void) {
 	finalEmissive *=  vEmissiveInfos.y;
 	finalEmissive *=  vEmissiveInfos.y;
 #endif
 #endif
 
 
-// _____________________________ Highlights on Alpha _____________________________
-#ifdef ALPHABLEND
-	float luminanceOverAlpha = 0.0;
-	#if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
-		luminanceOverAlpha += getLuminance(finalRadianceScaled);
-	#endif
-
-	#if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
-		luminanceOverAlpha += getLuminance(finalSpecularScaled);
-	#endif
-
-	#if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
-		alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
-	#endif
-#endif
-
 // _______________________________________________________________________________
 // _______________________________________________________________________________
 // _____________________________ Composition _____________________________________
 // _____________________________ Composition _____________________________________
 	// Reflection already includes the environment intensity.
 	// Reflection already includes the environment intensity.
-	vec4 finalColor = vec4(finalDiffuse			* ambientOcclusionColor * vLightingIntensity.x +
-#ifdef REFLECTION
-						finalIrradiance			* ambientOcclusionColor * vLightingIntensity.z +
-#endif
-#ifdef SPECULARTERM
-// Computed in the previous step to help with alpha luminance.
-//						finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
-						finalSpecularScaled +
-#endif
-#ifdef REFLECTION
-// Comupted in the previous step to help with alpha luminance.
-//						finalRadiance			* vLightingIntensity.z +
-						finalRadianceScaled +
-#endif
-#ifdef REFRACTION
-						finalRefraction			* vLightingIntensity.z +
+	vec4 finalColor = vec4(
+		finalDiffuse			* ambientOcclusionColor * vLightingIntensity.x +
+#ifndef UNLIT
+	#ifdef REFLECTION
+		finalIrradiance			* ambientOcclusionColor * vLightingIntensity.z +
+	#endif
+	#ifdef SPECULARTERM
+	// Computed in the previous step to help with alpha luminance.
+	//	finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
+		finalSpecularScaled +
+	#endif
+	#ifdef REFLECTION
+	// Comupted in the previous step to help with alpha luminance.
+	//	finalRadiance			* vLightingIntensity.z +
+		finalRadianceScaled +
+	#endif
+	#ifdef REFRACTION
+		finalRefraction			* vLightingIntensity.z +
+	#endif
 #endif
 #endif
-						finalEmissive			* vLightingIntensity.y,
-						alpha);
+		finalEmissive			* vLightingIntensity.y,
+		alpha);
 
 
 // _____________________________ LightMappping _____________________________________
 // _____________________________ LightMappping _____________________________________
 #ifdef LIGHTMAP
 #ifdef LIGHTMAP

+ 12 - 1
src/babylon.scene.ts

@@ -170,7 +170,18 @@
             return this._imageProcessingConfiguration;
             return this._imageProcessingConfiguration;
         }
         }
 
 
-        public forceWireframe = false;
+        private _forceWireframe = false;
+        public set forceWireframe(value: boolean) {
+            if (this._forceWireframe === value) {
+                return;
+            }
+            this._forceWireframe = value;
+            this.markAllMaterialsAsDirty(Material.MiscDirtyFlag);
+        }
+        public get forceWireframe(): boolean {
+            return this._forceWireframe;
+        }
+
         private _forcePointsCloud = false;
         private _forcePointsCloud = false;
         public set forcePointsCloud(value: boolean) {
         public set forcePointsCloud(value: boolean) {
             if (this._forcePointsCloud === value) {
             if (this._forcePointsCloud === value) {

+ 6 - 0
tests/validation/validation.js

@@ -217,6 +217,12 @@ function runTest(index, done) {
             });
             });
     }
     }
     else if (test.playgroundId) {
     else if (test.playgroundId) {
+        if (test.playgroundId[0] !== "#" || test.playgroundId.indexOf("#", 1) === -1) {
+            console.error("Invalid playground id");
+            done(false);
+            return;
+        }
+
         var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var snippetUrl = "//babylonjs-api2.azurewebsites.net/snippets";
         var pgRoot = "/Playground"
         var pgRoot = "/Playground"
         var xmlHttp = new XMLHttpRequest();
         var xmlHttp = new XMLHttpRequest();