Quellcode durchsuchen

Add PointLight shadow fallback to spot

sevan vor 8 Jahren
Ursprung
Commit
af7fde3fd2

+ 5 - 2
Tools/Gulp/config.json

@@ -25,7 +25,7 @@
         "minimalWithBuilder": ["meshBuilder", "standardMaterial", "freeCamera", "hemisphericLight"],
         "minimalViewer": [
                 "meshBuilder", "animations", "arcRotateCamera", "additionalTextures", "textureFormats", "debug",
-                "multiMaterial"
+                "multiMaterial", "pbrMaterial", "pointLight", "directionalLight", "spotLight"
         ],
         "distributed": ["minimalViewer"]
     },
@@ -299,7 +299,7 @@
                 "fogFragmentDeclaration",
                 "clipPlaneFragment",
                 "bumpFragment",
-                "pbrLightFunctionsCall",
+                "lightFragment",
                 "logDepthFragment",
                 "fogFragment"
             ]
@@ -345,6 +345,7 @@
         "pointLight" : 
         {
             "files": [
+                "../../src/Lights/babylon.shadowLight.js",
                 "../../src/Lights/babylon.pointLight.js"
             ],
             "dependUpon" : [
@@ -354,6 +355,7 @@
         "directionalLight" : 
         {
             "files": [
+                "../../src/Lights/babylon.shadowLight.js",
                 "../../src/Lights/babylon.directionalLight.js"
             ],
             "dependUpon" : [
@@ -363,6 +365,7 @@
         "spotLight" : 
         {
             "files": [
+                "../../src/Lights/babylon.shadowLight.js",
                 "../../src/Lights/babylon.spotLight.js"
             ],
             "dependUpon" : [

+ 127 - 36
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -3,6 +3,13 @@
         getShadowMap(): RenderTargetTexture;
  
         isReady(subMesh: SubMesh, useInstances: boolean): boolean;
+
+        prepareDefines(defines: MaterialDefines, lightIndex: number): void;
+        bindShadowLight(lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean;
+
+        recreateShadowMap(): void;
+
+        serialize(): any;
         dispose(): void;
     }
  
@@ -95,15 +102,7 @@
             }
 
             this._filter = value;
-
-            if (this.usePoissonSampling || this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
-                this._shadowMap.anisotropicFilteringLevel = 16;
-                this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
-            } else {
-                this._shadowMap.anisotropicFilteringLevel = 1;
-                this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
-            }
-
+            this._applyFilterValues();
             this._light._markMeshesAsLightDirty();
         }
 
@@ -143,7 +142,7 @@
         public get useBlurExponentialShadowMap(): boolean {
             return this.filter === ShadowGenerator.FILTER_BLUREXPONENTIALSHADOWMAP;
         }
-        public set useBlurExponentialShadowMap(value: boolean) {            
+        public set useBlurExponentialShadowMap(value: boolean) {
             if (this._light.needCube() && value) {
                 this.useExponentialShadowMap = true; // Blurring the cubemap is going to be too expensive. Reverting to unblurred version
             } else {
@@ -174,6 +173,8 @@
         private _currentFaceIndexCache = 0;
 
         private _useFullFloat = true;
+        private _textureType: number;
+        private _isCube = false;
 
         /**
          * Creates a ShadowGenerator object.  
@@ -185,27 +186,34 @@
          * Documentation : http://doc.babylonjs.com/tutorials/shadows  
          */
         constructor(mapSize: number, light: IShadowLight) {
+            this._mapSize = mapSize;
             this._light = light;
             this._scene = light.getScene();
-            this._mapSize = mapSize;
-
             light._shadowGenerator = this;
-            light._markMeshesAsLightDirty();
+
+            this._initializeGenerator(1);
 
             // Texture type fallback from float to int if not supported.
-            var textureType: number;
             var caps = this._scene.getEngine().getCaps();
             if (caps.textureFloat && caps.textureFloatLinearFiltering && caps.textureFloatRender) {
                 this._useFullFloat = true;
-                textureType = Engine.TEXTURETYPE_FLOAT;
+                this._textureType = Engine.TEXTURETYPE_FLOAT;
             }
             else {
                 this._useFullFloat = false;
-                textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
+                this._textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
             }
-            
+        }
+        
+        private _initializeGenerator(boxBlurOffset: number) {
+            var light = this._light;
+            var scene = this._scene;
+            var textureType = this._textureType;
+
+            light._markMeshesAsLightDirty();
+
             // Render target
-            this._shadowMap = new RenderTargetTexture(light.name + "_shadowMap", mapSize, this._scene, false, true, textureType, light.needCube());
+            this._shadowMap = new RenderTargetTexture(light.name + "_shadowMap", this._mapSize, this._scene, false, true, textureType, light.needCube());
             this._shadowMap.wrapU = Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.wrapV = Texture.CLAMP_ADDRESSMODE;
             this._shadowMap.anisotropicFilteringLevel = 1;
@@ -222,7 +230,7 @@
                 }
 
                 if (!this._shadowMap2) {
-                    this._shadowMap2 = new RenderTargetTexture(light.name + "_shadowMap", mapSize, this._scene, false, true, textureType);
+                    this._shadowMap2 = new RenderTargetTexture(light.name + "_shadowMap", this._mapSize, this._scene, false, true, textureType);
                     this._shadowMap2.wrapU = Texture.CLAMP_ADDRESSMODE;
                     this._shadowMap2.wrapV = Texture.CLAMP_ADDRESSMODE;
                     this._shadowMap2.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
@@ -232,7 +240,7 @@
                         effect.setTexture("textureSampler", this._shadowMap);
                     });
 
-                    this.blurBoxOffset = 1;
+                    this.blurBoxOffset = boxBlurOffset;
                 }
 
                 this._scene.postProcessManager.directRender([this._downSamplePostprocess, this._boxBlurPostprocess], this._shadowMap2.getInternalTexture());
@@ -325,6 +333,28 @@
                 }
             });
         }
+
+        private _applyFilterValues() {
+            if (this.usePoissonSampling || this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
+                this._shadowMap.anisotropicFilteringLevel = 16;
+                this._shadowMap.updateSamplingMode(Texture.BILINEAR_SAMPLINGMODE);
+            } else {
+                this._shadowMap.anisotropicFilteringLevel = 1;
+                this._shadowMap.updateSamplingMode(Texture.NEAREST_SAMPLINGMODE);
+            }
+        }
+
+        public recreateShadowMap() {
+            // Clean up existing data.
+            this._disposeRTTandPostProcesses();
+
+            // Reinitializes.
+            this._initializeGenerator(this.blurBoxOffset);
+
+            // Reaffect the filter.
+            this._applyFilterValues();
+        }
+
         /**
          * Boolean : true when the ShadowGenerator is finally computed.  
          */
@@ -400,12 +430,14 @@
 
             return this._effect.isReady();
         }
+
         /**
          * Returns a RenderTargetTexture object : the shadow map texture.  
          */
         public getShadowMap(): RenderTargetTexture {
             return this._shadowMap;
         }
+
         /**
          * Returns the most ready computed shadow map as a RenderTargetTexture object.  
          */
@@ -416,6 +448,7 @@
 
             return this._shadowMap;
         }
+
         /**
          * Returns the associated light object.  
          */
@@ -437,17 +470,16 @@
             this._currentFaceIndexCache = this._currentFaceIndex;
 
             var lightPosition = this._light.position;
-            Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentFaceIndex), this._lightDirection);
+            if (this._light.computeTransformedInformation()) {
+                lightPosition = this._light.transformedPosition;
+            }
 
+            Vector3.NormalizeToRef(this._light.getShadowDirection(this._currentFaceIndex), this._lightDirection);
             if (Math.abs(Vector3.Dot(this._lightDirection, Vector3.Up())) === 1.0) {
                 this._lightDirection.z = 0.0000000000001; // Required to avoid perfectly perpendicular light
             }
 
-            if (this._light.computeTransformedPosition()) {
-                lightPosition = this._light.transformedPosition;
-            }
-
-            if (this._light.needRefreshPerFrame() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
+            if (this._light.needProjectionMatrixCompute() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !this._lightDirection.equals(this._cachedDirection)) {
 
                 this._cachedPosition = lightPosition.clone();
                 this._cachedDirection = this._lightDirection.clone();
@@ -496,12 +528,11 @@
 
             return new Vector2(depth - fract / 255.0, fract);
         }
-        /**
-         * Disposes the ShadowGenerator.  
-         * Returns nothing.  
-         */
-        public dispose(): void {
-            this._shadowMap.dispose();
+
+        private _disposeRTTandPostProcesses() {
+            if (this._shadowMap) {
+                this._shadowMap.dispose();
+            }
 
             if (this._shadowMap2) {
                 this._shadowMap2.dispose();
@@ -514,6 +545,14 @@
             if (this._boxBlurPostprocess) {
                 this._boxBlurPostprocess.dispose();
             }
+        }
+
+        /**
+         * Disposes the ShadowGenerator.  
+         * Returns nothing.  
+         */
+        public dispose(): void {
+            this._disposeRTTandPostProcesses();
 
             this._light._shadowGenerator = null;
             this._light._markMeshesAsLightDirty();
@@ -523,9 +562,10 @@
          */
         public serialize(): any {
             var serializationObject: any = {};
+            var shadowMap = this.getShadowMap();
 
             serializationObject.lightId = this._light.id;
-            serializationObject.mapSize = this.getShadowMap().getRenderSize();
+            serializationObject.mapSize = shadowMap.getRenderSize();
             serializationObject.useExponentialShadowMap = this.useExponentialShadowMap;
             serializationObject.useBlurExponentialShadowMap = this.useBlurExponentialShadowMap;
             serializationObject.usePoissonSampling = this.usePoissonSampling;
@@ -534,8 +574,8 @@
             serializationObject.darkness = this.getDarkness();
 
             serializationObject.renderList = [];
-            for (var meshIndex = 0; meshIndex < this.getShadowMap().renderList.length; meshIndex++) {
-                var mesh = this.getShadowMap().renderList[meshIndex];
+            for (var meshIndex = 0; meshIndex < shadowMap.renderList.length; meshIndex++) {
+                var mesh = shadowMap.renderList[meshIndex];
 
                 serializationObject.renderList.push(mesh.id);
             }
@@ -549,11 +589,12 @@
             //casting to point light, as light is missing the position attr and typescript complains.
             var light = <PointLight>scene.getLightByID(parsedShadowGenerator.lightId);
             var shadowGenerator = new ShadowGenerator(parsedShadowGenerator.mapSize, light);
+            var shadowMap = shadowGenerator.getShadowMap();
 
             for (var meshIndex = 0; meshIndex < parsedShadowGenerator.renderList.length; meshIndex++) {
                 var meshes = scene.getMeshesByID(parsedShadowGenerator.renderList[meshIndex]);
                 meshes.forEach(function (mesh) {
-                    shadowGenerator.getShadowMap().renderList.push(mesh);
+                    shadowMap.renderList.push(mesh);
                 });
             }
 
@@ -598,5 +639,55 @@
 
             return shadowGenerator;
         }
+
+        /**
+         * This works according to the standard show defined and supported by the BJS materials.
+         */
+        public prepareDefines(defines: MaterialDefines, lightIndex: number): void {
+            var scene = this._scene;
+            var light = this._light;
+
+            if (!scene.shadowsEnabled || !light.shadowEnabled) {
+                return;
+            }
+
+            defines["SHADOW" + lightIndex] = true;
+
+            if (this.usePoissonSampling) {
+                defines["SHADOWPCF" + lightIndex] = true;
+            } 
+            else if (this.useExponentialShadowMap || this.useBlurExponentialShadowMap) {
+                defines["SHADOWESM" + lightIndex] = true;
+            }
+
+            if (light.needCube()) {
+                defines["SHADOWCUBE" + lightIndex] = true;
+            }
+        }
+
+        /**
+         * This works according to the standard show defined and supported by the BJS materials.
+         */
+        public bindShadowLight(lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean {
+            var light = this._light;
+            var scene = this._scene;
+
+            if (!scene.shadowsEnabled || !light.shadowEnabled) {
+                return;
+            }
+
+            if (!light.needCube()) {
+                effect.setMatrix("lightMatrix" + lightIndex, this.getTransformMatrix());
+            } else {
+                if (!depthValuesAlreadySet) {
+                    depthValuesAlreadySet = true;
+                    effect.setFloat2("depthValues", scene.activeCamera.minZ, scene.activeCamera.maxZ);
+                }
+            }
+            effect.setTexture("shadowSampler" + lightIndex, this.getShadowMapForRendering());
+            light._uniformBuffer.updateFloat3("shadowsInfo", this.getDarkness(), this.blurScale / this.getShadowMap().getSize().width, this.depthScale, lightIndex);
+
+            return depthValuesAlreadySet;
+        }
     }
 } 

+ 56 - 134
src/Lights/babylon.directionalLight.ts

@@ -1,30 +1,22 @@
 /// <reference path="babylon.light.ts" />
 
 module BABYLON {
-    export class DirectionalLight extends Light implements IShadowLight {
-        @serializeAsVector3()
-        public position: Vector3;
+    export class DirectionalLight extends ShadowLight {
 
-        @serializeAsVector3()
-        public direction: Vector3
-
-        private _transformedDirection: Vector3;
-        public transformedPosition: Vector3;
-        private _worldMatrix: Matrix;
+        private _shadowOrthoScale = 0.5;
 
         @serialize()
-        public shadowOrthoScale = 0.5;
+        public get shadowOrthoScale(): number {
+            return this._shadowOrthoScale
+        }
+        public set shadowOrthoScale(value: number) {
+            this._shadowOrthoScale = value;
+            this.forceProjectionMatrixCompute();
+        }
 
         @serialize()
         public autoUpdateExtends = true;
 
-        @serialize()
-        public shadowMinZ: number;
-        @serialize()
-        public shadowMaxZ: number;
-        
-        public customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
-
         // Cache
         private _orthoLeft = Number.MAX_VALUE;
         private _orthoRight = Number.MIN_VALUE;
@@ -43,164 +35,94 @@ module BABYLON {
             this.direction = direction;
         }
 
-        protected _buildUniformLayout(): void {
-             this._uniformBuffer.addUniform("vLightData", 4);
-             this._uniformBuffer.addUniform("vLightDiffuse", 4);
-             this._uniformBuffer.addUniform("vLightSpecular", 3);
-             this._uniformBuffer.addUniform("shadowsInfo", 3);
-             this._uniformBuffer.create();
-        }
-
         /**
          * Returns the string "DirectionalLight".  
          */
         public getClassName(): string {
             return "DirectionalLight";
-        }           
-        /**
-         * Returns the DirectionalLight absolute position in the World.  
-         */
-        public getAbsolutePosition(): Vector3 {
-            return this.transformedPosition ? this.transformedPosition : this.position;
-        }
-        /**
-         * Sets the DirectionalLight direction toward the passed target (Vector3).  
-         * Returns the updated DirectionalLight direction (Vector3).  
-         */
-        public setDirectionToTarget(target: Vector3): Vector3 {
-            this.direction = Vector3.Normalize(target.subtract(this.position));
-            return this.direction;
         }
 
         /**
-         * Return the depth scale used for the shadow map.
+         * Returns the integer 1.
          */
-        public getDepthScale(): number {
-            return 30.0;
+        public getTypeID(): number {
+            return 1;
         }
 
         /**
          * Sets the passed matrix "matrix" as projection matrix for the shadows cast by the light according to the passed view matrix.  
          * Returns the DirectionalLight.  
          */
-        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): DirectionalLight {
-            if (this.customProjectionMatrixBuilder) {
-                this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
-            } else {
-                var activeCamera = this.getScene().activeCamera;
+        protected _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
+            var activeCamera = this.getScene().activeCamera;
 
-                // Check extends
-                if (this.autoUpdateExtends || this._orthoLeft === Number.MAX_VALUE) {
-                    var tempVector3 = Vector3.Zero();
+            // Check extends
+            if (this.autoUpdateExtends || this._orthoLeft === Number.MAX_VALUE) {
+                var tempVector3 = Vector3.Zero();
 
-                    this._orthoLeft = Number.MAX_VALUE;
-                    this._orthoRight = Number.MIN_VALUE;
-                    this._orthoTop = Number.MIN_VALUE;
-                    this._orthoBottom = Number.MAX_VALUE;
+                this._orthoLeft = Number.MAX_VALUE;
+                this._orthoRight = Number.MIN_VALUE;
+                this._orthoTop = Number.MIN_VALUE;
+                this._orthoBottom = Number.MAX_VALUE;
 
-                    for (var meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
-                        var mesh = renderList[meshIndex];
+                for (var meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
+                    var mesh = renderList[meshIndex];
 
-                        if (!mesh) {
-                            continue;
-                        }
+                    if (!mesh) {
+                        continue;
+                    }
 
-                        var boundingInfo = mesh.getBoundingInfo();
+                    var boundingInfo = mesh.getBoundingInfo();
 
-                        if (!boundingInfo) {
-                            continue;
-                        }
+                    if (!boundingInfo) {
+                        continue;
+                    }
 
-                        var boundingBox = boundingInfo.boundingBox;
+                    var boundingBox = boundingInfo.boundingBox;
 
-                        for (var index = 0; index < boundingBox.vectorsWorld.length; index++) {
-                            Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
+                    for (var index = 0; index < boundingBox.vectorsWorld.length; index++) {
+                        Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
 
-                            if (tempVector3.x < this._orthoLeft)
-                                this._orthoLeft = tempVector3.x;
-                            if (tempVector3.y < this._orthoBottom)
-                                this._orthoBottom = tempVector3.y;
+                        if (tempVector3.x < this._orthoLeft)
+                            this._orthoLeft = tempVector3.x;
+                        if (tempVector3.y < this._orthoBottom)
+                            this._orthoBottom = tempVector3.y;
 
-                            if (tempVector3.x > this._orthoRight)
-                                this._orthoRight = tempVector3.x;
-                            if (tempVector3.y > this._orthoTop)
-                                this._orthoTop = tempVector3.y;
-                        }
+                        if (tempVector3.x > this._orthoRight)
+                            this._orthoRight = tempVector3.x;
+                        if (tempVector3.y > this._orthoTop)
+                            this._orthoTop = tempVector3.y;
                     }
                 }
+            }
 
-                var xOffset = this._orthoRight - this._orthoLeft;
-                var yOffset = this._orthoTop - this._orthoBottom;
+            var xOffset = this._orthoRight - this._orthoLeft;
+            var yOffset = this._orthoTop - this._orthoBottom;
 
-                Matrix.OrthoOffCenterLHToRef(this._orthoLeft - xOffset * this.shadowOrthoScale, this._orthoRight + xOffset * this.shadowOrthoScale,
-                    this._orthoBottom - yOffset * this.shadowOrthoScale, this._orthoTop + yOffset * this.shadowOrthoScale,
-                    this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
-            }
-            return this;
+            Matrix.OrthoOffCenterLHToRef(this._orthoLeft - xOffset * this.shadowOrthoScale, this._orthoRight + xOffset * this.shadowOrthoScale,
+                this._orthoBottom - yOffset * this.shadowOrthoScale, this._orthoTop + yOffset * this.shadowOrthoScale,
+                this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
         }
 
-        /**
-         * Boolean : true by default.  
-         */
-        public needRefreshPerFrame(): boolean {
-            return true;
-        }
-        /**
-         * Boolean : false by default.  
-         */
-        public needCube(): boolean {
-            return false;
-        }
-        /**
-         * Returns the light direction (Vector3) for any passed face index.    
-         */
-        public getShadowDirection(faceIndex?: number): Vector3 {
-            return this.direction;
-        }
-        /**
-         * Computes the light transformed position in case the light is parented. Returns true if parented, else false.  
-         */
-        public computeTransformedPosition(): boolean {
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this.transformedPosition) {
-                    this.transformedPosition = Vector3.Zero();
-                }
-                Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
-                return true;
-            }
-            return false;
+        protected _buildUniformLayout(): void {
+             this._uniformBuffer.addUniform("vLightData", 4);
+             this._uniformBuffer.addUniform("vLightDiffuse", 4);
+             this._uniformBuffer.addUniform("vLightSpecular", 3);
+             this._uniformBuffer.addUniform("shadowsInfo", 3);
+             this._uniformBuffer.create();
         }
+
         /**
          * Sets the passed Effect object with the DirectionalLight transformed position (or position if not parented) and the passed name.  
          * Returns the DirectionalLight.  
          */
         public transferToEffect(effect: Effect, lightIndex: string): DirectionalLight {
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this._transformedDirection) {
-                    this._transformedDirection = Vector3.Zero();
-                }
-                Vector3.TransformNormalToRef(this.direction, this.parent.getWorldMatrix(), this._transformedDirection);
-                this._uniformBuffer.updateFloat4("vLightData", this._transformedDirection.x, this._transformedDirection.y, this._transformedDirection.z, 1, lightIndex);
+            if (this.computeTransformedInformation()) {
+               this._uniformBuffer.updateFloat4("vLightData", this.transformedDirection.x, this.transformedDirection.y, this.transformedDirection.z, 1, lightIndex);
                 return this;
             }
             this._uniformBuffer.updateFloat4("vLightData", this.direction.x, this.direction.y, this.direction.z, 1, lightIndex);
             return this;
         }
-
-        public _getWorldMatrix(): Matrix {
-            if (!this._worldMatrix) {
-                this._worldMatrix = Matrix.Identity();
-            }
-
-            Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
-
-            return this._worldMatrix;
-        }
-        /**
-         * Returns the integer 1.  
-         */
-        public getTypeID(): number {
-            return 1;
-        }
     }
 }  

+ 48 - 38
src/Lights/babylon.light.ts

@@ -1,28 +1,4 @@
 module BABYLON {
-
-    export interface IShadowLight extends Light {
-        id: string;
-        position: Vector3;
-        transformedPosition: Vector3;
-        name: string;
-        shadowMinZ: number;
-        shadowMaxZ: number;
-
-        computeTransformedPosition(): boolean;
-        getScene(): Scene;
-
-        customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
-        setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void;
-        getDepthScale(): number;
-
-        needRefreshPerFrame(): boolean;
-        needCube(): boolean;
-
-        getShadowDirection(faceIndex?: number): Vector3;
-
-        _shadowGenerator: IShadowGenerator;
-    }
-
     export class Light extends Node {
 
         //lightmapMode Consts
@@ -188,6 +164,21 @@
             this._computePhotometricScale();
         };
 
+        /**
+         * Defines the rendering priority of the lights. It can help in case of fallback or number of lights
+         * exceeding the allowed number of the materials.
+         */
+        @serialize()
+        @expandToProperty("_reorderLightsInScene")
+        public renderPriority: number = 0;
+
+        /**
+         * Defines wether or not the shadows are enabled for this light. This can help turning off/on shadow without detaching
+         * the current shadow generator.
+         */
+        @serialize()
+        public shadowEnabled: boolean = true;
+
         private _includedOnlyMeshes: AbstractMesh[];
         public get includedOnlyMeshes(): AbstractMesh[] {
             return this._includedOnlyMeshes;
@@ -205,7 +196,7 @@
         public set excludedMeshes(value: AbstractMesh[]) {
             this._excludedMeshes = value;
             this._hookArrayForExcluded(value);
-        }        
+        }
 
         @serialize("excludeWithLayerMask")
         private _excludeWithLayerMask = 0;
@@ -216,7 +207,7 @@
         public set excludeWithLayerMask(value: number) {
             this._excludeWithLayerMask = value;
             this._resyncMeshes();
-        }        
+        }
 
         @serialize("includeOnlyWithLayerMask")
         private _includeOnlyWithLayerMask = 0;
@@ -227,7 +218,7 @@
         public set includeOnlyWithLayerMask(value: number) {
             this._includeOnlyWithLayerMask = value;
             this._resyncMeshes();
-        }          
+        }
 
         @serialize("lightmapMode")
         private _lightmapMode = 0;
@@ -239,13 +230,13 @@
             if (this._lightmapMode === value) {
                 return;
             }
-            
+
             this._lightmapMode = value;
             this._markMeshesAsLightDirty();
         }
 
-        public _shadowGenerator: IShadowGenerator;
         private _parentedWorldMatrix: Matrix;
+        public _shadowGenerator: IShadowGenerator;
         public _excludedMeshesIds = new Array<string>();
         public _includedOnlyMeshesIds = new Array<string>();
 
@@ -277,23 +268,23 @@
          */
         public getClassName(): string {
             return "Light";
-        }        
+        }
 
         /**
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
          */
-        public toString(fullDetails? : boolean) : string {
+        public toString(fullDetails?: boolean): string {
             var ret = "Name: " + this.name;
             ret += ", type: " + (["Point", "Directional", "Spot", "Hemispheric"])[this.getTypeID()];
-            if (this.animations){
-                for (var i = 0; i < this.animations.length; i++){
-                   ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
+            if (this.animations) {
+                for (var i = 0; i < this.animations.length; i++) {
+                    ret += ", animation[0]: " + this.animations[i].toString(fullDetails);
                 }
             }
-            if (fullDetails){
+            if (fullDetails) {
             }
             return ret;
-        } 
+        }
 
 
         /**
@@ -379,6 +370,21 @@
         }
 
         /**
+		 * Sort function to order lights for rendering.
+		 * @param a First SPECTRE.Light object to compare to second.
+		 * @param b Second SPECTRE.Light object to compare first.
+		 * @return -1 to reduce's a's index relative to be, 0 for no change, 1 to increase a's index relative to b.
+		 */
+        public static compareLightsPriority(a: Light, b: Light): number {
+            //shadow-casting lights have priority over non-shadow-casting lights
+            //the renderPrioirty is a secondary sort criterion
+            if (a.shadowEnabled !== b.shadowEnabled) {
+                return (b.shadowEnabled ? 1 : 0) - (a.shadowEnabled ? 1 : 0);
+            }
+            return b.renderPriority - a.renderPriority;
+        }
+
+        /**
          * Disposes the light.  
          */
         public dispose(): void {
@@ -454,7 +460,7 @@
 
             // Animations  
             Animation.AppendSerializedAnimations(this, serializationObject);
-            serializationObject.ranges = this.serializeAnimationRanges();  
+            serializationObject.ranges = this.serializeAnimationRanges();
 
             return serializationObject;
         }
@@ -479,7 +485,7 @@
         /**
          * Parses the passed "parsedLight" and returns a new instanced Light from this parsing.  
          */
-        public static Parse(parsedLight: any, scene: Scene): Light {            
+        public static Parse(parsedLight: any, scene: Scene): Light {
             var light = SerializationHelper.Parse(Light.GetConstructorFromName(parsedLight.type, parsedLight.name, scene), parsedLight, scene);
 
             // Inclusion / exclusions
@@ -643,5 +649,9 @@
             }
             return photometricScale;
         }
+
+        private _reorderLightsInScene(): void {
+            this.getScene().orderLightsByPriority();
+        }
     }
 }

+ 82 - 107
src/Lights/babylon.pointLight.ts

@@ -1,17 +1,38 @@
 module BABYLON {
-    export class PointLight extends Light implements IShadowLight {
-        private _worldMatrix: Matrix;
-        public transformedPosition: Vector3;
+    export class PointLight extends ShadowLight {
 
-        @serializeAsVector3()
-        public position: Vector3;
-
-        @serialize()
-        public shadowMinZ: number;
+        private _shadowAngle = Math.PI / 2;
+        /**
+         * Getter: In case of direction provided, the shadow will not use a cube texture but simulate a spot shadow as a fallback
+         * This specifies what angle the shadow will use to be created.
+         * 
+         * It default to 90 degrees to work nicely with the cube texture generation for point lights shadow maps.
+         */
         @serialize()
-        public shadowMaxZ: number;
-        
-        public customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
+        public get shadowAngle(): number {
+            return this._shadowAngle
+        }
+        /**
+         * Setter: In case of direction provided, the shadow will not use a cube texture but simulate a spot shadow as a fallback
+         * This specifies what angle the shadow will use to be created.
+         * 
+         * It default to 90 degrees to work nicely with the cube texture generation for point lights shadow maps.
+         */
+        public set shadowAngle(value: number) {
+            this._shadowAngle = value;
+            this.forceProjectionMatrixCompute();
+        }
+
+        /**
+         * In case of direction provided, the shadow will not use a cube texture but simulate a spot shadow as a fallback
+         */
+        public set direction(value: Vector3) {
+            var previousNeedCube = this.needCube();
+            this._direction = value;
+            if (this.needCube() !== previousNeedCube && this._shadowGenerator) {
+                this._shadowGenerator.recreateShadowMap();
+            }
+        }
 
         /**
          * Creates a PointLight object from the passed name and position (Vector3) and adds it in the scene.  
@@ -28,105 +49,53 @@
             this.position = position;
         }
 
-        protected _buildUniformLayout(): void {
-            this._uniformBuffer.addUniform("vLightData", 4);
-            this._uniformBuffer.addUniform("vLightDiffuse", 4);
-            this._uniformBuffer.addUniform("vLightSpecular", 3);
-            this._uniformBuffer.addUniform("shadowsInfo", 3);
-            this._uniformBuffer.create();
-        }
-
         /**
          * Returns the string "PointLight"
          */
         public getClassName(): string {
             return "PointLight";
-        } 
-        /**
-         * Returns a Vector3, the PointLight absolute position in the World.  
-         */
-        public getAbsolutePosition(): Vector3 {
-            return this.transformedPosition ? this.transformedPosition : this.position;
         }
+        
         /**
-         * Computes the PointLight transformed position if parented.  Returns true if ok, false if not parented.  
+         * Returns the integer 0.  
          */
-        public computeTransformedPosition(): boolean {
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this.transformedPosition) {
-                    this.transformedPosition = Vector3.Zero();
-                }
-
-                Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
-
-                return true;
-            }
-
-            return false;
+        public getTypeID(): number {
+            return 0;
         }
 
         /**
-         * Sets the passed Effect "effect" with the PointLight transformed position (or position, if none) and passed name (string).  
-         * Returns the PointLight.  
-         */
-        public transferToEffect(effect: Effect, lightIndex: string): PointLight {
-
-            if (this.parent && this.parent.getWorldMatrix) {
-                this.computeTransformedPosition();
-
-                this._uniformBuffer.updateFloat4("vLightData",
-                    this.transformedPosition.x,
-                    this.transformedPosition.y,
-                    this.transformedPosition.z,
-                    0.0,
-                    lightIndex); 
-                return this;
-            }
-
-            this._uniformBuffer.updateFloat4("vLightData", this.position.x, this.position.y, this.position.z, 0, lightIndex);
-            return this;
-        }
-        /**
-         * Boolean : returns true by default. 
+         * Boolean : returns true by default.
          */
         public needCube(): boolean {
-            return true;
-        }
-        /**
-         * Boolean : returns false by default.  
-         */
-        public needRefreshPerFrame(): boolean {
-            return false;
+            return !this.direction;
         }
 
         /**
          * Returns a new Vector3 aligned with the PointLight cube system according to the passed cube face index (integer).  
          */
         public getShadowDirection(faceIndex?: number): Vector3 {
-            switch (faceIndex) {
-                case 0:
-                    return new Vector3(1.0, 0.0, 0.0);
-                case 1:
-                    return new Vector3(-1.0, 0.0, 0.0);
-                case 2:
-                    return new Vector3(0.0, -1.0, 0.0);
-                case 3:
-                    return new Vector3(0.0, 1.0, 0.0);
-                case 4:
-                    return new Vector3(0.0, 0.0, 1.0);
-                case 5:
-                    return new Vector3(0.0, 0.0, -1.0);
+            if (this.direction) {
+                return super.getShadowDirection(faceIndex);
+            }
+            else {
+                switch (faceIndex) {
+                    case 0:
+                        return new Vector3(1.0, 0.0, 0.0);
+                    case 1:
+                        return new Vector3(-1.0, 0.0, 0.0);
+                    case 2:
+                        return new Vector3(0.0, -1.0, 0.0);
+                    case 3:
+                        return new Vector3(0.0, 1.0, 0.0);
+                    case 4:
+                        return new Vector3(0.0, 0.0, 1.0);
+                    case 5:
+                        return new Vector3(0.0, 0.0, -1.0);
+                }
             }
 
             return Vector3.Zero();
         }
-        
-        /**
-         * Return the depth scale used for the shadow map.
-         */
-        public getDepthScale(): number {
-            return 30.0;
-        }
 
         /**
          * Sets the passed matrix "matrix" as a left-handed perspective projection matrix with the following settings : 
@@ -135,31 +104,37 @@
          * - z-near and far equal to the active camera minZ and maxZ.  
          * Returns the PointLight.  
          */
-        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): PointLight {
-            if (this.customProjectionMatrixBuilder) {
-                this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
-            } else {
-                var activeCamera = this.getScene().activeCamera;
-                Matrix.PerspectiveFovLHToRef(Math.PI / 2, 1.0, 
-                this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
-            }
-            return this;
+        protected _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
+            var activeCamera = this.getScene().activeCamera;
+            Matrix.PerspectiveFovLHToRef(this.shadowAngle, 1.0, 
+            this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
         }
 
-        public _getWorldMatrix(): Matrix {
-            if (!this._worldMatrix) {
-                this._worldMatrix = Matrix.Identity();
-            }
-
-            Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
-
-            return this._worldMatrix;
+        protected _buildUniformLayout(): void {
+            this._uniformBuffer.addUniform("vLightData", 4);
+            this._uniformBuffer.addUniform("vLightDiffuse", 4);
+            this._uniformBuffer.addUniform("vLightSpecular", 3);
+            this._uniformBuffer.addUniform("shadowsInfo", 3);
+            this._uniformBuffer.create();
         }
+
         /**
-         * Returns the integer 0.  
+         * Sets the passed Effect "effect" with the PointLight transformed position (or position, if none) and passed name (string).  
+         * Returns the PointLight.  
          */
-        public getTypeID(): number {
-            return 0;
+        public transferToEffect(effect: Effect, lightIndex: string): PointLight {
+            if (this.computeTransformedInformation()) {
+                this._uniformBuffer.updateFloat4("vLightData",
+                    this.transformedPosition.x,
+                    this.transformedPosition.y,
+                    this.transformedPosition.z,
+                    0.0,
+                    lightIndex); 
+                return this;
+            }
+
+            this._uniformBuffer.updateFloat4("vLightData", this.position.x, this.position.y, this.position.z, 0, lightIndex);
+            return this;
         }
     }
 } 

+ 181 - 0
src/Lights/babylon.shadowLight.ts

@@ -0,0 +1,181 @@
+module BABYLON {
+    export interface IShadowLight extends Light {
+        id: string;
+        position: Vector3;
+        direction: Vector3;
+        transformedPosition: Vector3;
+        transformedDirection: Vector3;
+        name: string;
+        shadowMinZ: number;
+        shadowMaxZ: number;
+
+        computeTransformedInformation(): boolean;
+        getScene(): Scene;
+
+        customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
+        setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): IShadowLight;
+        getDepthScale(): number;
+
+        needCube(): boolean;
+        needProjectionMatrixCompute(): boolean;
+        forceProjectionMatrixCompute(): void;
+
+        getShadowDirection(faceIndex?: number): Vector3;
+    }
+
+    export abstract class ShadowLight extends Light implements IShadowLight {
+
+        protected abstract _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void;
+
+        @serializeAsVector3()
+        public position: Vector3;
+
+        protected _direction: Vector3;
+        @serializeAsVector3()
+        public get direction(): Vector3 {
+            return this._direction;
+        }
+        public set direction(value: Vector3) {
+            this._direction = value;
+        }
+
+        private _shadowMinZ: number;
+        @serialize()
+        public get shadowMinZ(): number {
+            return this._shadowMinZ
+        }
+        public set shadowMinZ(value: number) {
+            this._shadowMinZ = value;
+            this.forceProjectionMatrixCompute();
+        }
+
+        private _shadowMaxZ: number;
+        @serialize()
+        public get shadowMaxZ(): number {
+            return this._shadowMaxZ
+        }
+        public set shadowMaxZ(value: number) {
+            this._shadowMaxZ = value;
+            this.forceProjectionMatrixCompute();
+        }
+
+        public customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
+
+        public transformedPosition: Vector3;
+
+        public transformedDirection: Vector3;
+
+        private _worldMatrix: Matrix;
+        private _needProjectionMatrixCompute: boolean = true;
+
+        /**
+         * Computes the light transformed position/direction in case the light is parented. Returns true if parented, else false.
+         */
+        public computeTransformedInformation(): boolean {
+            if (this.parent && this.parent.getWorldMatrix) {
+                if (!this.transformedPosition) {
+                    this.transformedPosition = Vector3.Zero();
+                }
+                Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
+
+                // In case the direction is present.
+                if (this.direction) {
+                    if (!this.transformedDirection) {
+                        this.transformedDirection = Vector3.Zero();
+                    }
+                    Vector3.TransformCoordinatesToRef(this.direction, this.parent.getWorldMatrix(), this.transformedDirection);
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Return the depth scale used for the shadow map.
+         */
+        public getDepthScale(): number {
+            return 30.0;
+        }
+
+        /**
+         * Returns the light direction (Vector3) for any passed face index.
+         */
+        public getShadowDirection(faceIndex?: number): Vector3 {
+            return this.transformedDirection ? this.transformedDirection : this.direction;
+        }
+
+        /**
+         * Returns the DirectionalLight absolute position in the World.
+         */
+        public getAbsolutePosition(): Vector3 {
+            return this.transformedPosition ? this.transformedPosition : this.position;
+        }
+
+        /**
+         * Sets the DirectionalLight direction toward the passed target (Vector3).
+         * Returns the updated DirectionalLight direction (Vector3).
+         */
+        public setDirectionToTarget(target: Vector3): Vector3 {
+            this.direction = Vector3.Normalize(target.subtract(this.position));
+            return this.direction;
+        }
+
+        /**
+         * Returns the light rotation (Vector3).
+         */
+        public getRotation(): Vector3 {
+            this.direction.normalize();
+            var xaxis = BABYLON.Vector3.Cross(this.direction, BABYLON.Axis.Y);
+            var yaxis = BABYLON.Vector3.Cross(xaxis, this.direction);
+            return Vector3.RotationFromAxis(xaxis, yaxis, this.direction);
+        }
+
+        /**
+         * Boolean : false by default.
+         */
+        public needCube(): boolean {
+            return false;
+        }
+
+        /**
+         * Specifies wether or not the projection matrix should be recomputed this frame.
+         */
+        public needProjectionMatrixCompute(): boolean {
+            return this._needProjectionMatrixCompute;
+        }
+
+        /**
+         * Forces the shadow generator to recompute the projection matrix even if position and direction did not changed.
+         */
+        public forceProjectionMatrixCompute(): void {
+            this._needProjectionMatrixCompute = true;
+        }
+
+        /**
+         * Get the world matrix of the sahdow lights.
+         */
+        public _getWorldMatrix(): Matrix {
+            if (!this._worldMatrix) {
+                this._worldMatrix = Matrix.Identity();
+            }
+
+            Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
+
+            return this._worldMatrix;
+        }
+
+        /**
+         * Sets the projection matrix according to the type of light and custom projection matrix definition.
+         * Returns the light.
+         */
+        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): IShadowLight {
+            if (this.customProjectionMatrixBuilder) {
+                this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
+            }
+            else {
+                this._setDefaultShadowProjectionMatrix(matrix, viewMatrix, renderList);
+            }
+            return this;
+        }
+    }
+}

+ 30 - 120
src/Lights/babylon.spotLight.ts

@@ -1,29 +1,19 @@
 module BABYLON {
-    export class SpotLight extends Light implements IShadowLight {
-        @serializeAsVector3()
-        public position: Vector3;
-
-        @serializeAsVector3()
-        public direction: Vector3;
+    export class SpotLight extends ShadowLight {
+        private _angle: number;
 
         @serialize()
-        public angle: number;
+        public get angle(): number {
+            return this._angle
+        }
+        public set angle(value: number) {
+            this._angle = value;
+            this.forceProjectionMatrixCompute();
+        }
 
         @serialize()
         public exponent: number;
         
-        @serialize()
-        public shadowMinZ: number;
-        @serialize()
-        public shadowMaxZ: number;
-
-        public transformedPosition: Vector3;
-
-        public customProjectionMatrixBuilder: (viewMatrix: Matrix, renderList: Array<AbstractMesh>, result: Matrix) => void;
-
-        private _transformedDirection: Vector3;
-        private _worldMatrix: Matrix;
-
         /**
          * Creates a SpotLight object in the scene with the passed parameters :   
          * - `position` (Vector3) is the initial SpotLight position,  
@@ -43,88 +33,39 @@
             this.exponent = exponent;
         }
 
-        protected _buildUniformLayout(): void {
-            this._uniformBuffer.addUniform("vLightData", 4);
-            this._uniformBuffer.addUniform("vLightDiffuse", 4);
-            this._uniformBuffer.addUniform("vLightSpecular", 3);
-            this._uniformBuffer.addUniform("vLightDirection", 3);
-            this._uniformBuffer.addUniform("shadowsInfo", 3);
-            this._uniformBuffer.create();
-        }
-        
         /**
-         * Returns the string "SpotLight".  
+         * Returns the string "SpotLight".
          */
         public getClassName(): string {
             return "SpotLight";
-        }         
-        /**
-         * Returns the SpotLight absolute position in the World (Vector3).  
-         */
-        public getAbsolutePosition(): Vector3 {
-            return this.transformedPosition ? this.transformedPosition : this.position;
         }
 
         /**
-         * Return the depth scale used for the shadow map.
+         * Returns the integer 2.
          */
-        public getDepthScale(): number {
-            return 30.0;
+        public getTypeID(): number {
+            return 2;
         }
-        
+
         /**
          * Sets the passed matrix "matrix" as perspective projection matrix for the shadows and the passed view matrix with the fov equal to the SpotLight angle and and aspect ratio of 1.0.  
          * Returns the SpotLight.  
          */
-        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): SpotLight {
-            if (this.customProjectionMatrixBuilder) {
-                this.customProjectionMatrixBuilder(viewMatrix, renderList, matrix);
-            } else {
-                var activeCamera = this.getScene().activeCamera;
-                Matrix.PerspectiveFovLHToRef(this.angle, 1.0, 
-                this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
-            }
-            return this;
-        }
-        /**
-         * Boolean : false by default.  
-         */
-        public needCube(): boolean {
-            return false;
-        }
-        /**
-         * Boolean : false by default.  
-         */
-        public needRefreshPerFrame(): boolean {
-            return false;
+        protected _setDefaultShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
+            var activeCamera = this.getScene().activeCamera;
+            Matrix.PerspectiveFovLHToRef(this.angle, 1.0, 
+            this.shadowMinZ !== undefined ? this.shadowMinZ : activeCamera.minZ, this.shadowMaxZ !== undefined ? this.shadowMaxZ : activeCamera.maxZ, matrix);
         }
-        /**
-         * Returns the SpotLight direction (Vector3) for any passed face index.  
-         */
-        public getShadowDirection(faceIndex?: number): Vector3 {
-            return this.direction;
-        }
-        /**
-         * Updates the SpotLight direction towards the passed target (Vector3).  
-         * Returns the updated direction.  
-         */
-        public setDirectionToTarget(target: Vector3): Vector3 {
-            this.direction = Vector3.Normalize(target.subtract(this.position));
-            return this.direction;
-        }
-        /**
-         * Computes the SpotLight transformed position if parented.  Returns true if parented, else false. 
-         */
-        public computeTransformedPosition(): boolean {
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this.transformedPosition) {
-                    this.transformedPosition = Vector3.Zero();
-                }
-                Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), this.transformedPosition);
-                return true;
-            }
-            return false;
+
+        protected _buildUniformLayout(): void {
+            this._uniformBuffer.addUniform("vLightData", 4);
+            this._uniformBuffer.addUniform("vLightDiffuse", 4);
+            this._uniformBuffer.addUniform("vLightSpecular", 3);
+            this._uniformBuffer.addUniform("vLightDirection", 3);
+            this._uniformBuffer.addUniform("shadowsInfo", 3);
+            this._uniformBuffer.create();
         }
+
         /**
          * Sets the passed Effect object with the SpotLight transfomed position (or position if not parented) and normalized direction.  
          * Return the SpotLight.   
@@ -132,14 +73,8 @@
         public transferToEffect(effect: Effect, lightIndex: string): SpotLight {
             var normalizeDirection;
 
-            if (this.parent && this.parent.getWorldMatrix) {
-                if (!this._transformedDirection) {
-                    this._transformedDirection = Vector3.Zero();
-                }
-
-                this.computeTransformedPosition();
-                
-                Vector3.TransformNormalToRef(this.direction, this.parent.getWorldMatrix(), this._transformedDirection);
+            if (this.computeTransformedInformation()) {
+                Vector3.TransformNormalToRef(this.direction, this.parent.getWorldMatrix(), this.transformedDirection);
 
                 this._uniformBuffer.updateFloat4("vLightData",
                     this.transformedPosition.x,
@@ -148,7 +83,7 @@
                     this.exponent,
                     lightIndex);
 
-                normalizeDirection = Vector3.Normalize(this._transformedDirection);
+                normalizeDirection = Vector3.Normalize(this.transformedDirection);
             } else {
                 this._uniformBuffer.updateFloat4("vLightData",
                     this.position.x,
@@ -168,30 +103,5 @@
                 lightIndex);
             return this;
         }
-
-        public _getWorldMatrix(): Matrix {
-            if (!this._worldMatrix) {
-                this._worldMatrix = Matrix.Identity();
-            }
-
-            Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, this._worldMatrix);
-
-            return this._worldMatrix;
-        }
-        /**
-         * Returns the integer 2.  
-         */
-        public getTypeID(): number {
-            return 2;
-        }
-        /**
-         * Returns the SpotLight rotation (Vector3).  
-         */
-        public getRotation(): Vector3 {
-            this.direction.normalize();
-            var xaxis = BABYLON.Vector3.Cross(this.direction, BABYLON.Axis.Y);
-            var yaxis = BABYLON.Vector3.Cross(xaxis, this.direction);
-            return Vector3.RotationFromAxis(xaxis, yaxis, this.direction);
-        }
     }
 }

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

@@ -1,5 +1,6 @@
 module BABYLON {
     class PBRMaterialDefines extends MaterialDefines {
+        public PBR = true;
         public ALBEDO = false;
         public AMBIENT = false;
         public AMBIENTINGRAYSCALE = false;
@@ -88,6 +89,12 @@
             super();
             this.rebuild();
         }
+
+        public reset(): void {
+            super.reset();
+            this.ALPHATESTVALUE = 0.4;
+            this.PBR = true;
+        }
     }
 
     /**

+ 14 - 29
src/Materials/babylon.materialHelper.ts

@@ -114,11 +114,11 @@
                     defines["DIRLIGHT" + lightIndex] = false;
 
                     var type;
-                    if (light.getTypeID() === 2) {
+                    if (light.getTypeID() === Light.LIGHTTYPEID_SPOTLIGHT) {
                         type = "SPOTLIGHT" + lightIndex;
-                    } else if (light.getTypeID() === 3) {
+                    } else if (light.getTypeID() === Light.LIGHTTYPEID_HEMISPHERICLIGHT) {
                         type = "HEMILIGHT" + lightIndex;
-                    } else if (light.getTypeID() === 0) {
+                    } else if (light.getTypeID() === Light.LIGHTTYPEID_POINTLIGHT) {
                         type = "POINTLIGHT" + lightIndex;
                     } else {
                         type = "DIRLIGHT" + lightIndex;
@@ -133,22 +133,15 @@
 
                     // Shadows
                     defines["SHADOW" + lightIndex] = false;
-                    if (scene.shadowsEnabled) {
-                        var shadowGenerator = <ShadowGenerator>light.getShadowGenerator();
-                        if (mesh && mesh.receiveShadows && shadowGenerator) {
-                            defines["SHADOW" + lightIndex] = true;
+                    defines["SHADOWPCF" + lightIndex] = false;
+                    defines["SHADOWESM" + lightIndex] = false;
+                    defines["SHADOWCUBE" + lightIndex] = false;
 
+                    if (mesh && mesh.receiveShadows && scene.shadowsEnabled && light.shadowEnabled) {
+                        var shadowGenerator = light.getShadowGenerator();
+                        if (shadowGenerator) {
                             shadowEnabled = true;
-
-                            defines["SHADOWPCF" + lightIndex] = false;
-                            defines["SHADOWESM" + lightIndex] = false;
-
-                            if (shadowGenerator.usePoissonSampling) {
-                                defines["SHADOWPCF" + lightIndex] = true;
-                            } 
-                            else if (shadowGenerator.useExponentialShadowMap || shadowGenerator.useBlurExponentialShadowMap) {
-                                defines["SHADOWESM" + lightIndex] = true;
-                            }
+                            shadowGenerator.prepareDefines(defines, lightIndex);
                         }
                     }
 
@@ -312,19 +305,11 @@
 
         // Bindings
         public static BindLightShadow(light: Light, scene: Scene, mesh: AbstractMesh, lightIndex: string, effect: Effect, depthValuesAlreadySet: boolean): boolean {
-            var shadowGenerator = <ShadowGenerator>light.getShadowGenerator();
-
-            if (mesh.receiveShadows && shadowGenerator) {
-                if (!(<any>light).needCube()) {
-                    effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
-                } else {
-                    if (!depthValuesAlreadySet) {
-                        depthValuesAlreadySet = true;
-                        effect.setFloat2("depthValues", scene.activeCamera.minZ, scene.activeCamera.maxZ);
-                    }
+            if (light.shadowEnabled && mesh.receiveShadows) {
+                var shadowGenerator = light.getShadowGenerator();
+                if (shadowGenerator) {
+                    depthValuesAlreadySet = shadowGenerator.bindShadowLight(lightIndex, effect, depthValuesAlreadySet);
                 }
-                effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMapForRendering());
-                light._uniformBuffer.updateFloat3("shadowsInfo", shadowGenerator.getDarkness(), shadowGenerator.blurScale / shadowGenerator.getShadowMap().getSize().width, shadowGenerator.depthScale, lightIndex);
             }
 
             return depthValuesAlreadySet;

+ 3 - 2
src/Mesh/babylon.abstractMesh.ts

@@ -1677,10 +1677,11 @@
                 // Shadow generators
                 var generator = light.getShadowGenerator();
                 if (generator) {
-                    meshIndex = generator.getShadowMap().renderList.indexOf(this);
+                    var shadowMap = generator.getShadowMap();
+                    meshIndex = shadowMap.renderList.indexOf(this);
 
                     if (meshIndex !== -1) {
-                        generator.getShadowMap().renderList.splice(meshIndex, 1);
+                        shadowMap.renderList.splice(meshIndex, 1);
                     }
                 }
             });

+ 24 - 12
src/Shaders/ShadersInclude/lightFragment.fx

@@ -2,32 +2,44 @@
     #if defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X})
         //No light calculation
     #else
-        #ifdef SPOTLIGHT{X}
-            info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
-        #endif
-        #ifdef HEMILIGHT{X}
-            info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, glossiness);
-        #endif
-        #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
-            info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
-        #endif
+		#ifdef PBR
+			#ifdef SPOTLIGHT{X}
+				info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR90, NdotL);
+			#endif
+			#ifdef HEMILIGHT{X}
+				info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, roughness, NdotV, specularEnvironmentR90, NdotL);
+			#endif
+			#if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
+				info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR90, NdotL);
+			#endif
+		#else
+			#ifdef SPOTLIGHT{X}
+				info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
+			#endif
+			#ifdef HEMILIGHT{X}
+				info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, glossiness);
+			#endif
+			#if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
+				info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
+			#endif
+		#endif
     #endif
 	#ifdef SHADOW{X}
 		#ifdef SHADOWESM{X}
-			#if defined(POINTLIGHT{X})
+			#if defined(SHADOWCUBE{X})
 				shadow = computeShadowWithESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
 			#else
 				shadow = computeShadowWithESM(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
 			#endif
 		#else	
 			#ifdef SHADOWPCF{X}
-				#if defined(POINTLIGHT{X})
+				#if defined(SHADOWCUBE{X})
 					shadow = computeShadowWithPCFCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
 				#else
 					shadow = computeShadowWithPCF(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
 				#endif
 			#else
-				#if defined(POINTLIGHT{X})
+				#if defined(SHADOWCUBE{X})
 					shadow = computeShadowCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x);
 				#else
 					shadow = computeShadow(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x);

+ 3 - 3
src/Shaders/ShadersInclude/lightFragmentDeclaration.fx

@@ -7,11 +7,11 @@
 		vec3 vLightSpecular{X} = vec3(0.);
 	#endif
 	#ifdef SHADOW{X}
-		#if defined(SPOTLIGHT{X}) || defined(DIRLIGHT{X})
+		#if defined(SHADOWCUBE{X})
+			uniform samplerCube shadowSampler{X};
+		#else
 			varying vec4 vPositionFromLight{X};
 			uniform sampler2D shadowSampler{X};
-		#else
-			uniform samplerCube shadowSampler{X};
 		#endif
 		uniform vec3 shadowsInfo{X};
 	#endif

+ 3 - 3
src/Shaders/ShadersInclude/lightUboDeclaration.fx

@@ -14,11 +14,11 @@
 	} light{X};
 
 #ifdef SHADOW{X}
-	#if defined(SPOTLIGHT{X}) || defined(DIRLIGHT{X})
+	#if defined(SHADOWCUBE{X})
+		uniform samplerCube shadowSampler{X};
+	#else
 		varying vec4 vPositionFromLight{X};
 		uniform sampler2D shadowSampler{X};
-	#else
-		uniform samplerCube shadowSampler{X};
 	#endif
 #endif
 

+ 0 - 57
src/Shaders/ShadersInclude/pbrLightFunctionsCall.fx

@@ -1,57 +0,0 @@
-#ifdef LIGHT{X}
-    #if defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X}) && defined(LIGHTMAPNOSPECULAR{X})
-        //No light calculation
-    #else
-        #ifdef SPOTLIGHT{X}
-            info = computeSpotLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR90, NdotL);
-        #endif
-        #ifdef HEMILIGHT{X}
-            info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, roughness, NdotV, specularEnvironmentR90, NdotL);
-        #endif
-        #if defined(POINTLIGHT{X}) || defined(DIRLIGHT{X})
-            info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR90, NdotL);
-        #endif
-    #endif
-    
-    #ifdef SHADOW{X}
-        #ifdef SHADOWESM{X}
-			#if defined(POINTLIGHT{X})
-				notShadowLevel = computeShadowWithESMCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
-			#else
-				notShadowLevel = computeShadowWithESM(light{X}.vPositionFromLight, shadowSampler{X}, light{X}.shadowsInfo.x, light{X}.shadowsInfo.z);
-			#endif
-        #else
-            #ifdef SHADOWPCF{X}
-                #if defined(POINTLIGHT{X})
-                    notShadowLevel = computeShadowWithPCFCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
-                #else
-                    notShadowLevel = computeShadowWithPCF(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.y, light{X}.shadowsInfo.x);
-                #endif
-            #else
-                #if defined(POINTLIGHT{X})
-                    notShadowLevel = computeShadowCube(light{X}.vLightData.xyz, shadowSampler{X}, light{X}.shadowsInfo.x);
-                #else
-                    notShadowLevel = computeShadow(vPositionFromLight{X}, shadowSampler{X}, light{X}.shadowsInfo.x);
-                #endif
-            #endif
-        #endif
-    #else
-        notShadowLevel = 1.;
-    #endif
-    
-    #if defined(LIGHTMAP) && defined(LIGHTMAPEXCLUDED{X})
-	    lightDiffuseContribution += lightmapColor * notShadowLevel;
-	    
-        #ifdef SPECULARTERM
-            #ifndef LIGHTMAPNOSPECULAR{X}
-                lightSpecularContribution += info.specular * notShadowLevel * lightmapColor;
-            #endif
-        #endif
-    #else
-        lightDiffuseContribution += info.diffuse * notShadowLevel;
-
-        #ifdef SPECULARTERM
-            lightSpecularContribution += info.specular * notShadowLevel;
-        #endif
-    #endif
-#endif

+ 1 - 1
src/Shaders/ShadersInclude/shadowsVertex.fx

@@ -1,5 +1,5 @@
 #ifdef SHADOWS
-	#if defined(SPOTLIGHT{X}) || defined(DIRLIGHT{X})
+	#if !defined(SHADOWCUBE{X})
 		vPositionFromLight{X} = lightMatrix{X} * worldPos;
 	#endif
 #endif

+ 1 - 1
src/Shaders/ShadersInclude/shadowsVertexDeclaration.fx

@@ -1,5 +1,5 @@
 #ifdef SHADOWS
-	#if defined(SPOTLIGHT{X}) || defined(DIRLIGHT{X})
+	#if !defined(SHADOWCUBE{X})
 		uniform mat4 lightMatrix{X};
 		varying vec4 vPositionFromLight{X};
 	#endif

+ 14 - 12
src/Shaders/pbr.fragment.fx

@@ -272,21 +272,11 @@ void main(void) {
 	// Compute roughness.
 	float roughness = clamp(1. - microSurface, 0.000001, 1.0);
 
-	// Lighting
-	vec3 lightDiffuseContribution = vec3(0., 0., 0.);
-
-#ifdef SPECULARTERM
-	vec3 lightSpecularContribution = vec3(0., 0., 0.);
-#endif
-	
-	float notShadowLevel = 1.; // 1 - shadowLevel
-
 	#ifdef LIGHTMAP
   		vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb * vLightmapInfos.y;
   	#endif
 
 	float NdotL = -1.;
-	lightingInfo info;
 
 	// Compute reflectance.
 	float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
@@ -297,10 +287,22 @@ void main(void) {
 	vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
 	vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
-#include<pbrLightFunctionsCall>[0..maxSimultaneousLights]
+	// Lighting
+	vec3 diffuseBase = vec3(0., 0., 0.);
+
+#ifdef SPECULARTERM
+	vec3 specularBase = vec3(0., 0., 0.);
+#endif
+	
+	lightingInfo info;
+	float shadow = 1.; // 1 - shadowLevel
+
+#include<lightFragment>[0..maxSimultaneousLights]
+
+	vec3 lightDiffuseContribution = diffuseBase;
 
 #ifdef SPECULARTERM
-	lightSpecularContribution *= vLightingIntensity.w;
+	vec3 lightSpecularContribution = specularBase * vLightingIntensity.w;
 #endif
 
 #ifdef OPACITY

+ 2 - 3
src/Tools/babylon.sceneSerializer.ts

@@ -266,9 +266,8 @@
                 light = scene.lights[index];
 
                 let shadowGenerator = light.getShadowGenerator();
-                // Only support serialization for official generator so far.
-                if (shadowGenerator && shadowGenerator instanceof ShadowGenerator) {
-                     serializationObject.shadowGenerators.push(<ShadowGenerator>shadowGenerator.serialize());
+                if (shadowGenerator) {
+                     serializationObject.shadowGenerators.push(shadowGenerator.serialize());
                 }
             }
 

+ 14 - 4
src/babylon.scene.ts

@@ -515,7 +515,7 @@
 
         public get lightsEnabled(): boolean {
             return this._lightsEnabled;
-        }    
+        }
 
         /**
         * All of the lights added to this scene.
@@ -1826,6 +1826,7 @@
             if (index !== -1) {
                 // Remove from the scene if mesh found 
                 this.lights.splice(index, 1);
+                this.orderLightsByPriority();
             }
             this.onLightRemovedObservable.notifyObservers(toRemove);
             return index;
@@ -1857,10 +1858,16 @@
 
         public addLight(newLight: Light) {
             newLight.uniqueId = this.getUniqueId();
-            var position = this.lights.push(newLight);
+            this.lights.push(newLight);
+            this.orderLightsByPriority();
+
             this.onNewLightAddedObservable.notifyObservers(newLight);
         }
 
+        public orderLightsByPriority(): void {
+            this.lights = this.lights.sort(Light.compareLightsPriority);
+        }
+
         public addCamera(newCamera: Camera) {
             newCamera.uniqueId = this.getUniqueId();
             var position = this.cameras.push(newCamera);
@@ -2953,8 +2960,11 @@
                     var light = this.lights[lightIndex];
                     var shadowGenerator = light.getShadowGenerator();
 
-                    if (light.isEnabled() && shadowGenerator && shadowGenerator.getShadowMap().getScene().textures.indexOf(shadowGenerator.getShadowMap()) !== -1) {
-                        this._renderTargets.push(shadowGenerator.getShadowMap());
+                    if (light.isEnabled() && light.shadowEnabled && shadowGenerator) {
+                        var shadowMap = shadowGenerator.getShadowMap();
+                        if (shadowMap.getScene().textures.indexOf(shadowMap) !== -1) {
+                            this._renderTargets.push(shadowMap);
+                        }
                     }
                 }
             }