Browse Source

Merge remote-tracking branch 'upstream/master' into AlignScaleDesignMode

nockawa 8 năm trước cách đây
mục cha
commit
13944bd56b
49 tập tin đã thay đổi với 3943 bổ sung3011 xóa
  1. 16 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonDirectionalLight.cs
  2. 4 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs
  3. 11 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs
  4. 3 3
      Tools/Gulp/package.json
  5. 85 31
      canvas2D/src/Engine/babylon.fontTexture.ts
  6. 18 6
      canvas2D/src/Engine/babylon.text2d.ts
  7. 26 26
      dist/preview release/babylon.core.js
  8. 1061 1010
      dist/preview release/babylon.d.ts
  9. 35 35
      dist/preview release/babylon.js
  10. 496 249
      dist/preview release/babylon.max.js
  11. 1061 1010
      dist/preview release/babylon.module.d.ts
  12. 34 34
      dist/preview release/babylon.noworker.js
  13. 8 1
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  14. 87 36
      dist/preview release/canvas2D/babylon.canvas2d.js
  15. 11 11
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  16. 249 249
      dist/preview release/inspector/babylon.inspector.bundle.js
  17. 1 1
      dist/preview release/inspector/babylon.inspector.min.js
  18. 1 4
      dist/preview release/loaders/babylon.glTFFileLoader.js
  19. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  20. 1 1
      dist/preview release/materialsLibrary/babylon.furMaterial.min.js
  21. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  22. 5 2
      dist/preview release/what's new.md
  23. 3 0
      localDev/index.html
  24. 2 1
      materialsLibrary/src/fur/fur.fragment.fx
  25. 8 1
      src/Bones/babylon.bone.ts
  26. 163 21
      src/Bones/babylon.boneLookController.ts
  27. 11 1
      src/Bones/babylon.skeleton.ts
  28. 3 3
      src/Cameras/babylon.camera.ts
  29. 1 1
      src/Layer/babylon.highlightlayer.ts
  30. 1 1
      src/Layer/babylon.layer.ts
  31. 1 1
      src/LensFlare/babylon.lensFlareSystem.ts
  32. 5 0
      src/Lights/Shadows/babylon.shadowGenerator.ts
  33. 1 1
      src/Lights/babylon.light.ts
  34. 2 1
      src/Materials/Textures/babylon.baseTexture.ts
  35. 14 1
      src/Materials/Textures/babylon.renderTargetTexture.ts
  36. 2 2
      src/Materials/babylon.material.ts
  37. 85 22
      src/Math/babylon.math.ts
  38. 61 42
      src/Mesh/babylon.abstractMesh.ts
  39. 33 0
      src/Mesh/babylon.groundMesh.ts
  40. 146 1
      src/Mesh/babylon.mesh.ts
  41. 1 1
      src/Particles/babylon.particleSystem.ts
  42. 6 4
      src/Particles/babylon.solidParticleSystem.ts
  43. 8 0
      src/Probes/babylon.reflectionProbe.ts
  44. 11 126
      src/Tools/babylon.sceneSerializer.ts
  45. 6 6
      src/Tools/babylon.tools.ts
  46. 137 55
      src/babylon.engine.ts
  47. 13 4
      src/babylon.mixins.ts
  48. 1 1
      src/babylon.node.ts
  49. 2 2
      src/babylon.scene.ts

+ 16 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonDirectionalLight.cs

@@ -0,0 +1,16 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonDirectionalLight : BabylonLight
+    {
+        [DataMember]
+        public float shadowOrthoScale { get; set; }
+
+        public BabylonDirectionalLight()
+        {
+            shadowOrthoScale = 0.5f;
+        }
+    }
+}

+ 4 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs

@@ -152,6 +152,9 @@ namespace BabylonExport.Entities
         public bool useRadianceOverAlpha { get; set; }
 
         [DataMember]
+        public bool usePhysicalLightFalloff { get; set; }
+
+        [DataMember]
         public float indexOfRefraction { get; set; }
 
         [DataMember]
@@ -179,6 +182,7 @@ namespace BabylonExport.Entities
             indexOfRefraction = 0.66f;
             useRadianceOverAlpha = true;
             useSpecularOverAlpha = true;
+            usePhysicalLightFalloff = true;
             useEmissiveAsIllumination = false;
 
             // Default Null Metallic Workflow

+ 11 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs

@@ -24,6 +24,9 @@ namespace BabylonExport.Entities
         public bool useBlurVarianceShadowMap { get; set; }
 
         [DataMember]
+        public float darkness { get; set; }
+
+        [DataMember]
         public float blurScale { get; set; }
 
         [DataMember]
@@ -35,5 +38,13 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool forceBackFacesOnly { get; set; }
 
+        public BabylonShadowGenerator()
+        {
+            darkness = 0;
+            blurScale = 2;
+            blurBoxOffset = 0;
+            bias = 0.00005f;
+            forceBackFacesOnly = false;
+        }
     }
 }

+ 3 - 3
Tools/Gulp/package.json

@@ -8,10 +8,10 @@
   "license": "(Apache-2.0)",
   "devDependencies": {
     "gulp": "^3.8.11",
-    "gulp-uglify": "~1.5.3",
+    "gulp-uglify": "^2.0.1",
     "gulp-sourcemaps": "~1.9.1",
-    "typescript": "^2.1.4",
-    "gulp-typescript": "^3.1.3",
+    "typescript": "~2.1.4",
+    "gulp-typescript": "^3.1.5",
     "through2": "~0.6.5",
     "gulp-util": "~3.0.4",
     "gulp-concat": "~2.5.2",

+ 85 - 31
canvas2D/src/Engine/babylon.fontTexture.ts

@@ -14,6 +14,10 @@
          */
         bottomRightUV: Vector2;
 
+        xOffset: number;
+        yOffset: number;
+        xAdvance: number;
+
         charWidth: number;
     }
 
@@ -154,7 +158,8 @@
         textureSize : Size;
         atlasName   : string;
         padding     : Vector4;       // Left, Top, Right, Bottom
-        lineHeight: number;
+        lineHeight  : number;
+        baseLine    : number;
         textureUrl  : string;
         textureFile : string;
     }
@@ -331,6 +336,7 @@
         private _xMargin: number;
         private _yMargin: number;
         private _offset: number;
+        private _baseLine: number;
         private _currentFreePosition: Vector2;
         private _curCharCount = 0;
         private _lastUpdateCharCount = -1;
@@ -339,6 +345,7 @@
         private _sdfContext: CanvasRenderingContext2D;
         private _sdfScale: number;
         private _usedCounter = 1;
+        public debugMode: boolean;
 
         get isDynamicFontTexture(): boolean {
             return true;
@@ -389,6 +396,7 @@
             super(null, scene, true, false, samplingMode);
 
             this.name = name;
+            this.debugMode = false;
 
             this.wrapU = Texture.CLAMP_ADDRESSMODE;
             this.wrapV = Texture.CLAMP_ADDRESSMODE;
@@ -397,7 +405,7 @@
             this._signedDistanceField = signedDistanceField;
             this._superSample = false;
 
-            // SDF will use supersample no matter what, the resolution is otherwise too poor to produce correct result
+            // SDF will use super sample no matter what, the resolution is otherwise too poor to produce correct result
             if (superSample || signedDistanceField) {
                 let sfont = this.getSuperSampleFont(font);
                 if (sfont) {
@@ -413,19 +421,22 @@
             this._context.fillStyle = "white";
             this._context.textBaseline = "top";
 
-            var res = this.getFontHeight(font);
+            var res = this.getFontHeight(font, "j$|");
             this._lineHeightSuper = res.height; //+4;
             this._lineHeight = this._superSample ? (Math.ceil(this._lineHeightSuper / 2)) : this._lineHeightSuper;
-            this._offset = res.offset - 1;
-            this._xMargin = 1 + Math.ceil(this._lineHeightSuper / 15);    // Right now this empiric formula seems to work...
-            this._yMargin = this._xMargin;
+            this._offset = res.offset;
+            res = this.getFontHeight(font, "f");
+            this._baseLine = res.height + res.offset - this._offset;
 
-            var maxCharWidth = this._context.measureText("W").width;
+            var maxCharWidth = Math.max(this._context.measureText("W").width, this._context.measureText("_").width);
             this._spaceWidthSuper = this._context.measureText(" ").width;
             this._spaceWidth = this._superSample ? (this._spaceWidthSuper / 2) : this._spaceWidthSuper;
 
+            this._xMargin = Math.ceil(maxCharWidth / 32);
+            this._yMargin = this._xMargin;
+
             // This is an approximate size, but should always be able to fit at least the maxCharCount
-            var totalEstSurface = (this._lineHeightSuper + this._yMargin) * (maxCharWidth + this._xMargin) * maxCharCount;
+            var totalEstSurface = (Math.ceil(this._lineHeightSuper) + (this._yMargin*2)) * (Math.ceil(maxCharWidth) + (this._xMargin*2)) * maxCharCount;
             var edge = Math.sqrt(totalEstSurface);
             var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
 
@@ -447,13 +458,13 @@
             this._context.clearRect(0, 0, textureSize.width, textureSize.height);
 
             // Create a canvas for the signed distance field mode, we only have to store one char, the purpose is to render a char scaled _sdfScale times
-            //  into this 2D context, then get the bitmap data, create the sdf char and push the result in the _context (which hold the whole Font Texture content)
+            //  into this 2D context, then get the bitmap data, create the SDF char and push the result in the _context (which hold the whole Font Texture content)
             // So you can see this context as an intermediate one, because it is.
             if (this._signedDistanceField) {
                 let sdfC = document.createElement("canvas");
                 let s = this._sdfScale;
-                sdfC.width = maxCharWidth * s;
-                sdfC.height = this._lineHeightSuper * s;
+                sdfC.width = (Math.ceil(maxCharWidth) + this._xMargin * 2) * s;
+                sdfC.height = (Math.ceil(this._lineHeightSuper) + this._yMargin * 2) * s;
                 let sdfCtx = sdfC.getContext("2d");
                 sdfCtx.scale(s, s);
                 sdfCtx.textBaseline = "top";
@@ -497,10 +508,10 @@
             var textureSize = this.getSize();
 
             // we reached the end of the current line?
-            let width = Math.round(measure.width);
+            let width = Math.ceil(measure.width);
             if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
-                this._currentFreePosition.y += this._lineHeightSuper + this._yMargin;
+                this._currentFreePosition.y += Math.ceil(this._lineHeightSuper + this._yMargin*2);
 
                 // No more room?
                 if (this._currentFreePosition.y > textureSize.height) {
@@ -508,22 +519,54 @@
                 }
             }
 
-            // In sdf mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the sdf map accurately)
+            let curPosX = this._currentFreePosition.x + 0.5;
+            let curPosY = this._currentFreePosition.y + 0.5;
+            let curPosXMargin = curPosX + this._xMargin;
+            let curPosYMargin = curPosY + this._yMargin;
+
+            let drawDebug = (ctx: CanvasRenderingContext2D) => {
+                ctx.strokeStyle = "green";
+                ctx.beginPath();
+                ctx.rect(curPosXMargin, curPosYMargin, width, this._lineHeightSuper);
+                ctx.closePath();
+                ctx.stroke();
+
+                ctx.strokeStyle = "blue";
+                ctx.beginPath();
+                ctx.moveTo(curPosXMargin, curPosYMargin + Math.round(this._baseLine));
+                ctx.lineTo(curPosXMargin + width, curPosYMargin + Math.round(this._baseLine));
+                ctx.closePath();
+                ctx.stroke();
+            }
+
+            // In SDF mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the SDF map accurately)
             if (this._signedDistanceField) {
+                let s = this._sdfScale;
                 this._sdfContext.clearRect(0, 0, this._sdfCanvas.width, this._sdfCanvas.height);
-                this._sdfContext.fillText(char, 0, -this._offset);
-                let data = this._sdfContext.getImageData(0, 0, width*this._sdfScale, this._sdfCanvas.height);
 
+                // Coordinates are subject to the context's scale
+                this._sdfContext.fillText(char, this._xMargin + 0.5, this._yMargin + 0.5 - this._offset);
+
+                // Canvas Pixel Coordinates, no scale
+                let data = this._sdfContext.getImageData(0, 0, (width + (this._xMargin * 2)) * s, this._sdfCanvas.height);
                 let res = this._computeSDFChar(data);
-                this._context.putImageData(res, this._currentFreePosition.x, this._currentFreePosition.y);
+                this._context.putImageData(res, curPosX, curPosY);
+                if (this.debugMode) {
+                    drawDebug(this._context);
+                }
             } else {
+                if (this.debugMode) {
+                    drawDebug(this._context);
+                }
+
                 // Draw the character in the HTML canvas
-                this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+                this._context.fillText(char, curPosXMargin, curPosYMargin - this._offset);
             }
 
             // Fill the CharInfo object
-            info.topLeftUV = new Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
-            info.bottomRightUV = new Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
+            info.topLeftUV = new Vector2((curPosXMargin) / textureSize.width, (this._currentFreePosition.y + this._yMargin) / textureSize.height);
+            info.bottomRightUV = new Vector2((curPosXMargin + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + this._yMargin) / textureSize.height));
+            info.yOffset = info.xOffset = 0;
 
             if (this._signedDistanceField) {
                 let off = 1/textureSize.width;
@@ -532,13 +575,14 @@
             }
 
             info.charWidth = this._superSample ? (width/2) : width;
+            info.xAdvance = info.charWidth;
 
             // Add the info structure
             this._charInfos.add(char, info);
             this._curCharCount++;
 
             // Set the next position
-            this._currentFreePosition.x += width + this._xMargin;
+            this._currentFreePosition.x += Math.ceil(width + this._xMargin*2);
 
             return info;
         }
@@ -710,7 +754,7 @@
         }
 
         // More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
-        private getFontHeight(font: string): {height: number, offset: number} {
+        private getFontHeight(font: string, chars: string): {height: number, offset: number} {
             var fontDraw = document.createElement("canvas");
             fontDraw.width = 600;
             fontDraw.height = 600;
@@ -719,7 +763,7 @@
             ctx.textBaseline = 'top';
             ctx.fillStyle = 'white';
             ctx.font = font;
-            ctx.fillText('jH|', 0, 0);
+            ctx.fillText(chars, 0, 0);
             var pixels = ctx.getImageData(0, 0, fontDraw.width, fontDraw.height).data;
             var start = -1;
             var end = -1;
@@ -742,7 +786,7 @@
                     }
                 }
             }
-            return { height: (end - start)+1, offset: start-1}
+            return { height: (end - start)+1, offset: start}
         }
 
         public get canRescale(): boolean {
@@ -827,13 +871,15 @@
             return obj;
         }
 
-        private _buildCharInfo(initialLine: string, obj: any, textureSize: Size, invertY: boolean, chars: StringDictionary<CharInfo>) {
+        private _buildCharInfo(bfi: BitmapFontInfo, initialLine: string, obj: any, textureSize: Size, invertY: boolean, chars: StringDictionary<CharInfo>) {
             let char: string = null;
             let x: number = null;
             let y: number = null;
-            let xadv: number = null;
             let width: number = null;
             let height: number = null;
+            let xoffset = 0;
+            let yoffset = 0;
+            let xadvance = 0;
             let ci = new CharInfo();
             for (let key in obj) {
                 let value = obj[key];
@@ -854,15 +900,22 @@
                         height = value;
                         break;
                     case "xadvance":
-                        xadv = value;
+                        xadvance = value;
+                        break;
+                    case "xoffset":
+                        xoffset = value;
+                        break;
+                    case "yoffset":
+                        yoffset = value;
                         break;
                 }
             }
 
             if (x != null && y != null && width != null && height != null && char != null) {
-                if (xadv) {
-                    width = xadv;
-                }
+                ci.xAdvance = xadvance;
+                ci.xOffset = xoffset;
+                ci.yOffset = bfi.lineHeight -height - yoffset;
+
                 if (invertY) {
                     ci.topLeftUV = new Vector2(1 - (x / textureSize.width), 1 - (y / textureSize.height));
                     ci.bottomRightUV = new Vector2(1 - ((x + width) / textureSize.width), 1 - ((y + height) / textureSize.height));
@@ -895,6 +948,7 @@
             //common
             var commonObj = this._parseStrToObj(fontStr.match(BMFontLoaderTxt.COMMON_EXP)[0]);
             bfi.lineHeight = commonObj["lineHeight"];
+            bfi.baseLine = commonObj["base"];
             bfi.textureSize = new Size(commonObj["scaleW"], commonObj["scaleH"]);
 
             var maxTextureSize = scene.getEngine()._gl.getParameter(0xd33);
@@ -918,7 +972,7 @@
                         let charLines = fontStr.match(BMFontLoaderTxt.CHAR_EXP);
                         for (let i = 0, li = charLines.length; i < li; i++) {
                             let charObj = this._parseStrToObj(charLines[i]);
-                            this._buildCharInfo(charLines[i], charObj, bfi.textureSize, invertY, bfi.charDic);
+                            this._buildCharInfo(bfi, charLines[i], charObj, bfi.textureSize, invertY, bfi.charDic);
                         }
 
                         //kerning

+ 18 - 6
canvas2D/src/Engine/babylon.text2d.ts

@@ -226,7 +226,11 @@
             }
             this._text = value;
             this._textSize = null;    // A change of text will reset the TextSize which will be recomputed next time it's used
-            this._size = null;
+
+            if(!this._sizeSetByUser){
+                this._size = null;
+            }
+
             this._updateCharCount();
 
             // Trigger a textSize to for a sizeChange if necessary, which is needed for layout to recompute
@@ -480,7 +484,12 @@
             this._tabulationSize     = (settings.tabulationSize == null) ? 4 : settings.tabulationSize;
             this._textSize           = null;
             this.text                = text;
-            this.size                = (settings.size==null) ? null : settings.size;
+            if(settings.size != null){
+                this.size = settings.size;
+                this._sizeSetByUser = true;
+            }else{
+                this.size = null;
+            }
             this.textAlignmentH      = (settings.textAlignmentH==null) ? Text2D.AlignLeft : settings.textAlignmentH;
             this.textAlignmentV      = (settings.textAlignmentV==null) ? Text2D.AlignTop : settings.textAlignmentV;
             this.textAlignment       = (settings.textAlignment==null) ? "" : settings.textAlignment;
@@ -594,6 +603,7 @@
                 let text = this.text;
                 let tabWidth = this._tabulationSize * texture.spaceWidth;
 
+                // First pass: analyze the text to build data like pixel length of each lines, width of each char, number of char per line
                 for (let i = 0; i < text.length; i++) {
                     let char = text[i];
                     numCharsCurrenLine++;
@@ -624,7 +634,7 @@
                     if (char === "\t") {
                         charWidth = tabWidth;
                     }else{
-                        charWidth = ci.charWidth;
+                        charWidth = ci.xAdvance;
                     }
 
                     offset.x += charWidth;
@@ -637,7 +647,7 @@
                         numCharsCurrentWord = 0;
                         widthCurrentWord = 0;
                     }else {
-                        widthCurrentWord += ci.charWidth;
+                        widthCurrentWord += ci.xAdvance;
                         numCharsCurrentWord++;
                     }
 
@@ -721,6 +731,7 @@
                     offset.y -= lineLengths.length * lh;
                 }
 
+                let lineHeight = texture.lineHeight;
                 for (let i = 0; i < lineLengths.length; i++) {
                     let numChars = charsPerLine[i];
                     let lineLength = lineLengths[i];
@@ -737,8 +748,8 @@
 
                         if(char !== "\t" && !this._isWhiteSpaceCharVert(char)){ 
                             //make sure space char gets processed here or overlapping can occur when text is set
-                            this.updateInstanceDataPart(d, offset);
                             let ci = texture.getChar(char);
+                            this.updateInstanceDataPart(d, new Vector2(offset.x + ci.xOffset, offset.y +ci.yOffset));
                             d.topLeftUV = ci.topLeftUV;
                             let suv = ci.bottomRightUV.subtract(ci.topLeftUV);
                             d.sizeUV = suv;
@@ -753,7 +764,7 @@
                     }
 
                     offset.x = offsetX;
-                    offset.y -= texture.lineHeight;
+                    offset.y -= lineHeight;
                 }
 
             }
@@ -864,6 +875,7 @@
         private _textSize: Size;
         private _wordWrap: boolean;
         private _textAlignment: string;
+        private _sizeSetByUser: boolean;
 
         public textAlignmentH: number;
         public textAlignmentV: number;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 26 - 26
dist/preview release/babylon.core.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1061 - 1010
dist/preview release/babylon.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 35 - 35
dist/preview release/babylon.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 496 - 249
dist/preview release/babylon.max.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1061 - 1010
dist/preview release/babylon.module.d.ts


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 34 - 34
dist/preview release/babylon.noworker.js


+ 8 - 1
dist/preview release/canvas2D/babylon.canvas2d.d.ts

@@ -697,6 +697,9 @@ declare module BABYLON {
          * The normalized ([0;1]) right/bottom position of the character in the texture
          */
         bottomRightUV: Vector2;
+        xOffset: number;
+        yOffset: number;
+        xAdvance: number;
         charWidth: number;
     }
     /**
@@ -767,6 +770,7 @@ declare module BABYLON {
         atlasName: string;
         padding: Vector4;
         lineHeight: number;
+        baseLine: number;
         textureUrl: string;
         textureFile: string;
     }
@@ -823,6 +827,7 @@ declare module BABYLON {
         private _xMargin;
         private _yMargin;
         private _offset;
+        private _baseLine;
         private _currentFreePosition;
         private _curCharCount;
         private _lastUpdateCharCount;
@@ -831,6 +836,7 @@ declare module BABYLON {
         private _sdfContext;
         private _sdfScale;
         private _usedCounter;
+        debugMode: boolean;
         readonly isDynamicFontTexture: boolean;
         static GetCachedFontTexture(scene: Scene, fontName: string, supersample?: boolean, signedDistanceField?: boolean): FontTexture;
         static ReleaseCachedFontTexture(scene: Scene, fontName: string, supersample?: boolean, signedDistanceField?: boolean): void;
@@ -852,7 +858,7 @@ declare module BABYLON {
         getChar(char: string): CharInfo;
         private _computeSDFChar(source);
         private getSuperSampleFont(font);
-        private getFontHeight(font);
+        private getFontHeight(font, chars);
         readonly canRescale: boolean;
         getContext(): CanvasRenderingContext2D;
         /**
@@ -4136,6 +4142,7 @@ declare module BABYLON {
         private _textSize;
         private _wordWrap;
         private _textAlignment;
+        private _sizeSetByUser;
         textAlignmentH: number;
         textAlignmentV: number;
     }

+ 87 - 36
dist/preview release/canvas2D/babylon.canvas2d.js

@@ -2000,12 +2000,13 @@ var BABYLON;
             _this._lastUpdateCharCount = -1;
             _this._usedCounter = 1;
             _this.name = name;
+            _this.debugMode = false;
             _this.wrapU = BABYLON.Texture.CLAMP_ADDRESSMODE;
             _this.wrapV = BABYLON.Texture.CLAMP_ADDRESSMODE;
             _this._sdfScale = 8;
             _this._signedDistanceField = signedDistanceField;
             _this._superSample = false;
-            // SDF will use supersample no matter what, the resolution is otherwise too poor to produce correct result
+            // SDF will use super sample no matter what, the resolution is otherwise too poor to produce correct result
             if (superSample || signedDistanceField) {
                 var sfont = _this.getSuperSampleFont(font);
                 if (sfont) {
@@ -2019,17 +2020,19 @@ var BABYLON;
             _this._context.font = font;
             _this._context.fillStyle = "white";
             _this._context.textBaseline = "top";
-            var res = _this.getFontHeight(font);
+            var res = _this.getFontHeight(font, "j$|");
             _this._lineHeightSuper = res.height; //+4;
             _this._lineHeight = _this._superSample ? (Math.ceil(_this._lineHeightSuper / 2)) : _this._lineHeightSuper;
-            _this._offset = res.offset - 1;
-            _this._xMargin = 1 + Math.ceil(_this._lineHeightSuper / 15); // Right now this empiric formula seems to work...
-            _this._yMargin = _this._xMargin;
-            var maxCharWidth = _this._context.measureText("W").width;
+            _this._offset = res.offset;
+            res = _this.getFontHeight(font, "f");
+            _this._baseLine = res.height + res.offset - _this._offset;
+            var maxCharWidth = Math.max(_this._context.measureText("W").width, _this._context.measureText("_").width);
             _this._spaceWidthSuper = _this._context.measureText(" ").width;
             _this._spaceWidth = _this._superSample ? (_this._spaceWidthSuper / 2) : _this._spaceWidthSuper;
+            _this._xMargin = Math.ceil(maxCharWidth / 32);
+            _this._yMargin = _this._xMargin;
             // This is an approximate size, but should always be able to fit at least the maxCharCount
-            var totalEstSurface = (_this._lineHeightSuper + _this._yMargin) * (maxCharWidth + _this._xMargin) * maxCharCount;
+            var totalEstSurface = (Math.ceil(_this._lineHeightSuper) + (_this._yMargin * 2)) * (Math.ceil(maxCharWidth) + (_this._xMargin * 2)) * maxCharCount;
             var edge = Math.sqrt(totalEstSurface);
             var textSize = Math.pow(2, Math.ceil(Math.log(edge) / Math.log(2)));
             // Create the texture that will store the font characters
@@ -2047,13 +2050,13 @@ var BABYLON;
             _this._context.imageSmoothingEnabled = false;
             _this._context.clearRect(0, 0, textureSize.width, textureSize.height);
             // Create a canvas for the signed distance field mode, we only have to store one char, the purpose is to render a char scaled _sdfScale times
-            //  into this 2D context, then get the bitmap data, create the sdf char and push the result in the _context (which hold the whole Font Texture content)
+            //  into this 2D context, then get the bitmap data, create the SDF char and push the result in the _context (which hold the whole Font Texture content)
             // So you can see this context as an intermediate one, because it is.
             if (_this._signedDistanceField) {
                 var sdfC = document.createElement("canvas");
                 var s = _this._sdfScale;
-                sdfC.width = maxCharWidth * s;
-                sdfC.height = _this._lineHeightSuper * s;
+                sdfC.width = (Math.ceil(maxCharWidth) + _this._xMargin * 2) * s;
+                sdfC.height = (Math.ceil(_this._lineHeightSuper) + _this._yMargin * 2) * s;
                 var sdfCtx = sdfC.getContext("2d");
                 sdfCtx.scale(s, s);
                 sdfCtx.textBaseline = "top";
@@ -2114,6 +2117,7 @@ var BABYLON;
          * @return the CharInfo instance corresponding to the given character
          */
         FontTexture.prototype.getChar = function (char) {
+            var _this = this;
             if (char.length !== 1) {
                 return null;
             }
@@ -2125,41 +2129,69 @@ var BABYLON;
             var measure = this._context.measureText(char);
             var textureSize = this.getSize();
             // we reached the end of the current line?
-            var width = Math.round(measure.width);
+            var width = Math.ceil(measure.width);
             if (this._currentFreePosition.x + width + this._xMargin > textureSize.width) {
                 this._currentFreePosition.x = 0;
-                this._currentFreePosition.y += this._lineHeightSuper + this._yMargin;
+                this._currentFreePosition.y += Math.ceil(this._lineHeightSuper + this._yMargin * 2);
                 // No more room?
                 if (this._currentFreePosition.y > textureSize.height) {
                     return this.getChar("!");
                 }
             }
-            // In sdf mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the sdf map accurately)
+            var curPosX = this._currentFreePosition.x + 0.5;
+            var curPosY = this._currentFreePosition.y + 0.5;
+            var curPosXMargin = curPosX + this._xMargin;
+            var curPosYMargin = curPosY + this._yMargin;
+            var drawDebug = function (ctx) {
+                ctx.strokeStyle = "green";
+                ctx.beginPath();
+                ctx.rect(curPosXMargin, curPosYMargin, width, _this._lineHeightSuper);
+                ctx.closePath();
+                ctx.stroke();
+                ctx.strokeStyle = "blue";
+                ctx.beginPath();
+                ctx.moveTo(curPosXMargin, curPosYMargin + Math.round(_this._baseLine));
+                ctx.lineTo(curPosXMargin + width, curPosYMargin + Math.round(_this._baseLine));
+                ctx.closePath();
+                ctx.stroke();
+            };
+            // In SDF mode we render the character in an intermediate 2D context which scale the character this._sdfScale times (which is required to compute the SDF map accurately)
             if (this._signedDistanceField) {
+                var s = this._sdfScale;
                 this._sdfContext.clearRect(0, 0, this._sdfCanvas.width, this._sdfCanvas.height);
-                this._sdfContext.fillText(char, 0, -this._offset);
-                var data = this._sdfContext.getImageData(0, 0, width * this._sdfScale, this._sdfCanvas.height);
+                // Coordinates are subject to the context's scale
+                this._sdfContext.fillText(char, this._xMargin + 0.5, this._yMargin + 0.5 - this._offset);
+                // Canvas Pixel Coordinates, no scale
+                var data = this._sdfContext.getImageData(0, 0, (width + (this._xMargin * 2)) * s, this._sdfCanvas.height);
                 var res = this._computeSDFChar(data);
-                this._context.putImageData(res, this._currentFreePosition.x, this._currentFreePosition.y);
+                this._context.putImageData(res, curPosX, curPosY);
+                if (this.debugMode) {
+                    drawDebug(this._context);
+                }
             }
             else {
+                if (this.debugMode) {
+                    drawDebug(this._context);
+                }
                 // Draw the character in the HTML canvas
-                this._context.fillText(char, this._currentFreePosition.x, this._currentFreePosition.y - this._offset);
+                this._context.fillText(char, curPosXMargin, curPosYMargin - this._offset);
             }
             // Fill the CharInfo object
-            info.topLeftUV = new BABYLON.Vector2(this._currentFreePosition.x / textureSize.width, this._currentFreePosition.y / textureSize.height);
-            info.bottomRightUV = new BABYLON.Vector2((this._currentFreePosition.x + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + 2) / textureSize.height));
+            info.topLeftUV = new BABYLON.Vector2((curPosXMargin) / textureSize.width, (this._currentFreePosition.y + this._yMargin) / textureSize.height);
+            info.bottomRightUV = new BABYLON.Vector2((curPosXMargin + width) / textureSize.width, info.topLeftUV.y + ((this._lineHeightSuper + this._yMargin) / textureSize.height));
+            info.yOffset = info.xOffset = 0;
             if (this._signedDistanceField) {
                 var off = 1 / textureSize.width;
                 info.topLeftUV.addInPlace(new BABYLON.Vector2(off, off));
                 info.bottomRightUV.addInPlace(new BABYLON.Vector2(off, off));
             }
             info.charWidth = this._superSample ? (width / 2) : width;
+            info.xAdvance = info.charWidth;
             // Add the info structure
             this._charInfos.add(char, info);
             this._curCharCount++;
             // Set the next position
-            this._currentFreePosition.x += width + this._xMargin;
+            this._currentFreePosition.x += Math.ceil(width + this._xMargin * 2);
             return info;
         };
         FontTexture.prototype._computeSDFChar = function (source) {
@@ -2302,7 +2334,7 @@ var BABYLON;
             return newFont;
         };
         // More info here: https://videlais.com/2014/03/16/the-many-and-varied-problems-with-measuring-font-height-for-html5-canvas/
-        FontTexture.prototype.getFontHeight = function (font) {
+        FontTexture.prototype.getFontHeight = function (font, chars) {
             var fontDraw = document.createElement("canvas");
             fontDraw.width = 600;
             fontDraw.height = 600;
@@ -2311,7 +2343,7 @@ var BABYLON;
             ctx.textBaseline = 'top';
             ctx.fillStyle = 'white';
             ctx.font = font;
-            ctx.fillText('jH|', 0, 0);
+            ctx.fillText(chars, 0, 0);
             var pixels = ctx.getImageData(0, 0, fontDraw.width, fontDraw.height).data;
             var start = -1;
             var end = -1;
@@ -2334,7 +2366,7 @@ var BABYLON;
                     }
                 }
             }
-            return { height: (end - start) + 1, offset: start - 1 };
+            return { height: (end - start) + 1, offset: start };
         };
         Object.defineProperty(FontTexture.prototype, "canRescale", {
             get: function () {
@@ -2410,13 +2442,15 @@ var BABYLON;
             }
             return obj;
         };
-        BMFontLoaderTxt.prototype._buildCharInfo = function (initialLine, obj, textureSize, invertY, chars) {
+        BMFontLoaderTxt.prototype._buildCharInfo = function (bfi, initialLine, obj, textureSize, invertY, chars) {
             var char = null;
             var x = null;
             var y = null;
-            var xadv = null;
             var width = null;
             var height = null;
+            var xoffset = 0;
+            var yoffset = 0;
+            var xadvance = 0;
             var ci = new CharInfo();
             for (var key in obj) {
                 var value = obj[key];
@@ -2437,14 +2471,20 @@ var BABYLON;
                         height = value;
                         break;
                     case "xadvance":
-                        xadv = value;
+                        xadvance = value;
+                        break;
+                    case "xoffset":
+                        xoffset = value;
+                        break;
+                    case "yoffset":
+                        yoffset = value;
                         break;
                 }
             }
             if (x != null && y != null && width != null && height != null && char != null) {
-                if (xadv) {
-                    width = xadv;
-                }
+                ci.xAdvance = xadvance;
+                ci.xOffset = xoffset;
+                ci.yOffset = bfi.lineHeight - height - yoffset;
                 if (invertY) {
                     ci.topLeftUV = new BABYLON.Vector2(1 - (x / textureSize.width), 1 - (y / textureSize.height));
                     ci.bottomRightUV = new BABYLON.Vector2(1 - ((x + width) / textureSize.width), 1 - ((y + height) / textureSize.height));
@@ -2476,6 +2516,7 @@ var BABYLON;
             //common
             var commonObj = this._parseStrToObj(fontStr.match(BMFontLoaderTxt_1.COMMON_EXP)[0]);
             bfi.lineHeight = commonObj["lineHeight"];
+            bfi.baseLine = commonObj["base"];
             bfi.textureSize = new BABYLON.Size(commonObj["scaleW"], commonObj["scaleH"]);
             var maxTextureSize = scene.getEngine()._gl.getParameter(0xd33);
             if (commonObj["scaleW"] > maxTextureSize.width || commonObj["scaleH"] > maxTextureSize.height) {
@@ -2500,7 +2541,7 @@ var BABYLON;
                         var charLines = fontStr.match(BMFontLoaderTxt_1.CHAR_EXP);
                         for (var i = 0, li = charLines.length; i < li; i++) {
                             var charObj = this._parseStrToObj(charLines[i]);
-                            this._buildCharInfo(charLines[i], charObj, bfi.textureSize, invertY, bfi.charDic);
+                            this._buildCharInfo(bfi, charLines[i], charObj, bfi.textureSize, invertY, bfi.charDic);
                         }
                         //kerning
                         var kerningLines = fontStr.match(BMFontLoaderTxt_1.KERNING_EXP);
@@ -14328,7 +14369,13 @@ var BABYLON;
             _this._tabulationSize = (settings.tabulationSize == null) ? 4 : settings.tabulationSize;
             _this._textSize = null;
             _this.text = text;
-            _this.size = (settings.size == null) ? null : settings.size;
+            if (settings.size != null) {
+                _this.size = settings.size;
+                _this._sizeSetByUser = true;
+            }
+            else {
+                _this.size = null;
+            }
             _this.textAlignmentH = (settings.textAlignmentH == null) ? Text2D_1.AlignLeft : settings.textAlignmentH;
             _this.textAlignmentV = (settings.textAlignmentV == null) ? Text2D_1.AlignTop : settings.textAlignmentV;
             _this.textAlignment = (settings.textAlignment == null) ? "" : settings.textAlignment;
@@ -14414,7 +14461,9 @@ var BABYLON;
                 }
                 this._text = value;
                 this._textSize = null; // A change of text will reset the TextSize which will be recomputed next time it's used
-                this._size = null;
+                if (!this._sizeSetByUser) {
+                    this._size = null;
+                }
                 this._updateCharCount();
                 // Trigger a textSize to for a sizeChange if necessary, which is needed for layout to recompute
                 var s = this.textSize;
@@ -14634,6 +14683,7 @@ var BABYLON;
                 var numWordsPerLine = 0;
                 var text = this.text;
                 var tabWidth = this._tabulationSize * texture.spaceWidth;
+                // First pass: analyze the text to build data like pixel length of each lines, width of each char, number of char per line
                 for (var i_1 = 0; i_1 < text.length; i_1++) {
                     var char = text[i_1];
                     numCharsCurrenLine++;
@@ -14658,7 +14708,7 @@ var BABYLON;
                         charWidth = tabWidth;
                     }
                     else {
-                        charWidth = ci.charWidth;
+                        charWidth = ci.xAdvance;
                     }
                     offset.x += charWidth;
                     charWidths[i_1] = charWidth;
@@ -14670,7 +14720,7 @@ var BABYLON;
                         widthCurrentWord = 0;
                     }
                     else {
-                        widthCurrentWord += ci.charWidth;
+                        widthCurrentWord += ci.xAdvance;
                         numCharsCurrentWord++;
                     }
                     if (this._wordWrap && numWordsPerLine > 0 && offset.x > contentAreaWidth) {
@@ -14741,6 +14791,7 @@ var BABYLON;
                 else {
                     offset.y -= lineLengths.length * lh;
                 }
+                var lineHeight = texture.lineHeight;
                 for (var i_3 = 0; i_3 < lineLengths.length; i_3++) {
                     var numChars = charsPerLine[i_3];
                     var lineLength = lineLengths[i_3];
@@ -14755,8 +14806,8 @@ var BABYLON;
                         var charWidth = charWidths[charNum];
                         if (char !== "\t" && !this._isWhiteSpaceCharVert(char)) {
                             //make sure space char gets processed here or overlapping can occur when text is set
-                            this.updateInstanceDataPart(d, offset);
                             var ci = texture.getChar(char);
+                            this.updateInstanceDataPart(d, new BABYLON.Vector2(offset.x + ci.xOffset, offset.y + ci.yOffset));
                             d.topLeftUV = ci.topLeftUV;
                             var suv = ci.bottomRightUV.subtract(ci.topLeftUV);
                             d.sizeUV = suv;
@@ -14769,7 +14820,7 @@ var BABYLON;
                         charNum++;
                     }
                     offset.x = offsetX;
-                    offset.y -= texture.lineHeight;
+                    offset.y -= lineHeight;
                 }
             }
             return true;

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 11 - 11
dist/preview release/canvas2D/babylon.canvas2d.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 249 - 249
dist/preview release/inspector/babylon.inspector.bundle.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 4
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -497,9 +497,6 @@ var BABYLON;
         var nodesToRootToAdd = [];
         getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
         newSkeleton.bones = [];
-        if (nodesToRoot.length === 0) {
-            newSkeleton.needInitialSkinMatrix = true;
-        }
         // Joints
         for (var i = 0; i < skins.jointNames.length; i++) {
             var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
@@ -728,11 +725,11 @@ var BABYLON;
             var mat = BABYLON.Matrix.FromArray(node.matrix);
             mat.decompose(scaling, rotation, position);
             configureNode(newNode, position, rotation, scaling);
-            newNode.computeWorldMatrix(true);
         }
         else {
             configureNode(newNode, BABYLON.Vector3.FromArray(node.translation), BABYLON.Quaternion.FromArray(node.rotation), BABYLON.Vector3.FromArray(node.scale));
         }
+        newNode.computeWorldMatrix(true);
     };
     /**
     * Imports a node

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/materialsLibrary/babylon.furMaterial.min.js


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


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

@@ -5,6 +5,7 @@
 ### Major updates
  - WebGL2 context support.WebGL2 is initialized instead of WebGL 1 when available ([deltakosh](https://github.com/deltakosh))
  - Support for [Vertex Array Objects](https://www.opengl.org/registry/specs/ARB/vertex_array_object.txt) ([deltakosh](https://github.com/deltakosh))
+ - Support for multisample render targets. [Demo](http://www.babylonjs-playground.com/#12MKMN) ([deltakosh](https://github.com/deltakosh))
  - New Unity 5 Editor Toolkit. Complete pipeline integration [Doc](TODO) - ([MackeyK24](https://github.com/MackeyK24))
  - New DebugLayer. [Doc](TODO) - ([temechon](https://github.com/temechon))
  - New `VideoTexture.CreateFromWebCam` to generate video texture using WebRTC. [Demo](https://www.babylonjs-playground.com#1R77YT#2) - (Sebastien Vandenberghe)(https://github.com/sebavanmicrosoft) / ([deltakosh](https://github.com/deltakosh))
@@ -25,10 +26,12 @@
 - GroundMesh : `getHeightAtCoordinates()`, `getNormalAtCoordinates()` and `getNormalAtCoordinatesToRef()` can now work with rotated grounds ([jerome](https://github.com/jbousquie))  
 - `GroundMesh`, `facetData` and `SolidParticleSystem` improvement in normal computations ([jerome](https://github.com/jbousquie))   
 - Added `AbstractMesh.addRotation()` ([jerome](https://github.com/jbousquie))  
-- Added `Quaternion.QuaternionRotationFromAxis()` and `Quaternion.QuaternionRotationFromAxisToRef()` ([jerome](https://github.com/jbousquie), thanks to [abow](https://github.com/abow))   
+- Added `Quaternion.RotationQuaternionFromAxis()` and `Quaternion.RotationQuaternionFromAxisToRef()` ([jerome](https://github.com/jbousquie), thanks to [abow](https://github.com/abow))   
 - Added `Curve3.CreateCatmullRomSpline()` ([jerome](https://github.com/jbousquie) and [BitOfGold](https://github.com/BitOfGold))  
 - Added the optional parameter`colorFilter` to `CreateGroundFromHeightMap()` ([jerome](https://github.com/jbousquie))  
-- Improved the internal code of `Vector3.RotationFromAxisToRef()` ([jerome](https://github.com/jbousquie), thanks to [abow](https://github.com/abow))   
+- Improved the internal code of `Vector3.RotationFromAxisToRef()` ([jerome](https://github.com/jbousquie), thanks to [abow](https://github.com/abow))  
+- GroundMeshes are now serialized correctly ([deltakosh](https://github.com/deltakosh))
+- Added `mesh.markVerticesDataAsUpdatable()` to allow a specific vertexbuffer to become updatable ([deltakosh](https://github.com/deltakosh)) 
 
  
 ### Bug fixes

+ 3 - 0
localDev/index.html

@@ -2,6 +2,9 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
 	<title>Local Development</title>
+	
+    <script src="https://babylonjs.azurewebsites.net/cannon.js"></script>
+    <script src="https://babylonjs.azurewebsites.net/Oimo.js"></script>
 	<script src="../assets/refs/dat.gui.min.js"></script>
 	<script src="../tools/DevLoader/BabylonLoader.js"></script>
 

+ 2 - 1
materialsLibrary/src/fur/fur.fragment.fx

@@ -6,6 +6,7 @@ uniform vec4 vDiffuseColor;
 
 // Input
 uniform vec4 furColor;
+uniform float furLength;
 varying vec3 vPositionW;
 varying float vfur_length;
 
@@ -111,7 +112,7 @@ void main(void) {
 	#ifdef HIGHLEVEL
 	vec4 color = vec4(finalDiffuse, alpha);
 	#else
-	float r = vfur_length * 0.5;
+	float r = vfur_length / furLength * 0.5;
 	vec4 color = vec4(finalDiffuse * (0.5 + r), alpha);
 	#endif
 	

+ 8 - 1
src/Bones/babylon.bone.ts

@@ -626,13 +626,20 @@ module BABYLON {
 
             }else{
                 
+                var wm;
+                
+                //mesh.getWorldMatrix() needs to be called before skeleton.computeAbsoluteTransforms()
+                if(mesh){
+                    wm = mesh.getWorldMatrix();
+                }
+                
                 this._skeleton.computeAbsoluteTransforms();
                 
                 var tmat = Tmp.Matrix[0];
 
                 if (mesh) {
                     tmat.copyFrom(this.getAbsoluteTransform());
-                    tmat.multiplyToRef(mesh.getWorldMatrix(), tmat);
+                    tmat.multiplyToRef(wm, tmat);
                 }else{
                     tmat = this.getAbsoluteTransform();
                 }

+ 163 - 21
src/Bones/babylon.boneLookController.ts

@@ -1,6 +1,11 @@
 module BABYLON {
     export class BoneLookController {
 
+        private static _tmpVecs: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];
+        private static _tmpQuat = Quaternion.Identity();
+        private static _tmpMat1 = Matrix.Identity();
+        private static _tmpMat2 = Matrix.Identity();
+
         public target: Vector3;
         public mesh: AbstractMesh;
         public bone: Bone;
@@ -9,16 +14,63 @@ module BABYLON {
         public adjustYaw = 0;
         public adjustPitch = 0;
         public adjustRoll = 0;
+
+        public slerpAmount = 1;
+
+        private _minYaw:number;
+        private _maxYaw:number;
+        private _minPitch:number;
+        private _maxPitch:number;
+        private _minYawSin:number;
+        private _minYawCos:number;
+        private _maxYawSin:number;
+        private _maxYawCos:number;
+        private _minPitchTan:number;
+        private _maxPitchTan:number;
         
-        private _tmpVec1 = Vector3.Zero();
-        private _tmpVec2 = Vector3.Zero();
-        private _tmpVec3 = Vector3.Zero();
-        private _tmpVec4 = Vector3.Zero();
+        private _boneQuat:Quaternion = Quaternion.Identity();
         
-        private _tmpMat1 = Matrix.Identity();
-        private _tmpMat2 = Matrix.Identity();
+        private _slerping = false;
+
+        get minYaw():number{
+            return this._minYaw;
+        }
+
+        set minYaw(value:number){
+            this._minYaw = value;
+            this._minYawSin = Math.sin(value);
+            this._minYawCos = Math.cos(value);
+        }
+
+        get maxYaw():number{
+            return this._maxYaw;
+        }
+
+        set maxYaw(value:number){
+            this._maxYaw = value;
+            this._maxYawSin = Math.sin(value);
+            this._maxYawCos = Math.cos(value);
+        }
+
+        get minPitch():number{
+            return this._minPitch;
+        }
+
+        set minPitch(value:number){
+            this._minPitch = value;
+            this._minPitchTan = Math.tan(value);
+        }
 
-        constructor(mesh: AbstractMesh, bone: Bone, target: Vector3, options?: {adjustYaw?: number, adjustPitch?: number, adjustRoll?: number} ){
+        get maxPitch():number{
+            return this._maxPitch;
+        }
+
+        set maxPitch(value:number){
+            this._maxPitch = value;
+            this._maxPitchTan = Math.tan(value);
+        }
+
+        constructor(mesh: AbstractMesh, bone: Bone, target: Vector3, options?: {adjustYaw?: number, adjustPitch?: number, adjustRoll?: number, slerpAmount?: number, maxYaw?:number, minYaw?:number, maxPitch?:number, minPitch?:number} ){
 
             this.mesh = mesh;
             this.bone = bone;
@@ -38,6 +90,26 @@ module BABYLON {
                     this.adjustRoll = options.adjustRoll;
                 }
 
+                if(options.maxYaw != undefined){
+                    this.maxYaw = options.maxYaw;
+                }
+
+                if(options.minYaw != undefined){
+                    this.minYaw = options.minYaw;
+                }
+
+                if(options.maxPitch != undefined){
+                    this.maxPitch = options.maxPitch;
+                }
+
+                if(options.minPitch != undefined){
+                    this.minPitch = options.minPitch;
+                }
+
+                if(options.slerpAmount != undefined){
+                    this.slerpAmount = options.slerpAmount;
+                }
+
             }
 
         }
@@ -46,33 +118,103 @@ module BABYLON {
                 
             var bone = this.bone;
             var target = this.target;
+            var mat1 = BoneLookController._tmpMat1;
+            var mat2 = BoneLookController._tmpMat2;
+
+            var parentBone = bone.getParent();
+
+            if(parentBone){
+                if(this._maxPitch != undefined || this._minPitch != undefined){
+                    var localTarget = BoneLookController._tmpVecs[4];
+                    var _tmpVec5 = BoneLookController._tmpVecs[5];
+                    parentBone.getLocalPositionFromAbsoluteToRef(target, this.mesh, localTarget);
+                    bone.getPositionToRef(Space.LOCAL, null, _tmpVec5);
+                    localTarget.x -= _tmpVec5.x;
+                    localTarget.y -= _tmpVec5.y;
+                    localTarget.z -= _tmpVec5.z;
+                    var xzlen = Math.sqrt(localTarget.x*localTarget.x + localTarget.z*localTarget.z);
+                    var pitch = Math.atan2(localTarget.y, xzlen);
+
+                    if(pitch > this._maxPitch){
+                        localTarget.y = this._maxPitchTan*xzlen + _tmpVec5.y;
+                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
+                        target = localTarget;
+                    }else if(pitch < this._minPitch){
+                        localTarget.y = this._minPitchTan*xzlen + _tmpVec5.y;
+                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
+                        target = localTarget;
+                    }
+                }
 
-            var bonePos = this._tmpVec1;
-            var zaxis = this._tmpVec2;
-            var xaxis = this._tmpVec3;
-            var yaxis = this._tmpVec4;
-            var mat1 = this._tmpMat1;
-            var mat2 = this._tmpMat2;
+                if(this._maxYaw != undefined || this._minYaw != undefined){
+                    var localTarget = BoneLookController._tmpVecs[6];
+                    var _tmpVec7 = BoneLookController._tmpVecs[7];
+                    parentBone.getLocalPositionFromAbsoluteToRef(target, this.mesh, localTarget);
+                    bone.getPositionToRef(Space.LOCAL, null, _tmpVec7);
+                    localTarget.x -= _tmpVec7.x;
+                    localTarget.z -= _tmpVec7.z;
+                    var yaw = Math.atan2(localTarget.x, localTarget.z);
+                    var xzlen = Math.sqrt(localTarget.x*localTarget.x + localTarget.z*localTarget.z);
+                    
+                    if(yaw > this._maxYaw){
+                        localTarget.z = this._maxYawCos*xzlen;
+                        localTarget.x = this._maxYawSin*xzlen;
+                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
+                        target = localTarget;
+                    }else if(yaw < this._minYaw){
+                        localTarget.z = this._minYawCos*xzlen;
+                        localTarget.x = this._minYawSin*xzlen;
+                        parentBone.getAbsolutePositionFromLocalToRef(localTarget, this.mesh, localTarget);
+                        target = localTarget;
+                    }
+                }
 
-            bone.getAbsolutePositionToRef(this.mesh, bonePos);
+            }
+
+            var bonePos = BoneLookController._tmpVecs[0];
+            var zaxis = BoneLookController._tmpVecs[1];
+            var xaxis = BoneLookController._tmpVecs[2];
+            var yaxis = BoneLookController._tmpVecs[3];
+            var _tmpQuat = BoneLookController._tmpQuat;
 
+            bone.getAbsolutePositionToRef(this.mesh, bonePos);
             target.subtractToRef(bonePos, zaxis);
             zaxis.normalize();
-
-            BABYLON.Vector3.CrossToRef(this.upAxis, zaxis, xaxis);
+            Vector3.CrossToRef(this.upAxis, zaxis, xaxis);
             xaxis.normalize();
-
-            BABYLON.Vector3.CrossToRef(zaxis, xaxis, yaxis);
+            Vector3.CrossToRef(zaxis, xaxis, yaxis);
             yaxis.normalize();
+            Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
+
+            if(xaxis.x === 0 && xaxis.y === 0 && xaxis.z === 0){
+                return;
+            }
 
-            BABYLON.Matrix.FromXYZAxesToRef(xaxis, yaxis, zaxis, mat1);
+            if(yaxis.x === 0 && yaxis.y === 0 && yaxis.z === 0){
+                return;
+            }
+
+            if(zaxis.x === 0 && zaxis.y === 0 && zaxis.z === 0){
+                return;
+            }
 
             if (this.adjustYaw || this.adjustPitch || this.adjustRoll) {
-                BABYLON.Matrix.RotationYawPitchRollToRef(this.adjustYaw, this.adjustPitch, this.adjustRoll, mat2);
+                Matrix.RotationYawPitchRollToRef(this.adjustYaw, this.adjustPitch, this.adjustRoll, mat2);
                 mat2.multiplyToRef(mat1, mat1);
             }
 
-            this.bone.setRotationMatrix(mat1, BABYLON.Space.WORLD, this.mesh);
+            if (this.slerpAmount < 1) {
+                if (!this._slerping) {
+                    this.bone.getRotationQuaternionToRef(Space.WORLD, this.mesh, this._boneQuat);
+                }
+                Quaternion.FromRotationMatrixToRef(mat1, _tmpQuat);
+                Quaternion.SlerpToRef(this._boneQuat, _tmpQuat, this.slerpAmount, this._boneQuat);
+                this.bone.setRotationQuaternion(this._boneQuat, Space.WORLD, this.mesh);
+                this._slerping = true;
+            } else {
+                this.bone.setRotationMatrix(mat1, Space.WORLD, this.mesh);
+                this._slerping = false;
+            }
 
         }
 

+ 11 - 1
src/Bones/babylon.skeleton.ts

@@ -16,10 +16,17 @@
 
         private _lastAbsoluteTransformsUpdateId = -1;
 
+        // Events
+        /**
+         * An event triggered before computing the skeleton's matrices
+         * @type {BABYLON.Observable}
+         */
+        public onBeforeComputeObservable = new Observable<Skeleton>();
+
         constructor(public name: string, public id: string, scene: Scene) {
             this.bones = [];
 
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
 
             scene.skeletons.push(this);
 
@@ -208,6 +215,9 @@
         }
 
         public _computeTransformMatrices(targetMatrix: Float32Array, initialSkinMatrix: Matrix): void {
+
+            this.onBeforeComputeObservable.notifyObservers(this);
+
             for (var index = 0; index < this.bones.length; index++) {
                 var bone = this.bones[index];
                 var parentBone = bone.getParent();

+ 3 - 3
src/Cameras/babylon.camera.ts

@@ -138,10 +138,10 @@
         constructor(name: string, position: Vector3, scene: Scene) {
             super(name, scene);
 
-            scene.addCamera(this);
+            this.getScene().addCamera(this);
 
-            if (!scene.activeCamera) {
-                scene.activeCamera = this;
+            if (!this.getScene().activeCamera) {
+                this.getScene().activeCamera = this;
             }
 
             this.position = position;

+ 1 - 1
src/Layer/babylon.highlightlayer.ts

@@ -249,7 +249,7 @@ module BABYLON {
          * @param options Sets of none mandatory options to use with the layer (see IHighlightLayerOptions for more information)
          */
         constructor(name: string, scene: Scene, options?: IHighlightLayerOptions) {
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             var engine = scene.getEngine();
             this._engine = engine;
             this._maxSize = this._engine.getCaps().maxTextureSize;

+ 1 - 1
src/Layer/babylon.layer.ts

@@ -64,7 +64,7 @@
             this.isBackground = isBackground === undefined ? true : isBackground;
             this.color = color === undefined ? new Color4(1, 1, 1, 1) : color;
 
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             this._scene.layers.push(this);
 
             var engine = scene.getEngine();

+ 1 - 1
src/LensFlare/babylon.lensFlareSystem.ts

@@ -18,7 +18,7 @@
 
         constructor(public name: string, emitter: any, scene: Scene) {
 
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             this._emitter = emitter;
             this.id = name;
             scene.lensFlareSystems.push(this);

+ 5 - 0
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -484,6 +484,7 @@
             serializationObject.useVarianceShadowMap = this.useVarianceShadowMap;
             serializationObject.usePoissonSampling = this.usePoissonSampling;
             serializationObject.forceBackFacesOnly = this.forceBackFacesOnly;
+            serializationObject.darkness = this.getDarkness();
 
             serializationObject.renderList = [];
             for (var meshIndex = 0; meshIndex < this.getShadowMap().renderList.length; meshIndex++) {
@@ -529,6 +530,10 @@
                 shadowGenerator.bias = parsedShadowGenerator.bias;
             }
 
+            if (parsedShadowGenerator.darkness) {
+                shadowGenerator.setDarkness(parsedShadowGenerator.darkness);
+            }
+
             shadowGenerator.forceBackFacesOnly = parsedShadowGenerator.forceBackFacesOnly;
 
             return shadowGenerator;

+ 1 - 1
src/Lights/babylon.light.ts

@@ -94,7 +94,7 @@
          */
         constructor(name: string, scene: Scene) {
             super(name, scene);
-            scene.addLight(this);
+            this.getScene().addLight(this);
         }
         /**
          * Returns the string "Light".  

+ 2 - 1
src/Materials/Textures/babylon.baseTexture.ts

@@ -69,7 +69,7 @@
         private _uid: string;
 
         constructor(scene: Scene) {
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             this._scene.textures.push(this);
             this._uid = null;
         }
@@ -179,6 +179,7 @@
             this.getScene().stopAnimation(this);
 
             // Remove from scene
+            this._scene._removePendingData(this);
             var index = this._scene.textures.indexOf(this);
 
             if (index >= 0) {

+ 14 - 1
src/Materials/Textures/babylon.renderTargetTexture.ts

@@ -99,6 +99,7 @@
         private _currentRefreshId = -1;
         private _refreshRate = 1;
         private _textureMatrix: Matrix;
+        private _samples = 1;
         protected _renderTargetOptions: {
             generateMipMaps: boolean,
             type: number,
@@ -141,6 +142,18 @@
             this._renderingManager = new RenderingManager(scene);
         }
 
+        public get samples(): number {
+            return this._samples;
+        }
+
+        public set samples(value: number) {
+            if (this._samples === value) {
+                return;
+            }
+            
+            this._samples = this.getScene().getEngine().updateRenderTargetTextureSampleCount(this._texture, value);
+        }
+
         public resetRefreshCounter(): void {
             this._currentRefreshId = -1;
         }
@@ -295,7 +308,7 @@
                         continue;
                     }
 
-                    if (this.renderList.indexOf(particleSystem.emitter) >= 0) {
+                    if (currentRenderList.indexOf(particleSystem.emitter) >= 0) {
                         this._renderingManager.dispatchParticles(particleSystem);
                     }
                 }

+ 2 - 2
src/Materials/babylon.material.ts

@@ -213,7 +213,7 @@
             this.name = name;
             this.id = name;
 
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
 
             if (scene.useRightHandedSystem) {
                 this.sideOrientation = Material.ClockWiseSideOrientation;
@@ -222,7 +222,7 @@
             }
 
             if (!doNotAdd) {
-                scene.materials.push(this);
+                this._scene.materials.push(this);
             }
         }
 

+ 85 - 22
src/Math/babylon.math.ts

@@ -1603,10 +1603,10 @@
          * The cross product is then orthogonal to both "left" and "right". 
          */
         public static CrossToRef(left: Vector3, right: Vector3, result: Vector3): void {
-            Tmp.Vector3[0].x = left.y * right.z - left.z * right.y;
-            Tmp.Vector3[0].y = left.z * right.x - left.x * right.z;
-            Tmp.Vector3[0].z = left.x * right.y - left.y * right.x;
-            result.copyFrom(Tmp.Vector3[0]);
+            MathTmp.Vector3[0].x = left.y * right.z - left.z * right.y;
+            MathTmp.Vector3[0].y = left.z * right.x - left.x * right.z;
+            MathTmp.Vector3[0].z = left.x * right.y - left.y * right.x;
+            result.copyFrom(MathTmp.Vector3[0]);
         }
 
         /**
@@ -1735,8 +1735,8 @@
          * The same than RotationFromAxis but updates the passed ref Vector3 parameter instead of returning a new Vector3.  
          */
         public static RotationFromAxisToRef(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Vector3): void {
-            var quat = BABYLON.Tmp.Quaternion[1];
-            Quaternion.QuaternionRotationFromAxisToRef(axis1, axis2, axis3, quat);
+            var quat = MathTmp.Quaternion[0];
+            Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             quat.toEulerAnglesToRef(ref);
         }
     }
@@ -2598,6 +2598,12 @@
             }
         }
         /**
+         * Returns a new Quaternion set to (0.0, 0.0, 0.0).  
+         */
+        public static Zero(): Quaternion {
+            return new Quaternion(0.0, 0.0, 0.0, 0.0);
+        }
+        /**
          * Returns a new Quaternion as the inverted current Quaternion.  
          */
         public static Inverse(q: Quaternion): Quaternion {
@@ -2693,9 +2699,9 @@
          * cf to Vector3.RotationFromAxis() documentation.  
          * Note : axis1, axis2 and axis3 are normalized during this operation.   
          */
-         public static QuaternionRotationFromAxis(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): Quaternion {
+         public static RotationQuaternionFromAxis(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): Quaternion {
             var quat = new Quaternion(0.0, 0.0, 0.0, 0.0);
-            Quaternion.QuaternionRotationFromAxisToRef(axis1, axis2, axis3, quat);
+            Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             return quat;
         }
         /**
@@ -2703,8 +2709,8 @@
          * cf to Vector3.RotationFromAxis() documentation.  
          * Note : axis1, axis2 and axis3 are normalized during this operation.   
          */
-        public static QuaternionRotationFromAxisToRef(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): void {
-            var rotMat = Tmp.Matrix[0];
+        public static RotationQuaternionFromAxisToRef(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): void {
+            var rotMat = MathTmp.Matrix[0];
             BABYLON.Matrix.FromXYZAxesToRef(axis1.normalize(), axis2.normalize(), axis3.normalize(), rotMat);
             BABYLON.Quaternion.FromRotationMatrixToRef(rotMat, ref);
         }
@@ -2915,6 +2921,16 @@
             return this;
         }
         /**
+         * Inserts the translation vector (using 3 x floats) in the current Matrix.  
+         * Returns the updated Matrix.  
+         */
+        public setTranslationFromFloats(x: number, y: number, z: number): Matrix {
+            this.m[12] = x;
+            this.m[13] = y;
+            this.m[14] = z;
+            return this;
+        }
+                /**
          * Inserts the translation vector in the current Matrix.  
          * Returns the updated Matrix.  
          */
@@ -2931,6 +2947,26 @@
             return new Vector3(this.m[12], this.m[13], this.m[14]);
         }
         /**
+         * Fill a Vector3 with the extracted translation from the Matrix.  
+         */
+        public getTranslationToRef(result:Vector3): Matrix {
+            result.x = this.m[12];
+            result.y = this.m[13];
+            result.z = this.m[14];
+
+            return this;
+        }
+        /**
+         * Remove rotation and scaling part from the Matrix. 
+         * Returns the updated Matrix. 
+         */
+        public removeRotationAndScaling(): Matrix {
+            this.setRowFromFloats(0, 1, 0, 0, 0);
+            this.setRowFromFloats(1, 0, 1, 0, 0);
+            this.setRowFromFloats(2, 0, 0, 1, 0);
+            return this;
+        }        
+        /**
          * Returns a new Matrix set with the multiplication result of the current Matrix and the passed one.  
          */
         public multiply(other: Matrix): Matrix {
@@ -3092,9 +3128,9 @@
                 this.m[0] / scale.x, this.m[1] / scale.x, this.m[2] / scale.x, 0,
                 this.m[4] / scale.y, this.m[5] / scale.y, this.m[6] / scale.y, 0,
                 this.m[8] / scale.z, this.m[9] / scale.z, this.m[10] / scale.z, 0,
-                0, 0, 0, 1, Tmp.Matrix[0]);
+                0, 0, 0, 1, MathTmp.Matrix[0]);
 
-            Quaternion.FromRotationMatrixToRef(Tmp.Matrix[0], rotation);
+            Quaternion.FromRotationMatrixToRef(MathTmp.Matrix[0], rotation);
 
             return true;
         }
@@ -3208,6 +3244,22 @@
             this.m[i + 3] = row.w;
             return this;
         }
+        
+        /**
+         * Sets the index-th row of the current matrix with the passed 4 x float values.
+         * Returns the updated Matrix.    
+         */
+        public setRowFromFloats(index: number, x: number, y: number, z: number, w: number): Matrix {
+            if (index < 0 || index > 3) {
+                return this;
+            }
+            var i = index * 4;
+            this.m[i + 0] = x;
+            this.m[i + 1] = y;
+            this.m[i + 2] = z;
+            this.m[i + 3] = w;
+            return this;
+        }
         /**
          * Returns a new Matrix set from the 16 passed floats.  
          */
@@ -3242,19 +3294,25 @@
          * Returns a new Matrix composed by the passed scale (vector3), rotation (quaternion) and translation (vector3).  
          */
         public static Compose(scale: Vector3, rotation: Quaternion, translation: Vector3): Matrix {
-            var result = Matrix.FromValues(scale.x, 0, 0, 0,
+            var result = Matrix.Identity();
+            Matrix.ComposeToRef(scale, rotation, translation, result);
+            return result;
+        }
+
+          /**
+         * Update a Matrix with values composed by the passed scale (vector3), rotation (quaternion) and translation (vector3).  
+         */
+        public static ComposeToRef(scale: Vector3, rotation: Quaternion, translation: Vector3, result: Matrix): void {
+            Matrix.FromValuesToRef(scale.x, 0, 0, 0,
                 0, scale.y, 0, 0,
                 0, 0, scale.z, 0,
-                0, 0, 0, 1);
+                0, 0, 0, 1, MathTmp.Matrix[1]);
 
-            var rotationMatrix = Matrix.Identity();
-            rotation.toRotationMatrix(rotationMatrix);
-            result = result.multiply(rotationMatrix);
+            rotation.toRotationMatrix(MathTmp.Matrix[0]);
+            MathTmp.Matrix[1].multiplyToRef(MathTmp.Matrix[0], result);
 
             result.setTranslation(translation);
-
-            return result;
-        }
+        }      
         /**
          * Returns a new indentity Matrix.  
          */
@@ -4909,11 +4967,16 @@
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
             Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
         public static Vector4: Vector4[] = [Vector4.Zero(), Vector4.Zero(), Vector4.Zero()];  // 3 temp Vector4 at once should be enough
-        public static Quaternion: Quaternion[] = [new Quaternion(0.0, 0.0, 0.0, 0.0), 
-            new Quaternion(0.0, 0.0, 0.0, 0.0)];                // 2 temp Quaternion at once should be enough
+        public static Quaternion: Quaternion[] = [Quaternion.Zero(), Quaternion.Zero()];                // 2 temp Quaternion at once should be enough
         public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero(),
             Matrix.Zero(), Matrix.Zero()];                      // 6 temp Matrices at once should be enough
     }
+    // Same as Tmp but not exported to keep it onyl for math functions to avoid conflicts
+    class MathTmp {
+        public static Vector3: Vector3[] = [Vector3.Zero()];
+        public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero()];
+        public static Quaternion: Quaternion[] = [Quaternion.Zero()];
+    }
 }

+ 61 - 42
src/Mesh/babylon.abstractMesh.ts

@@ -203,7 +203,6 @@
         // Cache
         private _localWorld = Matrix.Zero();
         public _worldMatrix = Matrix.Zero();
-        private _rotateYByPI = Matrix.RotationY(Math.PI);
         private _absolutePosition = Vector3.Zero();
         private _collisionsTransformMatrix = Matrix.Zero();
         private _collisionsScalingMatrix = Matrix.Zero();
@@ -259,7 +258,7 @@
         constructor(name: string, scene: Scene) {
             super(name, scene);
 
-            scene.addMesh(this);
+            this.getScene().addMesh(this);
         }
 
         /**
@@ -869,63 +868,83 @@
             this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
             Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
 
-            // Billboarding
-            if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && this.getScene().activeCamera) {
-                Tmp.Vector3[0].copyFrom(this.position);
-                var localPosition = Tmp.Vector3[0];
-
-                if (this.parent && this.parent.getWorldMatrix) {
-                    this._markSyncedWithParent();
+            // Mesh referal
+            var completeMeshReferalMatrix = Tmp.Matrix[6];
+            if (this._meshToBoneReferal && this.parent && this.parent.getWorldMatrix) {
+                this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), completeMeshReferalMatrix);
+            }
 
-                    var parentMatrix: Matrix;
-                    if (this._meshToBoneReferal) {
-                        this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
-                        parentMatrix = Tmp.Matrix[6];
+            // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
+            if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && this.getScene().activeCamera) {
+                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
+                    // Need to decompose each rotation here
+                    var currentPosition = Tmp.Vector3[3];
+
+                    if (this.parent && this.parent.getWorldMatrix) {
+                        if (this._meshToBoneReferal) {
+                            Vector3.TransformCoordinatesToRef(this.position, completeMeshReferalMatrix, currentPosition);
+                        } else {
+                            Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
+                        }
                     } else {
-                        parentMatrix = this.parent.getWorldMatrix();
+                        currentPosition.copyFrom(this.position);
                     }
 
-                    Vector3.TransformNormalToRef(localPosition, parentMatrix, Tmp.Vector3[1]);
-                    localPosition = Tmp.Vector3[1];
-                }
-
-                var zero = this.getScene().activeCamera.globalPosition.clone();
+                    currentPosition.subtractInPlace(this.getScene().activeCamera.globalPosition);
 
-                if (this.parent && (<any>this.parent).position) {
-                    localPosition.addInPlace((<any>this.parent).position);
-                    Matrix.TranslationToRef(localPosition.x, localPosition.y, localPosition.z, Tmp.Matrix[2]);
-                }
+                    var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X)
+                    {
+                        finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
+                    }
+                    
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y)
+                    {
+                        finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
+                    }
+                    
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z)
+                    {
+                        finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
+                    }
+ 
+                    Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
+                } else {
+                    Tmp.Matrix[1].copyFrom(this.getScene().activeCamera.getViewMatrix());
 
-                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
-                    if (this.billboardMode & AbstractMesh.BILLBOARDMODE_X)
-                        zero.x = localPosition.x + Epsilon;
-                    if (this.billboardMode & AbstractMesh.BILLBOARDMODE_Y)
-                        zero.y = localPosition.y + Epsilon;
-                    if (this.billboardMode & AbstractMesh.BILLBOARDMODE_Z)
-                        zero.z = localPosition.z + Epsilon;
+                    Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+                    Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
                 }
 
-                Matrix.LookAtLHToRef(localPosition, zero, Vector3.Up(), Tmp.Matrix[3]);
-                Tmp.Matrix[3].m[12] = Tmp.Matrix[3].m[13] = Tmp.Matrix[3].m[14] = 0;
-
-                Tmp.Matrix[3].invert();
-
-                Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[3], this._localWorld);
-                this._rotateYByPI.multiplyToRef(this._localWorld, Tmp.Matrix[5]);
+                Tmp.Matrix[1].copyFrom(Tmp.Matrix[5]);
+                Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
             }
 
             // Local world
             Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localWorld);
 
             // Parent
-            if (this.parent && this.parent.getWorldMatrix && this.billboardMode === AbstractMesh.BILLBOARDMODE_NONE) {
+            if (this.parent && this.parent.getWorldMatrix) {
                 this._markSyncedWithParent();
 
-                if (this._meshToBoneReferal) {
-                    this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
-                    Tmp.Matrix[6].multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), this._worldMatrix);
+                if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
+                    if (this._meshToBoneReferal) {
+                        Tmp.Matrix[5].copyFrom(completeMeshReferalMatrix);
+                    } else {
+                        Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
+                    }
+                    
+                    this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
+                    Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
+                    this._worldMatrix.copyFrom(this._localWorld);
+                    this._worldMatrix.setTranslation(Tmp.Vector3[5]);
+                    
                 } else {
-                    this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
+                    if (this._meshToBoneReferal) {
+                        this._localWorld.multiplyToRef(completeMeshReferalMatrix, this._worldMatrix);
+                    } else {
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
+                    }
                 }
             } else {
                 this._worldMatrix.copyFrom(this._localWorld);

+ 33 - 0
src/Mesh/babylon.groundMesh.ts

@@ -229,5 +229,38 @@ module BABYLON {
             }
             return this;
         }
+
+        public serialize(serializationObject: any): void {
+            super.serialize(serializationObject);
+            serializationObject.subdivisionsX = this._subdivisionsX;
+            serializationObject.subdivisionsY = this._subdivisionsY;
+
+            serializationObject.minX = this._minX;
+            serializationObject.maxX = this._maxX;
+
+            serializationObject.minZ = this._minZ;
+            serializationObject.maxZ = this._maxZ;
+
+            serializationObject.width = this._width;
+            serializationObject.height = this._height;
+        }
+
+        public static Parse(parsedMesh: any, scene: Scene): GroundMesh {
+            var result = new GroundMesh(parsedMesh.name, scene);
+
+            result._subdivisionsX = parsedMesh.subdivisionsX || 1;
+            result._subdivisionsY = parsedMesh.subdivisionsY || 1;
+
+            result._minX = parsedMesh.minX;
+            result._maxX = parsedMesh.maxX;
+
+            result._minZ = parsedMesh.minZ;
+            result._maxZ = parsedMesh.maxZ;
+
+            result._width = parsedMesh.width;
+            result._height = parsedMesh.height;
+
+            return result;
+        }
     }
 }

+ 146 - 1
src/Mesh/babylon.mesh.ts

@@ -707,6 +707,14 @@
             return this;
         }
 
+        public markVerticesDataAsUpdatable(kind: string, updatable = true) {
+            if (this.getVertexBuffer(kind).isUpdatable() === updatable) {
+                return;
+            }
+
+            this.setVerticesData(kind, this.getVerticesData(kind), updatable);
+        }
+
         /**
          * Sets the mesh VertexBuffer.  
          * Returns the Mesh.  
@@ -1837,6 +1845,137 @@
             return this;
         }
 
+        public serialize(serializationObject: any): void {
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.type = this.getClassName();
+
+            if (Tags.HasTags(this)) {
+                serializationObject.tags = Tags.GetTags(this);
+            }
+
+            serializationObject.position = this.position.asArray();
+
+            if (this.rotationQuaternion) {
+                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
+            } else if (this.rotation) {
+                serializationObject.rotation = this.rotation.asArray();
+            }
+
+            serializationObject.scaling = this.scaling.asArray();
+            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+
+            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.isVisible = this.isVisible;
+            serializationObject.infiniteDistance = this.infiniteDistance;
+            serializationObject.pickable = this.isPickable;
+
+            serializationObject.receiveShadows = this.receiveShadows;
+
+            serializationObject.billboardMode = this.billboardMode;
+            serializationObject.visibility = this.visibility;
+
+            serializationObject.checkCollisions = this.checkCollisions;
+            serializationObject.isBlocker = this.isBlocker;
+
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+
+            // Geometry
+            var geometry = this._geometry;
+            if (geometry) {
+                var geometryId = geometry.id;
+                serializationObject.geometryId = geometryId;
+
+                // SubMeshes
+                serializationObject.subMeshes = [];
+                for (var subIndex = 0; subIndex < this.subMeshes.length; subIndex++) {
+                    var subMesh = this.subMeshes[subIndex];
+
+                    serializationObject.subMeshes.push({
+                        materialIndex: subMesh.materialIndex,
+                        verticesStart: subMesh.verticesStart,
+                        verticesCount: subMesh.verticesCount,
+                        indexStart: subMesh.indexStart,
+                        indexCount: subMesh.indexCount
+                    });
+                }
+            }
+
+            // Material
+            if (this.material) {
+                serializationObject.materialId = this.material.id;
+            } else {
+                this.material = null;
+            }
+
+            // Skeleton
+            if (this.skeleton) {
+                serializationObject.skeletonId = this.skeleton.id;
+            }
+
+            // Physics
+            //TODO implement correct serialization for physics impostors.
+            if (this.getPhysicsImpostor()) {
+                serializationObject.physicsMass = this.getPhysicsMass();
+                serializationObject.physicsFriction = this.getPhysicsFriction();
+                serializationObject.physicsRestitution = this.getPhysicsRestitution();
+                serializationObject.physicsImpostor = this.getPhysicsImpostor().type;
+            }
+
+            // Metadata
+            if (this.metadata) {
+                serializationObject.metadata = this.metadata;
+            }
+
+            // Instances
+            serializationObject.instances = [];
+            for (var index = 0; index < this.instances.length; index++) {
+                var instance = this.instances[index];
+                var serializationInstance: any = {
+                    name: instance.name,
+                    position: instance.position.asArray(),
+                    scaling: instance.scaling.asArray()
+                };
+                if (instance.rotationQuaternion) {
+                    serializationInstance.rotationQuaternion = instance.rotationQuaternion.asArray();
+                } else if (instance.rotation) {
+                    serializationInstance.rotation = instance.rotation.asArray();
+                }
+                serializationObject.instances.push(serializationInstance);
+
+                // Animations
+                Animation.AppendSerializedAnimations(instance, serializationInstance);
+                serializationInstance.ranges = instance.serializeAnimationRanges();
+            }
+
+            // Animations
+            Animation.AppendSerializedAnimations(this, serializationObject);
+            serializationObject.ranges = this.serializeAnimationRanges();
+
+            // Layer mask
+            serializationObject.layerMask = this.layerMask;
+
+            // Alpha
+            serializationObject.alphaIndex = this.alphaIndex;
+            serializationObject.hasVertexAlpha = this.hasVertexAlpha;
+            
+            // Overlay
+            serializationObject.overlayAlpha = this.overlayAlpha;
+            serializationObject.overlayColor = this.overlayColor.asArray();
+            serializationObject.renderOverlay = this.renderOverlay;
+
+            // Fog
+            serializationObject.applyFog = this.applyFog;
+
+            // Action Manager
+            if (this.actionManager) {
+                serializationObject.actions = this.actionManager.serialize(this.name);
+            }
+        }
+
         // Statics
         /**
          * Returns a new Mesh object what is a deep copy of the passed mesh.   
@@ -1844,7 +1983,13 @@
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {
-            var mesh = new Mesh(parsedMesh.name, scene);
+            var mesh : Mesh;
+
+            if (parsedMesh.type && parsedMesh.type === "GroundMesh") {
+                mesh = GroundMesh.Parse(parsedMesh, scene);
+            } else {
+                mesh = new Mesh(parsedMesh.name, scene);
+            }
             mesh.id = parsedMesh.id;
 
             Tags.AddTagsTo(mesh, parsedMesh.tags);

+ 1 - 1
src/Particles/babylon.particleSystem.ts

@@ -106,7 +106,7 @@
             this.id = name;
             this._capacity = capacity;
 
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
 
             this._customEffect = customEffect;
 

+ 6 - 4
src/Particles/babylon.solidParticleSystem.ts

@@ -129,7 +129,7 @@
         */
         constructor(name: string, scene: Scene, options?: { updatable?: boolean; isPickable?: boolean; particleIntersection?: boolean; boundingSphereOnly?: boolean; bSphereRadiusFactor?: number }) {
             this.name = name;
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             this._camera = <TargetCamera>scene.activeCamera;
             this._pickable = options ? options.isPickable : false;
             this._particlesIntersect = options ? options.particleIntersection : false;
@@ -602,15 +602,17 @@
 
             // if the particles will always face the camera
             if (this.billboard) {
+                this.mesh.computeWorldMatrix(true);
                 // compute the camera position and un-rotate it by the current mesh rotation
                 if (this.mesh._worldMatrix.decompose(this._scale, this._quaternion, this._translation)) {
                     this._quaternionToRotationMatrix();
                     this._rotMatrix.invertToRef(this._invertMatrix);
                     this._camera._currentTarget.subtractToRef(this._camera.globalPosition, this._camDir);
-                    Vector3.TransformCoordinatesToRef(this._camDir, this._invertMatrix, this._cam_axisZ);
+                    Vector3.TransformNormalToRef(this._camDir, this._invertMatrix, this._cam_axisZ);                  
                     this._cam_axisZ.normalize();
-                    // set two orthogonal vectors (_cam_axisX and and _cam_axisY) to the rotated camDir axis (_cam_axisZ)
-                    Vector3.CrossToRef(this._cam_axisZ, this._axisX, this._cam_axisY);
+                    // same for camera up vector extracted from the cam view matrix
+                    var view = this._camera.getViewMatrix(true);
+                    Vector3.TransformNormalFromFloatsToRef(view.m[1], view.m[5], view.m[9], this._invertMatrix, this._cam_axisY);
                     Vector3.CrossToRef(this._cam_axisY, this._cam_axisZ, this._cam_axisX);
                     this._cam_axisY.normalize();
                     this._cam_axisX.normalize();

+ 8 - 0
src/Probes/babylon.reflectionProbe.ts

@@ -59,6 +59,14 @@
             this._projectionMatrix = Matrix.PerspectiveFovLH(Math.PI / 2, 1, scene.activeCamera.minZ, scene.activeCamera.maxZ);
         }
 
+        public get samples(): number {
+            return this._renderTargetTexture.samples;
+        }
+
+        public set samples(value: number) {
+            this._renderTargetTexture.samples = value;
+        }
+
         public get refreshRate(): number {
             return this._renderTargetTexture.refreshRate;
         }

+ 11 - 126
src/Tools/babylon.sceneSerializer.ts

@@ -43,137 +43,18 @@
     var serializeMesh = (mesh: Mesh, serializationScene: any): any => {
         var serializationObject: any = {};
 
-        serializationObject.name = mesh.name;
-        serializationObject.id = mesh.id;
-
-        if (Tags.HasTags(mesh)) {
-            serializationObject.tags = Tags.GetTags(mesh);
-        }
-
-        serializationObject.position = mesh.position.asArray();
-
-        if (mesh.rotationQuaternion) {
-            serializationObject.rotationQuaternion = mesh.rotationQuaternion.asArray();
-        } else if (mesh.rotation) {
-            serializationObject.rotation = mesh.rotation.asArray();
-        }
-
-        serializationObject.scaling = mesh.scaling.asArray();
-        serializationObject.localMatrix = mesh.getPivotMatrix().asArray();
-
-        serializationObject.isEnabled = mesh.isEnabled();
-        serializationObject.isVisible = mesh.isVisible;
-        serializationObject.infiniteDistance = mesh.infiniteDistance;
-        serializationObject.pickable = mesh.isPickable;
-
-        serializationObject.receiveShadows = mesh.receiveShadows;
-
-        serializationObject.billboardMode = mesh.billboardMode;
-        serializationObject.visibility = mesh.visibility;
-
-        serializationObject.checkCollisions = mesh.checkCollisions;
-        serializationObject.isBlocker = mesh.isBlocker;
-
-        // Parent
-        if (mesh.parent) {
-            serializationObject.parentId = mesh.parent.id;
-        }
-
-        // Geometry
-        var geometry = mesh._geometry;
+        // Geometry      
+        var geometry = mesh._geometry;      
         if (geometry) {
-            var geometryId = geometry.id;
-            serializationObject.geometryId = geometryId;
-
-            if (!mesh.getScene().getGeometryByID(geometryId)) {
-                // geometry was in the memory but not added to the scene, nevertheless it's better to serialize to be able to reload the mesh with its geometry
+            if (!mesh.getScene().getGeometryByID(geometry.id)) {
+                // Geometry was in the memory but not added to the scene, nevertheless it's better to serialize to be able to reload the mesh with its geometry
                 serializeGeometry(geometry, serializationScene.geometries);
             }
-
-            // SubMeshes
-            serializationObject.subMeshes = [];
-            for (var subIndex = 0; subIndex < mesh.subMeshes.length; subIndex++) {
-                var subMesh = mesh.subMeshes[subIndex];
-
-                serializationObject.subMeshes.push({
-                    materialIndex: subMesh.materialIndex,
-                    verticesStart: subMesh.verticesStart,
-                    verticesCount: subMesh.verticesCount,
-                    indexStart: subMesh.indexStart,
-                    indexCount: subMesh.indexCount
-                });
-            }
-        }
-
-        // Material
-        if (mesh.material) {
-            serializationObject.materialId = mesh.material.id;
-        } else {
-            mesh.material = null;
         }
 
-        // Skeleton
-        if (mesh.skeleton) {
-            serializationObject.skeletonId = mesh.skeleton.id;
-        }
-
-        // Physics
-        //TODO implement correct serialization for physics impostors.
-        if (mesh.getPhysicsImpostor()) {
-            serializationObject.physicsMass = mesh.getPhysicsMass();
-            serializationObject.physicsFriction = mesh.getPhysicsFriction();
-            serializationObject.physicsRestitution = mesh.getPhysicsRestitution();
-            serializationObject.physicsImpostor = mesh.getPhysicsImpostor().type;
-        }
-
-        // Metadata
-        if (mesh.metadata) {
-            serializationObject.metadata = mesh.metadata;
-        }
-
-        // Instances
-        serializationObject.instances = [];
-        for (var index = 0; index < mesh.instances.length; index++) {
-            var instance = mesh.instances[index];
-            var serializationInstance: any = {
-                name: instance.name,
-                position: instance.position.asArray(),
-                scaling: instance.scaling.asArray()
-            };
-            if (instance.rotationQuaternion) {
-                serializationInstance.rotationQuaternion = instance.rotationQuaternion.asArray();
-            } else if (instance.rotation) {
-                serializationInstance.rotation = instance.rotation.asArray();
-            }
-            serializationObject.instances.push(serializationInstance);
-
-            // Animations
-            Animation.AppendSerializedAnimations(instance, serializationInstance);
-            serializationInstance.ranges = instance.serializeAnimationRanges();
-        }
-
-        // Animations
-        Animation.AppendSerializedAnimations(mesh, serializationObject);
-        serializationObject.ranges = mesh.serializeAnimationRanges();
-
-        // Layer mask
-        serializationObject.layerMask = mesh.layerMask;
-
-        // Alpha
-        serializationObject.alphaIndex = mesh.alphaIndex;
-        serializationObject.hasVertexAlpha = mesh.hasVertexAlpha;
-        
-        // Overlay
-        serializationObject.overlayAlpha = mesh.overlayAlpha;
-        serializationObject.overlayColor = mesh.overlayColor.asArray();
-        serializationObject.renderOverlay = mesh.renderOverlay;
-
-        // Fog
-        serializationObject.applyFog = mesh.applyFog;
-
-        // Action Manager
-        if (mesh.actionManager) {
-            serializationObject.actions = mesh.actionManager.serialize(mesh.name);
+        // Custom
+        if (mesh.serialize) {
+            mesh.serialize(serializationObject);
         }
 
         return serializationObject;
@@ -235,6 +116,8 @@
         public static Serialize(scene: Scene): any {
             var serializationObject: any = {};
 
+            SceneSerializer.ClearCache();
+
             // Scene
             serializationObject.useDelayedTextureLoading = scene.useDelayedTextureLoading;
             serializationObject.autoClear = scene.autoClear;
@@ -401,6 +284,8 @@
         public static SerializeMesh(toSerialize: any /* Mesh || Mesh[] */, withParents: boolean = false, withChildren: boolean = false): any {
             var serializationObject: any = {};
 
+            SceneSerializer.ClearCache();
+
             toSerialize = (toSerialize instanceof Array) ? toSerialize : [toSerialize];
 
             if (withParents || withChildren) {

+ 6 - 6
src/Tools/babylon.tools.ts

@@ -908,8 +908,6 @@
             return "[" + padStr(date.getHours()) + ":" + padStr(date.getMinutes()) + ":" + padStr(date.getSeconds()) + "]: " + message;
         }
 
-        public static Log: (message: string) => void = Tools._LogEnabled;
-
         private static _LogDisabled(message: string): void {
             // nothing to do
         }
@@ -921,8 +919,6 @@
             Tools._AddLogEntry(entry);
         }
 
-        public static Warn: (message: string) => void = Tools._WarnEnabled;
-
         private static _WarnDisabled(message: string): void {
             // nothing to do
         }
@@ -934,8 +930,6 @@
             Tools._AddLogEntry(entry);
         }
 
-        public static Error: (message: string) => void = Tools._ErrorEnabled;
-
         private static _ErrorDisabled(message: string): void {
             // nothing to do
         }
@@ -948,6 +942,12 @@
             Tools._AddLogEntry(entry);
         }
 
+        public static Log: (message: string) => void = Tools._LogEnabled;
+
+        public static Warn: (message: string) => void = Tools._WarnEnabled;
+
+        public static Error: (message: string) => void = Tools._ErrorEnabled;
+
         public static get LogCache(): string {
             return Tools._LogCache;
         }

+ 137 - 55
src/babylon.engine.ts

@@ -196,6 +196,29 @@
      * The engine class is responsible for interfacing with all lower-level APIs such as WebGL and Audio.
      */
     export class Engine {
+        public static Instances = new Array<Engine>();
+
+        public static get LastCreatedEngine(): Engine {
+            if (Engine.Instances.length === 0) {
+                return null;
+            }
+
+            return Engine.Instances[Engine.Instances.length - 1];
+        }
+
+        public static get LastCreatedScene(): Scene {
+            var lastCreatedEngine = Engine.LastCreatedEngine;
+            if (!lastCreatedEngine) {
+                return null;
+            }
+            
+            if (lastCreatedEngine.scenes.length === 0) {
+                return null;
+            }
+
+            return lastCreatedEngine.scenes[lastCreatedEngine.scenes.length - 1];
+        }
+
         // Const statics
         private static _ALPHA_DISABLE = 0;
         private static _ALPHA_ADD = 1;
@@ -513,6 +536,8 @@
         constructor(canvas: HTMLCanvasElement, antialias?: boolean, options?: EngineOptions, adaptToDeviceRatio = false) {
             this._renderingCanvas = canvas;
 
+            Engine.Instances.push(this);
+
             this._externalData = new StringDictionary<Object>();
 
             options = options || {};
@@ -1191,10 +1216,9 @@
 
         public bindFramebuffer(texture: WebGLTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number): void {
             this._currentRenderTarget = texture;
-            this.bindUnboundFramebuffer(texture._framebuffer);
+            this.bindUnboundFramebuffer(texture._MSAAFramebuffer ? texture._MSAAFramebuffer : texture._framebuffer);
             var gl = this._gl;
             if (texture.isCube) {
-
                 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, 0);
             }
 
@@ -1212,8 +1236,19 @@
 
         public unBindFramebuffer(texture: WebGLTexture, disableGenerateMipMaps = false): void {
             this._currentRenderTarget = null;
+
+            // If MSAA, we need to bitblt back to main texture
+            var gl = this._gl;
+
+            if (texture._MSAAFramebuffer) {
+                gl.bindFramebuffer(gl.READ_FRAMEBUFFER, texture._MSAAFramebuffer);
+                gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, texture._framebuffer);
+                gl.blitFramebuffer( 0, 0, texture._width, texture._height, 
+                                    0, 0, texture._width, texture._height,
+                                    gl.COLOR_BUFFER_BIT, gl.NEAREST);
+            }
+
             if (texture.generateMipMaps && !disableGenerateMipMaps) {
-                var gl = this._gl;
                 this._bindTextureDirectly(gl.TEXTURE_2D, texture);
                 gl.generateMipmap(gl.TEXTURE_2D);
                 this._bindTextureDirectly(gl.TEXTURE_2D, null);
@@ -2442,33 +2477,13 @@
 
             gl.texImage2D(gl.TEXTURE_2D, 0, this._getRGBABufferInternalSizedFormat(type), width, height, 0, gl.RGBA, this._getWebGLTextureType(type), null);
 
-            var depthStencilBuffer: WebGLRenderbuffer;
-
-            // Create the depth/stencil buffer
-            if (generateStencilBuffer) {
-                depthStencilBuffer = gl.createRenderbuffer();
-                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
-                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
-            }
-            else if (generateDepthBuffer) {
-                depthStencilBuffer = gl.createRenderbuffer();
-                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
-                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
-            }
-
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
-
-            // Manage attachments
-            if (generateStencilBuffer) {
-                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
-            }
-            else if (generateDepthBuffer) {
-                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
-            }
             gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
 
+            texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, width, height);
+
             if (generateMipMaps) {
                 this._gl.generateMipmap(this._gl.TEXTURE_2D);
             }
@@ -2479,9 +2494,6 @@
             this.bindUnboundFramebuffer(null);
 
             texture._framebuffer = framebuffer;
-            if (generateDepthBuffer) {
-                texture._depthBuffer = depthStencilBuffer;
-            }
             texture._baseWidth = width;
             texture._baseHeight = height;
             texture._width = width;
@@ -2491,6 +2503,8 @@
             texture.references = 1;
             texture.samplingMode = samplingMode;
             texture.type = type;
+            texture._generateDepthBuffer = generateDepthBuffer;
+            texture._generateStencilBuffer = generateStencilBuffer;
 
             this.resetTextureCache();
 
@@ -2499,6 +2513,83 @@
             return texture;
         }
 
+        private _setupFramebufferDepthAttachments(generateStencilBuffer: boolean, generateDepthBuffer: boolean, width: number, height: number, samples = 1): WebGLRenderbuffer {
+            var depthStencilBuffer: WebGLRenderbuffer = null;
+            var gl = this._gl;
+
+            // Create the depth/stencil buffer
+            if (generateStencilBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+
+                if (samples > 1) {
+                    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.DEPTH_STENCIL, width, height);
+                } else {
+                    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
+                }
+
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+            }
+            else if (generateDepthBuffer) {
+                depthStencilBuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
+
+                if (samples > 1) {
+                    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.DEPTH_COMPONENT16, width, height);
+                } else {
+                    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
+                }
+
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+            }
+
+            return depthStencilBuffer;
+        }
+
+        public updateRenderTargetTextureSampleCount(texture: WebGLTexture, samples: number) : number {
+            if (this.webGLVersion < 2) {
+                return 1;
+            }
+            var gl = this._gl;
+
+            samples = Math.min(samples, gl.getParameter(gl.MAX_SAMPLES));
+
+            // Dispose previous render buffers
+            if (texture._depthStencilBuffer) {
+                gl.deleteRenderbuffer(texture._depthStencilBuffer);
+            }
+
+            if (texture._MSAAFramebuffer) {
+                gl.deleteFramebuffer(texture._MSAAFramebuffer);
+            }
+
+            if (texture._MSAARenderBuffer) {
+                gl.deleteRenderbuffer(texture._MSAARenderBuffer);
+            }
+
+            if (samples > 1) {
+                texture._MSAAFramebuffer = gl.createFramebuffer();
+                this.bindUnboundFramebuffer(texture._MSAAFramebuffer);
+
+                var colorRenderbuffer = gl.createRenderbuffer();
+                gl.bindRenderbuffer(gl.RENDERBUFFER, colorRenderbuffer);
+                gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples, gl.RGBA8, texture._width, texture._height);
+
+                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorRenderbuffer);
+
+                texture._MSAARenderBuffer = colorRenderbuffer;
+            } else {
+                this.bindUnboundFramebuffer(texture._framebuffer);
+            }
+
+            texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(texture._generateStencilBuffer, texture._generateDepthBuffer, texture._width, texture._height, samples);
+
+            gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+            this.bindUnboundFramebuffer(null);
+
+            return samples;
+        }
+
         public createRenderTargetCubeTexture(size: number, options?: any): WebGLTexture {
             var gl = this._gl;
 
@@ -2538,32 +2629,11 @@
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
-            // Create the depth buffer
-            var depthStencilBuffer: WebGLRenderbuffer;
-
-            // Create the depth/stencil buffer
-            if (generateStencilBuffer) {
-                depthStencilBuffer = gl.createRenderbuffer();
-                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
-                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
-            }
-            else if (generateDepthBuffer) {
-                depthStencilBuffer = gl.createRenderbuffer();
-                gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
-                gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
-            }
-
             // Create the framebuffer
             var framebuffer = gl.createFramebuffer();
             this.bindUnboundFramebuffer(framebuffer);
 
-            // Manage attachments
-            if (generateStencilBuffer) {
-                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
-            }
-            else if (generateDepthBuffer) {
-                gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
-            }
+            texture._depthStencilBuffer = this._setupFramebufferDepthAttachments(generateStencilBuffer, generateDepthBuffer, size, size);
 
             // Mipmaps
             if (texture.generateMipMaps) {
@@ -2577,9 +2647,6 @@
             this.bindUnboundFramebuffer(null);
 
             texture._framebuffer = framebuffer;
-            if (generateDepthBuffer) {
-                texture._depthBuffer = depthStencilBuffer;
-            }
             texture._width = size;
             texture._height = size;
             texture.isReady = true;
@@ -2893,8 +2960,16 @@
                 gl.deleteFramebuffer(texture._framebuffer);
             }
 
-            if (texture._depthBuffer) {
-                gl.deleteRenderbuffer(texture._depthBuffer);
+            if (texture._depthStencilBuffer) {
+                gl.deleteRenderbuffer(texture._depthStencilBuffer);
+            }
+
+            if (texture._MSAAFramebuffer) {
+                gl.deleteFramebuffer(texture._MSAAFramebuffer);
+            }
+
+            if (texture._MSAARenderBuffer) {
+                gl.deleteRenderbuffer(texture._MSAARenderBuffer);
             }
 
             gl.deleteTexture(texture);
@@ -3211,6 +3286,13 @@
             document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
             document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
             document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
+
+            // Remove from Instances
+            var index = Engine.Instances.indexOf(this);
+
+            if (index >= 0) {
+                Engine.Instances.splice(index, 1);
+            }
         }
 
         // Loading screen

+ 13 - 4
src/babylon.mixins.ts

@@ -39,10 +39,14 @@ interface WebGLRenderingContext {
     createVertexArray(): any;
     bindVertexArray(vao: WebGLVertexArrayObject): void;
     deleteVertexArray(vao: WebGLVertexArrayObject): void;
-}
 
-interface AudioContext extends EventTarget {
-    decodeAudioData(audioData: ArrayBuffer, successCallback: DecodeSuccessCallback, errorCallback?: any): void;
+    blitFramebuffer(srcX0 : number, srcY0 : number, srcX1 : number, srcY1 : number, dstX0 : number, dstY0 : number, dstX1 : number, dstY1 : number, mask : number, filter : number) : void;
+    renderbufferStorageMultisample(target : number, samples : number, internalformat : number, width : number, height : number) : void;
+
+    MAX_SAMPLES: number;
+    RGBA8: number;
+    READ_FRAMEBUFFER : number;
+    DRAW_FRAMEBUFFER : number;
 }
 
 interface HTMLURL {
@@ -74,6 +78,7 @@ interface CanvasRenderingContext2D {
     mozImageSmoothingEnabled: boolean;
     oImageSmoothingEnabled: boolean;
     webkitImageSmoothingEnabled: boolean;
+    msImageSmoothingEnabled: boolean;
 }
 
 interface WebGLTexture {
@@ -94,11 +99,15 @@ interface WebGLTexture {
     _workingCanvas: HTMLCanvasElement;
     _workingContext: CanvasRenderingContext2D;
     _framebuffer: WebGLFramebuffer;
-    _depthBuffer: WebGLRenderbuffer;
+    _depthStencilBuffer: WebGLRenderbuffer;
+    _MSAAFramebuffer: WebGLFramebuffer;
+    _MSAARenderBuffer: WebGLRenderbuffer;
     _cachedCoordinatesMode: number;
     _cachedWrapU: number;
     _cachedWrapV: number;
     _isDisabled: boolean;
+    _generateStencilBuffer: boolean;
+    _generateDepthBuffer: boolean;
 }
 
 interface WebGLBuffer {

+ 1 - 1
src/babylon.node.ts

@@ -94,7 +94,7 @@ module BABYLON {
         constructor(name: string, scene: Scene) {
             this.name = name;
             this.id = name;
-            this._scene = scene;
+            this._scene = scene || Engine.LastCreatedScene;
             this._initCache();
         }
 

+ 2 - 2
src/babylon.scene.ts

@@ -570,9 +570,9 @@
          * @param {BABYLON.Engine} engine - the engine to be used to render this scene.
          */
         constructor(engine: Engine) {
-            this._engine = engine;
+            this._engine = engine || Engine.LastCreatedEngine;
 
-            engine.scenes.push(this);
+            this._engine.scenes.push(this);
 
             this._externalData = new StringDictionary<Object>();
             this._uid = null;