David Catuhe 9 gadi atpakaļ
vecāks
revīzija
57c55731c4

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 24 - 24
dist/preview release/babylon.core.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 5653 - 5520
dist/preview release/babylon.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 37 - 37
dist/preview release/babylon.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 765 - 366
dist/preview release/babylon.max.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 37 - 37
dist/preview release/babylon.noworker.js


+ 3 - 0
src/Canvas2d/babylon.canvas2d.js

@@ -668,6 +668,9 @@ var BABYLON;
                 throw Error("Can't use Canvas Background with the caching strategy TOPLEVELGROUPS");
             }
         };
+        Canvas2D.prototype.onPrimBecomesDirty = function () {
+            this._addPrimToDirtyList(this);
+        };
         Canvas2D.prototype._updateCanvasState = function () {
             // Check if the update has already been made for this render Frame
             if (this.scene.getRenderId() === this._updateRenderId) {

+ 285 - 0
src/Canvas2d/babylon.ellipse2d.js

@@ -0,0 +1,285 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var Ellipse2DRenderCache = (function (_super) {
+        __extends(Ellipse2DRenderCache, _super);
+        function Ellipse2DRenderCache(engine, modelKey, isTransparent) {
+            _super.call(this, engine, modelKey, isTransparent);
+        }
+        Ellipse2DRenderCache.prototype.render = function (instanceInfo, context) {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+            var engine = instanceInfo._owner.owner.engine;
+            var depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+            var cur;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            }
+            if (this.effectFill) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffers(this.fillVB, this.fillIB, [1], 4, this.effectFill);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);
+                    }
+                }
+            }
+            if (this.effectBorder) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffers(this.borderVB, this.borderIB, [1], 4, this.effectBorder);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        };
+        Ellipse2DRenderCache.prototype.dispose = function () {
+            if (!_super.prototype.dispose.call(this)) {
+                return false;
+            }
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+            return true;
+        };
+        return Ellipse2DRenderCache;
+    }(BABYLON.ModelRenderCache));
+    BABYLON.Ellipse2DRenderCache = Ellipse2DRenderCache;
+    var Ellipse2DInstanceData = (function (_super) {
+        __extends(Ellipse2DInstanceData, _super);
+        function Ellipse2DInstanceData(partId) {
+            _super.call(this, partId, 1);
+        }
+        Object.defineProperty(Ellipse2DInstanceData.prototype, "properties", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        __decorate([
+            BABYLON.instanceData()
+        ], Ellipse2DInstanceData.prototype, "properties", null);
+        return Ellipse2DInstanceData;
+    }(BABYLON.Shape2DInstanceData));
+    BABYLON.Ellipse2DInstanceData = Ellipse2DInstanceData;
+    var Ellipse2D = (function (_super) {
+        __extends(Ellipse2D, _super);
+        function Ellipse2D() {
+            _super.apply(this, arguments);
+        }
+        Object.defineProperty(Ellipse2D.prototype, "actualSize", {
+            get: function () {
+                return this.size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Ellipse2D.prototype, "size", {
+            get: function () {
+                return this._size;
+            },
+            set: function (value) {
+                this._size = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Ellipse2D.prototype, "subdivisions", {
+            get: function () {
+                return this._subdivisions;
+            },
+            set: function (value) {
+                this._subdivisions = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Ellipse2D.prototype.levelIntersect = function (intersectInfo) {
+            var x = intersectInfo._localPickPosition.x;
+            var y = intersectInfo._localPickPosition.y;
+            var w = this.size.width / 2;
+            var h = this.size.height / 2;
+            return ((x * x) / (w * w) + (y * y) / (h * h)) <= 1;
+        };
+        Ellipse2D.prototype.updateLevelBoundingInfo = function () {
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        };
+        Ellipse2D.prototype.setupEllipse2D = function (owner, parent, id, position, size, subdivisions, fill, border, borderThickness) {
+            if (subdivisions === void 0) { subdivisions = 64; }
+            if (borderThickness === void 0) { borderThickness = 1; }
+            this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
+            this.size = size;
+            this.subdivisions = subdivisions;
+        };
+        Ellipse2D.Create = function (parent, id, x, y, width, height, subdivisions, fill, border, borderThickness) {
+            if (subdivisions === void 0) { subdivisions = 64; }
+            BABYLON.Prim2DBase.CheckParent(parent);
+            var ellipse = new Ellipse2D();
+            ellipse.setupEllipse2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), new BABYLON.Size(width, height), subdivisions, fill, border, borderThickness);
+            return ellipse;
+        };
+        Ellipse2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
+            var renderCache = new Ellipse2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        };
+        Ellipse2D.prototype.setupModelRenderCache = function (modelRenderCache) {
+            var renderCache = modelRenderCache;
+            var engine = this.owner.engine;
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                var vbSize = this.subdivisions + 1;
+                var vb = new Float32Array(vbSize);
+                for (var i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+                var triCount = vbSize - 1;
+                var ib = new Float32Array(triCount * 3);
+                for (var i = 0; i < triCount; i++) {
+                    ib[i * 3 + 0] = 0;
+                    ib[i * 3 + 2] = i + 1;
+                    ib[i * 3 + 1] = i + 2;
+                }
+                ib[triCount * 3 - 2] = 1;
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = triCount * 3;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["index"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            // Need to create WebGL resource for border part?
+            if (this.border) {
+                var vbSize = this.subdivisions * 2;
+                var vb = new Float32Array(vbSize);
+                for (var i = 0; i < vbSize; i++) {
+                    vb[i] = i;
+                }
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+                var triCount = vbSize;
+                var rs = triCount / 2;
+                var ib = new Float32Array(triCount * 3);
+                for (var i = 0; i < rs; i++) {
+                    var r0 = i;
+                    var r1 = (i + 1) % rs;
+                    ib[i * 6 + 0] = rs + r1;
+                    ib[i * 6 + 1] = rs + r0;
+                    ib[i * 6 + 2] = r0;
+                    ib[i * 6 + 3] = r1;
+                    ib[i * 6 + 4] = rs + r1;
+                    ib[i * 6 + 5] = r0;
+                }
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = (triCount * 3);
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["index"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "ellipse2d", fragment: "ellipse2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            return renderCache;
+        };
+        Ellipse2D.prototype.createInstanceDataParts = function () {
+            var res = new Array();
+            if (this.border) {
+                res.push(new Ellipse2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Ellipse2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        };
+        Ellipse2D.prototype.refreshInstanceDataPart = function (part) {
+            if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
+                return false;
+            }
+            if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
+                var d = part;
+                var size = this.size;
+                d.properties = new BABYLON.Vector3(size.width, size.height, this.subdivisions);
+            }
+            else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
+                var d = part;
+                var size = this.size;
+                d.properties = new BABYLON.Vector3(size.width, size.height, this.subdivisions);
+            }
+            return true;
+        };
+        __decorate([
+            BABYLON.instanceLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Ellipse2D.sizeProperty = pi; }, false, true)
+        ], Ellipse2D.prototype, "size", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Ellipse2D.subdivisionsProperty = pi; })
+        ], Ellipse2D.prototype, "subdivisions", null);
+        Ellipse2D = __decorate([
+            BABYLON.className("Ellipse2D")
+        ], Ellipse2D);
+        return Ellipse2D;
+    }(BABYLON.Shape2D));
+    BABYLON.Ellipse2D = Ellipse2D;
+})(BABYLON || (BABYLON = {}));

+ 2 - 1
src/Canvas2d/babylon.group2d.js

@@ -247,6 +247,7 @@ var BABYLON;
                         }
                     });
                     // Everything is updated, clear the dirty list
+                    this._primDirtyList.forEach(function (p) { return p._resetPropertiesDirty(); });
                     this._primDirtyList.splice(0);
                 }
             }
@@ -300,9 +301,9 @@ var BABYLON;
                                 // Update the WebGL buffer to match the new content of the instances data
                                 engine._gl.bindBuffer(engine._gl.ARRAY_BUFFER, v._instancesPartsBuffer[i]);
                                 engine._gl.bufferSubData(engine._gl.ARRAY_BUFFER, 0, instanceData_1);
-                                v._dirtyInstancesData = false;
                             }
                         }
+                        v._dirtyInstancesData = false;
                     }
                     // Submit render only if we have something to render (everything may be hidden and the floatarray empty)
                     if (!_this.owner.supportInstancedArray || totalRenderCount_1 > 0) {

+ 970 - 0
src/Canvas2d/babylon.lines2d.js

@@ -0,0 +1,970 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var Lines2DRenderCache = (function (_super) {
+        __extends(Lines2DRenderCache, _super);
+        function Lines2DRenderCache(engine, modelKey, isTransparent) {
+            _super.call(this, engine, modelKey, isTransparent);
+        }
+        Lines2DRenderCache.prototype.render = function (instanceInfo, context) {
+            // Do nothing if the shader is still loading/preparing 
+            if ((this.effectFill && !this.effectFill.isReady()) || (this.effectBorder && !this.effectBorder.isReady())) {
+                return false;
+            }
+            var engine = instanceInfo._owner.owner.engine;
+            var depthFunction = 0;
+            if (this.effectFill && this.effectBorder) {
+                depthFunction = engine.getDepthFunction();
+                engine.setDepthFunctionToLessOrEqual();
+            }
+            var cur;
+            if (this.isTransparent) {
+                cur = engine.getAlphaMode();
+                engine.setAlphaMode(BABYLON.Engine.ALPHA_COMBINE);
+            }
+            if (this.effectFill) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_FILLPARTID.toString());
+                engine.enableEffect(this.effectFill);
+                engine.bindBuffers(this.fillVB, this.fillIB, [2], 2 * 4, this.effectFill);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingFillAttributes) {
+                        // Compute the offset locations of the attributes in the vertex shader that will be mapped to the instance buffer data
+                        this.instancingFillAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_FILLPARTID, this.effectFill);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingFillAttributes);
+                    engine.draw(true, 0, this.fillIndicesCount, count);
+                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingFillAttributes);
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectFill, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.fillIndicesCount);
+                    }
+                }
+            }
+            if (this.effectBorder) {
+                var partIndex = instanceInfo._partIndexFromId.get(BABYLON.Shape2D.SHAPE2D_BORDERPARTID.toString());
+                engine.enableEffect(this.effectBorder);
+                engine.bindBuffers(this.borderVB, this.borderIB, [2], 2 * 4, this.effectBorder);
+                var count = instanceInfo._instancesPartsData[partIndex].usedElementCount;
+                if (instanceInfo._owner.owner.supportInstancedArray) {
+                    if (!this.instancingBorderAttributes) {
+                        this.instancingBorderAttributes = this.loadInstancingAttributes(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, this.effectBorder);
+                    }
+                    engine.updateAndBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], null, this.instancingBorderAttributes);
+                    engine.draw(true, 0, this.borderIndicesCount, count);
+                    engine.unBindInstancesBuffer(instanceInfo._instancesPartsBuffer[partIndex], this.instancingBorderAttributes);
+                }
+                else {
+                    for (var i = 0; i < count; i++) {
+                        this.setupUniforms(this.effectBorder, partIndex, instanceInfo._instancesPartsData[partIndex], i);
+                        engine.draw(true, 0, this.borderIndicesCount);
+                    }
+                }
+            }
+            if (this.isTransparent) {
+                engine.setAlphaMode(cur);
+            }
+            if (this.effectFill && this.effectBorder) {
+                engine.setDepthFunction(depthFunction);
+            }
+            return true;
+        };
+        Lines2DRenderCache.prototype.dispose = function () {
+            if (!_super.prototype.dispose.call(this)) {
+                return false;
+            }
+            if (this.fillVB) {
+                this._engine._releaseBuffer(this.fillVB);
+                this.fillVB = null;
+            }
+            if (this.fillIB) {
+                this._engine._releaseBuffer(this.fillIB);
+                this.fillIB = null;
+            }
+            if (this.effectFill) {
+                this._engine._releaseEffect(this.effectFill);
+                this.effectFill = null;
+            }
+            if (this.borderVB) {
+                this._engine._releaseBuffer(this.borderVB);
+                this.borderVB = null;
+            }
+            if (this.borderIB) {
+                this._engine._releaseBuffer(this.borderIB);
+                this.borderIB = null;
+            }
+            if (this.effectBorder) {
+                this._engine._releaseEffect(this.effectBorder);
+                this.effectBorder = null;
+            }
+            return true;
+        };
+        return Lines2DRenderCache;
+    }(BABYLON.ModelRenderCache));
+    BABYLON.Lines2DRenderCache = Lines2DRenderCache;
+    var Lines2DInstanceData = (function (_super) {
+        __extends(Lines2DInstanceData, _super);
+        function Lines2DInstanceData(partId) {
+            _super.call(this, partId, 1);
+        }
+        Object.defineProperty(Lines2DInstanceData.prototype, "boundingMin", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2DInstanceData.prototype, "boundingMax", {
+            get: function () {
+                return null;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        __decorate([
+            BABYLON.instanceData()
+        ], Lines2DInstanceData.prototype, "boundingMin", null);
+        __decorate([
+            BABYLON.instanceData()
+        ], Lines2DInstanceData.prototype, "boundingMax", null);
+        return Lines2DInstanceData;
+    }(BABYLON.Shape2DInstanceData));
+    BABYLON.Lines2DInstanceData = Lines2DInstanceData;
+    var Lines2D = (function (_super) {
+        __extends(Lines2D, _super);
+        function Lines2D() {
+            _super.apply(this, arguments);
+        }
+        Object.defineProperty(Lines2D, "NoCap", {
+            get: function () { return Lines2D._noCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "RoundCap", {
+            get: function () { return Lines2D._roundCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "TriangleCap", {
+            get: function () { return Lines2D._triangleCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "SquareAnchorCap", {
+            get: function () { return Lines2D._squareAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "RoundAnchorCap", {
+            get: function () { return Lines2D._roundAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "DiamondAnchorCap", {
+            get: function () { return Lines2D._diamondAnchorCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D, "ArrowCap", {
+            get: function () { return Lines2D._arrowCap; },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "actualSize", {
+            get: function () {
+                return this.size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "points", {
+            get: function () {
+                return this._points;
+            },
+            set: function (value) {
+                this._points = value;
+                this._levelBoundingInfoDirty = true;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "fillThickness", {
+            get: function () {
+                return this._fillThickness;
+            },
+            set: function (value) {
+                this._fillThickness = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "closed", {
+            get: function () {
+                return this._closed;
+            },
+            set: function (value) {
+                this._closed = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "startCap", {
+            get: function () {
+                return this._startCap;
+            },
+            set: function (value) {
+                this._startCap = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "endCap", {
+            get: function () {
+                return this._endCap;
+            },
+            set: function (value) {
+                this._endCap = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Lines2D.prototype.levelIntersect = function (intersectInfo) {
+            var pl = this.points.length;
+            var l = this.closed ? pl + 1 : pl;
+            var originOffset = new BABYLON.Vector2(-0.5, -0.5);
+            var p = intersectInfo._localPickPosition;
+            var prevA = this.transformPointWithOrigin(this._contour[0], originOffset);
+            var prevB = this.transformPointWithOrigin(this._contour[1], originOffset);
+            for (var i = 1; i < l; i++) {
+                var curA = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 0], originOffset);
+                var curB = this.transformPointWithOrigin(this._contour[(i % pl) * 2 + 1], originOffset);
+                if (BABYLON.Vector2.PointInTriangle(p, prevA, prevB, curA)) {
+                    return true;
+                }
+                if (BABYLON.Vector2.PointInTriangle(p, curA, prevB, curB)) {
+                    return true;
+                }
+                prevA = curA;
+                prevB = curB;
+            }
+            return false;
+        };
+        Object.defineProperty(Lines2D.prototype, "size", {
+            get: function () {
+                return this._size;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "boundingMin", {
+            get: function () {
+                return this._boundingMin;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Lines2D.prototype, "boundingMax", {
+            get: function () {
+                return this._boundingMax;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Lines2D.prototype.getUsedShaderCategories = function (dataPart) {
+            var res = _super.prototype.getUsedShaderCategories.call(this, dataPart);
+            // Remove the BORDER category, we don't use it in the VertexShader
+            var i = res.indexOf(BABYLON.Shape2D.SHAPE2D_CATEGORY_BORDER);
+            if (i !== -1) {
+                res.splice(i, 1);
+            }
+            return res;
+        };
+        Lines2D.prototype.updateLevelBoundingInfo = function () {
+            BABYLON.BoundingInfo2D.CreateFromSizeToRef(this.size, this._levelBoundingInfo, this.origin);
+        };
+        Lines2D.prototype.setupLines2D = function (owner, parent, id, position, points, fillThickness, startCap, endCap, fill, border, borderThickness) {
+            if (borderThickness === void 0) { borderThickness = 1; }
+            this.setupShape2D(owner, parent, id, position, true, fill, border, borderThickness);
+            this.fillThickness = fillThickness;
+            this.startCap = startCap;
+            this.endCap = endCap;
+            this.points = points;
+            this.closed = false;
+            this._size = BABYLON.Size.Zero();
+            this._boundingMin = BABYLON.Vector2.Zero();
+            this._boundingMax = BABYLON.Vector2.Zero();
+        };
+        Lines2D.Create = function (parent, id, x, y, points, fillThickness, startCap, endCap, fill, border, borderThickness) {
+            if (startCap === void 0) { startCap = Lines2D.NoCap; }
+            if (endCap === void 0) { endCap = Lines2D.NoCap; }
+            BABYLON.Prim2DBase.CheckParent(parent);
+            var lines = new Lines2D();
+            lines.setupLines2D(parent.owner, parent, id, new BABYLON.Vector2(x, y), points, fillThickness, startCap, endCap, fill, border, borderThickness);
+            return lines;
+        };
+        Lines2D.prototype.createModelRenderCache = function (modelKey, isTransparent) {
+            var renderCache = new Lines2DRenderCache(this.owner.engine, modelKey, isTransparent);
+            return renderCache;
+        };
+        Lines2D.prototype.setupModelRenderCache = function (modelRenderCache) {
+            var _this = this;
+            var renderCache = modelRenderCache;
+            var engine = this.owner.engine;
+            // Init min/max because their being computed here
+            this.boundingMin = new BABYLON.Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
+            this.boundingMax = new BABYLON.Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
+            var perp = function (v, res) {
+                res.x = v.y;
+                res.y = -v.x;
+            };
+            var direction = function (a, b, res) {
+                a.subtractToRef(b, res);
+                res.normalize();
+            };
+            var tps = BABYLON.Vector2.Zero();
+            var computeMiter = function (tangent, miter, a, b) {
+                a.addToRef(b, tangent);
+                tangent.normalize();
+                miter.x = -tangent.y;
+                miter.y = tangent.x;
+                tps.x = -a.y;
+                tps.y = a.x;
+                return 1 / BABYLON.Vector2.Dot(miter, tps);
+            };
+            var intersect = function (x1, y1, x2, y2, x3, y3, x4, y4) {
+                var d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
+                if (d === 0)
+                    return false;
+                var xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; // Intersection point is xi/yi, just in case...
+                //let yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; // That's why I left it commented
+                if (xi < Math.min(x1, x2) || xi > Math.max(x1, x2))
+                    return false;
+                if (xi < Math.min(x3, x4) || xi > Math.max(x3, x4))
+                    return false;
+                return true;
+            };
+            var startDir = BABYLON.Vector2.Zero();
+            var endDir = BABYLON.Vector2.Zero();
+            var updateMinMax = function (array, offset) {
+                if (offset >= array.length) {
+                    return;
+                }
+                _this._boundingMin.x = Math.min(_this._boundingMin.x, array[offset]);
+                _this._boundingMax.x = Math.max(_this._boundingMax.x, array[offset]);
+                _this._boundingMin.y = Math.min(_this._boundingMin.y, array[offset + 1]);
+                _this._boundingMax.y = Math.max(_this._boundingMax.y, array[offset + 1]);
+            };
+            var store = function (array, contour, index, max, p, n, halfThickness, borderThickness, detectFlip) {
+                var borderMode = borderThickness != null && !isNaN(borderThickness);
+                var off = index * (borderMode ? 8 : 4);
+                // Mandatory because we'll be out of bound in case of closed line, for the very last point (which is a duplicate of the first that we don't store in the vb)
+                if (off >= array.length) {
+                    return;
+                }
+                // Store start/end normal, we need it for the cap construction
+                if (index === 0) {
+                    perp(n, startDir);
+                }
+                else if (index === max - 1) {
+                    perp(n, endDir);
+                    endDir.x *= -1;
+                    endDir.y *= -1;
+                }
+                var swap = false;
+                array[off + 0] = p.x + n.x * halfThickness;
+                array[off + 1] = p.y + n.y * halfThickness;
+                array[off + 2] = p.x + n.x * -halfThickness;
+                array[off + 3] = p.y + n.y * -halfThickness;
+                updateMinMax(array, off);
+                updateMinMax(array, off + 2);
+                // If an index is given we check if the two segments formed between [index+0;detectFlip+0] and [index+2;detectFlip+2] intersect themselves.
+                // It should not be the case, they should be parallel, so if they cross, we switch the order of storage to ensure we'll have parallel lines
+                if (detectFlip !== undefined) {
+                    // Flip if intersect
+                    var flipOff = detectFlip * (borderMode ? 8 : 4);
+                    if (intersect(array[off + 0], array[off + 1], array[flipOff + 0], array[flipOff + 1], array[off + 2], array[off + 3], array[flipOff + 2], array[flipOff + 3])) {
+                        swap = true;
+                        var tps_1 = array[off + 0];
+                        array[off + 0] = array[off + 2];
+                        array[off + 2] = tps_1;
+                        tps_1 = array[off + 1];
+                        array[off + 1] = array[off + 3];
+                        array[off + 3] = tps_1;
+                    }
+                }
+                if (borderMode) {
+                    var t = halfThickness + borderThickness;
+                    array[off + 4] = p.x + n.x * (swap ? -t : t);
+                    array[off + 5] = p.y + n.y * (swap ? -t : t);
+                    array[off + 6] = p.x + n.x * (swap ? t : -t);
+                    array[off + 7] = p.y + n.y * (swap ? t : -t);
+                    updateMinMax(array, off + 4);
+                    updateMinMax(array, off + 6);
+                }
+                if (contour) {
+                    off += borderMode ? 4 : 0;
+                    contour.push(new BABYLON.Vector2(array[off + 0], array[off + 1]));
+                    contour.push(new BABYLON.Vector2(array[off + 2], array[off + 3]));
+                }
+            };
+            var sd = Lines2D._roundCapSubDiv;
+            var getCapSize = function (type, border) {
+                if (border === void 0) { border = false; }
+                // If no array given, we call this to get the size
+                var vbsize = 0, ibsize = 0;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        // If the line is not close and we're computing border, we add the size to generate the edge border
+                        if (!_this.closed && border) {
+                            vbsize = 4;
+                            ibsize = 6;
+                        }
+                        else {
+                            vbsize = ibsize = 0;
+                        }
+                        break;
+                    case Lines2D.RoundCap:
+                        if (border) {
+                            vbsize = sd;
+                            ibsize = (sd - 2) * 3;
+                        }
+                        else {
+                            vbsize = (sd / 2) + 1;
+                            ibsize = (sd / 2) * 3;
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 24;
+                        }
+                        else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.TriangleCap:
+                        if (border) {
+                            vbsize = 6;
+                            ibsize = 12;
+                        }
+                        else {
+                            vbsize = 3;
+                            ibsize = 3;
+                        }
+                        break;
+                    case Lines2D.DiamondAnchorCap:
+                        if (border) {
+                            vbsize = 10;
+                            ibsize = 24;
+                        }
+                        else {
+                            vbsize = 5;
+                            ibsize = 9;
+                        }
+                        break;
+                    case Lines2D.SquareAnchorCap:
+                        if (border) {
+                            vbsize = 12;
+                            ibsize = 30;
+                        }
+                        else {
+                            vbsize = 4;
+                            ibsize = 6;
+                        }
+                        break;
+                    case Lines2D.RoundAnchorCap:
+                        if (border) {
+                            vbsize = sd * 2;
+                            ibsize = (sd - 1) * 6;
+                        }
+                        else {
+                            vbsize = sd + 1;
+                            ibsize = (sd + 1) * 3;
+                        }
+                        break;
+                }
+                return { vbsize: vbsize * 2, ibsize: ibsize };
+            };
+            var v = BABYLON.Vector2.Zero();
+            var storeVertex = function (vb, baseOffset, index, basePos, rotation, vertex) {
+                var c = Math.cos(rotation);
+                var s = Math.sin(rotation);
+                v.x = (c * vertex.x) + (-s * vertex.y) + basePos.x;
+                v.y = (s * vertex.x) + (c * vertex.y) + basePos.y;
+                var offset = baseOffset + (index * 2);
+                vb[offset + 0] = v.x;
+                vb[offset + 1] = v.y;
+                updateMinMax(vb, offset);
+                return (baseOffset + index * 2) / 2;
+            };
+            var storeIndex = function (ib, baseOffset, index, vertexIndex) {
+                ib[baseOffset + index] = vertexIndex;
+            };
+            var buildCap = function (vb, vbi, ib, ibi, pos, thickness, borderThickness, type, capDir) {
+                // Compute the transformation from the direction of the cap to build relative to our default orientation [1;0] (our cap are by default pointing toward right, horizontal
+                var dir = new BABYLON.Vector2(1, 0);
+                var angle = Math.atan2(capDir.y, capDir.x) - Math.atan2(dir.y, dir.x);
+                var ht = thickness / 2;
+                var t = thickness;
+                var borderMode = borderThickness != null;
+                var bt = borderThickness;
+                switch (type) {
+                    case Lines2D.NoCap:
+                        if (borderMode && !_this.closed) {
+                            var vi = 0;
+                            var ii = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht + bt));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(bt, ht + bt));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(bt, -(ht + bt)));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -(ht + bt)));
+                            storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v2);
+                            storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v1);
+                            storeIndex(ib, ibi, ii++, v3);
+                            storeIndex(ib, ibi, ii++, v4);
+                        }
+                        break;
+                    case Lines2D.ArrowCap:
+                        ht *= 2;
+                    case Lines2D.TriangleCap:
+                        {
+                            if (borderMode) {
+                                var f = type === Lines2D.TriangleCap ? bt : Math.sqrt(bt * bt * 2);
+                                var v1 = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, ht));
+                                var v2 = storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(ht, 0));
+                                var v3 = storeVertex(vb, vbi, 2, pos, angle, new BABYLON.Vector2(0, -ht));
+                                var v4 = storeVertex(vb, vbi, 3, pos, angle, new BABYLON.Vector2(0, ht + f));
+                                var v5 = storeVertex(vb, vbi, 4, pos, angle, new BABYLON.Vector2(ht + f, 0));
+                                var v6 = storeVertex(vb, vbi, 5, pos, angle, new BABYLON.Vector2(0, -(ht + f)));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v5);
+                                if (type === Lines2D.ArrowCap) {
+                                    var rht = thickness / 2;
+                                    var v7 = storeVertex(vb, vbi, 6, pos, angle, new BABYLON.Vector2(0, rht + bt));
+                                    var v8 = storeVertex(vb, vbi, 7, pos, angle, new BABYLON.Vector2(-bt, rht + bt));
+                                    var v9 = storeVertex(vb, vbi, 8, pos, angle, new BABYLON.Vector2(-bt, ht + f));
+                                    var v10 = storeVertex(vb, vbi, 9, pos, angle, new BABYLON.Vector2(0, -(rht + bt)));
+                                    var v11 = storeVertex(vb, vbi, 10, pos, angle, new BABYLON.Vector2(-bt, -(rht + bt)));
+                                    var v12 = storeVertex(vb, vbi, 11, pos, angle, new BABYLON.Vector2(-bt, -(ht + f)));
+                                    storeIndex(ib, ibi, ii++, v7);
+                                    storeIndex(ib, ibi, ii++, v8);
+                                    storeIndex(ib, ibi, ii++, v9);
+                                    storeIndex(ib, ibi, ii++, v7);
+                                    storeIndex(ib, ibi, ii++, v9);
+                                    storeIndex(ib, ibi, ii++, v4);
+                                    storeIndex(ib, ibi, ii++, v10);
+                                    storeIndex(ib, ibi, ii++, v12);
+                                    storeIndex(ib, ibi, ii++, v11);
+                                    storeIndex(ib, ibi, ii++, v10);
+                                    storeIndex(ib, ibi, ii++, v6);
+                                    storeIndex(ib, ibi, ii++, v12);
+                                }
+                            }
+                            else {
+                                var v1 = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, ht));
+                                var v2 = storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(ht, 0));
+                                var v3 = storeVertex(vb, vbi, 2, pos, angle, new BABYLON.Vector2(0, -ht));
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                            }
+                            break;
+                        }
+                    case Lines2D.RoundCap:
+                        {
+                            if (borderMode) {
+                                var curA = -Math.PI / 2;
+                                var incA = Math.PI / (sd / 2 - 1);
+                                var ii = 0;
+                                for (var i = 0; i < (sd / 2); i++) {
+                                    var v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                    var v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * (ht + bt), Math.sin(curA) * (ht + bt)));
+                                    if (i > 0) {
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1);
+                                    }
+                                    curA += incA;
+                                }
+                            }
+                            else {
+                                var c = storeVertex(vb, vbi, 0, pos, angle, new BABYLON.Vector2(0, 0));
+                                var curA = -Math.PI / 2;
+                                var incA = Math.PI / (sd / 2 - 1);
+                                storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                curA += incA;
+                                for (var i = 1; i < (sd / 2); i++) {
+                                    var v2 = storeVertex(vb, vbi, i + 1, pos, angle, new BABYLON.Vector2(Math.cos(curA) * ht, Math.sin(curA) * ht));
+                                    storeIndex(ib, ibi, i * 3 + 0, c);
+                                    storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                    storeIndex(ib, ibi, i * 3 + 2, v2);
+                                    curA += incA;
+                                }
+                            }
+                            break;
+                        }
+                    case Lines2D.SquareAnchorCap:
+                        {
+                            var vi = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, t));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2, t));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2, -t));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -t));
+                            if (borderMode) {
+                                var v5 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht + bt));
+                                var v6 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, ht + bt));
+                                var v7 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, t + bt));
+                                var v8 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2 + bt, t + bt));
+                                var v9 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(t * 2 + bt, -(t + bt)));
+                                var v10 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, -(t + bt)));
+                                var v11 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-bt, -(ht + bt)));
+                                var v12 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -(ht + bt)));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v5);
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v11);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v11);
+                                storeIndex(ib, ibi, ii++, v12);
+                                storeIndex(ib, ibi, ii++, v4);
+                            }
+                            else {
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                                storeIndex(ib, ibi, 3, v1);
+                                storeIndex(ib, ibi, 4, v3);
+                                storeIndex(ib, ibi, 5, v4);
+                            }
+                            break;
+                        }
+                    case Lines2D.RoundAnchorCap:
+                        {
+                            var cpos = Math.sqrt(t * t - ht * ht);
+                            var center = new BABYLON.Vector2(cpos, 0);
+                            var curA = BABYLON.Tools.ToRadians(-150);
+                            var incA = BABYLON.Tools.ToRadians(300) / (sd - 1);
+                            if (borderMode) {
+                                var ii = 0;
+                                for (var i = 0; i < sd; i++) {
+                                    var v1 = storeVertex(vb, vbi, i * 2 + 0, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                    var v2 = storeVertex(vb, vbi, i * 2 + 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * (t + bt), Math.sin(curA) * (t + bt)));
+                                    if (i > 0) {
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1 - 2);
+                                        storeIndex(ib, ibi, ii++, v2);
+                                        storeIndex(ib, ibi, ii++, v1);
+                                    }
+                                    curA += incA;
+                                }
+                            }
+                            else {
+                                var c = storeVertex(vb, vbi, 0, pos, angle, center);
+                                storeVertex(vb, vbi, 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                curA += incA;
+                                for (var i = 1; i < sd; i++) {
+                                    var v2 = storeVertex(vb, vbi, i + 1, pos, angle, new BABYLON.Vector2(cpos + Math.cos(curA) * t, Math.sin(curA) * t));
+                                    storeIndex(ib, ibi, i * 3 + 0, c);
+                                    storeIndex(ib, ibi, i * 3 + 1, v2 - 1);
+                                    storeIndex(ib, ibi, i * 3 + 2, v2);
+                                    curA += incA;
+                                }
+                                storeIndex(ib, ibi, sd * 3 + 0, c);
+                                storeIndex(ib, ibi, sd * 3 + 1, c + 1);
+                                storeIndex(ib, ibi, sd * 3 + 2, c + sd);
+                            }
+                            break;
+                        }
+                    case Lines2D.DiamondAnchorCap:
+                        {
+                            var vi = 0;
+                            var v1 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, ht));
+                            var v2 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, t));
+                            var v3 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht * 3, 0));
+                            var v4 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, -t));
+                            var v5 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(0, -ht));
+                            if (borderMode) {
+                                var f = Math.sqrt(bt * bt * 2);
+                                var v6 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-f, ht));
+                                var v7 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, t + f));
+                                var v8 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht * 3 + f, 0));
+                                var v9 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(ht, -(t + f)));
+                                var v10 = storeVertex(vb, vbi, vi++, pos, angle, new BABYLON.Vector2(-f, -ht));
+                                var ii = 0;
+                                storeIndex(ib, ibi, ii++, v6);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v1);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v7);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v2);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v8);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v3);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v9);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v4);
+                                storeIndex(ib, ibi, ii++, v10);
+                                storeIndex(ib, ibi, ii++, v5);
+                            }
+                            else {
+                                storeIndex(ib, ibi, 0, v1);
+                                storeIndex(ib, ibi, 1, v2);
+                                storeIndex(ib, ibi, 2, v3);
+                                storeIndex(ib, ibi, 3, v1);
+                                storeIndex(ib, ibi, 4, v3);
+                                storeIndex(ib, ibi, 5, v5);
+                                storeIndex(ib, ibi, 6, v5);
+                                storeIndex(ib, ibi, 7, v3);
+                                storeIndex(ib, ibi, 8, v4);
+                            }
+                            break;
+                        }
+                }
+                return null;
+            };
+            var buildLine = function (vb, contour, ht, bt) {
+                var lineA = BABYLON.Vector2.Zero();
+                var lineB = BABYLON.Vector2.Zero();
+                var tangent = BABYLON.Vector2.Zero();
+                var miter = BABYLON.Vector2.Zero();
+                var curNormal = null;
+                if (_this.closed) {
+                    _this.points.push(_this.points[0]);
+                }
+                var total = _this.points.length;
+                for (var i = 1; i < total; i++) {
+                    var last = _this.points[i - 1];
+                    var cur = _this.points[i];
+                    var next = (i < (_this.points.length - 1)) ? _this.points[i + 1] : null;
+                    direction(cur, last, lineA);
+                    if (!curNormal) {
+                        curNormal = BABYLON.Vector2.Zero();
+                        perp(lineA, curNormal);
+                    }
+                    if (i === 1) {
+                        store(vb, contour, 0, total, _this.points[0], curNormal, ht, bt);
+                    }
+                    if (!next) {
+                        perp(lineA, curNormal);
+                        store(vb, contour, i, total, _this.points[i], curNormal, ht, bt, i - 1);
+                    }
+                    else {
+                        direction(next, cur, lineB);
+                        var miterLen = computeMiter(tangent, miter, lineA, lineB);
+                        store(vb, contour, i, total, _this.points[i], miter, miterLen * ht, miterLen * bt, i - 1);
+                    }
+                }
+                if (_this.points.length > 2 && _this.closed) {
+                    var last2 = _this.points[total - 2];
+                    var cur2 = _this.points[0];
+                    var next2 = _this.points[1];
+                    direction(cur2, last2, lineA);
+                    direction(next2, cur2, lineB);
+                    perp(lineA, curNormal);
+                    var miterLen2 = computeMiter(tangent, miter, lineA, lineB);
+                    store(vb, null, 0, total, _this.points[0], miter, miterLen2 * ht, miterLen2 * bt, 1);
+                    // Patch contour
+                    if (contour) {
+                        var off = (bt == null) ? 0 : 4;
+                        contour[0].x = vb[off + 0];
+                        contour[0].y = vb[off + 1];
+                        contour[1].x = vb[off + 2];
+                        contour[1].y = vb[off + 3];
+                    }
+                }
+                // Remove the point we added at the beginning
+                if (_this.closed) {
+                    _this.points.splice(total - 1);
+                }
+            };
+            var contour = new Array();
+            // Need to create WebGL resources for fill part?
+            if (this.fill) {
+                var startCapInfo = getCapSize(this.startCap);
+                var endCapInfo = getCapSize(this.endCap);
+                var count = this.points.length;
+                var vbSize = (count * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                var vb = new Float32Array(vbSize);
+                var ht = this.fillThickness / 2;
+                var total = this.points.length;
+                buildLine(vb, this.border ? null : contour, ht);
+                var max = total * 2;
+                var triCount = (count - (this.closed ? 0 : 1)) * 2;
+                var ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (var i = 0; i < triCount; i += 2) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 1;
+                    ib[i * 3 + 2] = (i + 2) % max;
+                    ib[i * 3 + 3] = i + 1;
+                    ib[i * 3 + 4] = (i + 3) % max;
+                    ib[i * 3 + 5] = (i + 2) % max;
+                }
+                buildCap(vb, count * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, null, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, null, this.endCap, endDir);
+                renderCache.fillVB = engine.createVertexBuffer(vb);
+                renderCache.fillIB = engine.createIndexBuffer(ib);
+                renderCache.fillIndicesCount = ib.length;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_FILLPARTID, ["position"]);
+                renderCache.effectFill = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            // Need to create WebGL resources for border part?
+            if (this.border) {
+                var startCapInfo = getCapSize(this.startCap, true);
+                var endCapInfo = getCapSize(this.endCap, true);
+                var count = this.points.length;
+                var vbSize = (count * 2 * 2 * 2) + startCapInfo.vbsize + endCapInfo.vbsize;
+                var vb = new Float32Array(vbSize);
+                var ht = this.fillThickness / 2;
+                var bt = this.borderThickness;
+                var total = this.points.length;
+                buildLine(vb, contour, ht, bt);
+                var max = total * 2 * 2;
+                var triCount = (count - (this.closed ? 0 : 1)) * 2 * 2;
+                var ib = new Float32Array(triCount * 3 + startCapInfo.ibsize + endCapInfo.ibsize);
+                for (var i = 0; i < triCount; i += 4) {
+                    ib[i * 3 + 0] = i + 0;
+                    ib[i * 3 + 1] = i + 2;
+                    ib[i * 3 + 2] = (i + 6) % max;
+                    ib[i * 3 + 3] = i + 0;
+                    ib[i * 3 + 4] = (i + 6) % max;
+                    ib[i * 3 + 5] = (i + 4) % max;
+                    ib[i * 3 + 6] = i + 3;
+                    ib[i * 3 + 7] = i + 1;
+                    ib[i * 3 + 8] = (i + 5) % max;
+                    ib[i * 3 + 9] = i + 3;
+                    ib[i * 3 + 10] = (i + 5) % max;
+                    ib[i * 3 + 11] = (i + 7) % max;
+                }
+                buildCap(vb, count * 2 * 2 * 2, ib, triCount * 3, this.points[0], this.fillThickness, this.borderThickness, this.startCap, startDir);
+                buildCap(vb, (count * 2 * 2 * 2) + startCapInfo.vbsize, ib, (triCount * 3) + startCapInfo.ibsize, this.points[total - 1], this.fillThickness, this.borderThickness, this.endCap, endDir);
+                renderCache.borderVB = engine.createVertexBuffer(vb);
+                renderCache.borderIB = engine.createIndexBuffer(ib);
+                renderCache.borderIndicesCount = ib.length;
+                var ei = this.getDataPartEffectInfo(BABYLON.Shape2D.SHAPE2D_BORDERPARTID, ["position"]);
+                renderCache.effectBorder = engine.createEffect({ vertex: "lines2d", fragment: "lines2d" }, ei.attributes, ei.uniforms, [], ei.defines, null);
+            }
+            this._contour = contour;
+            var bs = this._boundingMax.subtract(this._boundingMin);
+            this._size.width = bs.x;
+            this._size.height = bs.y;
+            return renderCache;
+        };
+        Lines2D.prototype.createInstanceDataParts = function () {
+            var res = new Array();
+            if (this.border) {
+                res.push(new Lines2DInstanceData(BABYLON.Shape2D.SHAPE2D_BORDERPARTID));
+            }
+            if (this.fill) {
+                res.push(new Lines2DInstanceData(BABYLON.Shape2D.SHAPE2D_FILLPARTID));
+            }
+            return res;
+        };
+        Lines2D.prototype.refreshInstanceDataPart = function (part) {
+            if (!_super.prototype.refreshInstanceDataPart.call(this, part)) {
+                return false;
+            }
+            if (part.id === BABYLON.Shape2D.SHAPE2D_BORDERPARTID) {
+                var d = part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            else if (part.id === BABYLON.Shape2D.SHAPE2D_FILLPARTID) {
+                var d = part;
+                d.boundingMin = this.boundingMin;
+                d.boundingMax = this.boundingMax;
+            }
+            return true;
+        };
+        Lines2D._noCap = 0;
+        Lines2D._roundCap = 1;
+        Lines2D._triangleCap = 2;
+        Lines2D._squareAnchorCap = 3;
+        Lines2D._roundAnchorCap = 4;
+        Lines2D._diamondAnchorCap = 5;
+        Lines2D._arrowCap = 6;
+        Lines2D._roundCapSubDiv = 36;
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 1, function (pi) { return Lines2D.pointsProperty = pi; })
+        ], Lines2D.prototype, "points", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 2, function (pi) { return Lines2D.fillThicknessProperty = pi; })
+        ], Lines2D.prototype, "fillThickness", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 3, function (pi) { return Lines2D.closedProperty = pi; })
+        ], Lines2D.prototype, "closed", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 4, function (pi) { return Lines2D.startCapProperty = pi; })
+        ], Lines2D.prototype, "startCap", null);
+        __decorate([
+            BABYLON.modelLevelProperty(BABYLON.Shape2D.SHAPE2D_PROPCOUNT + 5, function (pi) { return Lines2D.endCapProperty = pi; })
+        ], Lines2D.prototype, "endCap", null);
+        Lines2D = __decorate([
+            BABYLON.className("Lines2D")
+        ], Lines2D);
+        return Lines2D;
+    }(BABYLON.Shape2D));
+    BABYLON.Lines2D = Lines2D;
+})(BABYLON || (BABYLON = {}));

+ 40 - 4
src/Canvas2d/babylon.renderablePrim2d.js

@@ -27,7 +27,7 @@ var BABYLON;
             }
         };
         InstanceClassInfo.prototype.getInstancingAttributeInfos = function (effect, categories) {
-            var catInline = categories.join(";");
+            var catInline = ";" + categories.join(";") + ";";
             var res = new Array();
             var curInfo = this;
             while (curInfo) {
@@ -76,7 +76,6 @@ var BABYLON;
     }());
     BABYLON.InstanceClassInfo = InstanceClassInfo;
     var InstancePropInfo = (function () {
-        //uniformLocation: WebGLUniformLocation;
         function InstancePropInfo() {
             this.instanceOffset = new BABYLON.StringDictionary();
         }
@@ -114,6 +113,11 @@ var BABYLON;
                 this.dataType = 6 /* Color4 */;
                 return;
             }
+            if (val instanceof BABYLON.Size) {
+                this.size = 8;
+                this.dataType = 7 /* Size */;
+                return;
+            }
             return;
         };
         InstancePropInfo.prototype.writeData = function (array, offset, val) {
@@ -173,6 +177,13 @@ var BABYLON;
                         }
                         break;
                     }
+                case 7 /* Size */:
+                    {
+                        var s = val;
+                        array[offset + 0] = s.width;
+                        array[offset + 1] = s.height;
+                        break;
+                    }
             }
         };
         return InstancePropInfo;
@@ -191,11 +202,19 @@ var BABYLON;
             info = new InstancePropInfo();
             info.attributeName = shaderAttributeName;
             info.category = category || null;
+            if (info.category) {
+                info.delimitedCategory = ";" + info.category + ";";
+            }
             node.levelContent.add(instanceDataName, info);
             descriptor.get = function () {
                 return null;
             };
             descriptor.set = function (val) {
+                // Check that we're not trying to set a property that belongs to a category that is not allowed (current)
+                // Quit if it's the case, otherwise we could overwrite data somewhere...
+                if (info.category && InstanceClassInfo._CurCategories.indexOf(info.delimitedCategory) === -1) {
+                    return;
+                }
                 if (!info.size) {
                     info.setSize(val);
                     node.classContent.mapProperty(info, true);
@@ -391,7 +410,7 @@ var BABYLON;
                         this.isVisible = true;
                         // We manually trigger refreshInstanceData for the only sake of evaluating each instance property size and offset in the instance data, this can only be made at runtime. Once it's done we have all the information to create the instance data buffer.
                         //console.log("Build Prop Layout for " + Tools.getClassName(this._instanceDataParts[0]));
-                        var joinCat = cat.join(";");
+                        var joinCat = ";" + cat.join(";") + ";";
                         joinedUsedCatList.push(joinCat);
                         InstanceClassInfo._CurCategories = joinCat;
                         var obj = this.beforeRefreshForLayoutConstruction(dataPart);
@@ -432,7 +451,7 @@ var BABYLON;
                         gii._partIndexFromId.add(this._modelRenderCache._partIdList[j].toString(), j);
                         for (var _a = 0, _b = this._instanceDataParts; _a < _b.length; _a++) {
                             var part = _b[_a];
-                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = this.getUsedShaderCategories(part).join(";");
+                            gii._instancesPartsUsedShaderCategories[gii._partIndexFromId.get(part.id.toString())] = ";" + this.getUsedShaderCategories(part).join(";") + ";";
                         }
                     }
                 }
@@ -477,6 +496,23 @@ var BABYLON;
                 this._visibilityChanged = false; // Reset the flag as we've handled the case
             }
         };
+        /**
+         * Transform a given point using the Primitive's origin setting.
+         * This method requires the Primitive's actualSize to be accurate
+         * @param p the point to transform
+         * @param originOffset an offset applied on the current origin before performing the transformation. Depending on which frame of reference your data is expressed you may have to apply a offset. (if you data is expressed from the bottom/left, no offset is required. If it's expressed from the center the a [-0.5;-0.5] offset has to be applied.
+         * @param res an allocated Vector2 that will receive the transformed content
+         */
+        RenderablePrim2D.prototype.transformPointWithOriginByRef = function (p, originOffset, res) {
+            var actualSize = this.actualSize;
+            res.x = p.x - ((this.origin.x + (originOffset ? originOffset.x : 0)) * actualSize.width);
+            res.y = p.y - ((this.origin.y + (originOffset ? originOffset.y : 0)) * actualSize.height);
+        };
+        RenderablePrim2D.prototype.transformPointWithOrigin = function (p, originOffset) {
+            var res = new BABYLON.Vector2(0, 0);
+            this.transformPointWithOriginByRef(p, originOffset, res);
+            return res;
+        };
         RenderablePrim2D.prototype.getDataPartEffectInfo = function (dataPartId, vertexBufferAttributes) {
             var dataPart = BABYLON.Tools.first(this._instanceDataParts, function (i) { return i.id === dataPartId; });
             if (!dataPart) {

+ 13 - 0
src/Canvas2d/babylon.smartPropertyPrim.js

@@ -190,6 +190,16 @@ var BABYLON;
                 propDic.forEach(function (k, v) {
                     if (v.kind === Prim2DPropInfo.PROPKIND_MODEL) {
                         var propVal = _this[v.name];
+                        // Special case, array, this WON'T WORK IN ALL CASES, all entries have to be of the same type and it must be a BJS well known one
+                        if (propVal && propVal.constructor === Array) {
+                            var firstVal = propVal[0];
+                            if (!firstVal) {
+                                propVal = 0;
+                            }
+                            else {
+                                propVal = BABYLON.Tools.hashCodeFromStream(BABYLON.Tools.arrayOrStringFeeder(propVal));
+                            }
+                        }
                         modelKey += v.name + ":" + ((propVal != null) ? ((v.typeLevelCompare) ? BABYLON.Tools.getClassName(propVal) : propVal.toString()) : "[null]") + ";";
                     }
                 });
@@ -350,6 +360,9 @@ var BABYLON;
             this._instanceDirtyFlags &= ~flags;
             return this._instanceDirtyFlags;
         };
+        SmartPropertyPrim.prototype._resetPropertiesDirty = function () {
+            this._instanceDirtyFlags = 0;
+        };
         Object.defineProperty(SmartPropertyPrim.prototype, "levelBoundingInfo", {
             /**
              * Retrieve the boundingInfo for this Primitive, computed based on the primitive itself and NOT its children

+ 27 - 0
src/Math/babylon.math.js

@@ -370,12 +370,22 @@ var BABYLON;
         Vector2.prototype.add = function (otherVector) {
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         };
+        Vector2.prototype.addToRef = function (otherVector, result) {
+            result.x = this.x + otherVector.x;
+            result.y = this.y + otherVector.y;
+            return this;
+        };
         Vector2.prototype.addVector3 = function (otherVector) {
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         };
         Vector2.prototype.subtract = function (otherVector) {
             return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
         };
+        Vector2.prototype.subtractToRef = function (otherVector, result) {
+            result.x = this.x - otherVector.x;
+            result.y = this.y - otherVector.y;
+            return this;
+        };
         Vector2.prototype.subtractInPlace = function (otherVector) {
             this.x -= otherVector.x;
             this.y -= otherVector.y;
@@ -520,6 +530,13 @@ var BABYLON;
             result.x = x;
             result.y = y;
         };
+        Vector2.PointInTriangle = function (p, p0, p1, p2) {
+            var a = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y);
+            var sign = a < 0 ? -1 : 1;
+            var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign;
+            var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign;
+            return s > 0 && t > 0 && (s + t) < 2 * a * sign;
+        };
         Vector2.Distance = function (value1, value2) {
             return Math.sqrt(Vector2.DistanceSquared(value1, value2));
         };
@@ -528,6 +545,16 @@ var BABYLON;
             var y = value1.y - value2.y;
             return (x * x) + (y * y);
         };
+        Vector2.DistanceOfPointFromSegment = function (p, segA, segB) {
+            var l2 = Vector2.DistanceSquared(segA, segB);
+            if (l2 === 0.0) {
+                return Vector2.Distance(p, segA);
+            }
+            var v = segB.subtract(segA);
+            var t = Math.max(0, Math.min(1, Vector2.Dot(p.subtract(segA), v) / l2));
+            var proj = segA.add(v.multiplyByFloats(t, t));
+            return Vector2.Distance(p, proj);
+        };
         return Vector2;
     }());
     BABYLON.Vector2 = Vector2;

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

@@ -452,10 +452,6 @@
             return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
         }
 
-        public addVector3(otherVector: Vector3): Vector2 {
-            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
-        }
-
         public addToRef(otherVector: Vector2, result: Vector2): Vector2 {
             result.x = this.x + otherVector.x;
             result.y = this.y + otherVector.y;
@@ -463,6 +459,10 @@
             return this;
         }
 
+        public addVector3(otherVector: Vector3): Vector2 {
+            return new Vector2(this.x + otherVector.x, this.y + otherVector.y);
+        }
+
         public subtract(otherVector: Vector2): Vector2 {
             return new Vector2(this.x - otherVector.x, this.y - otherVector.y);
         }

+ 2 - 2
src/Tools/babylon.dynamicFloatArray.js

@@ -87,7 +87,7 @@ var BABYLON;
             var sortedAll = this._allEntries.sort(function (a, b) { return a.offset - b.offset; });
             var firstFreeSlotOffset = sortedFree[0].offset;
             var freeZoneSize = 1;
-            var occupiedZoneSize = this.usedElementCount * s;
+            var occupiedZoneSize = (this.usedElementCount + 1) * s;
             var prevOffset = sortedFree[0].offset;
             for (var i = 1; i < sortedFree.length; i++) {
                 // If the first free (which means everything before is occupied) is greater or equal the occupied zone size, it means everything is defragmented, we can quit
@@ -129,7 +129,7 @@ var BABYLON;
                 // Free Zone is smaller or equal so it's no longer a free zone, set the new one to the current location
                 if (freeZoneSize <= usedRange) {
                     firstFreeSlotOffset = curMoveOffset + s;
-                    freeZoneSize = 1;
+                    freeZoneSize = 1 + copyCount;
                 }
                 else {
                     freeZoneSize = ((curOffset - firstFreeSlotOffset) / s) + 1;

+ 37 - 0
src/Tools/babylon.tools.js

@@ -146,6 +146,43 @@ var BABYLON;
                 maximum: maximum
             };
         };
+        Tools.Vector2ArrayFeeder = function (array) {
+            return function (index) {
+                var isFloatArray = (array.BYTES_PER_ELEMENT !== undefined);
+                var length = isFloatArray ? array.length / 2 : array.length;
+                if (index >= length) {
+                    return null;
+                }
+                if (isFloatArray) {
+                    var fa = array;
+                    return new BABYLON.Vector2(fa[index * 2 + 0], fa[index * 2 + 1]);
+                }
+                var a = array;
+                return a[index];
+            };
+        };
+        Tools.ExtractMinAndMaxVector2 = function (feeder, bias) {
+            if (bias === void 0) { bias = null; }
+            var minimum = new BABYLON.Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
+            var maximum = new BABYLON.Vector2(-Number.MAX_VALUE, -Number.MAX_VALUE);
+            var i = 0;
+            var cur = feeder(i++);
+            while (cur) {
+                minimum = BABYLON.Vector2.Minimize(cur, minimum);
+                maximum = BABYLON.Vector2.Maximize(cur, maximum);
+                cur = feeder(i++);
+            }
+            if (bias) {
+                minimum.x -= minimum.x * bias.x + bias.y;
+                minimum.y -= minimum.y * bias.x + bias.y;
+                maximum.x += maximum.x * bias.x + bias.y;
+                maximum.y += maximum.y * bias.x + bias.y;
+            }
+            return {
+                minimum: minimum,
+                maximum: maximum
+            };
+        };
         Tools.MakeArray = function (obj, allowsNullUndefined) {
             if (allowsNullUndefined !== true && (obj === undefined || obj == null))
                 return undefined;