소스 검색

New rewored StandardMaterial.isReady for better memory usage and performance

David Catuhe 10 년 전
부모
커밋
bbf7b6fafb

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 12 - 11
dist/preview release - alpha/babylon.2.2.js


+ 238 - 103
dist/preview release - alpha/babylon.2.2.max.js

@@ -18121,6 +18121,110 @@ var BABYLON;
         return FresnelParameters;
     })();
     BABYLON.FresnelParameters = FresnelParameters;
+    var StandardMaterialDefines = (function () {
+        function StandardMaterialDefines() {
+            this.DIFFUSE = false;
+            this.AMBIENT = false;
+            this.OPACITY = false;
+            this.OPACITYRGB = false;
+            this.REFLECTION = false;
+            this.EMISSIVE = false;
+            this.SPECULAR = false;
+            this.BUMP = false;
+            this.SPECULAROVERALPHA = false;
+            this.CLIPPLANE = false;
+            this.ALPHATEST = false;
+            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.POINTDIRLIGHT0 = false;
+            this.POINTDIRLIGHT1 = false;
+            this.POINTDIRLIGHT2 = false;
+            this.POINTDIRLIGHT3 = 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;
+            this.EMISSIVEFRESNEL = false;
+            this.FRESNEL = false;
+            this.NORMAL = false;
+            this.UV1 = false;
+            this.UV2 = false;
+            this.VERTEXCOLOR = false;
+            this.VERTEXALPHA = false;
+            this.BONES = false;
+            this.BONES4 = false;
+            this.BonesPerMesh = 0;
+            this.INSTANCES = false;
+        }
+        StandardMaterialDefines.prototype.isEqual = function (other) {
+            for (var prop in this) {
+                if (this[prop] != other[prop]) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        StandardMaterialDefines.prototype.cloneTo = function (other) {
+            for (var prop in this) {
+                other[prop] = this[prop];
+            }
+        };
+        StandardMaterialDefines.prototype.reset = function () {
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+                if (prop == "BonesPerMesh") {
+                    this[prop] = 0;
+                    continue;
+                }
+                this[prop] = false;
+            }
+        };
+        StandardMaterialDefines.prototype.toString = function () {
+            var result = "";
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+                if (prop == "BonesPerMesh" && this[prop] > 0) {
+                    result += "#define BonesPerMesh " + this[prop] + "\n";
+                    continue;
+                }
+                if (this[prop]) {
+                    result += "#define " + prop + "\n";
+                }
+            }
+            return result;
+        };
+        return StandardMaterialDefines;
+    })();
     var StandardMaterial = (function (_super) {
         __extends(StandardMaterial, _super);
         function StandardMaterial(name, scene) {
@@ -18134,12 +18238,14 @@ var BABYLON;
             this.useAlphaFromDiffuseTexture = false;
             this.useSpecularOverAlpha = true;
             this.fogEnabled = true;
-            this._cachedDefines = null;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
             this._scaledDiffuse = new BABYLON.Color3();
             this._scaledSpecular = new BABYLON.Color3();
+            this._defines = new StandardMaterialDefines();
+            this._cachedDefines = new StandardMaterialDefines();
+            this._cachedDefines.BonesPerMesh = -1;
             this.getRenderTargetTextures = function () {
                 _this._renderTargets.reset();
                 if (_this.reflectionTexture && _this.reflectionTexture.isRenderTarget) {
@@ -18174,10 +18280,9 @@ var BABYLON;
                 }
             }
             var engine = scene.getEngine();
-            var defines = [];
-            var fallbacks = new BABYLON.EffectFallbacks();
             var needNormals = false;
             var needUVs = false;
+            this._defines.reset();
             // Textures
             if (scene.texturesEnabled) {
                 if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
@@ -18186,7 +18291,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define DIFFUSE");
+                        this._defines.DIFFUSE = true;
                     }
                 }
                 if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
@@ -18195,7 +18300,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define AMBIENT");
+                        this._defines.AMBIENT = true;
                     }
                 }
                 if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
@@ -18204,9 +18309,9 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define OPACITY");
+                        this._defines.OPACITY = true;
                         if (this.opacityTexture.getAlphaFromRGB) {
-                            defines.push("#define OPACITYRGB");
+                            this._defines.OPACITYRGB = true;
                         }
                     }
                 }
@@ -18217,8 +18322,7 @@ var BABYLON;
                     else {
                         needNormals = true;
                         needUVs = true;
-                        defines.push("#define REFLECTION");
-                        fallbacks.addFallback(0, "REFLECTION");
+                        this._defines.REFLECTION = true;
                     }
                 }
                 if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
@@ -18227,7 +18331,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define EMISSIVE");
+                        this._defines.EMISSIVE = true;
                     }
                 }
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
@@ -18236,8 +18340,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define SPECULAR");
-                        fallbacks.addFallback(0, "SPECULAR");
+                        this._defines.SPECULAR = true;
                     }
                 }
             }
@@ -18247,36 +18350,28 @@ var BABYLON;
                 }
                 else {
                     needUVs = true;
-                    defines.push("#define BUMP");
-                    fallbacks.addFallback(0, "BUMP");
+                    this._defines.BUMP = true;
                 }
             }
             // Effect
-            if (this.useSpecularOverAlpha) {
-                defines.push("#define SPECULAROVERALPHA");
-                fallbacks.addFallback(0, "SPECULAROVERALPHA");
-            }
             if (scene.clipPlane) {
-                defines.push("#define CLIPPLANE");
+                this._defines.CLIPPLANE = true;
             }
             if (engine.getAlphaTesting()) {
-                defines.push("#define ALPHATEST");
+                this._defines.ALPHATEST = true;
             }
             if (this._shouldUseAlphaFromDiffuseTexture()) {
-                defines.push("#define ALPHAFROMDIFFUSE");
+                this._defines.ALPHAFROMDIFFUSE = true;
             }
             // Point size
             if (this.pointsCloud || scene.forcePointsCloud) {
-                defines.push("#define POINTSIZE");
+                this._defines.POINTSIZE = true;
             }
             // Fog
             if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== BABYLON.Scene.FOGMODE_NONE && this.fogEnabled) {
-                defines.push("#define FOG");
-                fallbacks.addFallback(1, "FOG");
+                this._defines.FOG = true;
             }
-            var shadowsActivated = false;
             var lightIndex = 0;
-            this._specularTermEnabled = false;
             if (scene.lightsEnabled) {
                 for (var index = 0; index < scene.lights.length; index++) {
                     var light = scene.lights[index];
@@ -18307,53 +18402,33 @@ var BABYLON;
                         continue;
                     }
                     needNormals = true;
-                    defines.push("#define LIGHT" + lightIndex);
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
-                    }
+                    this._defines["LIGHT" + lightIndex] = true;
                     var type;
                     if (light instanceof BABYLON.SpotLight) {
-                        type = "#define SPOTLIGHT" + lightIndex;
+                        type = "SPOTLIGHT" + lightIndex;
                     }
                     else if (light instanceof BABYLON.HemisphericLight) {
-                        type = "#define HEMILIGHT" + lightIndex;
+                        type = "HEMILIGHT" + lightIndex;
                     }
                     else {
-                        type = "#define POINTDIRLIGHT" + lightIndex;
-                    }
-                    defines.push(type);
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, type.replace("#define ", ""));
+                        type = "POINTDIRLIGHT" + lightIndex;
                     }
+                    this._defines[type] = true;
                     // Specular
                     if (!light.specular.equalsFloats(0, 0, 0)) {
-                        if (!this._specularTermEnabled) {
-                            this._specularTermEnabled = true;
-                            defines.push("#define SPECULARTERM");
-                            fallbacks.addFallback(0, "SPECULARTERM");
-                        }
+                        this._defines.SPECULARTERM = true;
                     }
                     // Shadows
                     if (scene.shadowsEnabled) {
                         var shadowGenerator = light.getShadowGenerator();
                         if (mesh && mesh.receiveShadows && shadowGenerator) {
-                            defines.push("#define SHADOW" + lightIndex);
-                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
-                            if (!shadowsActivated) {
-                                defines.push("#define SHADOWS");
-                                shadowsActivated = true;
-                            }
+                            this._defines["SHADOW" + lightIndex] = true;
+                            this._defines.SHADOWS = true;
                             if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
-                                defines.push("#define SHADOWVSM" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
-                                }
+                                this._defines["SHADOWVSM" + lightIndex] = true;
                             }
                             if (shadowGenerator.usePoissonSampling) {
-                                defines.push("#define SHADOWPCF" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
-                                }
+                                this._defines["SHADOWPCF" + lightIndex] = true;
                             }
                         }
                     }
@@ -18368,83 +18443,143 @@ var BABYLON;
                     this.opacityFresnelParameters && this.opacityFresnelParameters.isEnabled ||
                     this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled ||
                     this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
-                    var fresnelRank = 1;
                     if (this.diffuseFresnelParameters && this.diffuseFresnelParameters.isEnabled) {
-                        defines.push("#define DIFFUSEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "DIFFUSEFRESNEL");
-                        fresnelRank++;
+                        this._defines.DIFFUSEFRESNEL = true;
                     }
                     if (this.opacityFresnelParameters && this.opacityFresnelParameters.isEnabled) {
-                        defines.push("#define OPACITYFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "OPACITYFRESNEL");
-                        fresnelRank++;
+                        this._defines.OPACITYFRESNEL = true;
                     }
                     if (this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
-                        defines.push("#define REFLECTIONFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "REFLECTIONFRESNEL");
-                        fresnelRank++;
+                        this._defines.REFLECTIONFRESNEL = true;
                     }
                     if (this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled) {
-                        defines.push("#define EMISSIVEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "EMISSIVEFRESNEL");
-                        fresnelRank++;
+                        this._defines.EMISSIVEFRESNEL = true;
                     }
                     needNormals = true;
-                    defines.push("#define FRESNEL");
-                    fallbacks.addFallback(fresnelRank - 1, "FRESNEL");
+                    this._defines.FRESNEL = true;
                 }
             }
+            if (this._defines.SPECULARTERM && this.useSpecularOverAlpha) {
+                this._defines.SPECULAROVERALPHA = true;
+            }
             // Attribs
-            var attribs = [BABYLON.VertexBuffer.PositionKind];
             if (mesh) {
                 if (needNormals && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
-                    attribs.push(BABYLON.VertexBuffer.NormalKind);
-                    defines.push("#define NORMAL");
+                    this._defines.NORMAL = true;
                 }
                 if (needUVs) {
                     if (mesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                        attribs.push(BABYLON.VertexBuffer.UVKind);
-                        defines.push("#define UV1");
+                        this._defines.UV1 = true;
                     }
                     if (mesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
-                        attribs.push(BABYLON.VertexBuffer.UV2Kind);
-                        defines.push("#define UV2");
+                        this._defines.UV2 = true;
                     }
                 }
                 if (mesh.useVertexColors && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
-                    attribs.push(BABYLON.VertexBuffer.ColorKind);
-                    defines.push("#define VERTEXCOLOR");
+                    this._defines.VERTEXCOLOR = true;
                     if (mesh.hasVertexAlpha) {
-                        defines.push("#define VERTEXALPHA");
+                        this._defines.VERTEXALPHA = true;
                     }
                 }
                 if (mesh.useBones) {
-                    attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
-                    attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
-                    defines.push("#define BONES");
-                    defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
-                    defines.push("#define BONES4");
-                    fallbacks.addFallback(0, "BONES4");
+                    this._defines.BONES = true;
+                    this._defines.BonesPerMesh = (mesh.skeleton.bones.length + 1);
+                    this._defines.BONES4 = true;
                 }
                 // Instances
                 if (useInstances) {
-                    defines.push("#define INSTANCES");
+                    this._defines.INSTANCES = true;
+                }
+            }
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+                scene.resetCachedMaterial();
+                // Fallbacks
+                var fallbacks = new BABYLON.EffectFallbacks();
+                if (this._defines.REFLECTION) {
+                    fallbacks.addFallback(0, "REFLECTION");
+                }
+                if (this._defines.SPECULAR) {
+                    fallbacks.addFallback(0, "SPECULAR");
+                }
+                if (this._defines.BUMP) {
+                    fallbacks.addFallback(0, "BUMP");
+                }
+                if (this._defines.SPECULAROVERALPHA) {
+                    fallbacks.addFallback(0, "SPECULAROVERALPHA");
+                }
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+                for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                    if (!this._defines["LIGHT" + lightIndex]) {
+                        continue;
+                    }
+                    if (lightIndex > 0) {
+                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
+                    }
+                    if (this._defines["SHADOW" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
+                    }
+                    if (this._defines["SHADOWPCF" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                    }
+                    if (this._defines["SHADOWVSM" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                    }
+                }
+                if (this._defines.SPECULARTERM) {
+                    fallbacks.addFallback(0, "SPECULARTERM");
+                }
+                if (this._defines.DIFFUSEFRESNEL) {
+                    fallbacks.addFallback(1, "DIFFUSEFRESNEL");
+                }
+                if (this._defines.OPACITYFRESNEL) {
+                    fallbacks.addFallback(2, "OPACITYFRESNEL");
+                }
+                if (this._defines.REFLECTIONFRESNEL) {
+                    fallbacks.addFallback(3, "REFLECTIONFRESNEL");
+                }
+                if (this._defines.EMISSIVEFRESNEL) {
+                    fallbacks.addFallback(4, "EMISSIVEFRESNEL");
+                }
+                if (this._defines.FRESNEL) {
+                    fallbacks.addFallback(4, "FRESNEL");
+                }
+                if (this._defines.BONES4) {
+                    fallbacks.addFallback(0, "BONES4");
+                }
+                //Attributes
+                var attribs = [BABYLON.VertexBuffer.PositionKind];
+                if (this._defines.NORMAL) {
+                    attribs.push(BABYLON.VertexBuffer.NormalKind);
+                }
+                if (this._defines.UV1) {
+                    attribs.push(BABYLON.VertexBuffer.UVKind);
+                }
+                if (this._defines.UV2) {
+                    attribs.push(BABYLON.VertexBuffer.UV2Kind);
+                }
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(BABYLON.VertexBuffer.ColorKind);
+                }
+                if (this._defines.BONES) {
+                    attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
+                    attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
+                }
+                if (this._defines.INSTANCES) {
                     attribs.push("world0");
                     attribs.push("world1");
                     attribs.push("world2");
                     attribs.push("world3");
                 }
-            }
-            // Get correct effect      
-            var join = defines.join("\n");
-            if (this._cachedDefines !== join) {
-                this._cachedDefines = join;
-                scene.resetCachedMaterial();
                 // Legacy browser patch
                 var shaderName = "default";
                 if (!scene.getEngine().getCaps().standardDerivatives) {
                     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",
@@ -18561,7 +18696,7 @@ var BABYLON;
                 this._scaledSpecular.b = this.specularColor.b * BABYLON.Tools.Clamp(1.0 - this.emissiveColor.b);
                 this._effect.setVector3("vEyePosition", scene.activeCamera.position);
                 this._effect.setColor3("vAmbientColor", this._globalAmbientColor);
-                if (this._specularTermEnabled) {
+                if (this._defines.SPECULARTERM) {
                     this._effect.setColor4("vSpecularColor", this._scaledSpecular, this.specularPower);
                 }
                 this._effect.setColor3("vEmissiveColor", this.emissiveColor);
@@ -18599,7 +18734,7 @@ var BABYLON;
                     }
                     light.diffuse.scaleToRef(light.intensity, this._scaledDiffuse);
                     this._effect.setColor4("vLightDiffuse" + lightIndex, this._scaledDiffuse, light.range);
-                    if (this._specularTermEnabled) {
+                    if (this._defines.SPECULARTERM) {
                         light.specular.scaleToRef(light.intensity, this._scaledSpecular);
                         this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
                     }
@@ -19947,13 +20082,13 @@ var BABYLON;
                     mesh.setVerticesData(BABYLON.VertexBuffer.MatricesWeightsKind, parsedGeometry.matricesWeights, false);
                 }
                 mesh.setIndices(parsedGeometry.indices);
-                // SubMeshes
-                if (parsedGeometry.subMeshes) {
-                    mesh.subMeshes = [];
-                    for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
-                        var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
-                        var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
-                    }
+            }
+            // SubMeshes
+            if (parsedGeometry.subMeshes) {
+                mesh.subMeshes = [];
+                for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
+                    var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
+                    var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
                 }
             }
             // Flat shading

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 18 - 17
dist/preview release - alpha/babylon.2.2.noworker.js


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

@@ -2,6 +2,7 @@
   - **Major updates**
     - Meshes can now be attached to bones. See [documentation here](http://babylondoc.azurewebsites.net/page.php?p=22421) and [sample here](http://www.babylonjs-playground.com/#11BH6Z#18) [deltakosh](https://github.com/deltakosh)
     - HDR Rendering pipeline. See [demo here]() [julien-moreau](https://github.com/julien-moreau)
+    - New rewored StandardMaterial.isReady for better memory usage and performance [deltakosh](https://github.com/deltakosh)
   - **Updates**
     - New parameter for ArcRotateCamera.zoomOn to preserve maxZ [deltakosh](https://github.com/deltakosh)
     - PickingInfo.getNormal can now use either vertices normals or vertices positions [deltakosh](https://github.com/deltakosh)

+ 7 - 7
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -1008,13 +1008,13 @@ var BABYLON;
                     mesh.setVerticesData(BABYLON.VertexBuffer.MatricesWeightsKind, parsedGeometry.matricesWeights, false);
                 }
                 mesh.setIndices(parsedGeometry.indices);
-                // SubMeshes
-                if (parsedGeometry.subMeshes) {
-                    mesh.subMeshes = [];
-                    for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
-                        var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
-                        var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
-                    }
+            }
+            // SubMeshes
+            if (parsedGeometry.subMeshes) {
+                mesh.subMeshes = [];
+                for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
+                    var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
+                    var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
                 }
             }
             // Flat shading

+ 7 - 7
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -1245,15 +1245,15 @@
             }
 
             mesh.setIndices(parsedGeometry.indices);
+        }
 
-            // SubMeshes
-            if (parsedGeometry.subMeshes) {
-                mesh.subMeshes = [];
-                for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
-                    var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
+        // SubMeshes
+        if (parsedGeometry.subMeshes) {
+            mesh.subMeshes = [];
+            for (var subIndex = 0; subIndex < parsedGeometry.subMeshes.length; subIndex++) {
+                var parsedSubMesh = parsedGeometry.subMeshes[subIndex];
 
-                    var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
-                }
+                var subMesh = new BABYLON.SubMesh(parsedSubMesh.materialIndex, parsedSubMesh.verticesStart, parsedSubMesh.verticesCount, parsedSubMesh.indexStart, parsedSubMesh.indexCount, mesh);
             }
         }
 

+ 231 - 96
src/Materials/babylon.standardMaterial.js

@@ -18,6 +18,110 @@ var BABYLON;
         return FresnelParameters;
     })();
     BABYLON.FresnelParameters = FresnelParameters;
+    var StandardMaterialDefines = (function () {
+        function StandardMaterialDefines() {
+            this.DIFFUSE = false;
+            this.AMBIENT = false;
+            this.OPACITY = false;
+            this.OPACITYRGB = false;
+            this.REFLECTION = false;
+            this.EMISSIVE = false;
+            this.SPECULAR = false;
+            this.BUMP = false;
+            this.SPECULAROVERALPHA = false;
+            this.CLIPPLANE = false;
+            this.ALPHATEST = false;
+            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.POINTDIRLIGHT0 = false;
+            this.POINTDIRLIGHT1 = false;
+            this.POINTDIRLIGHT2 = false;
+            this.POINTDIRLIGHT3 = 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;
+            this.EMISSIVEFRESNEL = false;
+            this.FRESNEL = false;
+            this.NORMAL = false;
+            this.UV1 = false;
+            this.UV2 = false;
+            this.VERTEXCOLOR = false;
+            this.VERTEXALPHA = false;
+            this.BONES = false;
+            this.BONES4 = false;
+            this.BonesPerMesh = 0;
+            this.INSTANCES = false;
+        }
+        StandardMaterialDefines.prototype.isEqual = function (other) {
+            for (var prop in this) {
+                if (this[prop] != other[prop]) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        StandardMaterialDefines.prototype.cloneTo = function (other) {
+            for (var prop in this) {
+                other[prop] = this[prop];
+            }
+        };
+        StandardMaterialDefines.prototype.reset = function () {
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+                if (prop == "BonesPerMesh") {
+                    this[prop] = 0;
+                    continue;
+                }
+                this[prop] = false;
+            }
+        };
+        StandardMaterialDefines.prototype.toString = function () {
+            var result = "";
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+                if (prop == "BonesPerMesh" && this[prop] > 0) {
+                    result += "#define BonesPerMesh " + this[prop] + "\n";
+                    continue;
+                }
+                if (this[prop]) {
+                    result += "#define " + prop + "\n";
+                }
+            }
+            return result;
+        };
+        return StandardMaterialDefines;
+    })();
     var StandardMaterial = (function (_super) {
         __extends(StandardMaterial, _super);
         function StandardMaterial(name, scene) {
@@ -31,12 +135,14 @@ var BABYLON;
             this.useAlphaFromDiffuseTexture = false;
             this.useSpecularOverAlpha = true;
             this.fogEnabled = true;
-            this._cachedDefines = null;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
             this._globalAmbientColor = new BABYLON.Color3(0, 0, 0);
             this._scaledDiffuse = new BABYLON.Color3();
             this._scaledSpecular = new BABYLON.Color3();
+            this._defines = new StandardMaterialDefines();
+            this._cachedDefines = new StandardMaterialDefines();
+            this._cachedDefines.BonesPerMesh = -1;
             this.getRenderTargetTextures = function () {
                 _this._renderTargets.reset();
                 if (_this.reflectionTexture && _this.reflectionTexture.isRenderTarget) {
@@ -71,10 +177,9 @@ var BABYLON;
                 }
             }
             var engine = scene.getEngine();
-            var defines = [];
-            var fallbacks = new BABYLON.EffectFallbacks();
             var needNormals = false;
             var needUVs = false;
+            this._defines.reset();
             // Textures
             if (scene.texturesEnabled) {
                 if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
@@ -83,7 +188,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define DIFFUSE");
+                        this._defines.DIFFUSE = true;
                     }
                 }
                 if (this.ambientTexture && StandardMaterial.AmbientTextureEnabled) {
@@ -92,7 +197,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define AMBIENT");
+                        this._defines.AMBIENT = true;
                     }
                 }
                 if (this.opacityTexture && StandardMaterial.OpacityTextureEnabled) {
@@ -101,9 +206,9 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define OPACITY");
+                        this._defines.OPACITY = true;
                         if (this.opacityTexture.getAlphaFromRGB) {
-                            defines.push("#define OPACITYRGB");
+                            this._defines.OPACITYRGB = true;
                         }
                     }
                 }
@@ -114,8 +219,7 @@ var BABYLON;
                     else {
                         needNormals = true;
                         needUVs = true;
-                        defines.push("#define REFLECTION");
-                        fallbacks.addFallback(0, "REFLECTION");
+                        this._defines.REFLECTION = true;
                     }
                 }
                 if (this.emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
@@ -124,7 +228,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define EMISSIVE");
+                        this._defines.EMISSIVE = true;
                     }
                 }
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
@@ -133,8 +237,7 @@ var BABYLON;
                     }
                     else {
                         needUVs = true;
-                        defines.push("#define SPECULAR");
-                        fallbacks.addFallback(0, "SPECULAR");
+                        this._defines.SPECULAR = true;
                     }
                 }
             }
@@ -144,36 +247,28 @@ var BABYLON;
                 }
                 else {
                     needUVs = true;
-                    defines.push("#define BUMP");
-                    fallbacks.addFallback(0, "BUMP");
+                    this._defines.BUMP = true;
                 }
             }
             // Effect
-            if (this.useSpecularOverAlpha) {
-                defines.push("#define SPECULAROVERALPHA");
-                fallbacks.addFallback(0, "SPECULAROVERALPHA");
-            }
             if (scene.clipPlane) {
-                defines.push("#define CLIPPLANE");
+                this._defines.CLIPPLANE = true;
             }
             if (engine.getAlphaTesting()) {
-                defines.push("#define ALPHATEST");
+                this._defines.ALPHATEST = true;
             }
             if (this._shouldUseAlphaFromDiffuseTexture()) {
-                defines.push("#define ALPHAFROMDIFFUSE");
+                this._defines.ALPHAFROMDIFFUSE = true;
             }
             // Point size
             if (this.pointsCloud || scene.forcePointsCloud) {
-                defines.push("#define POINTSIZE");
+                this._defines.POINTSIZE = true;
             }
             // Fog
             if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== BABYLON.Scene.FOGMODE_NONE && this.fogEnabled) {
-                defines.push("#define FOG");
-                fallbacks.addFallback(1, "FOG");
+                this._defines.FOG = true;
             }
-            var shadowsActivated = false;
             var lightIndex = 0;
-            this._specularTermEnabled = false;
             if (scene.lightsEnabled) {
                 for (var index = 0; index < scene.lights.length; index++) {
                     var light = scene.lights[index];
@@ -204,53 +299,33 @@ var BABYLON;
                         continue;
                     }
                     needNormals = true;
-                    defines.push("#define LIGHT" + lightIndex);
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
-                    }
+                    this._defines["LIGHT" + lightIndex] = true;
                     var type;
                     if (light instanceof BABYLON.SpotLight) {
-                        type = "#define SPOTLIGHT" + lightIndex;
+                        type = "SPOTLIGHT" + lightIndex;
                     }
                     else if (light instanceof BABYLON.HemisphericLight) {
-                        type = "#define HEMILIGHT" + lightIndex;
+                        type = "HEMILIGHT" + lightIndex;
                     }
                     else {
-                        type = "#define POINTDIRLIGHT" + lightIndex;
-                    }
-                    defines.push(type);
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, type.replace("#define ", ""));
+                        type = "POINTDIRLIGHT" + lightIndex;
                     }
+                    this._defines[type] = true;
                     // Specular
                     if (!light.specular.equalsFloats(0, 0, 0)) {
-                        if (!this._specularTermEnabled) {
-                            this._specularTermEnabled = true;
-                            defines.push("#define SPECULARTERM");
-                            fallbacks.addFallback(0, "SPECULARTERM");
-                        }
+                        this._defines.SPECULARTERM = true;
                     }
                     // Shadows
                     if (scene.shadowsEnabled) {
                         var shadowGenerator = light.getShadowGenerator();
                         if (mesh && mesh.receiveShadows && shadowGenerator) {
-                            defines.push("#define SHADOW" + lightIndex);
-                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
-                            if (!shadowsActivated) {
-                                defines.push("#define SHADOWS");
-                                shadowsActivated = true;
-                            }
+                            this._defines["SHADOW" + lightIndex] = true;
+                            this._defines.SHADOWS = true;
                             if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
-                                defines.push("#define SHADOWVSM" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
-                                }
+                                this._defines["SHADOWVSM" + lightIndex] = true;
                             }
                             if (shadowGenerator.usePoissonSampling) {
-                                defines.push("#define SHADOWPCF" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
-                                }
+                                this._defines["SHADOWPCF" + lightIndex] = true;
                             }
                         }
                     }
@@ -265,83 +340,143 @@ var BABYLON;
                     this.opacityFresnelParameters && this.opacityFresnelParameters.isEnabled ||
                     this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled ||
                     this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
-                    var fresnelRank = 1;
                     if (this.diffuseFresnelParameters && this.diffuseFresnelParameters.isEnabled) {
-                        defines.push("#define DIFFUSEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "DIFFUSEFRESNEL");
-                        fresnelRank++;
+                        this._defines.DIFFUSEFRESNEL = true;
                     }
                     if (this.opacityFresnelParameters && this.opacityFresnelParameters.isEnabled) {
-                        defines.push("#define OPACITYFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "OPACITYFRESNEL");
-                        fresnelRank++;
+                        this._defines.OPACITYFRESNEL = true;
                     }
                     if (this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
-                        defines.push("#define REFLECTIONFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "REFLECTIONFRESNEL");
-                        fresnelRank++;
+                        this._defines.REFLECTIONFRESNEL = true;
                     }
                     if (this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled) {
-                        defines.push("#define EMISSIVEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "EMISSIVEFRESNEL");
-                        fresnelRank++;
+                        this._defines.EMISSIVEFRESNEL = true;
                     }
                     needNormals = true;
-                    defines.push("#define FRESNEL");
-                    fallbacks.addFallback(fresnelRank - 1, "FRESNEL");
+                    this._defines.FRESNEL = true;
                 }
             }
+            if (this._defines.SPECULARTERM && this.useSpecularOverAlpha) {
+                this._defines.SPECULAROVERALPHA = true;
+            }
             // Attribs
-            var attribs = [BABYLON.VertexBuffer.PositionKind];
             if (mesh) {
                 if (needNormals && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.NormalKind)) {
-                    attribs.push(BABYLON.VertexBuffer.NormalKind);
-                    defines.push("#define NORMAL");
+                    this._defines.NORMAL = true;
                 }
                 if (needUVs) {
                     if (mesh.isVerticesDataPresent(BABYLON.VertexBuffer.UVKind)) {
-                        attribs.push(BABYLON.VertexBuffer.UVKind);
-                        defines.push("#define UV1");
+                        this._defines.UV1 = true;
                     }
                     if (mesh.isVerticesDataPresent(BABYLON.VertexBuffer.UV2Kind)) {
-                        attribs.push(BABYLON.VertexBuffer.UV2Kind);
-                        defines.push("#define UV2");
+                        this._defines.UV2 = true;
                     }
                 }
                 if (mesh.useVertexColors && mesh.isVerticesDataPresent(BABYLON.VertexBuffer.ColorKind)) {
-                    attribs.push(BABYLON.VertexBuffer.ColorKind);
-                    defines.push("#define VERTEXCOLOR");
+                    this._defines.VERTEXCOLOR = true;
                     if (mesh.hasVertexAlpha) {
-                        defines.push("#define VERTEXALPHA");
+                        this._defines.VERTEXALPHA = true;
                     }
                 }
                 if (mesh.useBones) {
-                    attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
-                    attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
-                    defines.push("#define BONES");
-                    defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
-                    defines.push("#define BONES4");
-                    fallbacks.addFallback(0, "BONES4");
+                    this._defines.BONES = true;
+                    this._defines.BonesPerMesh = (mesh.skeleton.bones.length + 1);
+                    this._defines.BONES4 = true;
                 }
                 // Instances
                 if (useInstances) {
-                    defines.push("#define INSTANCES");
+                    this._defines.INSTANCES = true;
+                }
+            }
+            // Get correct effect      
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
+                scene.resetCachedMaterial();
+                // Fallbacks
+                var fallbacks = new BABYLON.EffectFallbacks();
+                if (this._defines.REFLECTION) {
+                    fallbacks.addFallback(0, "REFLECTION");
+                }
+                if (this._defines.SPECULAR) {
+                    fallbacks.addFallback(0, "SPECULAR");
+                }
+                if (this._defines.BUMP) {
+                    fallbacks.addFallback(0, "BUMP");
+                }
+                if (this._defines.SPECULAROVERALPHA) {
+                    fallbacks.addFallback(0, "SPECULAROVERALPHA");
+                }
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+                for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                    if (!this._defines["LIGHT" + lightIndex]) {
+                        continue;
+                    }
+                    if (lightIndex > 0) {
+                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
+                    }
+                    if (this._defines["SHADOW" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
+                    }
+                    if (this._defines["SHADOWPCF" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                    }
+                    if (this._defines["SHADOWVSM" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                    }
+                }
+                if (this._defines.SPECULARTERM) {
+                    fallbacks.addFallback(0, "SPECULARTERM");
+                }
+                if (this._defines.DIFFUSEFRESNEL) {
+                    fallbacks.addFallback(1, "DIFFUSEFRESNEL");
+                }
+                if (this._defines.OPACITYFRESNEL) {
+                    fallbacks.addFallback(2, "OPACITYFRESNEL");
+                }
+                if (this._defines.REFLECTIONFRESNEL) {
+                    fallbacks.addFallback(3, "REFLECTIONFRESNEL");
+                }
+                if (this._defines.EMISSIVEFRESNEL) {
+                    fallbacks.addFallback(4, "EMISSIVEFRESNEL");
+                }
+                if (this._defines.FRESNEL) {
+                    fallbacks.addFallback(4, "FRESNEL");
+                }
+                if (this._defines.BONES4) {
+                    fallbacks.addFallback(0, "BONES4");
+                }
+                //Attributes
+                var attribs = [BABYLON.VertexBuffer.PositionKind];
+                if (this._defines.NORMAL) {
+                    attribs.push(BABYLON.VertexBuffer.NormalKind);
+                }
+                if (this._defines.UV1) {
+                    attribs.push(BABYLON.VertexBuffer.UVKind);
+                }
+                if (this._defines.UV2) {
+                    attribs.push(BABYLON.VertexBuffer.UV2Kind);
+                }
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(BABYLON.VertexBuffer.ColorKind);
+                }
+                if (this._defines.BONES) {
+                    attribs.push(BABYLON.VertexBuffer.MatricesIndicesKind);
+                    attribs.push(BABYLON.VertexBuffer.MatricesWeightsKind);
+                }
+                if (this._defines.INSTANCES) {
                     attribs.push("world0");
                     attribs.push("world1");
                     attribs.push("world2");
                     attribs.push("world3");
                 }
-            }
-            // Get correct effect      
-            var join = defines.join("\n");
-            if (this._cachedDefines !== join) {
-                this._cachedDefines = join;
-                scene.resetCachedMaterial();
                 // Legacy browser patch
                 var shaderName = "default";
                 if (!scene.getEngine().getCaps().standardDerivatives) {
                     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",
@@ -458,7 +593,7 @@ var BABYLON;
                 this._scaledSpecular.b = this.specularColor.b * BABYLON.Tools.Clamp(1.0 - this.emissiveColor.b);
                 this._effect.setVector3("vEyePosition", scene.activeCamera.position);
                 this._effect.setColor3("vAmbientColor", this._globalAmbientColor);
-                if (this._specularTermEnabled) {
+                if (this._defines.SPECULARTERM) {
                     this._effect.setColor4("vSpecularColor", this._scaledSpecular, this.specularPower);
                 }
                 this._effect.setColor3("vEmissiveColor", this.emissiveColor);
@@ -496,7 +631,7 @@ var BABYLON;
                     }
                     light.diffuse.scaleToRef(light.intensity, this._scaledDiffuse);
                     this._effect.setColor4("vLightDiffuse" + lightIndex, this._scaledDiffuse, light.range);
-                    if (this._specularTermEnabled) {
+                    if (this._defines.SPECULARTERM) {
                         light.specular.scaleToRef(light.intensity, this._scaledSpecular);
                         this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
                     }

+ 267 - 103
src/Materials/babylon.standardMaterial.ts

@@ -9,6 +9,117 @@
         public power = 1;
     }
 
+    class StandardMaterialDefines {
+        public DIFFUSE = false;
+        public AMBIENT = false;
+        public OPACITY = false;
+        public OPACITYRGB = false;
+        public REFLECTION = false;
+        public EMISSIVE = false;
+        public SPECULAR = false;
+        public BUMP = false;
+        public SPECULAROVERALPHA = false;
+        public CLIPPLANE = false;
+        public ALPHATEST = false;
+        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 POINTDIRLIGHT0 = false;
+        public POINTDIRLIGHT1 = false;
+        public POINTDIRLIGHT2 = false;
+        public POINTDIRLIGHT3 = 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;
+        public EMISSIVEFRESNEL = false;
+        public FRESNEL = false;
+        public NORMAL = false;
+        public UV1 = false;
+        public UV2 = false;
+        public VERTEXCOLOR = false;
+        public VERTEXALPHA = false;
+        public BONES = false;
+        public BONES4 = false;
+        public BonesPerMesh = 0;
+        public INSTANCES = false;
+
+        public isEqual(other: StandardMaterialDefines): boolean {
+            for (var prop in this) {
+                if (this[prop] != other[prop]) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public cloneTo(other: StandardMaterialDefines): void {
+            for (var prop in this) {
+                other[prop] = this[prop];
+            }
+        }
+
+        public reset(): void {
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+                if (prop == "BonesPerMesh") {
+                    this[prop] = 0;
+                    continue;
+                }
+
+                this[prop] = false;
+            }
+        }
+
+        public toString(): string {
+            var result = "";
+            for (var prop in this) {
+                if (typeof this[prop] === "function") {
+                    continue;
+                }
+
+                if (prop == "BonesPerMesh" && this[prop] > 0) {
+                    result += "#define BonesPerMesh " + this[prop] + "\n";
+                    continue;
+                }
+
+                if (this[prop]) {
+                    result += "#define " + prop + "\n";
+                }
+            }
+
+            return result;
+        }
+    }
+
     export class StandardMaterial extends Material {
         public diffuseTexture: BaseTexture;
         public ambientTexture: BaseTexture;
@@ -32,18 +143,21 @@
         public reflectionFresnelParameters: FresnelParameters;
         public emissiveFresnelParameters: FresnelParameters;
 
-        private _cachedDefines = null;
         private _renderTargets = new SmartArray<RenderTargetTexture>(16);
         private _worldViewProjectionMatrix = Matrix.Zero();
         private _globalAmbientColor = new Color3(0, 0, 0);
         private _scaledDiffuse = new Color3();
         private _scaledSpecular = new Color3();
         private _renderId: number;
-        private _specularTermEnabled: boolean;
+
+        private _defines = new StandardMaterialDefines();
+        private _cachedDefines = new StandardMaterialDefines();
 
         constructor(name: string, scene: Scene) {
             super(name, scene);
 
+            this._cachedDefines.BonesPerMesh = -1;
+
             this.getRenderTargetTextures = (): SmartArray<RenderTargetTexture> => {
                 this._renderTargets.reset();
 
@@ -88,11 +202,11 @@
             }
 
             var engine = scene.getEngine();
-            var defines = [];
-            var fallbacks = new EffectFallbacks();
             var needNormals = false;
             var needUVs = false;
 
+            this._defines.reset();
+
             // Textures
             if (scene.texturesEnabled) {
                 if (this.diffuseTexture && StandardMaterial.DiffuseTextureEnabled) {
@@ -100,7 +214,7 @@
                         return false;
                     } else {
                         needUVs = true;
-                        defines.push("#define DIFFUSE");
+                        this._defines.DIFFUSE = true;
                     }
                 }
 
@@ -109,7 +223,7 @@
                         return false;
                     } else {
                         needUVs = true;
-                        defines.push("#define AMBIENT");
+                        this._defines.AMBIENT = true;
                     }
                 }
 
@@ -118,10 +232,10 @@
                         return false;
                     } else {
                         needUVs = true;
-                        defines.push("#define OPACITY");
+                        this._defines.OPACITY = true;
 
                         if (this.opacityTexture.getAlphaFromRGB) {
-                            defines.push("#define OPACITYRGB");
+                            this._defines.OPACITYRGB = true;
                         }
                     }
                 }
@@ -132,8 +246,7 @@
                     } else {
                         needNormals = true;
                         needUVs = true;
-                        defines.push("#define REFLECTION");
-                        fallbacks.addFallback(0, "REFLECTION");
+                        this._defines.REFLECTION = true;
                     }
                 }
 
@@ -142,7 +255,7 @@
                         return false;
                     } else {
                         needUVs = true;
-                        defines.push("#define EMISSIVE");
+                        this._defines.EMISSIVE = true;
                     }
                 }
 
@@ -151,8 +264,7 @@
                         return false;
                     } else {
                         needUVs = true;
-                        defines.push("#define SPECULAR");
-                        fallbacks.addFallback(0, "SPECULAR");
+                        this._defines.SPECULAR = true;
                     }
                 }
             }
@@ -162,43 +274,34 @@
                     return false;
                 } else {
                     needUVs = true;
-                    defines.push("#define BUMP");
-                    fallbacks.addFallback(0, "BUMP");
+                    this._defines.BUMP = true;
                 }
             }
 
             // Effect
-            if (this.useSpecularOverAlpha) {
-                defines.push("#define SPECULAROVERALPHA");
-                fallbacks.addFallback(0, "SPECULAROVERALPHA");
-            }
-
             if (scene.clipPlane) {
-                defines.push("#define CLIPPLANE");
+                this._defines.CLIPPLANE = true;
             }
 
             if (engine.getAlphaTesting()) {
-                defines.push("#define ALPHATEST");
+                this._defines.ALPHATEST = true;
             }
 
             if (this._shouldUseAlphaFromDiffuseTexture()) {
-                defines.push("#define ALPHAFROMDIFFUSE");
+                this._defines.ALPHAFROMDIFFUSE = true;
             }
 
             // Point size
             if (this.pointsCloud || scene.forcePointsCloud) {
-                defines.push("#define POINTSIZE");
+                this._defines.POINTSIZE = true;
             }
 
             // Fog
             if (scene.fogEnabled && mesh && mesh.applyFog && scene.fogMode !== Scene.FOGMODE_NONE && this.fogEnabled) {
-                defines.push("#define FOG");
-                fallbacks.addFallback(1, "FOG");
+                this._defines.FOG = true;
             }
-
-            var shadowsActivated = false;
+            
             var lightIndex = 0;
-            this._specularTermEnabled = false;
             if (scene.lightsEnabled) {
                 for (var index = 0; index < scene.lights.length; index++) {
                     var light = scene.lights[index];
@@ -237,59 +340,38 @@
                         continue;
                     }
                     needNormals = true;
-                    defines.push("#define LIGHT" + lightIndex);
-
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
-                    }
+                    this._defines["LIGHT" + lightIndex] = true;
 
                     var type;
                     if (light instanceof SpotLight) {
-                        type = "#define SPOTLIGHT" + lightIndex;
+                        type = "SPOTLIGHT" + lightIndex;
                     } else if (light instanceof HemisphericLight) {
-                        type = "#define HEMILIGHT" + lightIndex;
+                        type = "HEMILIGHT" + lightIndex;
                     } else {
-                        type = "#define POINTDIRLIGHT" + lightIndex;
+                        type = "POINTDIRLIGHT" + lightIndex;
                     }
 
-                    defines.push(type);
-                    if (lightIndex > 0) {
-                        fallbacks.addFallback(lightIndex, type.replace("#define ", ""));
-                    }
+                    this._defines[type] = true;
 
                     // Specular
                     if (!light.specular.equalsFloats(0, 0, 0)) {
-                        if (!this._specularTermEnabled) {
-                            this._specularTermEnabled = true;
-                            defines.push("#define SPECULARTERM");
-                            fallbacks.addFallback(0, "SPECULARTERM");
-                        }
+                        this. _defines.SPECULARTERM = true;
                     }
 
                     // Shadows
                     if (scene.shadowsEnabled) {
                         var shadowGenerator = light.getShadowGenerator();
                         if (mesh && mesh.receiveShadows && shadowGenerator) {
-                            defines.push("#define SHADOW" + lightIndex);
-                            fallbacks.addFallback(0, "SHADOW" + lightIndex);
+                            this._defines["SHADOW" + lightIndex] = true;
 
-                            if (!shadowsActivated) {
-                                defines.push("#define SHADOWS");
-                                shadowsActivated = true;
-                            }
+                            this._defines.SHADOWS = true;
 
                             if (shadowGenerator.useVarianceShadowMap || shadowGenerator.useBlurVarianceShadowMap) {
-                                defines.push("#define SHADOWVSM" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
-                                }
+                                this._defines["SHADOWVSM" + lightIndex] = true;
                             }
 
                             if (shadowGenerator.usePoissonSampling) {
-                                defines.push("#define SHADOWPCF" + lightIndex);
-                                if (lightIndex > 0) {
-                                    fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
-                                }
+                                this._defines["SHADOWPCF" + lightIndex] = true;
                             }
                         }
                     }
@@ -307,96 +389,178 @@
                     this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled ||
                     this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
 
-                    var fresnelRank = 1;
-
                     if (this.diffuseFresnelParameters && this.diffuseFresnelParameters.isEnabled) {
-                        defines.push("#define DIFFUSEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "DIFFUSEFRESNEL");
-                        fresnelRank++;
+                        this._defines.DIFFUSEFRESNEL = true;
                     }
 
                     if (this.opacityFresnelParameters && this.opacityFresnelParameters.isEnabled) {
-                        defines.push("#define OPACITYFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "OPACITYFRESNEL");
-                        fresnelRank++;
+                        this._defines.OPACITYFRESNEL = true;
                     }
 
                     if (this.reflectionFresnelParameters && this.reflectionFresnelParameters.isEnabled) {
-                        defines.push("#define REFLECTIONFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "REFLECTIONFRESNEL");
-                        fresnelRank++;
+                        this._defines.REFLECTIONFRESNEL = true;
                     }
 
                     if (this.emissiveFresnelParameters && this.emissiveFresnelParameters.isEnabled) {
-                        defines.push("#define EMISSIVEFRESNEL");
-                        fallbacks.addFallback(fresnelRank, "EMISSIVEFRESNEL");
-                        fresnelRank++;
+                        this._defines.EMISSIVEFRESNEL = true;
                     }
 
                     needNormals = true;
-                    defines.push("#define FRESNEL");
-                    fallbacks.addFallback(fresnelRank - 1, "FRESNEL");
+                    this._defines.FRESNEL = true;
                 }
             }
 
+            if (this._defines.SPECULARTERM && this.useSpecularOverAlpha) {
+                this._defines.SPECULAROVERALPHA = true;
+            }
 
             // Attribs
-            var attribs = [VertexBuffer.PositionKind];
             if (mesh) {
                 if (needNormals && mesh.isVerticesDataPresent(VertexBuffer.NormalKind)) {
-                    attribs.push(VertexBuffer.NormalKind);
-                    defines.push("#define NORMAL");
+                    this._defines.NORMAL = true;
                 }
                 if (needUVs) {
                     if (mesh.isVerticesDataPresent(VertexBuffer.UVKind)) {
-                        attribs.push(VertexBuffer.UVKind);
-                        defines.push("#define UV1");
+                        this._defines.UV1 = true;
                     }
                     if (mesh.isVerticesDataPresent(VertexBuffer.UV2Kind)) {
-                        attribs.push(VertexBuffer.UV2Kind);
-                        defines.push("#define UV2");
+                        this._defines.UV2 = true;
                     }
                 }
                 if (mesh.useVertexColors && mesh.isVerticesDataPresent(VertexBuffer.ColorKind)) {
-                    attribs.push(VertexBuffer.ColorKind);
-                    defines.push("#define VERTEXCOLOR");
+                    this._defines.VERTEXCOLOR = true;
 
                     if (mesh.hasVertexAlpha) {
-                        defines.push("#define VERTEXALPHA");
+                        this._defines.VERTEXALPHA = true
                     }
                 }
                 if (mesh.useBones) {
-                    attribs.push(VertexBuffer.MatricesIndicesKind);
-                    attribs.push(VertexBuffer.MatricesWeightsKind);
-                    defines.push("#define BONES");
-                    defines.push("#define BonesPerMesh " + (mesh.skeleton.bones.length + 1));
-                    defines.push("#define BONES4");
-                    fallbacks.addFallback(0, "BONES4");
+                    this._defines.BONES = true;
+                    this._defines.BonesPerMesh = (mesh.skeleton.bones.length + 1);
+                    this._defines.BONES4 = true;
                 }
 
                 // Instances
                 if (useInstances) {
-                    defines.push("#define INSTANCES");
-                    attribs.push("world0");
-                    attribs.push("world1");
-                    attribs.push("world2");
-                    attribs.push("world3");
+                    this._defines.INSTANCES = true;
                 }
             }
 
             // Get correct effect      
-            var join = defines.join("\n");
-            if (this._cachedDefines !== join) {
-                this._cachedDefines = join;
+            if (!this._defines.isEqual(this._cachedDefines)) {
+                this._defines.cloneTo(this._cachedDefines);
 
                 scene.resetCachedMaterial();
 
+                // Fallbacks
+                var fallbacks = new EffectFallbacks();
+                if (this._defines.REFLECTION) {
+                    fallbacks.addFallback(0, "REFLECTION");
+                }
+
+                if (this._defines.SPECULAR) {
+                    fallbacks.addFallback(0, "SPECULAR");
+                }
+
+                if (this._defines.BUMP) {
+                    fallbacks.addFallback(0, "BUMP");
+                }
+
+                if (this._defines.SPECULAROVERALPHA) {
+                    fallbacks.addFallback(0, "SPECULAROVERALPHA");
+                }
+
+                if (this._defines.FOG) {
+                    fallbacks.addFallback(1, "FOG");
+                }
+
+                for (var lightIndex = 0; lightIndex < maxSimultaneousLights; lightIndex++) {
+                    if (!this._defines["LIGHT" + lightIndex]) {
+                        continue;
+                    }
+
+                    if (lightIndex > 0) {
+                        fallbacks.addFallback(lightIndex, "LIGHT" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOW" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOW" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOWPCF" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWPCF" + lightIndex);
+                    }
+
+                    if (this._defines["SHADOWVSM" + lightIndex]) {
+                        fallbacks.addFallback(0, "SHADOWVSM" + lightIndex);
+                    }
+                }
+
+                if (this._defines.SPECULARTERM) {
+                    fallbacks.addFallback(0, "SPECULARTERM");
+                }
+
+                if (this._defines.DIFFUSEFRESNEL) {
+                    fallbacks.addFallback(1, "DIFFUSEFRESNEL");
+                }
+
+                if (this._defines.OPACITYFRESNEL) {
+                    fallbacks.addFallback(2, "OPACITYFRESNEL");
+                }
+
+                if (this._defines.REFLECTIONFRESNEL) {
+                    fallbacks.addFallback(3, "REFLECTIONFRESNEL");
+                }
+
+                if (this._defines.EMISSIVEFRESNEL) {
+                    fallbacks.addFallback(4, "EMISSIVEFRESNEL");
+                }
+
+                if (this._defines.FRESNEL) {
+                    fallbacks.addFallback(4, "FRESNEL");
+                }
+                
+                if (this._defines.BONES4) {
+                    fallbacks.addFallback(0, "BONES4");
+                }
+
+                //Attributes
+                var attribs = [VertexBuffer.PositionKind];
+
+                if (this._defines.NORMAL) {
+                    attribs.push(VertexBuffer.NormalKind);
+                }
+
+                if (this._defines.UV1) {
+                    attribs.push(VertexBuffer.UVKind);
+                }
+
+                if (this._defines.UV2) {
+                    attribs.push(VertexBuffer.UV2Kind);
+                }
+
+                if (this._defines.VERTEXCOLOR) {
+                    attribs.push(VertexBuffer.ColorKind);
+                }
+
+                if (this._defines.BONES) {
+                    attribs.push(VertexBuffer.MatricesIndicesKind);
+                    attribs.push(VertexBuffer.MatricesWeightsKind);
+                }
+
+                if (this._defines.INSTANCES) {
+                    attribs.push("world0");
+                    attribs.push("world1");
+                    attribs.push("world2");
+                    attribs.push("world3");
+                }
+
                 // Legacy browser patch
                 var shaderName = "default";
                 if (!scene.getEngine().getCaps().standardDerivatives) {
                     shaderName = "legacydefault";
                 }
-
+                var join = this._defines.toString();
                 this._effect = scene.getEngine().createEffect(shaderName,
                     attribs,
                     ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vDiffuseColor", "vSpecularColor", "vEmissiveColor",
@@ -548,7 +712,7 @@
                 this._effect.setVector3("vEyePosition", scene.activeCamera.position);
                 this._effect.setColor3("vAmbientColor", this._globalAmbientColor);
 
-                if (this._specularTermEnabled) {
+                if (this._defines.SPECULARTERM) {
                     this._effect.setColor4("vSpecularColor", this._scaledSpecular, this.specularPower);
                 }
                 this._effect.setColor3("vEmissiveColor", this.emissiveColor);
@@ -590,7 +754,7 @@
 
                     light.diffuse.scaleToRef(light.intensity, this._scaledDiffuse);
                     this._effect.setColor4("vLightDiffuse" + lightIndex, this._scaledDiffuse, light.range);
-                    if (this._specularTermEnabled) {
+                    if (this._defines.SPECULARTERM) {
                         light.specular.scaleToRef(light.intensity, this._scaledSpecular);
                         this._effect.setColor3("vLightSpecular" + lightIndex, this._scaledSpecular);
                     }