Przeglądaj źródła

Migrating simple material to dynamic lights counting

David Catuhe 9 lat temu
rodzic
commit
0b5466d6ec

Plik diff jest za duży
+ 6 - 6
dist/preview release/babylon.core.js


Plik diff jest za duży
+ 3969 - 3965
dist/preview release/babylon.d.ts


Plik diff jest za duży
+ 20 - 20
dist/preview release/babylon.js


Plik diff jest za duży
+ 66 - 55
dist/preview release/babylon.max.js


Plik diff jest za duży
+ 6 - 6
dist/preview release/babylon.noworker.js


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

@@ -14,6 +14,7 @@
     - Unity3D exporter: Added support for lightmaps ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Unity3D exporter: Added support for export and run (local webserver) ([davrous](https://github.com/davrous), [deltakosh](https://github.com/deltakosh))
     - Moved PBR Material to core ([deltakosh](https://github.com/deltakosh))
+    - StandardMaterial.maxSimultaneousLights can define how many dynamic lights the material can handle ([deltakosh](https://github.com/deltakosh))
   - **Updates**
     - Added postprocess.enablePixelPerfectMode to avoid texture scaling/stretching when dealing with non-power of 2 resolutions. cannot be used on post-processes chain ([deltakosh](https://github.com/deltakosh))
     - Added skeleton.getBoneIndexByName(boneName: string) [dad72](https://github.com/dad72)

Plik diff jest za duży
+ 15 - 47
materialsLibrary/dist/babylon.simpleMaterial.js


Plik diff jest za duży
+ 1 - 1
materialsLibrary/dist/babylon.simpleMaterial.min.js


+ 1 - 0
materialsLibrary/dist/dts/babylon.simpleMaterial.d.ts

@@ -4,6 +4,7 @@ declare module BABYLON {
         diffuseTexture: BaseTexture;
         diffuseColor: Color3;
         disableLighting: boolean;
+        maxSimultaneousLights: number;
         private _worldViewProjectionMatrix;
         private _scaledDiffuse;
         private _renderId;

+ 17 - 50
materialsLibrary/materials/simple/babylon.simpleMaterial.ts

@@ -9,39 +9,6 @@ module BABYLON {
         public ALPHATEST = false;
         public POINTSIZE = false;
         public FOG = false;
-        public LIGHT0 = false;
-        public LIGHT1 = false;
-        public LIGHT2 = false;
-        public LIGHT3 = false;
-        public SPOTLIGHT0 = false;
-        public SPOTLIGHT1 = false;
-        public SPOTLIGHT2 = false;
-        public SPOTLIGHT3 = false;
-        public HEMILIGHT0 = false;
-        public HEMILIGHT1 = false;
-        public HEMILIGHT2 = false;
-        public HEMILIGHT3 = false;
-        public DIRLIGHT0 = false;
-        public DIRLIGHT1 = false;
-        public DIRLIGHT2 = false;
-        public DIRLIGHT3 = false;
-        public POINTLIGHT0 = false;
-        public POINTLIGHT1 = false;
-        public POINTLIGHT2 = false;
-        public POINTLIGHT3 = false;        
-        public SHADOW0 = false;
-        public SHADOW1 = false;
-        public SHADOW2 = false;
-        public SHADOW3 = false;
-        public SHADOWS = false;
-        public SHADOWVSM0 = false;
-        public SHADOWVSM1 = false;
-        public SHADOWVSM2 = false;
-        public SHADOWVSM3 = false;
-        public SHADOWPCF0 = false;
-        public SHADOWPCF1 = false;
-        public SHADOWPCF2 = false;
-        public SHADOWPCF3 = false;
         public NORMAL = false;
         public UV1 = false;
         public UV2 = false;
@@ -53,7 +20,7 @@ module BABYLON {
 
         constructor() {
             super();
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
     }
 
@@ -65,7 +32,10 @@ module BABYLON {
         public diffuseColor = new Color3(1, 1, 1);
         
         @serialize()
-        public disableLighting = false;
+        public disableLighting = false;        
+        
+        @serialize()
+        public maxSimultaneousLights = 4;
 
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _scaledDiffuse = new Color3();
@@ -163,9 +133,8 @@ module BABYLON {
                 this._defines.FOG = true;
             }
 
-            var lightIndex = 0;
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
 
             // Attribs
@@ -211,7 +180,7 @@ module BABYLON {
                     fallbacks.addFallback(1, "FOG");
                 }
 
-                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
                 
                 if (this._defines.NUM_BONE_INFLUENCERS > 0) {
                     fallbacks.addCPUSkinningFallback(0, mesh);
@@ -241,23 +210,21 @@ module BABYLON {
 
                 var shaderName = "simple";
                 var join = this._defines.toString();
-                this._effect = scene.getEngine().createEffect(shaderName,
-                    attribs,
-                    ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vDiffuseColor",
-                        "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                        "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                        "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                        "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vDiffuseColor",
                         "vFogInfos", "vFogColor", "pointSize",
                         "vDiffuseInfos", 
                         "mBones",
-                        "vClipPlane", "diffuseMatrix",
-                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues"
-                    ],
+                        "vClipPlane", "diffuseMatrix", "depthValues"
+                    ];
+                    
+                MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+                
+                this._effect = scene.getEngine().createEffect(shaderName,
+                    attribs, uniforms,
                     ["diffuseSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                     ],
-                    join, fallbacks, this.onCompiled, this.onError);
+                    join, fallbacks, this.onCompiled, this.onError, {maxSimultaneousLights: this.maxSimultaneousLights});
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -315,7 +282,7 @@ module BABYLON {
 
             // Lights
             if (scene.lightsEnabled && !this.disableLighting) {
-                MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);          
+                MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);          
             }
 
             // View

+ 2 - 8
materialsLibrary/materials/simple/simple.fragment.fx

@@ -16,10 +16,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
 
 
 #include<lightsFragmentFunctions>
@@ -77,10 +74,7 @@ void main(void) {
 	float shadow = 1.;
     float glossiness = 0.;
     
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..maxSimultaneousLights]
 
 
 #ifdef VERTEXALPHA

Plik diff jest za duży
+ 4551 - 849
materialsLibrary/test/refs/babylon.max.js


+ 19 - 2
src/Materials/babylon.effect.js

@@ -49,7 +49,7 @@ var BABYLON;
     })();
     BABYLON.EffectFallbacks = EffectFallbacks;
     var Effect = (function () {
-        function Effect(baseName, attributesNames, uniformsNames, samplers, engine, defines, fallbacks, onCompiled, onError) {
+        function Effect(baseName, attributesNames, uniformsNames, samplers, engine, defines, fallbacks, onCompiled, onError, indexParameters) {
             var _this = this;
             this._isReady = false;
             this._compilationError = "";
@@ -62,6 +62,7 @@ var BABYLON;
             this._attributesNames = attributesNames;
             this.onError = onError;
             this.onCompiled = onCompiled;
+            this._indexParameters = indexParameters;
             var vertexSource;
             var fragmentSource;
             if (baseName.vertexElement) {
@@ -206,7 +207,23 @@ var BABYLON;
                         }
                     }
                     if (match[4]) {
-                        includeContent = includeContent.replace(/\{X\}/g, match[5]);
+                        var indexString = match[5];
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
+                            }
+                            for (var i = minIndex; i <= maxIndex; i++) {
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
+                            }
+                        }
+                        else {
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
                     // Replace
                     returnValue = returnValue.replace(match[0], includeContent);

+ 23 - 2
src/Materials/babylon.effect.ts

@@ -73,11 +73,12 @@
         private _attributes: number[];
         private _uniforms: WebGLUniformLocation[];
         public _key: string;
+        private _indexParameters: any;
 
         private _program: WebGLProgram;
         private _valueCache = [];
 
-        constructor(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], engine, defines?: string, fallbacks?: EffectFallbacks, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void) {
+        constructor(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], engine, defines?: string, fallbacks?: EffectFallbacks, onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, indexParameters?: any) {
             this._engine = engine;
             this.name = baseName;
             this.defines = defines;
@@ -88,6 +89,8 @@
             this.onError = onError;
             this.onCompiled = onCompiled;
 
+            this._indexParameters = indexParameters;
+
             var vertexSource;
             var fragmentSource;
 
@@ -260,7 +263,25 @@
                     }
 
                     if (match[4]) {
-                        includeContent = includeContent.replace(/\{X\}/g, match[5]);
+                        var indexString = match[5];
+
+                        if (indexString.indexOf("..") !== -1) {
+                            var indexSplits = indexString.split("..");
+                            var minIndex = parseInt(indexSplits[0]);
+                            var maxIndex = parseInt(indexSplits[1]);
+                            var sourceIncludeContent = includeContent.slice(0);
+                            includeContent = "";
+
+                            if (isNaN(maxIndex)) {
+                                maxIndex = this._indexParameters[indexSplits[1]];
+                            }
+
+                            for (var i = minIndex; i <= maxIndex; i++) {
+                                includeContent += sourceIncludeContent.replace(/\{X\}/g, i) + "\n";
+                            }
+                        } else {
+                            includeContent = includeContent.replace(/\{X\}/g, indexString);
+                        }
                     }
 
                     // Replace

+ 3 - 0
src/Materials/babylon.material.js

@@ -9,6 +9,9 @@ var BABYLON;
     var MaterialDefines = (function () {
         function MaterialDefines() {
         }
+        MaterialDefines.prototype.rebuild = function () {
+            this._keys = Object.keys(this);
+        };
         MaterialDefines.prototype.isEqual = function (other) {
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];

+ 4 - 0
src/Materials/babylon.material.ts

@@ -2,6 +2,10 @@
     export class MaterialDefines {
         _keys: string[];
 
+        public rebuild() {
+            this._keys = Object.keys(this);
+        } 
+
         public isEqual(other: MaterialDefines): boolean {
             for (var index = 0; index < this._keys.length; index++) {
                 var prop = this._keys[index];

+ 23 - 5
src/Materials/babylon.materialHelper.js

@@ -1,12 +1,13 @@
 var BABYLON;
 (function (BABYLON) {
-    var maxSimultaneousLights = 4;
     var MaterialHelper = (function () {
         function MaterialHelper() {
         }
-        MaterialHelper.PrepareDefinesForLights = function (scene, mesh, defines) {
+        MaterialHelper.PrepareDefinesForLights = function (scene, mesh, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             var lightIndex = 0;
             var needNormals = false;
+            var needRebuild = false;
             for (var index = 0; index < scene.lights.length; index++) {
                 var light = scene.lights[index];
                 if (!light.isEnabled()) {
@@ -36,6 +37,9 @@ var BABYLON;
                     continue;
                 }
                 needNormals = true;
+                if (defines["LIGHT" + lightIndex] === undefined) {
+                    needRebuild = true;
+                }
                 defines["LIGHT" + lightIndex] = true;
                 var type;
                 if (light instanceof BABYLON.SpotLight) {
@@ -73,12 +77,25 @@ var BABYLON;
                 if (lightIndex === maxSimultaneousLights)
                     break;
             }
+            if (needRebuild) {
+                defines.rebuild();
+            }
             return needNormals;
         };
-        MaterialHelper.HandleFallbacksForShadows = function (defines, fallbacks) {
+        MaterialHelper.PrepareUniformsListForList = function (uniformsList, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                 if (!defines["LIGHT" + lightIndex]) {
-                    continue;
+                    break;
+                }
+                uniformsList.push("vLightData" + lightIndex, "vLightDiffuse" + lightIndex, "vLightSpecular" + lightIndex, "vLightDirection" + lightIndex, "vLightGround" + lightIndex, "lightMatrix" + lightIndex, "shadowsInfo" + lightIndex);
+            }
+        };
+        MaterialHelper.HandleFallbacksForShadows = function (defines, fallbacks, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
+            for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                if (!defines["LIGHT" + lightIndex]) {
+                    break;
                 }
                 if (lightIndex > 0) {
                     fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
@@ -149,7 +166,8 @@ var BABYLON;
                 light.transferToEffect(effect, "vLightData" + lightIndex, "vLightGround" + lightIndex);
             }
         };
-        MaterialHelper.BindLights = function (scene, mesh, effect, defines) {
+        MaterialHelper.BindLights = function (scene, mesh, effect, defines, maxSimultaneousLights) {
+            if (maxSimultaneousLights === void 0) { maxSimultaneousLights = 4; }
             var lightIndex = 0;
             var depthValuesAlreadySet = false;
             for (var index = 0; index < scene.lights.length; index++) {

+ 33 - 6
src/Materials/babylon.materialHelper.ts

@@ -1,10 +1,10 @@
 module BABYLON {
-    var maxSimultaneousLights = 4;
-
     export class MaterialHelper {
-        public static PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, defines: MaterialDefines): boolean {
+        public static PrepareDefinesForLights(scene: Scene, mesh: AbstractMesh, defines: MaterialDefines, maxSimultaneousLights = 4): boolean {
             var lightIndex = 0;
             var needNormals = false;
+            var needRebuild = false;
+
             for (var index = 0; index < scene.lights.length; index++) {
                 var light = scene.lights[index];
 
@@ -42,6 +42,11 @@
                     continue;
                 }
                 needNormals = true;
+
+                if (defines["LIGHT" + lightIndex] === undefined) {
+                    needRebuild = true;
+                }
+
                 defines["LIGHT" + lightIndex] = true;
 
                 var type;
@@ -85,13 +90,35 @@
                     break;
             }
 
+            if (needRebuild) {
+                defines.rebuild();
+            }
+
             return needNormals;
         }
 
-        public static HandleFallbacksForShadows(defines: MaterialDefines, fallbacks: EffectFallbacks): void {
+        public static PrepareUniformsListForList(uniformsList: string[], defines: MaterialDefines, maxSimultaneousLights = 4): void {
             for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
                 if (!defines["LIGHT" + lightIndex]) {
-                    continue;
+                    break;
+                }
+
+                uniformsList.push(
+                    "vLightData" + lightIndex,
+                    "vLightDiffuse" + lightIndex,
+                    "vLightSpecular" + lightIndex,
+                    "vLightDirection" + lightIndex,
+                    "vLightGround" + lightIndex,
+                    "lightMatrix" + lightIndex,
+                    "shadowsInfo" + lightIndex
+                );
+            }
+        }
+
+        public static HandleFallbacksForShadows(defines: MaterialDefines, fallbacks: EffectFallbacks, maxSimultaneousLights = 4): void {
+            for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                if (!defines["LIGHT" + lightIndex]) {
+                    break;
                 }
 
                 if (lightIndex > 0) {
@@ -169,7 +196,7 @@
             }
         }
 
-        public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines) {
+        public static BindLights(scene: Scene, mesh: AbstractMesh, effect: Effect, defines: MaterialDefines, maxSimultaneousLights = 4) {
             var lightIndex = 0;
             var depthValuesAlreadySet = false;
             for (var index = 0; index < scene.lights.length; index++) {

+ 14 - 45
src/Materials/babylon.standardMaterial.js

@@ -31,40 +31,7 @@ var BABYLON;
             this.ALPHAFROMDIFFUSE = false;
             this.POINTSIZE = false;
             this.FOG = false;
-            this.LIGHT0 = false;
-            this.LIGHT1 = false;
-            this.LIGHT2 = false;
-            this.LIGHT3 = false;
-            this.SPOTLIGHT0 = false;
-            this.SPOTLIGHT1 = false;
-            this.SPOTLIGHT2 = false;
-            this.SPOTLIGHT3 = false;
-            this.HEMILIGHT0 = false;
-            this.HEMILIGHT1 = false;
-            this.HEMILIGHT2 = false;
-            this.HEMILIGHT3 = false;
-            this.POINTLIGHT0 = false;
-            this.POINTLIGHT1 = false;
-            this.POINTLIGHT2 = false;
-            this.POINTLIGHT3 = false;
-            this.DIRLIGHT0 = false;
-            this.DIRLIGHT1 = false;
-            this.DIRLIGHT2 = false;
-            this.DIRLIGHT3 = false;
             this.SPECULARTERM = false;
-            this.SHADOW0 = false;
-            this.SHADOW1 = false;
-            this.SHADOW2 = false;
-            this.SHADOW3 = false;
-            this.SHADOWS = false;
-            this.SHADOWVSM0 = false;
-            this.SHADOWVSM1 = false;
-            this.SHADOWVSM2 = false;
-            this.SHADOWVSM3 = false;
-            this.SHADOWPCF0 = false;
-            this.SHADOWPCF1 = false;
-            this.SHADOWPCF2 = false;
-            this.SHADOWPCF3 = false;
             this.DIFFUSEFRESNEL = false;
             this.OPACITYFRESNEL = false;
             this.REFLECTIONFRESNEL = false;
@@ -100,7 +67,7 @@ var BABYLON;
             this.REFRACTION = false;
             this.REFRACTIONMAP_3D = false;
             this.REFLECTIONOVERALPHA = false;
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
         return StandardMaterialDefines;
     })(BABYLON.MaterialDefines);
@@ -129,6 +96,7 @@ var BABYLON;
             this.invertRefractionY = true;
             this.useLightmapAsShadowmap = false;
             this.useGlossinessFromSpecularMapAlpha = false;
+            this.maxSimultaneousLights = 4;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
@@ -360,7 +328,7 @@ var BABYLON;
                 this._defines.FOG = true;
             }
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = BABYLON.MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
             if (StandardMaterial.FresnelEnabled) {
                 // Fresnel
@@ -455,7 +423,7 @@ var BABYLON;
                 if (this._defines.LOGARITHMICDEPTH) {
                     fallbacks.addFallback(0, "LOGARITHMICDEPTH");
                 }
-                BABYLON.MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                BABYLON.MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
                 if (this._defines.SPECULARTERM) {
                     fallbacks.addFallback(0, "SPECULARTERM");
                 }
@@ -496,21 +464,19 @@ var BABYLON;
                     shaderName = "legacydefault";
                 }
                 var join = this._defines.toString();
-                this._effect = scene.getEngine().createEffect(shaderName, attribs, ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
-                    "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                    "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                    "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                    "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
                     "vFogInfos", "vFogColor", "pointSize",
                     "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
                     "mBones",
                     "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                    "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues",
+                    "depthValues",
                     "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
                     "logarithmicDepthConstant"
-                ], ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
+                ];
+                BABYLON.MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+                this._effect = scene.getEngine().createEffect(shaderName, attribs, uniforms, ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                     "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
-                ], join, fallbacks, this.onCompiled, this.onError);
+                ], join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights - 1 });
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -649,7 +615,7 @@ var BABYLON;
                 this._effect.setColor4("vDiffuseColor", this.diffuseColor, this.alpha * mesh.visibility);
                 // Lights
                 if (scene.lightsEnabled && !this.disableLighting) {
-                    BABYLON.MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);
+                    BABYLON.MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);
                 }
                 // View
                 if (scene.fogEnabled && mesh.applyFog && scene.fogMode !== BABYLON.Scene.FOGMODE_NONE || this.reflectionTexture || this.refractionTexture) {
@@ -851,6 +817,9 @@ var BABYLON;
         ], StandardMaterial.prototype, "useGlossinessFromSpecularMapAlpha", void 0);
         __decorate([
             BABYLON.serialize()
+        ], StandardMaterial.prototype, "maxSimultaneousLights", void 0);
+        __decorate([
+            BABYLON.serialize()
         ], StandardMaterial.prototype, "useLogarithmicDepth", null);
         return StandardMaterial;
     })(BABYLON.Material);

+ 21 - 52
src/Materials/babylon.standardMaterial.ts

@@ -16,40 +16,7 @@
         public ALPHAFROMDIFFUSE = false;
         public POINTSIZE = false;
         public FOG = false;
-        public LIGHT0 = false;
-        public LIGHT1 = false;
-        public LIGHT2 = false;
-        public LIGHT3 = false;
-        public SPOTLIGHT0 = false;
-        public SPOTLIGHT1 = false;
-        public SPOTLIGHT2 = false;
-        public SPOTLIGHT3 = false;
-        public HEMILIGHT0 = false;
-        public HEMILIGHT1 = false;
-        public HEMILIGHT2 = false;
-        public HEMILIGHT3 = false;
-        public POINTLIGHT0 = false;
-        public POINTLIGHT1 = false;
-        public POINTLIGHT2 = false;
-        public POINTLIGHT3 = false;
-        public DIRLIGHT0 = false;
-        public DIRLIGHT1 = false;
-        public DIRLIGHT2 = false;
-        public DIRLIGHT3 = false;
         public SPECULARTERM = false;
-        public SHADOW0 = false;
-        public SHADOW1 = false;
-        public SHADOW2 = false;
-        public SHADOW3 = false;
-        public SHADOWS = false;
-        public SHADOWVSM0 = false;
-        public SHADOWVSM1 = false;
-        public SHADOWVSM2 = false;
-        public SHADOWVSM3 = false;
-        public SHADOWPCF0 = false;
-        public SHADOWPCF1 = false;
-        public SHADOWPCF2 = false;
-        public SHADOWPCF3 = false;
         public DIFFUSEFRESNEL = false;
         public OPACITYFRESNEL = false;
         public REFLECTIONFRESNEL = false;
@@ -88,7 +55,7 @@
 
         constructor() {
             super();
-            this._keys = Object.keys(this);
+            this.rebuild();
         }
     }
 
@@ -195,6 +162,9 @@
         @serialize()
         public useGlossinessFromSpecularMapAlpha = false;
 
+        @serialize()
+        public maxSimultaneousLights = 4;
+
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _globalAmbientColor = new Color3(0, 0, 0);
@@ -467,7 +437,7 @@
             }
 
             if (scene.lightsEnabled && !this.disableLighting) {
-                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines);
+                needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, this._defines, this.maxSimultaneousLights);
             }
 
             if (StandardMaterial.FresnelEnabled) {
@@ -586,7 +556,7 @@
                     fallbacks.addFallback(0, "LOGARITHMICDEPTH");
                 }
 
-                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks);
+                MaterialHelper.HandleFallbacksForShadows(this._defines, fallbacks, this.maxSimultaneousLights);
 
                 if (this._defines.SPECULARTERM) {
                     fallbacks.addFallback(0, "SPECULARTERM");
@@ -640,25 +610,24 @@
                     shaderName = "legacydefault";
                 }
                 var join = this._defines.toString();
+                var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
+                    "vFogInfos", "vFogColor", "pointSize",
+                    "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
+                    "mBones",
+                    "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
+                    "depthValues",
+                    "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
+                    "logarithmicDepthConstant"
+                ];
+
+                MaterialHelper.PrepareUniformsListForList(uniforms, this._defines, this.maxSimultaneousLights);
+
                 this._effect = scene.getEngine().createEffect(shaderName,
-                    attribs,
-                    ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
-                        "vLightData0", "vLightDiffuse0", "vLightSpecular0", "vLightDirection0", "vLightGround0", "lightMatrix0",
-                        "vLightData1", "vLightDiffuse1", "vLightSpecular1", "vLightDirection1", "vLightGround1", "lightMatrix1",
-                        "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
-                        "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
-                        "vFogInfos", "vFogColor", "pointSize",
-                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
-                        "mBones",
-                        "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
-                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3", "depthValues",
-                        "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor", "refractionLeftColor", "refractionRightColor",
-                        "logarithmicDepthConstant"
-                    ],
+                    attribs, uniforms,
                     ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                     ],
-                    join, fallbacks, this.onCompiled, this.onError);
+                    join, fallbacks, this.onCompiled, this.onError, { maxSimultaneousLights: this.maxSimultaneousLights - 1 });
             }
             if (!this._effect.isReady()) {
                 return false;
@@ -838,7 +807,7 @@
 
                 // Lights
                 if (scene.lightsEnabled && !this.disableLighting) {
-                    MaterialHelper.BindLights(scene, mesh, this._effect, this._defines);
+                    MaterialHelper.BindLights(scene, mesh, this._effect, this._defines, this.maxSimultaneousLights);
                 }
 
                 // View

+ 4 - 0
src/Physics/babylon.physicsImpostor.js

@@ -324,6 +324,10 @@ var BABYLON;
         };
         PhysicsImpostor.prototype.dispose = function () {
             var _this = this;
+            //no dispose if no physics engine is available.
+            if (!this._physicsEngine) {
+                return;
+            }
             this._joints.forEach(function (j) {
                 _this._physicsEngine.removeJoint(_this, j.otherImpostor, j.joint);
             });

+ 0 - 1
src/Physics/babylon.physicsImpostor.ts

@@ -439,4 +439,3 @@ module BABYLON {
         public static HeightmapImpostor = 9;
     }
 }
-

+ 2 - 8
src/Shaders/default.fragment.fx

@@ -32,10 +32,7 @@ varying vec4 vColor;
 #include<helperFunctions>
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..maxSimultaneousLights]
 
 #include<lightsFragmentFunctions>
 #include<shadowsFragmentFunctions>
@@ -225,10 +222,7 @@ void main(void) {
 #endif
 	float shadow = 1.;
 
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..maxSimultaneousLights]
 
 	// Refraction
 	vec3 refractionColor = vec3(0., 0., 0.);

+ 2 - 8
src/Shaders/legacydefault.fragment.fx

@@ -18,10 +18,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 #include<lightsFragmentFunctions>
 #include<shadowsFragmentFunctions>
@@ -136,10 +133,7 @@ void main(void) {
 #endif
 	float shadow = 1.;
 
-#include<lightFragment>[0]
-#include<lightFragment>[1]
-#include<lightFragment>[2]
-#include<lightFragment>[3]
+#include<lightFragment>[0..3]
 
 	// Reflection
 	vec3 reflectionColor = vec3(0., 0., 0.);

+ 2 - 8
src/Shaders/legacypbr.fragment.fx

@@ -43,10 +43,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 // Samplers
 #ifdef ALBEDO
@@ -203,10 +200,7 @@ void main(void) {
     float NdotL = -1.;
     lightingInfo info;
 
-#include<pbrLightFunctionsCall>[0]
-#include<pbrLightFunctionsCall>[1]
-#include<pbrLightFunctionsCall>[2]
-#include<pbrLightFunctionsCall>[3]
+#include<pbrLightFunctionsCall>[0..3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 2 - 8
src/Shaders/pbr.fragment.fx

@@ -55,10 +55,7 @@ varying vec4 vColor;
 #endif
 
 // Lights
-#include<lightFragmentDeclaration>[0]
-#include<lightFragmentDeclaration>[1]
-#include<lightFragmentDeclaration>[2]
-#include<lightFragmentDeclaration>[3]
+#include<lightFragmentDeclaration>[0..3]
 
 // Samplers
 #ifdef ALBEDO
@@ -280,10 +277,7 @@ void main(void) {
     float NdotL = -1.;
     lightingInfo info;
 
-#include<pbrLightFunctionsCall>[0]
-#include<pbrLightFunctionsCall>[1]
-#include<pbrLightFunctionsCall>[2]
-#include<pbrLightFunctionsCall>[3]
+#include<pbrLightFunctionsCall>[0..3]
 
 #ifdef SPECULARTERM
     lightSpecularContribution *= vLightingIntensity.w;

+ 100 - 0
src/Tools/babylon.smartCollection.js

@@ -0,0 +1,100 @@
+var BABYLON;
+(function (BABYLON) {
+    var SmartCollection = (function () {
+        function SmartCollection(capacity) {
+            if (capacity === void 0) { capacity = 10; }
+            this.count = 0;
+            this._initialCapacity = capacity;
+            this.items = {};
+            this._keys = new Array(this._initialCapacity);
+        }
+        SmartCollection.prototype.add = function (key, item) {
+            if (this.items[key] != undefined) {
+                return -1;
+            }
+            this.items[key] = item;
+            //literal keys are always strings, but we keep source type of key in _keys array
+            this._keys[this.count++] = key;
+            if (this.count > this._keys.length) {
+                this._keys.length *= 2;
+            }
+            return this.count;
+        };
+        SmartCollection.prototype.remove = function (key) {
+            if (this.items[key] == undefined) {
+                return -1;
+            }
+            return this.removeItemOfIndex(this.indexOf(key));
+        };
+        SmartCollection.prototype.removeItemOfIndex = function (index) {
+            if (index < this.count && index > -1) {
+                delete this.items[this._keys[index]];
+                //here, shifting by hand is better optimised than .splice
+                while (index < this.count) {
+                    this._keys[index] = this._keys[index + 1];
+                    index++;
+                }
+            }
+            else {
+                return -1;
+            }
+            return --this.count;
+        };
+        SmartCollection.prototype.indexOf = function (key) {
+            for (var i = 0; i !== this.count; i++) {
+                if (this._keys[i] === key) {
+                    return i;
+                }
+            }
+            return -1;
+        };
+        SmartCollection.prototype.item = function (key) {
+            return this.items[key];
+        };
+        SmartCollection.prototype.getAllKeys = function () {
+            if (this.count > 0) {
+                var keys = new Array(this.count);
+                for (var i = 0; i < this.count; i++) {
+                    keys[i] = this._keys[i];
+                }
+                return keys;
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getKeyByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this._keys[index];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.getItemByIndex = function (index) {
+            if (index < this.count && index > -1) {
+                return this.items[this._keys[index]];
+            }
+            else {
+                return undefined;
+            }
+        };
+        SmartCollection.prototype.empty = function () {
+            if (this.count > 0) {
+                this.count = 0;
+                this.items = {};
+                this._keys = new Array(this._initialCapacity);
+            }
+        };
+        SmartCollection.prototype.forEach = function (block) {
+            var key;
+            for (key in this.items) {
+                if (this.items.hasOwnProperty(key)) {
+                    block(this.items[key]);
+                }
+            }
+        };
+        return SmartCollection;
+    })();
+    BABYLON.SmartCollection = SmartCollection;
+})(BABYLON || (BABYLON = {}));

+ 2 - 2
src/babylon.engine.js

@@ -869,14 +869,14 @@ var BABYLON;
                 }
             }
         };
-        Engine.prototype.createEffect = function (baseName, attributesNames, uniformsNames, samplers, defines, fallbacks, onCompiled, onError) {
+        Engine.prototype.createEffect = function (baseName, attributesNames, uniformsNames, samplers, defines, fallbacks, onCompiled, onError, indexParameters) {
             var vertex = baseName.vertexElement || baseName.vertex || baseName;
             var fragment = baseName.fragmentElement || baseName.fragment || baseName;
             var name = vertex + "+" + fragment + "@" + defines;
             if (this._compiledEffects[name]) {
                 return this._compiledEffects[name];
             }
-            var effect = new BABYLON.Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError);
+            var effect = new BABYLON.Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters);
             effect._key = name;
             this._compiledEffects[name] = effect;
             return effect;

+ 2 - 2
src/babylon.engine.ts

@@ -1040,7 +1040,7 @@
         }
 
         public createEffect(baseName: any, attributesNames: string[], uniformsNames: string[], samplers: string[], defines: string, fallbacks?: EffectFallbacks,
-            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void): Effect {
+            onCompiled?: (effect: Effect) => void, onError?: (effect: Effect, errors: string) => void, indexParameters?: any): Effect {
             var vertex = baseName.vertexElement || baseName.vertex || baseName;
             var fragment = baseName.fragmentElement || baseName.fragment || baseName;
 
@@ -1049,7 +1049,7 @@
                 return this._compiledEffects[name];
             }
 
-            var effect = new Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError);
+            var effect = new Effect(baseName, attributesNames, uniformsNames, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters);
             effect._key = name;
             this._compiledEffects[name] = effect;