فهرست منبع

Added Material.sideOrientation
First alpha of lightmapTexture property on StdMaterial

David catuhe 10 سال پیش
والد
کامیت
c6aae2264e

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 868 - 860
dist/preview release/babylon.d.ts


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 25 - 21
dist/preview release/babylon.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 66 - 14
dist/preview release/babylon.max.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 24 - 20
dist/preview release/babylon.noworker.js


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

@@ -1,6 +1,8 @@
 - 2.3.0:
   - **Major updates**
+    - New `StandardMaterial.lightmapTexture` which can be controlled with `StandardMaterial.lightmapThreshold`. [Demo here](#NEEDDEMO) ([deltakosh](https://github.com/deltakosh))
   - **Updates**
+    - New `Material.sideOrientation` property to define clockwise or counter-clockwise faces selection. [Demo here](http://www.babylonjs-playground.com/#1TZJQY) ([deltakosh](https://github.com/deltakosh))
   - **Bug fixes**
   - **Breaking changes**
 

+ 5 - 1
src/Audio/babylon.sound.js

@@ -35,6 +35,7 @@ var BABYLON;
             this._coneInnerAngle = 360;
             this._coneOuterAngle = 360;
             this._coneOuterGain = 0;
+            this._isOutputConnected = false;
             this.name = name;
             this._scene = scene;
             this._readyToPlayCallback = readyToPlayCallback;
@@ -213,8 +214,11 @@ var BABYLON;
         };
         Sound.prototype.connectToSoundTrackAudioNode = function (soundTrackAudioNode) {
             if (BABYLON.Engine.audioEngine.canUseWebAudio) {
-                this._ouputAudioNode.disconnect();
+                if (this._isOutputConnected) {
+                    this._ouputAudioNode.disconnect();
+                }
                 this._ouputAudioNode.connect(soundTrackAudioNode);
+                this._isOutputConnected = true;
             }
         };
         /**

+ 5 - 1
src/Audio/babylon.sound.ts

@@ -39,6 +39,7 @@
         private _connectedMesh: AbstractMesh;
         private _customAttenuationFunction: (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => number;
         private _registerFunc: (connectedMesh: AbstractMesh) => any;
+        private _isOutputConnected = false;
 
         /**
         * Create a sound and attach it to a scene
@@ -233,8 +234,11 @@
 
         public connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode) {
             if (Engine.audioEngine.canUseWebAudio) {
-                this._ouputAudioNode.disconnect();
+                if (this._isOutputConnected) {
+                    this._ouputAudioNode.disconnect();
+                }
                 this._ouputAudioNode.connect(soundTrackAudioNode);
+                this._isOutputConnected = true;
             }
         }
 

+ 1 - 0
src/Debug/babylon.debugLayer.js

@@ -512,6 +512,7 @@ var BABYLON;
                 this._generateCheckBox(this._optionsSubsetDiv, "Opacity", BABYLON.StandardMaterial.OpacityTextureEnabled, function (element) { BABYLON.StandardMaterial.OpacityTextureEnabled = element.checked; });
                 this._generateCheckBox(this._optionsSubsetDiv, "Reflection", BABYLON.StandardMaterial.ReflectionTextureEnabled, function (element) { BABYLON.StandardMaterial.ReflectionTextureEnabled = element.checked; });
                 this._generateCheckBox(this._optionsSubsetDiv, "Fresnel", BABYLON.StandardMaterial.FresnelEnabled, function (element) { BABYLON.StandardMaterial.FresnelEnabled = element.checked; });
+                this._generateCheckBox(this._optionsSubsetDiv, "Lightmap", BABYLON.StandardMaterial.LightmapEnabled, function (element) { BABYLON.StandardMaterial.LightmapEnabled = element.checked; });
                 this._optionsSubsetDiv.appendChild(document.createElement("br"));
                 this._generateTexBox(this._optionsSubsetDiv, "<b>Options:</b>", this.accentColor);
                 this._generateCheckBox(this._optionsSubsetDiv, "Animations", this._scene.animationsEnabled, function (element) { _this._scene.animationsEnabled = element.checked; });

+ 1 - 0
src/Debug/babylon.debugLayer.ts

@@ -657,6 +657,7 @@
                 this._generateCheckBox(this._optionsSubsetDiv, "Opacity", StandardMaterial.OpacityTextureEnabled, (element) => { StandardMaterial.OpacityTextureEnabled = element.checked });
                 this._generateCheckBox(this._optionsSubsetDiv, "Reflection", StandardMaterial.ReflectionTextureEnabled, (element) => { StandardMaterial.ReflectionTextureEnabled = element.checked });
                 this._generateCheckBox(this._optionsSubsetDiv, "Fresnel", StandardMaterial.FresnelEnabled, (element) => { StandardMaterial.FresnelEnabled = element.checked });
+                this._generateCheckBox(this._optionsSubsetDiv, "Lightmap", StandardMaterial.LightmapEnabled, (element) => { StandardMaterial.LightmapEnabled = element.checked });
                 this._optionsSubsetDiv.appendChild(document.createElement("br"));
                 this._generateTexBox(this._optionsSubsetDiv, "<b>Options:</b>", this.accentColor);
                 this._generateCheckBox(this._optionsSubsetDiv, "Animations", this._scene.animationsEnabled, (element) => { this._scene.animationsEnabled = element.checked });

+ 4 - 0
src/Loading/Plugins/babylon.babylonFileLoader.js

@@ -143,6 +143,10 @@ var BABYLON;
             if (parsedMaterial.emissiveTexture) {
                 material.emissiveTexture = loadTexture(rootUrl, parsedMaterial.emissiveTexture, scene);
             }
+            if (parsedMaterial.lightmapTexture) {
+                material.lightmapTexture = loadTexture(rootUrl, parsedMaterial.lightmapTexture, scene);
+                material.lightmapThreshold = parsedMaterial.lightmapThreshold;
+            }
             if (parsedMaterial.emissiveFresnelParameters) {
                 material.emissiveFresnelParameters = parseFresnelParameters(parsedMaterial.emissiveFresnelParameters);
             }

+ 5 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -175,6 +175,11 @@
             material.emissiveTexture = loadTexture(rootUrl, parsedMaterial.emissiveTexture, scene);
         }
 
+        if (parsedMaterial.lightmapTexture) {
+            material.lightmapTexture = loadTexture(rootUrl, parsedMaterial.lightmapTexture, scene);
+            material.lightmapThreshold = parsedMaterial.lightmapThreshold;
+        }
+
         if (parsedMaterial.emissiveFresnelParameters) {
             material.emissiveFresnelParameters = parseFresnelParameters(parsedMaterial.emissiveFresnelParameters);
         }

+ 18 - 1
src/Materials/babylon.material.js

@@ -8,6 +8,7 @@ var BABYLON;
             this.state = "";
             this.alpha = 1.0;
             this.backFaceCulling = true;
+            this.sideOrientation = Material.CounterClockWiseSideOrientation;
             this.alphaMode = BABYLON.Engine.ALPHA_COMBINE;
             this.disableDepthWrite = false;
             this._wasPreviouslyReady = false;
@@ -41,6 +42,20 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Material, "ClockWiseSideOrientation", {
+            get: function () {
+                return Material._ClockWiseSideOrientation;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Material, "CounterClockWiseSideOrientation", {
+            get: function () {
+                return Material._CounterClockWiseSideOrientation;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Material.prototype, "wireframe", {
             get: function () {
                 return this._fillMode === Material.WireFrameFillMode;
@@ -94,7 +109,7 @@ var BABYLON;
         Material.prototype._preBind = function () {
             var engine = this._scene.getEngine();
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling, this.zOffset);
+            engine.setState(this.backFaceCulling, this.zOffset, false, this.sideOrientation === Material.ClockWiseSideOrientation);
         };
         Material.prototype.bind = function (world, mesh) {
             this._scene._cachedMaterial = this;
@@ -135,6 +150,8 @@ var BABYLON;
         Material._TriangleFillMode = 0;
         Material._WireFrameFillMode = 1;
         Material._PointFillMode = 2;
+        Material._ClockWiseSideOrientation = 0;
+        Material._CounterClockWiseSideOrientation = 1;
         return Material;
     })();
     BABYLON.Material = Material;

+ 13 - 1
src/Materials/babylon.material.ts

@@ -16,12 +16,24 @@
             return Material._PointFillMode;
         }
 
+        private static _ClockWiseSideOrientation = 0;
+        private static _CounterClockWiseSideOrientation = 1;
+
+        public static get ClockWiseSideOrientation(): number {
+            return Material._ClockWiseSideOrientation;
+        }
+
+        public static get CounterClockWiseSideOrientation(): number {
+            return Material._CounterClockWiseSideOrientation;
+        }
+
         public id: string;
         public checkReadyOnEveryCall = true;
         public checkReadyOnlyOnce = false;
         public state = "";
         public alpha = 1.0;
         public backFaceCulling = true;
+        public sideOrientation = Material.CounterClockWiseSideOrientation;
         public onCompiled: (effect: Effect) => void;
         public onError: (effect: Effect, errors: string) => void;
         public onDispose: () => void;
@@ -105,7 +117,7 @@
             var engine = this._scene.getEngine();
 
             engine.enableEffect(this._effect);
-            engine.setState(this.backFaceCulling, this.zOffset);
+            engine.setState(this.backFaceCulling, this.zOffset, false, this.sideOrientation === Material.ClockWiseSideOrientation);
         }
 
         public bind(world: Matrix, mesh?: Mesh): void {

+ 20 - 3
src/Materials/babylon.standardMaterial.js

@@ -81,6 +81,7 @@ var BABYLON;
             this.ROUGHNESS = false;
             this.EMISSIVEASILLUMINATION = false;
             this.REFLECTIONFRESNELFROMSPECULAR = false;
+            this.LIGHTMAP = false;
             this._keys = Object.keys(this);
         }
         StandardMaterialDefines.prototype.isEqual = function (other) {
@@ -140,6 +141,7 @@ var BABYLON;
             this.useSpecularOverAlpha = true;
             this.fogEnabled = true;
             this.roughness = 0;
+            this.lightmapThreshold = 0;
             this.useGlossinessFromSpecularMapAlpha = false;
             this._renderTargets = new BABYLON.SmartArray(16);
             this._worldViewProjectionMatrix = BABYLON.Matrix.Zero();
@@ -240,6 +242,15 @@ var BABYLON;
                         this._defines.EMISSIVE = true;
                     }
                 }
+                if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
+                    if (!this.lightmapTexture.isReady()) {
+                        return false;
+                    }
+                    else {
+                        needUVs = true;
+                        this._defines.LIGHTMAP = true;
+                    }
+                }
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
                     if (!this.specularTexture.isReady()) {
                         return false;
@@ -499,13 +510,13 @@ var BABYLON;
                     "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
                     "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
                     "vFogInfos", "vFogColor", "pointSize",
-                    "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos",
+                    "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos",
                     "mBones",
-                    "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix",
+                    "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix",
                     "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
                     "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor",
                     "roughness"
-                ], ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler",
+                ], ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler",
                     "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                 ], join, fallbacks, this.onCompiled, this.onError);
             }
@@ -587,6 +598,11 @@ var BABYLON;
                     this._effect.setFloat2("vEmissiveInfos", this.emissiveTexture.coordinatesIndex, this.emissiveTexture.level);
                     this._effect.setMatrix("emissiveMatrix", this.emissiveTexture.getTextureMatrix());
                 }
+                if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
+                    this._effect.setTexture("lightmapSampler", this.lightmapTexture);
+                    this._effect.setFloat3("vLightmapInfos", this.lightmapTexture.coordinatesIndex, this.lightmapTexture.level, this.lightmapThreshold);
+                    this._effect.setMatrix("lightmapMatrix", this.lightmapTexture.getTextureMatrix());
+                }
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
                     this._effect.setTexture("specularSampler", this.specularTexture);
                     this._effect.setFloat2("vSpecularInfos", this.specularTexture.coordinatesIndex, this.specularTexture.level);
@@ -776,6 +792,7 @@ var BABYLON;
         StandardMaterial.SpecularTextureEnabled = true;
         StandardMaterial.BumpTextureEnabled = true;
         StandardMaterial.FresnelEnabled = true;
+        StandardMaterial.LightmapEnabled = true;
         return StandardMaterial;
     })(BABYLON.Material);
     BABYLON.StandardMaterial = StandardMaterial;

+ 24 - 3
src/Materials/babylon.standardMaterial.ts

@@ -72,6 +72,7 @@
         public ROUGHNESS = false;
         public EMISSIVEASILLUMINATION = false;
         public REFLECTIONFRESNELFROMSPECULAR = false;
+        public LIGHTMAP = false;
 
         _keys: string[];
 
@@ -139,6 +140,7 @@
         public emissiveTexture: BaseTexture;
         public specularTexture: BaseTexture;
         public bumpTexture: BaseTexture;
+        public lightmapTexture: BaseTexture;
 
         public ambientColor = new Color3(0, 0, 0);
         public diffuseColor = new Color3(1, 1, 1);
@@ -153,6 +155,8 @@
 
         public roughness = 0;
 
+        public lightmapThreshold = 0;
+
         public diffuseFresnelParameters: FresnelParameters;
         public opacityFresnelParameters: FresnelParameters;
         public reflectionFresnelParameters: FresnelParameters;
@@ -280,6 +284,15 @@
                     }
                 }
 
+                if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
+                    if (!this.lightmapTexture.isReady()) {
+                        return false;
+                    } else {
+                        needUVs = true;
+                        this._defines.LIGHTMAP = true;
+                    }
+                }
+
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
                     if (!this.specularTexture.isReady()) {
                         return false;
@@ -599,14 +612,14 @@
                         "vLightData2", "vLightDiffuse2", "vLightSpecular2", "vLightDirection2", "vLightGround2", "lightMatrix2",
                         "vLightData3", "vLightDiffuse3", "vLightSpecular3", "vLightDirection3", "vLightGround3", "lightMatrix3",
                         "vFogInfos", "vFogColor", "pointSize",
-                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos",
+                        "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos", "vLightmapInfos",
                         "mBones",
-                        "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix",
+                        "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix", "lightmapMatrix",
                         "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
                         "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor",
                         "roughness"
                     ],
-                    ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler",
+                    ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler", "lightmapSampler",
                         "shadowSampler0", "shadowSampler1", "shadowSampler2", "shadowSampler3"
                     ],
                     join, fallbacks, this.onCompiled, this.onError);
@@ -712,6 +725,13 @@
                     this._effect.setMatrix("emissiveMatrix", this.emissiveTexture.getTextureMatrix());
                 }
 
+                if (this.lightmapTexture && StandardMaterial.LightmapEnabled) {
+                    this._effect.setTexture("lightmapSampler", this.lightmapTexture);
+
+                    this._effect.setFloat3("vLightmapInfos", this.lightmapTexture.coordinatesIndex, this.lightmapTexture.level, this.lightmapThreshold);
+                    this._effect.setMatrix("lightmapMatrix", this.lightmapTexture.getTextureMatrix());
+                }
+
                 if (this.specularTexture && StandardMaterial.SpecularTextureEnabled) {
                     this._effect.setTexture("specularSampler", this.specularTexture);
 
@@ -943,5 +963,6 @@
         public static SpecularTextureEnabled = true;
         public static BumpTextureEnabled = true;
         public static FresnelEnabled = true;
+        public static LightmapEnabled = true;
     }
 } 

+ 7 - 2
src/Math/babylon.math.js

@@ -2047,9 +2047,14 @@ var BABYLON;
             // Z axis
             target.subtractToRef(eye, this._zAxis);
             this._zAxis.normalize();
-            // X axis
+            // X axis            
             Vector3.CrossToRef(up, this._zAxis, this._xAxis);
-            this._xAxis.normalize();
+            if (this._xAxis.lengthSquared() === 0) {
+                this._xAxis.x = 1.0;
+            }
+            else {
+                this._xAxis.normalize();
+            }
             // Y axis
             Vector3.CrossToRef(this._zAxis, this._xAxis, this._yAxis);
             this._yAxis.normalize();

+ 7 - 2
src/Math/babylon.math.ts

@@ -2578,9 +2578,14 @@
             target.subtractToRef(eye, this._zAxis);
             this._zAxis.normalize();
 
-            // X axis
+            // X axis            
             Vector3.CrossToRef(up, this._zAxis, this._xAxis);
-            this._xAxis.normalize();
+
+            if (this._xAxis.lengthSquared() === 0) {
+                this._xAxis.x = 1.0;
+            } else {
+                this._xAxis.normalize();
+            }
 
             // Y axis
             Vector3.CrossToRef(this._zAxis, this._xAxis, this._yAxis);

+ 5 - 4
src/Mesh/babylon.abstractMesh.js

@@ -226,8 +226,9 @@ var BABYLON;
                 this.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
                 this.rotation = BABYLON.Vector3.Zero();
             }
+            var rotationQuaternion;
             if (!space || space === BABYLON.Space.LOCAL) {
-                var rotationQuaternion = BABYLON.Quaternion.RotationAxis(axis, amount);
+                rotationQuaternion = BABYLON.Quaternion.RotationAxis(axis, amount);
                 this.rotationQuaternion = this.rotationQuaternion.multiply(rotationQuaternion);
             }
             else {
@@ -445,7 +446,7 @@ var BABYLON;
                     localPosition.addInPlace(this.parent.position);
                     BABYLON.Matrix.TranslationToRef(localPosition.x, localPosition.y, localPosition.z, this._localTranslation);
                 }
-                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) != AbstractMesh.BILLBOARDMODE_ALL) {
+                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
                     if (this.billboardMode & AbstractMesh.BILLBOARDMODE_X)
                         zero.x = localPosition.x + BABYLON.Engine.Epsilon;
                     if (this.billboardMode & AbstractMesh.BILLBOARDMODE_Y)
@@ -568,7 +569,7 @@ var BABYLON;
         AbstractMesh.prototype.setPhysicsState = function (impostor, options) {
             var physicsEngine = this.getScene().getPhysicsEngine();
             if (!physicsEngine) {
-                return;
+                return null;
             }
             impostor = impostor || BABYLON.PhysicsEngine.NoImpostor;
             if (impostor.impostor) {
@@ -578,7 +579,7 @@ var BABYLON;
             }
             if (impostor === BABYLON.PhysicsEngine.NoImpostor) {
                 physicsEngine._unregisterMesh(this);
-                return;
+                return null;
             }
             if (!options) {
                 options = { mass: 0, friction: 0.2, restitution: 0.2 };

+ 5 - 5
src/Mesh/babylon.abstractMesh.ts

@@ -232,9 +232,9 @@
                 this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
                 this.rotation = Vector3.Zero();
             }
-
+            var rotationQuaternion: Quaternion;
             if (!space || space === Space.LOCAL) {
-                var rotationQuaternion = Quaternion.RotationAxis(axis, amount);
+                rotationQuaternion = Quaternion.RotationAxis(axis, amount);
                 this.rotationQuaternion = this.rotationQuaternion.multiply(rotationQuaternion);
             }
             else {
@@ -493,7 +493,7 @@
                     Matrix.TranslationToRef(localPosition.x, localPosition.y, localPosition.z, this._localTranslation);
                 }
 
-                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) != AbstractMesh.BILLBOARDMODE_ALL) {
+                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
                     if (this.billboardMode & AbstractMesh.BILLBOARDMODE_X)
                         zero.x = localPosition.x + Engine.Epsilon;
                     if (this.billboardMode & AbstractMesh.BILLBOARDMODE_Y)
@@ -650,7 +650,7 @@
             var physicsEngine = this.getScene().getPhysicsEngine();
 
             if (!physicsEngine) {
-                return;
+                return null;
             }
 
             impostor = impostor || PhysicsEngine.NoImpostor;
@@ -663,7 +663,7 @@
 
             if (impostor === PhysicsEngine.NoImpostor) {
                 physicsEngine._unregisterMesh(this);
-                return;
+                return null;
             }
 
             if (!options) {

+ 20 - 0
src/Shaders/default.fragment.fx

@@ -131,6 +131,12 @@ uniform vec2 vEmissiveInfos;
 uniform sampler2D emissiveSampler;
 #endif
 
+#ifdef LIGHTMAP
+varying vec2 vLightmapUV;
+uniform vec3 vLightmapInfos;
+uniform sampler2D lightmapSampler;
+#endif
+
 #if defined(SPECULAR) && defined(SPECULARTERM)
 varying vec2 vSpecularUV;
 uniform vec2 vSpecularInfos;
@@ -812,6 +818,20 @@ void main(void) {
     vec4 color = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor, alpha);
 #endif
 
+#ifdef LIGHTMAP
+	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV).rgb * vLightmapInfos.y;
+	float lightmapIllum = clamp(dot(lightmapColor, vec3(0.3, 0.59, 0.11)), 0., 1.);
+
+	if (lightmapIllum > vLightmapInfos.z)
+	{
+		color.rgb += lightmapColor;
+	}
+	else
+	{
+		color.rgb *= lightmapColor;
+	}
+#endif
+
 #ifdef FOG
 	float fog = CalcFogFactor();
 	color.rgb = fog * color.rgb + (1.0 - fog) * vFogColor;

+ 17 - 0
src/Shaders/default.vertex.fx

@@ -59,6 +59,12 @@ uniform vec2 vEmissiveInfos;
 uniform mat4 emissiveMatrix;
 #endif
 
+#ifdef LIGHTMAP
+varying vec2 vLightmapUV;
+uniform vec3 vLightmapInfos;
+uniform mat4 lightmapMatrix;
+#endif
+
 #if defined(SPECULAR) && defined(SPECULARTERM)
 varying vec2 vSpecularUV;
 uniform vec2 vSpecularInfos;
@@ -208,6 +214,17 @@ void main(void) {
 	}
 #endif
 
+#ifdef LIGHTMAP
+	if (vLightmapInfos.x == 0.)
+	{
+		vLightmapUV = vec2(lightmapMatrix * vec4(uv, 1.0, 0.0));
+	}
+	else
+	{
+		vLightmapUV = vec2(lightmapMatrix * vec4(uv2, 1.0, 0.0));
+	}
+#endif
+
 #if defined(SPECULAR) && defined(SPECULARTERM)
 	if (vSpecularInfos.x == 0.)
 	{

+ 4 - 0
src/Tools/babylon.sceneSerializer.js

@@ -236,6 +236,10 @@ var BABYLON;
         if (material.emissiveTexture) {
             serializationObject.emissiveTexture = serializeTexture(material.emissiveTexture);
         }
+        if (material.lightmapTexture) {
+            serializationObject.lightmapTexture = serializeTexture(material.lightmapTexture);
+            serializationObject.lightmapThreshold = material.lightmapThreshold;
+        }
         if (material.emissiveFresnelParameters) {
             serializationObject.emissiveFresnelParameters = serializeFresnelParameter(material.emissiveFresnelParameters);
         }

+ 5 - 0
src/Tools/babylon.sceneSerializer.ts

@@ -268,6 +268,11 @@
             serializationObject.emissiveTexture = serializeTexture(material.emissiveTexture);
         }
 
+        if (material.lightmapTexture) {
+            serializationObject.lightmapTexture = serializeTexture(material.lightmapTexture);
+            serializationObject.lightmapThreshold = material.lightmapThreshold;
+        }
+
         if (material.emissiveFresnelParameters) {
             serializationObject.emissiveFresnelParameters = serializeFresnelParameter(material.emissiveFresnelParameters);
         }

+ 6 - 3
src/babylon.engine.js

@@ -617,7 +617,7 @@ var BABYLON;
         });
         Object.defineProperty(Engine, "Version", {
             get: function () {
-                return "2.2.0-beta";
+                return "2.3.0-alpha";
             },
             enumerable: true,
             configurable: true
@@ -1202,12 +1202,15 @@ var BABYLON;
             this._gl.uniform4f(uniform, color3.r, color3.g, color3.b, alpha);
         };
         // States
-        Engine.prototype.setState = function (culling, zOffset, force) {
+        Engine.prototype.setState = function (culling, zOffset, force, reverseSide) {
             if (zOffset === void 0) { zOffset = 0; }
+            if (reverseSide === void 0) { reverseSide = false; }
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
-                    this._depthCullingState.cullFace = this.cullBackFaces ? this._gl.BACK : this._gl.FRONT;
+                    var showSide = reverseSide ? this._gl.FRONT : this._gl.BACK;
+                    var hideSide = reverseSide ? this._gl.BACK : this._gl.FRONT;
+                    this._depthCullingState.cullFace = this.cullBackFaces ? showSide : hideSide;
                     this._depthCullingState.cull = true;
                 }
                 else {

+ 5 - 3
src/babylon.engine.ts

@@ -481,7 +481,7 @@
         }
 
         public static get Version(): string {
-            return "2.2.0-beta";
+            return "2.3.0-alpha";
         }
 
         // Updatable statics so stick with vars here
@@ -1429,11 +1429,13 @@
         }
 
         // States
-        public setState(culling: boolean, zOffset: number = 0, force?: boolean): void {
+        public setState(culling: boolean, zOffset: number = 0, force?: boolean, reverseSide = false): void {
             // Culling        
             if (this._depthCullingState.cull !== culling || force) {
                 if (culling) {
-                    this._depthCullingState.cullFace = this.cullBackFaces ? this._gl.BACK : this._gl.FRONT;
+                    var showSide = reverseSide ? this._gl.FRONT : this._gl.BACK;
+                    var hideSide = reverseSide ? this._gl.BACK : this._gl.FRONT;
+                    this._depthCullingState.cullFace = this.cullBackFaces ? showSide : hideSide;
                     this._depthCullingState.cull = true;
                 } else {
                     this._depthCullingState.cull = false;