Просмотр исходного кода

Merge remote-tracking branch 'BabylonJS/master'

MackeyK24 8 лет назад
Родитель
Сommit
f352d432ea
38 измененных файлов с 3097 добавлено и 2825 удалено
  1. 3 3
      Tools/Gulp/package.json
  2. 85 31
      canvas2D/src/Engine/babylon.fontTexture.ts
  3. 6 4
      canvas2D/src/Engine/babylon.text2d.ts
  4. 25 25
      dist/preview release/babylon.core.js
  5. 1146 1136
      dist/preview release/babylon.d.ts
  6. 35 35
      dist/preview release/babylon.js
  7. 126 57
      dist/preview release/babylon.max.js
  8. 1146 1136
      dist/preview release/babylon.module.d.ts
  9. 34 34
      dist/preview release/babylon.noworker.js
  10. 8 1
      dist/preview release/canvas2D/babylon.canvas2d.d.ts
  11. 87 36
      dist/preview release/canvas2D/babylon.canvas2d.js
  12. 11 11
      dist/preview release/canvas2D/babylon.canvas2d.min.js
  13. 249 249
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 1 1
      dist/preview release/inspector/babylon.inspector.min.js
  15. 1 4
      dist/preview release/loaders/babylon.glTFFileLoader.js
  16. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  17. 1 1
      dist/preview release/materialsLibrary/babylon.furMaterial.min.js
  18. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  19. 1 1
      dist/preview release/what's new.md
  20. 8 1
      src/Bones/babylon.bone.ts
  21. 11 1
      src/Bones/babylon.skeleton.ts
  22. 3 3
      src/Cameras/babylon.camera.ts
  23. 1 1
      src/Layer/babylon.highlightlayer.ts
  24. 1 1
      src/Layer/babylon.layer.ts
  25. 1 1
      src/LensFlare/babylon.lensFlareSystem.ts
  26. 1 1
      src/Lights/babylon.light.ts
  27. 2 1
      src/Materials/Textures/babylon.baseTexture.ts
  28. 1 1
      src/Materials/Textures/babylon.renderTargetTexture.ts
  29. 2 2
      src/Materials/babylon.material.ts
  30. 14 4
      src/Math/babylon.math.ts
  31. 40 25
      src/Mesh/babylon.abstractMesh.ts
  32. 1 1
      src/Particles/babylon.particleSystem.ts
  33. 1 1
      src/Particles/babylon.solidParticleSystem.ts
  34. 6 6
      src/Tools/babylon.tools.ts
  35. 32 0
      src/babylon.engine.ts
  36. 1 4
      src/babylon.mixins.ts
  37. 1 1
      src/babylon.node.ts
  38. 2 2
      src/babylon.scene.ts

+ 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

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

@@ -603,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++;
@@ -633,7 +634,7 @@
                     if (char === "\t") {
                         charWidth = tabWidth;
                     }else{
-                        charWidth = ci.charWidth;
+                        charWidth = ci.xAdvance;
                     }
 
                     offset.x += charWidth;
@@ -646,7 +647,7 @@
                         numCharsCurrentWord = 0;
                         widthCurrentWord = 0;
                     }else {
-                        widthCurrentWord += ci.charWidth;
+                        widthCurrentWord += ci.xAdvance;
                         numCharsCurrentWord++;
                     }
 
@@ -730,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];
@@ -746,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;
@@ -762,7 +764,7 @@
                     }
 
                     offset.x = offsetX;
-                    offset.y -= texture.lineHeight;
+                    offset.y -= lineHeight;
                 }
 
             }

Разница между файлами не показана из-за своего большого размера
+ 25 - 25
dist/preview release/babylon.core.js


Разница между файлами не показана из-за своего большого размера
+ 1146 - 1136
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 35 - 35
dist/preview release/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 126 - 57
dist/preview release/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 1146 - 1136
dist/preview release/babylon.module.d.ts


Разница между файлами не показана из-за своего большого размера
+ 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;

Разница между файлами не показана из-за своего большого размера
+ 11 - 11
dist/preview release/canvas2D/babylon.canvas2d.min.js


Разница между файлами не показана из-за своего большого размера
+ 249 - 249
dist/preview release/inspector/babylon.inspector.bundle.js


Разница между файлами не показана из-за своего большого размера
+ 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

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.furMaterial.min.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


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

@@ -25,7 +25,7 @@
 - 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))   

+ 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();
                 }

+ 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);

+ 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) {

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

@@ -295,7 +295,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);
             }
         }
 

+ 14 - 4
src/Math/babylon.math.ts

@@ -1736,7 +1736,7 @@
          */
         public static RotationFromAxisToRef(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Vector3): void {
             var quat = MathTmp.Quaternion[0];
-            Quaternion.QuaternionRotationFromAxisToRef(axis1, axis2, axis3, quat);
+            Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             quat.toEulerAnglesToRef(ref);
         }
     }
@@ -2699,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;
         }
         /**
@@ -2709,7 +2709,7 @@
          * 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 {
+        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);
@@ -2947,6 +2947,16 @@
             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. 
          */

+ 40 - 25
src/Mesh/babylon.abstractMesh.ts

@@ -258,7 +258,7 @@
         constructor(name: string, scene: Scene) {
             super(name, scene);
 
-            scene.addMesh(this);
+            this.getScene().addMesh(this);
         }
 
         /**
@@ -868,39 +868,52 @@
             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.Matrix[1].copyFrom(this.getScene().activeCamera.getViewMatrix());
+            // Mesh referal
+            var completeMeshReferalMatrix = Tmp.Matrix[6];
+            if (this._meshToBoneReferal && this.parent && this.parent.getWorldMatrix) {
+                this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), completeMeshReferalMatrix);
+            }
 
-                Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
-                Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
+            // 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 {
+                        currentPosition.copyFrom(this.position);
+                    }
 
-                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL)
-                {
-                    // Need to extract rotation vectors
-                    var scale = Tmp.Vector3[2];
-                    var rotation = Tmp.Quaternion[0];
-                    var translation = Tmp.Vector3[3];
-                    Tmp.Matrix[0].decompose(scale, rotation, translation);
+                    currentPosition.subtractInPlace(this.getScene().activeCamera.globalPosition);
 
-                    var finalQuaternion = Tmp.Quaternion[1];
-                    finalQuaternion.w = rotation.w;
+                    var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
                     if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X)
                     {
-                        finalQuaternion.x = rotation.x;
+                        finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
                     }
                     
                     if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y)
                     {
-                        finalQuaternion.y = rotation.y;
+                        finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
                     }
                     
                     if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z)
                     {
-                        finalQuaternion.z = rotation.z;
+                        finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
                     }
  
-                    Matrix.ComposeToRef(scale, finalQuaternion, translation, Tmp.Matrix[0]);
+                    Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
+                } else {
+                    Tmp.Matrix[1].copyFrom(this.getScene().activeCamera.getViewMatrix());
+
+                    Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+                    Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
                 }
 
                 Tmp.Matrix[1].copyFrom(Tmp.Matrix[5]);
@@ -916,17 +929,19 @@
 
                 if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
                     if (this._meshToBoneReferal) {
-                        this.parent.getWorldMatrix().multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), Tmp.Matrix[5]);
+                        Tmp.Matrix[5].copyFrom(completeMeshReferalMatrix);
                     } else {
                         Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
                     }
-                    Tmp.Matrix[5].removeRotationAndScaling();
-
-                    this._localWorld.multiplyToRef(Tmp.Matrix[5], this._worldMatrix);
+                    
+                    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 {
                     if (this._meshToBoneReferal) {
-                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
-                        Tmp.Matrix[6].multiplyToRef(this._meshToBoneReferal.getWorldMatrix(), this._worldMatrix);
+                        this._localWorld.multiplyToRef(completeMeshReferalMatrix, this._worldMatrix);
                     } else {
                         this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
                     }

+ 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;
 

+ 1 - 1
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;

+ 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;
         }

+ 32 - 0
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 || {};
@@ -3211,6 +3236,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

+ 1 - 4
src/babylon.mixins.ts

@@ -41,10 +41,6 @@ interface WebGLRenderingContext {
     deleteVertexArray(vao: WebGLVertexArrayObject): void;
 }
 
-interface AudioContext extends EventTarget {
-    decodeAudioData(audioData: ArrayBuffer, successCallback: DecodeSuccessCallback, errorCallback?: any): void;
-}
-
 interface HTMLURL {
     createObjectURL(param1: any, param2?: any);
 }
@@ -74,6 +70,7 @@ interface CanvasRenderingContext2D {
     mozImageSmoothingEnabled: boolean;
     oImageSmoothingEnabled: boolean;
     webkitImageSmoothingEnabled: boolean;
+    msImageSmoothingEnabled: boolean;
 }
 
 interface WebGLTexture {

+ 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;