浏览代码

Merge remote-tracking branch 'origin/master' into worker-collisions

Raanan Weber 10 年之前
父节点
当前提交
6d1af7b626

+ 58 - 24
Babylon/Math/babylon.math.js

@@ -2606,15 +2606,17 @@ var BABYLON;
     })();
     BABYLON.Path2 = Path2;
     var Path3D = (function () {
-        function Path3D(path) {
+        function Path3D(path, firstNormal) {
             this.path = path;
             this._curve = new Array();
             this._distances = new Array();
             this._tangents = new Array();
             this._normals = new Array();
             this._binormals = new Array();
-            this._curve = path.slice(); // copy array  
-            this._compute();
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p] = path[p].clone(); // hard copy
+            }
+            this._compute(firstNormal);
         }
         Path3D.prototype.getCurve = function () {
             return this._curve;
@@ -2631,28 +2633,30 @@ var BABYLON;
         Path3D.prototype.getDistances = function () {
             return this._distances;
         };
-        Path3D.prototype.update = function (path) {
-            for (var i = 0; i < path.length; i++) {
-                this._curve[i] = path[i];
+        Path3D.prototype.update = function (path, firstNormal) {
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p].x = path[p].x;
+                this._curve[p].y = path[p].y;
+                this._curve[p].z = path[p].z;
             }
-            this._compute();
+            this._compute(firstNormal);
             return this;
         };
         // private function compute() : computes tangents, normals and binormals
-        Path3D.prototype._compute = function () {
+        Path3D.prototype._compute = function (firstNormal) {
             var l = this._curve.length;
             // first and last tangents
-            this._tangents[0] = this._curve[1].subtract(this._curve[0]);
+            this._tangents[0] = this._getFirstNonNullVector(0);
             this._tangents[0].normalize();
             this._tangents[l - 1] = this._curve[l - 1].subtract(this._curve[l - 2]);
             this._tangents[l - 1].normalize();
             // normals and binormals at first point : arbitrary vector with _normalVector()
             var tg0 = this._tangents[0];
-            var pp0 = this._normalVector(this._curve[0], tg0);
+            var pp0 = this._normalVector(this._curve[0], tg0, firstNormal);
             this._normals[0] = pp0;
             this._normals[0].normalize();
             this._binormals[0] = Vector3.Cross(tg0, this._normals[0]);
-            this._normals[0].normalize();
+            this._binormals[0].normalize();
             this._distances[0] = 0;
             // normals and binormals : next points
             var prev; // previous vector (segment)
@@ -2662,9 +2666,9 @@ var BABYLON;
             var prevBinor; // previous binormal
             for (var i = 1; i < l; i++) {
                 // tangents
-                prev = this._curve[i].subtract(this._curve[i - 1]);
+                prev = this._getLastNonNullVector(i);
                 if (i < l - 1) {
-                    cur = this._curve[i + 1].subtract(this._curve[i]);
+                    cur = this._getFirstNonNullVector(i);
                     this._tangents[i] = prev.add(cur);
                     this._tangents[i].normalize();
                 }
@@ -2680,20 +2684,50 @@ var BABYLON;
                 this._binormals[i].normalize();
             }
         };
-        // private function normalVector(v0, vt) :
-        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
-        Path3D.prototype._normalVector = function (v0, vt) {
-            var point;
-            if (vt.x !== 1) {
-                point = new Vector3(1, 0, 0);
+        // private function getFirstNonNullVector(index)
+        // returns the first non null vector from index : curve[index + N].subtract(curve[index])
+        Path3D.prototype._getFirstNonNullVector = function (index) {
+            var i = 1;
+            var nNVector = this._curve[index + i].subtract(this._curve[index]);
+            while (nNVector.length() == 0 && index + i + 1 < this._curve.length) {
+                i++;
+                nNVector = this._curve[index + i].subtract(this._curve[index]);
             }
-            else if (vt.y !== 1) {
-                point = new Vector3(0, 1, 0);
+            return nNVector;
+        };
+        // private function getLastNonNullVector(index)
+        // returns the last non null vector from index : curve[index].subtract(curve[index - N])
+        Path3D.prototype._getLastNonNullVector = function (index) {
+            var i = 1;
+            var nLVector = this._curve[index].subtract(this._curve[index - i]);
+            while (nLVector.length() == 0 && index > i + 1) {
+                i++;
+                nLVector = this._curve[index].subtract(this._curve[index - i]);
+            }
+            return nLVector;
+        };
+        // private function normalVector(v0, vt, va) :
+        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
+        // if va is passed, it returns the va projection on the plane orthogonal to vt at the point v0
+        Path3D.prototype._normalVector = function (v0, vt, va) {
+            var normal0;
+            if (va === undefined || va === null) {
+                var point;
+                if (vt.x !== 1) {
+                    point = new Vector3(1, 0, 0);
+                }
+                else if (vt.y !== 1) {
+                    point = new Vector3(0, 1, 0);
+                }
+                else if (vt.z !== 1) {
+                    point = new Vector3(0, 0, 1);
+                }
+                normal0 = Vector3.Cross(vt, point);
             }
-            else if (vt.z !== 1) {
-                point = new Vector3(0, 0, 1);
+            else {
+                normal0 = Vector3.Cross(vt, va);
+                Vector3.CrossToRef(normal0, vt, normal0);
             }
-            var normal0 = Vector3.Cross(vt, point);
             normal0.normalize();
             return normal0;
         };

+ 88 - 27
Babylon/Math/babylon.math.ts

@@ -3281,9 +3281,11 @@
         private _normals = new Array<Vector3>();
         private _binormals = new Array<Vector3>();
 
-        constructor(public path: Vector3[]) {
-            this._curve = path.slice();   // copy array  
-            this._compute();
+        constructor(public path: Vector3[], firstNormal?: Vector3) {
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p] = path[p].clone(); // hard copy
+            }
+            this._compute(firstNormal);
         }
 
         public getCurve(): Vector3[] {
@@ -3306,31 +3308,33 @@
             return this._distances;
         }
 
-        public update(path: Vector3[]): Path3D {
-            for (var i = 0; i < path.length; i++) {
-                this._curve[i] = path[i];
+        public update(path: Vector3[], firstNormal?: Vector3): Path3D {
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p].x = path[p].x;
+                this._curve[p].y = path[p].y;
+                this._curve[p].z = path[p].z;
             }
-            this._compute();
+            this._compute(firstNormal);
             return this;
         }
 
         // private function compute() : computes tangents, normals and binormals
-        private _compute() {
+        private _compute(firstNormal) {
             var l = this._curve.length;
 
             // first and last tangents
-            this._tangents[0] = this._curve[1].subtract(this._curve[0]);
+            this._tangents[0] = this._getFirstNonNullVector(0);
             this._tangents[0].normalize();
             this._tangents[l - 1] = this._curve[l - 1].subtract(this._curve[l - 2]);
             this._tangents[l - 1].normalize();
             
             // normals and binormals at first point : arbitrary vector with _normalVector()
             var tg0 = this._tangents[0];
-            var pp0 = this._normalVector(this._curve[0], tg0);
+            var pp0 = this._normalVector(this._curve[0], tg0, firstNormal);
             this._normals[0] = pp0;
             this._normals[0].normalize();
             this._binormals[0] = Vector3.Cross(tg0, this._normals[0]);
-            this._normals[0].normalize();
+            this._binormals[0].normalize();
             this._distances[0] = 0;
 
             // normals and binormals : next points
@@ -3342,9 +3346,9 @@
 
             for (var i = 1; i < l; i++) {
                 // tangents
-                prev = this._curve[i].subtract(this._curve[i - 1]);
+                prev = this._getLastNonNullVector(i);
                 if (i < l - 1) {
-                    cur = this._curve[i + 1].subtract(this._curve[i]);
+                    cur = this._getFirstNonNullVector(i);
                     this._tangents[i] = prev.add(cur);
                     this._tangents[i].normalize();
                 }
@@ -3362,21 +3366,53 @@
             }
         }
 
-        // private function normalVector(v0, vt) :
-        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
-        private _normalVector(v0: Vector3, vt: Vector3): Vector3 {
-            var point: Vector3;
+        // private function getFirstNonNullVector(index)
+        // returns the first non null vector from index : curve[index + N].subtract(curve[index])
+        private _getFirstNonNullVector(index: number): Vector3 {
+            var i = 1;
+            var nNVector: Vector3 = this._curve[index + i].subtract(this._curve[index]);
+            while (nNVector.length() == 0 && index + i + 1 < this._curve.length) {
+                i++;
+                nNVector = this._curve[index + i].subtract(this._curve[index]);
+            }
+            return nNVector;
+        }
 
-            if (vt.x !== 1) {     // search for a point in the plane
-                point = new Vector3(1, 0, 0);
+        // private function getLastNonNullVector(index)
+        // returns the last non null vector from index : curve[index].subtract(curve[index - N])
+        private _getLastNonNullVector(index: number): Vector3 {
+            var i = 1;
+            var nLVector: Vector3 = this._curve[index].subtract(this._curve[index - i]);
+            while (nLVector.length() == 0 && index > i + 1) {
+                i++;
+                nLVector = this._curve[index].subtract(this._curve[index - i]);
             }
-            else if (vt.y !== 1) {
-                point = new Vector3(0, 1, 0);
+            return nLVector;
+        }
+
+        // private function normalVector(v0, vt, va) :
+        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
+        // if va is passed, it returns the va projection on the plane orthogonal to vt at the point v0
+        private _normalVector(v0: Vector3, vt: Vector3, va: Vector3): Vector3 {
+            var normal0: Vector3;
+            if (va === undefined || va === null) {
+                var point: Vector3;
+                if (vt.x !== 1) {     // search for a point in the plane
+                    point = new Vector3(1, 0, 0);
+                }
+                else if (vt.y !== 1) {
+                    point = new Vector3(0, 1, 0);
+                }
+                else if (vt.z !== 1) {
+                    point = new Vector3(0, 0, 1);
+                }
+                normal0 = Vector3.Cross(vt, point);
             }
-            else if (vt.z !== 1) {
-                point = new Vector3(0, 0, 1);
+            else {
+                normal0 = Vector3.Cross(vt, va);
+                Vector3.CrossToRef(normal0, vt, normal0);
+                //normal0 = Vector3.Cross(normal0, vt);
             }
-            var normal0: Vector3 = Vector3.Cross(vt, point);
             normal0.normalize();
             return normal0;
         }
@@ -3384,8 +3420,9 @@
 
     export class Curve3 {
         private _points: Vector3[];
+        private _length:number = 0;
 
-        // QuadraticBezier(origin_V3, control_V3, destination_V3 )
+        // QuadraticBezier(origin_V3, control_V3, destination_V3, nbPoints)
         public static CreateQuadraticBezier(v0: Vector3, v1: Vector3, v2: Vector3, nbPoints: number): Curve3 {
             nbPoints = nbPoints > 2 ? nbPoints : 3;
             var bez = new Array<Vector3>();
@@ -3399,7 +3436,7 @@
             return new Curve3(bez);
         }
 
-        // CubicBezier(origin_V3, control1_V3, control2_V3, destination_V3)
+        // CubicBezier(origin_V3, control1_V3, control2_V3, destination_V3, nbPoints)
         public static CreateCubicBezier(v0: Vector3, v1: Vector3, v2: Vector3, v3: Vector3, nbPoints: number): Curve3 {
             nbPoints = nbPoints > 3 ? nbPoints : 4;
             var bez = new Array<Vector3>();
@@ -3413,14 +3450,29 @@
             return new Curve3(bez);
         }
 
+        // HermiteSpline(origin_V3, originTangent_V3, destination_V3, destinationTangent_V3, nbPoints)
+        public static CreateHermiteSpline(p1: Vector3, t1: Vector3, p2: Vector3, t2: Vector3, nbPoints: number): Curve3 {
+            var hermite = new Array<Vector3>();
+            var step = 1 / nbPoints;
+            for(var i = 0; i <= nbPoints; i++) {
+                hermite.push(BABYLON.Vector3.Hermite(p1, t1, p2, t2, i * step));
+            }
+            return new Curve3(hermite);
+        }
+
         constructor(points: Vector3[]) {
             this._points = points;
+            this._length = this._computeLength(points);
         }
 
         public getPoints() {
             return this._points;
         }
 
+        public length() {
+            return this._length;
+        }
+
         public continue(curve: Curve3): Curve3 {
             var lastPoint = this._points[this._points.length - 1];
             var continuedPoints = this._points.slice();
@@ -3428,7 +3480,16 @@
             for (var i = 1; i < curvePoints.length; i++) {
                 continuedPoints.push(curvePoints[i].subtract(curvePoints[0]).add(lastPoint));
             }
-            return new Curve3(continuedPoints);
+            var continuedCurve = new Curve3(continuedPoints);
+            return continuedCurve;
+        }
+
+        private _computeLength(path: Vector3[]): number {
+            var l = 0;
+            for (var i = 1; i < path.length; i++) {
+                l += (path[i].subtract(path[i - 1])).length();
+            }
+            return l;
         }
     }
 

+ 105 - 14
Babylon/Mesh/babylon.mesh.js

@@ -100,6 +100,34 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Mesh, "NO_CAP", {
+            get: function () {
+                return Mesh._NO_CAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_START", {
+            get: function () {
+                return Mesh._CAP_START;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_END", {
+            get: function () {
+                return Mesh._CAP_END;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_ALL", {
+            get: function () {
+                return Mesh._CAP_ALL;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Mesh.prototype, "hasLODLevels", {
             // Methods
             get: function () {
@@ -1088,23 +1116,23 @@ var BABYLON;
             return lines;
         };
         // Extrusion
-        Mesh.ExtrudeShape = function (name, shape, path, scale, rotation, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShape = function (name, shape, path, scale, rotation, cap, scene, updatable, sideOrientation, extrudedInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (extrudedInstance === void 0) { extrudedInstance = null; }
             scale = scale || 1;
             rotation = rotation || 0;
-            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, false, scene, updatable, sideOrientation, extrudedInstance);
+            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, extrudedInstance);
             return extruded;
         };
-        Mesh.ExtrudeShapeCustom = function (name, shape, path, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShapeCustom = function (name, shape, path, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, scene, updatable, sideOrientation, extrudedInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (extrudedInstance === void 0) { extrudedInstance = null; }
-            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, true, scene, updatable, sideOrientation, extrudedInstance);
+            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, extrudedInstance);
             return extrudedCustom;
         };
-        Mesh._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, custom, scene, updtbl, side, instance) {
+        Mesh._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance) {
             // extrusion geometry
-            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, custom) {
+            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var binormals = path3D.getBinormals();
@@ -1133,21 +1161,53 @@ var BABYLON;
                     angle += angleStep;
                     index++;
                 }
+                // cap
+                var capPath = function (shapePath) {
+                    var pointCap = Array();
+                    var barycenter = BABYLON.Vector3.Zero();
+                    var i;
+                    for (i = 0; i < shapePath.length; i++) {
+                        barycenter.addInPlace(shapePath[i]);
+                    }
+                    barycenter.scaleInPlace(1 / shapePath.length);
+                    for (i = 0; i < shapePath.length; i++) {
+                        pointCap.push(barycenter);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    default:
+                        break;
+                }
                 return shapePaths;
             };
             if (instance) {
                 var path3D = (instance.path3D).update(curve);
-                var pathArray = extrusionPathArray(shape, curve, instance.path3D, instance.pathArray, scale, rotation, scaleFunction, rotateFunction, custom);
+                var pathArray = extrusionPathArray(shape, curve, instance.path3D, instance.pathArray, scale, rotation, scaleFunction, rotateFunction, instance.cap, custom);
                 instance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, instance);
                 return instance;
             }
             // extruded shape creation
             var path3D = new BABYLON.Path3D(curve);
             var newShapePaths = new Array();
-            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, custom);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom);
             var extrudedGeneric = Mesh.CreateRibbon(name, pathArray, rbCA, rbCP, 0, scene, updtbl, side);
             extrudedGeneric.pathArray = pathArray;
             extrudedGeneric.path3D = path3D;
+            extrudedGeneric.cap = cap;
             return extrudedGeneric;
         };
         // Plane & ground
@@ -1201,11 +1261,11 @@ var BABYLON;
             }, scene.database);
             return ground;
         };
-        Mesh.CreateTube = function (name, path, radius, tessellation, radiusFunction, scene, updatable, sideOrientation, tubeInstance) {
+        Mesh.CreateTube = function (name, path, radius, tessellation, radiusFunction, cap, scene, updatable, sideOrientation, tubeInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (tubeInstance === void 0) { tubeInstance = null; }
             // tube geometry
-            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction) {
+            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var distances = path3D.getDistances();
@@ -1223,30 +1283,57 @@ var BABYLON;
                     rad = radiusFunctionFinal(i, distances[i]); // current radius
                     circlePath = Array(); // current circle array
                     normal = normals[i]; // current normal  
-                    for (var ang = 0; ang < pi2; ang += step) {
-                        rotationMatrix = BABYLON.Matrix.RotationAxis(tangents[i], ang);
+                    for (var t = 0; t < tessellation; t++) {
+                        rotationMatrix = BABYLON.Matrix.RotationAxis(tangents[i], step * t);
                         rotated = BABYLON.Vector3.TransformCoordinates(normal, rotationMatrix).scaleInPlace(rad).add(path[i]);
                         circlePath.push(rotated);
                     }
+                    circlePath.push(circlePath[0]);
                     circlePaths[index] = circlePath;
                     index++;
                 }
+                // cap
+                var capPath = function (nbPoints, pathIndex) {
+                    var pointCap = Array();
+                    for (var i = 0; i < nbPoints; i++) {
+                        pointCap.push(path[pathIndex]);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    default:
+                        break;
+                }
                 return circlePaths;
             };
             if (tubeInstance) {
                 var path3D = (tubeInstance.path3D).update(path);
-                var pathArray = tubePathArray(path, path3D, tubeInstance.pathArray, radius, tubeInstance.tessellation, radiusFunction);
+                var pathArray = tubePathArray(path, path3D, tubeInstance.pathArray, radius, tubeInstance.tessellation, radiusFunction, tubeInstance.cap);
                 tubeInstance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, tubeInstance);
                 return tubeInstance;
             }
             // tube creation
             var path3D = new BABYLON.Path3D(path);
             var newPathArray = new Array();
-            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap);
             var tube = Mesh.CreateRibbon(name, pathArray, false, true, 0, scene, updatable, sideOrientation);
             tube.pathArray = pathArray;
             tube.path3D = path3D;
             tube.tessellation = tessellation;
+            tube.cap = cap;
             return tube;
         };
         // Decals
@@ -1499,6 +1586,10 @@ var BABYLON;
         Mesh._BACKSIDE = 1;
         Mesh._DOUBLESIDE = 2;
         Mesh._DEFAULTSIDE = 0;
+        Mesh._NO_CAP = 0;
+        Mesh._CAP_START = 1;
+        Mesh._CAP_END = 2;
+        Mesh._CAP_ALL = 3;
         return Mesh;
     })(BABYLON.AbstractMesh);
     BABYLON.Mesh = Mesh;

+ 89 - 14
Babylon/Mesh/babylon.mesh.ts

@@ -11,6 +11,10 @@
         public static _BACKSIDE: number = 1;
         public static _DOUBLESIDE: number = 2;
         public static _DEFAULTSIDE: number = 0;
+        public static _NO_CAP = 0;
+        public static _CAP_START = 1;
+        public static _CAP_END = 2;
+        public static _CAP_ALL = 3;
 
         public static get FRONTSIDE(): number {
             return Mesh._FRONTSIDE;
@@ -27,6 +31,18 @@
         public static get DEFAULTSIDE(): number {
             return Mesh._DEFAULTSIDE;
         }
+        public static get NO_CAP(): number {
+            return Mesh._NO_CAP;
+        }
+        public static get CAP_START(): number {
+            return Mesh._CAP_START;
+        }
+        public static get CAP_END(): number {
+            return Mesh._CAP_END;
+        }
+        public static get CAP_ALL(): number {
+            return Mesh._CAP_ALL;
+        }
 
         // Members
         public delayLoadState = Engine.DELAYLOADSTATE_NONE;
@@ -1314,22 +1330,22 @@
         }
 
         // Extrusion
-        public static ExtrudeShape(name: string, shape: Vector3[], path: Vector3[], scale: number, rotation: number, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, extrudedInstance: Mesh = null): Mesh {
+        public static ExtrudeShape(name: string, shape: Vector3[], path: Vector3[], scale: number, rotation: number, cap: number, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, extrudedInstance: Mesh = null): Mesh {
             scale = scale || 1;
             rotation = rotation || 0;
-            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, false, scene, updatable, sideOrientation, extrudedInstance);
+            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, extrudedInstance);
             return extruded;
         }
 
-        public static ExtrudeShapeCustom(name: string, shape: Vector3[], path: Vector3[], scaleFunction, rotationFunction, ribbonCloseArray: boolean, ribbonClosePath: boolean, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, extrudedInstance: Mesh = null): Mesh {
-            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, true, scene, updatable, sideOrientation, extrudedInstance);
+        public static ExtrudeShapeCustom(name: string, shape: Vector3[], path: Vector3[], scaleFunction, rotationFunction, ribbonCloseArray: boolean, ribbonClosePath: boolean, cap: number, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, extrudedInstance: Mesh = null): Mesh {
+            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, extrudedInstance);
             return extrudedCustom;
         }
 
-        private static _ExtrudeShapeGeneric(name: string, shape: Vector3[], curve: Vector3[], scale: number, rotation: number, scaleFunction: { (i: number, distance: number): number; }, rotateFunction: { (i: number, distance: number): number; }, rbCA: boolean, rbCP: boolean, custom: boolean, scene: Scene, updtbl: boolean, side: number, instance: Mesh): Mesh {
+        private static _ExtrudeShapeGeneric(name: string, shape: Vector3[], curve: Vector3[], scale: number, rotation: number, scaleFunction: { (i: number, distance: number): number; }, rotateFunction: { (i: number, distance: number): number; }, rbCA: boolean, rbCP: boolean, cap: number, custom: boolean, scene: Scene, updtbl: boolean, side: number, instance: Mesh): Mesh {
             
             // extrusion geometry
-            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, custom) {
+            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var binormals = path3D.getBinormals();
@@ -1356,13 +1372,43 @@
                     angle += angleStep;
                     index++;
                 }
+                // cap
+                var capPath = function (shapePath) {
+                    var pointCap = Array<Vector3>();
+                    var barycenter = Vector3.Zero();
+                    var i: number;
+                    for (i = 0; i < shapePath.length; i++) {
+                        barycenter.addInPlace(shapePath[i]);
+                    }
+                    barycenter.scaleInPlace(1 / shapePath.length);
+                    for (i = 0; i < shapePath.length; i++) {
+                        pointCap.push(barycenter);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    default:
+                        break;
+                }
                 return shapePaths;
             };
 
             if (instance) { // instance update
                 
                 var path3D = ((<any>instance).path3D).update(curve);
-                var pathArray = extrusionPathArray(shape, curve,(<any>instance).path3D,(<any>instance).pathArray, scale, rotation, scaleFunction, rotateFunction, custom);
+                var pathArray = extrusionPathArray(shape, curve,(<any>instance).path3D,(<any>instance).pathArray, scale, rotation, scaleFunction, rotateFunction,(<any>instance).cap, custom);
                 instance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, instance);
 
                 return instance;
@@ -1371,11 +1417,13 @@
 
             var path3D = <any>new Path3D(curve);
             var newShapePaths = new Array<Array<Vector3>>();
-            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, custom);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom);
 
             var extrudedGeneric = Mesh.CreateRibbon(name, pathArray, rbCA, rbCP, 0, scene, updtbl, side);
             (<any>extrudedGeneric).pathArray = pathArray;
             (<any>extrudedGeneric).path3D = path3D;
+            (<any>extrudedGeneric).cap = cap;
 
             return extrudedGeneric;
         }
@@ -1451,10 +1499,10 @@
             return ground;
         }
 
-        public static CreateTube(name: string, path: Vector3[], radius: number, tessellation: number, radiusFunction: { (i: number, distance: number): number; }, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, tubeInstance: Mesh = null): Mesh {
+        public static CreateTube(name: string, path: Vector3[], radius: number, tessellation: number, radiusFunction: { (i: number, distance: number): number; }, cap: number, scene: Scene, updatable?: boolean, sideOrientation: number = Mesh.DEFAULTSIDE, tubeInstance: Mesh = null): Mesh {
             
             // tube geometry
-            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction) {
+            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var distances = path3D.getDistances();
@@ -1473,20 +1521,45 @@
                     rad = radiusFunctionFinal(i, distances[i]); // current radius
                     circlePath = Array<Vector3>();              // current circle array
                     normal = normals[i];                        // current normal  
-                    for (var ang = 0; ang < pi2; ang += step) {
-                        rotationMatrix = Matrix.RotationAxis(tangents[i], ang);
+                    for (var t = 0; t < tessellation; t++) {
+                        rotationMatrix = Matrix.RotationAxis(tangents[i], step * t);
                         rotated = Vector3.TransformCoordinates(normal, rotationMatrix).scaleInPlace(rad).add(path[i]);
                         circlePath.push(rotated);
                     }
+                    circlePath.push(circlePath[0]);
                     circlePaths[index] = circlePath;
                     index++;
                 }
+                // cap
+                var capPath = function (nbPoints, pathIndex) {
+                    var pointCap = Array<Vector3>();
+                    for (var i = 0; i < nbPoints; i++) {
+                        pointCap.push(path[pathIndex]);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    default:
+                        break;
+                }
                 return circlePaths;
             };
 
             if (tubeInstance) { // tube update
                 var path3D = ((<any>tubeInstance).path3D).update(path);
-                var pathArray = tubePathArray(path, path3D,(<any>tubeInstance).pathArray, radius,(<any>tubeInstance).tessellation, radiusFunction);
+                var pathArray = tubePathArray(path, path3D,(<any>tubeInstance).pathArray, radius,(<any>tubeInstance).tessellation, radiusFunction,(<any>tubeInstance).cap);
                 tubeInstance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, tubeInstance);
 
                 return tubeInstance;
@@ -1496,11 +1569,13 @@
 
             var path3D = <any>new Path3D(path);
             var newPathArray = new Array<Array<Vector3>>();
-            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap);
             var tube = Mesh.CreateRibbon(name, pathArray, false, true, 0, scene, updatable, sideOrientation);
             (<any>tube).pathArray = pathArray;
             (<any>tube).path3D = path3D;
             (<any>tube).tessellation = tessellation;
+            (<any>tube).cap = cap;
 
             return tube;
         }

+ 58 - 31
Babylon/Mesh/babylon.mesh.vertexData.js

@@ -925,40 +925,67 @@ var BABYLON;
          * @param {any} - normals   (number[] or Float32Array)
          */
         VertexData.ComputeNormals = function (positions, indices, normals) {
-            var positionVectors = [];
-            var facesOfVertices = [];
-            var index;
-            for (index = 0; index < positions.length; index += 3) {
-                var vector3 = new BABYLON.Vector3(positions[index], positions[index + 1], positions[index + 2]);
-                positionVectors.push(vector3);
-                facesOfVertices.push([]);
-            }
-            // Compute normals
-            var facesNormals = [];
-            for (index = 0; index < indices.length / 3; index++) {
+            var index = 0;
+            // temp Vector3
+            var p1 = BABYLON.Vector3.Zero();
+            var p2 = BABYLON.Vector3.Zero();
+            var p3 = BABYLON.Vector3.Zero();
+            var p1p2 = BABYLON.Vector3.Zero();
+            var p3p2 = BABYLON.Vector3.Zero();
+            var faceNormal = BABYLON.Vector3.Zero();
+            var vertexNormali1 = BABYLON.Vector3.Zero();
+            var vertexNormali2 = BABYLON.Vector3.Zero();
+            var vertexNormali3 = BABYLON.Vector3.Zero();
+            // indice triplet = 1 face
+            var nbFaces = indices.length / 3;
+            for (index = 0; index < nbFaces; index++) {
                 var i1 = indices[index * 3];
                 var i2 = indices[index * 3 + 1];
                 var i3 = indices[index * 3 + 2];
-                var p1 = positionVectors[i1];
-                var p2 = positionVectors[i2];
-                var p3 = positionVectors[i3];
-                var p1p2 = p1.subtract(p2);
-                var p3p2 = p3.subtract(p2);
-                facesNormals[index] = BABYLON.Vector3.Normalize(BABYLON.Vector3.Cross(p1p2, p3p2));
-                facesOfVertices[i1].push(index);
-                facesOfVertices[i2].push(index);
-                facesOfVertices[i3].push(index);
-            }
-            for (index = 0; index < positionVectors.length; index++) {
-                var faces = facesOfVertices[index];
-                var normal = BABYLON.Vector3.Zero();
-                for (var faceIndex = 0; faceIndex < faces.length; faceIndex++) {
-                    normal.addInPlace(facesNormals[faces[faceIndex]]);
-                }
-                normal = BABYLON.Vector3.Normalize(normal.scale(1.0 / faces.length));
-                normals[index * 3] = normal.x;
-                normals[index * 3 + 1] = normal.y;
-                normals[index * 3 + 2] = normal.z;
+                // setting the temp V3
+                BABYLON.Vector3.FromFloatsToRef(positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2], p1);
+                BABYLON.Vector3.FromFloatsToRef(positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2], p2);
+                BABYLON.Vector3.FromFloatsToRef(positions[i3 * 3], positions[i3 * 3 + 1], positions[i3 * 3 + 2], p3);
+                p1.subtractToRef(p2, p1p2);
+                p3.subtractToRef(p2, p3p2);
+                BABYLON.Vector3.CrossToRef(p1p2, p3p2, faceNormal);
+                faceNormal.normalize();
+                // All intermediate results are stored in the normals array :
+                // get the normals at i1, i2 and i3 indexes
+                normals[i1 * 3] = normals[i1 * 3] || 0.0;
+                normals[i1 * 3 + 1] = normals[i1 * 3 + 1] || 0.0;
+                normals[i1 * 3 + 2] = normals[i1 * 3 + 2] || 0.0;
+                normals[i2 * 3] = normals[i2 * 3] || 0.0;
+                normals[i2 * 3 + 1] = normals[i2 * 3 + 1] || 0.0;
+                normals[i2 * 3 + 2] = normals[i2 * 3 + 2] || 0.0;
+                normals[i3 * 3] = normals[i3 * 3] || 0.0;
+                normals[i3 * 3 + 1] = normals[i3 * 3 + 1] || 0.0;
+                normals[i3 * 3 + 2] = normals[i3 * 3 + 2] || 0.0;
+                // make intermediate vectors3 from normals values
+                BABYLON.Vector3.FromFloatsToRef(normals[i1 * 3], normals[i1 * 3 + 1], normals[i1 * 3 + 2], vertexNormali1);
+                BABYLON.Vector3.FromFloatsToRef(normals[i2 * 3], normals[i2 * 3 + 1], normals[i2 * 3 + 2], vertexNormali2);
+                BABYLON.Vector3.FromFloatsToRef(normals[i3 * 3], normals[i3 * 3 + 1], normals[i3 * 3 + 2], vertexNormali3);
+                // add the current face normals to these intermediate vectors3
+                vertexNormali1 = vertexNormali1.addInPlace(faceNormal);
+                vertexNormali2 = vertexNormali2.addInPlace(faceNormal);
+                vertexNormali3 = vertexNormali3.addInPlace(faceNormal);
+                // store back intermediate vectors3 into the normals array
+                normals[i1 * 3] = vertexNormali1.x;
+                normals[i1 * 3 + 1] = vertexNormali1.y;
+                normals[i1 * 3 + 2] = vertexNormali1.z;
+                normals[i2 * 3] = vertexNormali2.x;
+                normals[i2 * 3 + 1] = vertexNormali2.y;
+                normals[i2 * 3 + 2] = vertexNormali2.z;
+                normals[i3 * 3] = vertexNormali3.x;
+                normals[i3 * 3 + 1] = vertexNormali3.y;
+                normals[i3 * 3 + 2] = vertexNormali3.z;
+            }
+            for (index = 0; index < normals.length / 3; index++) {
+                BABYLON.Vector3.FromFloatsToRef(normals[index * 3], normals[index * 3 + 1], normals[index * 3 + 2], vertexNormali1);
+                vertexNormali1.normalize();
+                normals[index * 3] = vertexNormali1.x;
+                normals[index * 3 + 1] = vertexNormali1.y;
+                normals[index * 3 + 2] = vertexNormali1.z;
             }
         };
         VertexData._ComputeSides = function (sideOrientation, positions, indices, normals, uvs) {

+ 66 - 38
Babylon/Mesh/babylon.mesh.vertexData.ts

@@ -1161,48 +1161,76 @@
          * @param {any} - normals   (number[] or Float32Array)
          */
         public static ComputeNormals(positions: any, indices: any, normals: any) {
-            var positionVectors = [];
-            var facesOfVertices = [];
-            var index;
-
-            for (index = 0; index < positions.length; index += 3) {
-                var vector3 = new Vector3(<number> positions[index], <number> positions[index + 1], <number> positions[index + 2]);
-                positionVectors.push(vector3);
-                facesOfVertices.push([]);
-            }
-            // Compute normals
-            var facesNormals = [];
-            for (index = 0; index < indices.length / 3; index++) {
+            var index = 0;
+            
+            // temp Vector3
+            var p1 = BABYLON.Vector3.Zero();
+            var p2 = BABYLON.Vector3.Zero();
+            var p3 = BABYLON.Vector3.Zero();
+            var p1p2 = BABYLON.Vector3.Zero();
+            var p3p2 = BABYLON.Vector3.Zero();
+            var faceNormal = BABYLON.Vector3.Zero();
+
+            var vertexNormali1 = BABYLON.Vector3.Zero();
+            var vertexNormali2 = BABYLON.Vector3.Zero();
+            var vertexNormali3 = BABYLON.Vector3.Zero();
+            
+            // indice triplet = 1 face
+            var nbFaces = indices.length / 3;
+            for (index = 0; index < nbFaces; index++) {
                 var i1 = indices[index * 3];
                 var i2 = indices[index * 3 + 1];
                 var i3 = indices[index * 3 + 2];
-
-                var p1 = positionVectors[i1];
-                var p2 = positionVectors[i2];
-                var p3 = positionVectors[i3];
-
-                var p1p2 = p1.subtract(p2);
-                var p3p2 = p3.subtract(p2);
-
-                facesNormals[index] = Vector3.Normalize(Vector3.Cross(p1p2, p3p2));
-                facesOfVertices[i1].push(index);
-                facesOfVertices[i2].push(index);
-                facesOfVertices[i3].push(index);
+                
+                // setting the temp V3
+                BABYLON.Vector3.FromFloatsToRef(positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2], p1);
+                BABYLON.Vector3.FromFloatsToRef(positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2], p2);
+                BABYLON.Vector3.FromFloatsToRef(positions[i3 * 3], positions[i3 * 3 + 1], positions[i3 * 3 + 2], p3);
+
+                p1.subtractToRef(p2, p1p2);
+                p3.subtractToRef(p2, p3p2);
+
+                BABYLON.Vector3.CrossToRef(p1p2, p3p2, faceNormal);
+                faceNormal.normalize();
+    
+                // All intermediate results are stored in the normals array :
+                // get the normals at i1, i2 and i3 indexes
+                normals[i1 * 3] = normals[i1 * 3] || 0.0;
+                normals[i1 * 3 + 1] = normals[i1 * 3 + 1] || 0.0;
+                normals[i1 * 3 + 2] = normals[i1 * 3 + 2] || 0.0;
+                normals[i2 * 3] = normals[i2 * 3] || 0.0;
+                normals[i2 * 3 + 1] = normals[i2 * 3 + 1] || 0.0;
+                normals[i2 * 3 + 2] = normals[i2 * 3 + 2] || 0.0;
+                normals[i3 * 3] = normals[i3 * 3] || 0.0;
+                normals[i3 * 3 + 1] = normals[i3 * 3 + 1] || 0.0;
+                normals[i3 * 3 + 2] = normals[i3 * 3 + 2] || 0.0;
+                // make intermediate vectors3 from normals values
+                BABYLON.Vector3.FromFloatsToRef(normals[i1 * 3], normals[i1 * 3 + 1], normals[i1 * 3 + 2], vertexNormali1);
+                BABYLON.Vector3.FromFloatsToRef(normals[i2 * 3], normals[i2 * 3 + 1], normals[i2 * 3 + 2], vertexNormali2);
+                BABYLON.Vector3.FromFloatsToRef(normals[i3 * 3], normals[i3 * 3 + 1], normals[i3 * 3 + 2], vertexNormali3);
+                // add the current face normals to these intermediate vectors3
+                vertexNormali1 = vertexNormali1.addInPlace(faceNormal);
+                vertexNormali2 = vertexNormali2.addInPlace(faceNormal);
+                vertexNormali3 = vertexNormali3.addInPlace(faceNormal);
+                // store back intermediate vectors3 into the normals array
+                normals[i1 * 3] = vertexNormali1.x;
+                normals[i1 * 3 + 1] = vertexNormali1.y;
+                normals[i1 * 3 + 2] = vertexNormali1.z;
+                normals[i2 * 3] = vertexNormali2.x;
+                normals[i2 * 3 + 1] = vertexNormali2.y;
+                normals[i2 * 3 + 2] = vertexNormali2.z;
+                normals[i3 * 3] = vertexNormali3.x;
+                normals[i3 * 3 + 1] = vertexNormali3.y;
+                normals[i3 * 3 + 2] = vertexNormali3.z;
             }
-
-            for (index = 0; index < positionVectors.length; index++) {
-                var faces = facesOfVertices[index];
-
-                var normal = Vector3.Zero();
-                for (var faceIndex = 0; faceIndex < faces.length; faceIndex++) {
-                    normal.addInPlace(facesNormals[faces[faceIndex]]);
-                }
-
-                normal = Vector3.Normalize(normal.scale(1.0 / faces.length));
-
-                normals[index * 3] = normal.x;
-                normals[index * 3 + 1] = normal.y;
-                normals[index * 3 + 2] = normal.z;
+            
+            // last normalization
+            for (index = 0; index < normals.length / 3; index++) {
+                BABYLON.Vector3.FromFloatsToRef(normals[index * 3], normals[index * 3 + 1], normals[index * 3 + 2], vertexNormali1);
+                vertexNormali1.normalize();
+                normals[index * 3] = vertexNormali1.x;
+                normals[index * 3 + 1] = vertexNormali1.y;
+                normals[index * 3 + 2] = vertexNormali1.z;
             }
         }
 

+ 221 - 69
babylon.2.1-beta.debug.js

@@ -2611,15 +2611,17 @@ var __extends = this.__extends || function (d, b) {
     })();
     BABYLON.Path2 = Path2;
     var Path3D = (function () {
-        function Path3D(path) {
+        function Path3D(path, firstNormal) {
             this.path = path;
             this._curve = new Array();
             this._distances = new Array();
             this._tangents = new Array();
             this._normals = new Array();
             this._binormals = new Array();
-            this._curve = path.slice(); // copy array  
-            this._compute();
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p] = path[p].clone(); // hard copy
+            }
+            this._compute(firstNormal);
         }
         Path3D.prototype.getCurve = function () {
             return this._curve;
@@ -2636,28 +2638,30 @@ var __extends = this.__extends || function (d, b) {
         Path3D.prototype.getDistances = function () {
             return this._distances;
         };
-        Path3D.prototype.update = function (path) {
-            for (var i = 0; i < path.length; i++) {
-                this._curve[i] = path[i];
+        Path3D.prototype.update = function (path, firstNormal) {
+            for (var p = 0; p < path.length; p++) {
+                this._curve[p].x = path[p].x;
+                this._curve[p].y = path[p].y;
+                this._curve[p].z = path[p].z;
             }
-            this._compute();
+            this._compute(firstNormal);
             return this;
         };
         // private function compute() : computes tangents, normals and binormals
-        Path3D.prototype._compute = function () {
+        Path3D.prototype._compute = function (firstNormal) {
             var l = this._curve.length;
             // first and last tangents
-            this._tangents[0] = this._curve[1].subtract(this._curve[0]);
+            this._tangents[0] = this._getFirstNonNullVector(0);
             this._tangents[0].normalize();
             this._tangents[l - 1] = this._curve[l - 1].subtract(this._curve[l - 2]);
             this._tangents[l - 1].normalize();
             // normals and binormals at first point : arbitrary vector with _normalVector()
             var tg0 = this._tangents[0];
-            var pp0 = this._normalVector(this._curve[0], tg0);
+            var pp0 = this._normalVector(this._curve[0], tg0, firstNormal);
             this._normals[0] = pp0;
             this._normals[0].normalize();
             this._binormals[0] = Vector3.Cross(tg0, this._normals[0]);
-            this._normals[0].normalize();
+            this._binormals[0].normalize();
             this._distances[0] = 0;
             // normals and binormals : next points
             var prev; // previous vector (segment)
@@ -2667,9 +2671,9 @@ var __extends = this.__extends || function (d, b) {
             var prevBinor; // previous binormal
             for (var i = 1; i < l; i++) {
                 // tangents
-                prev = this._curve[i].subtract(this._curve[i - 1]);
+                prev = this._getLastNonNullVector(i);
                 if (i < l - 1) {
-                    cur = this._curve[i + 1].subtract(this._curve[i]);
+                    cur = this._getFirstNonNullVector(i);
                     this._tangents[i] = prev.add(cur);
                     this._tangents[i].normalize();
                 }
@@ -2685,20 +2689,50 @@ var __extends = this.__extends || function (d, b) {
                 this._binormals[i].normalize();
             }
         };
-        // private function normalVector(v0, vt) :
-        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
-        Path3D.prototype._normalVector = function (v0, vt) {
-            var point;
-            if (vt.x !== 1) {
-                point = new Vector3(1, 0, 0);
+        // private function getFirstNonNullVector(index)
+        // returns the first non null vector from index : curve[index + N].subtract(curve[index])
+        Path3D.prototype._getFirstNonNullVector = function (index) {
+            var i = 1;
+            var nNVector = this._curve[index + i].subtract(this._curve[index]);
+            while (nNVector.length() == 0 && index + i + 1 < this._curve.length) {
+                i++;
+                nNVector = this._curve[index + i].subtract(this._curve[index]);
             }
-            else if (vt.y !== 1) {
-                point = new Vector3(0, 1, 0);
+            return nNVector;
+        };
+        // private function getLastNonNullVector(index)
+        // returns the last non null vector from index : curve[index].subtract(curve[index - N])
+        Path3D.prototype._getLastNonNullVector = function (index) {
+            var i = 1;
+            var nLVector = this._curve[index].subtract(this._curve[index - i]);
+            while (nLVector.length() == 0 && index > i + 1) {
+                i++;
+                nLVector = this._curve[index].subtract(this._curve[index - i]);
             }
-            else if (vt.z !== 1) {
-                point = new Vector3(0, 0, 1);
+            return nLVector;
+        };
+        // private function normalVector(v0, vt, va) :
+        // returns an arbitrary point in the plane defined by the point v0 and the vector vt orthogonal to this plane
+        // if va is passed, it returns the va projection on the plane orthogonal to vt at the point v0
+        Path3D.prototype._normalVector = function (v0, vt, va) {
+            var normal0;
+            if (va === undefined || va === null) {
+                var point;
+                if (vt.x !== 1) {
+                    point = new Vector3(1, 0, 0);
+                }
+                else if (vt.y !== 1) {
+                    point = new Vector3(0, 1, 0);
+                }
+                else if (vt.z !== 1) {
+                    point = new Vector3(0, 0, 1);
+                }
+                normal0 = Vector3.Cross(vt, point);
+            }
+            else {
+                normal0 = Vector3.Cross(vt, va);
+                Vector3.CrossToRef(normal0, vt, normal0);
             }
-            var normal0 = Vector3.Cross(vt, point);
             normal0.normalize();
             return normal0;
         };
@@ -11010,6 +11044,34 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Mesh, "NO_CAP", {
+            get: function () {
+                return Mesh._NO_CAP;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_START", {
+            get: function () {
+                return Mesh._CAP_START;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_END", {
+            get: function () {
+                return Mesh._CAP_END;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Mesh, "CAP_ALL", {
+            get: function () {
+                return Mesh._CAP_ALL;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Mesh.prototype, "hasLODLevels", {
             // Methods
             get: function () {
@@ -11998,23 +12060,23 @@ var BABYLON;
             return lines;
         };
         // Extrusion
-        Mesh.ExtrudeShape = function (name, shape, path, scale, rotation, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShape = function (name, shape, path, scale, rotation, cap, scene, updatable, sideOrientation, extrudedInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (extrudedInstance === void 0) { extrudedInstance = null; }
             scale = scale || 1;
             rotation = rotation || 0;
-            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, false, scene, updatable, sideOrientation, extrudedInstance);
+            var extruded = Mesh._ExtrudeShapeGeneric(name, shape, path, scale, rotation, null, null, false, false, cap, false, scene, updatable, sideOrientation, extrudedInstance);
             return extruded;
         };
-        Mesh.ExtrudeShapeCustom = function (name, shape, path, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, scene, updatable, sideOrientation, extrudedInstance) {
+        Mesh.ExtrudeShapeCustom = function (name, shape, path, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, scene, updatable, sideOrientation, extrudedInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (extrudedInstance === void 0) { extrudedInstance = null; }
-            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, true, scene, updatable, sideOrientation, extrudedInstance);
+            var extrudedCustom = Mesh._ExtrudeShapeGeneric(name, shape, path, null, null, scaleFunction, rotationFunction, ribbonCloseArray, ribbonClosePath, cap, true, scene, updatable, sideOrientation, extrudedInstance);
             return extrudedCustom;
         };
-        Mesh._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, custom, scene, updtbl, side, instance) {
+        Mesh._ExtrudeShapeGeneric = function (name, shape, curve, scale, rotation, scaleFunction, rotateFunction, rbCA, rbCP, cap, custom, scene, updtbl, side, instance) {
             // extrusion geometry
-            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, custom) {
+            var extrusionPathArray = function (shape, curve, path3D, shapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var binormals = path3D.getBinormals();
@@ -12043,21 +12105,53 @@ var BABYLON;
                     angle += angleStep;
                     index++;
                 }
+                // cap
+                var capPath = function (shapePath) {
+                    var pointCap = Array();
+                    var barycenter = BABYLON.Vector3.Zero();
+                    var i;
+                    for (i = 0; i < shapePath.length; i++) {
+                        barycenter.addInPlace(shapePath[i]);
+                    }
+                    barycenter.scaleInPlace(1 / shapePath.length);
+                    for (i = 0; i < shapePath.length; i++) {
+                        pointCap.push(barycenter);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        shapePaths.unshift(capPath(shapePaths[0]));
+                        shapePaths.push(capPath(shapePaths[shapePaths.length - 1]));
+                        break;
+                    default:
+                        break;
+                }
                 return shapePaths;
             };
             if (instance) {
                 var path3D = (instance.path3D).update(curve);
-                var pathArray = extrusionPathArray(shape, curve, instance.path3D, instance.pathArray, scale, rotation, scaleFunction, rotateFunction, custom);
+                var pathArray = extrusionPathArray(shape, curve, instance.path3D, instance.pathArray, scale, rotation, scaleFunction, rotateFunction, instance.cap, custom);
                 instance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, instance);
                 return instance;
             }
             // extruded shape creation
             var path3D = new BABYLON.Path3D(curve);
             var newShapePaths = new Array();
-            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, custom);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = extrusionPathArray(shape, curve, path3D, newShapePaths, scale, rotation, scaleFunction, rotateFunction, cap, custom);
             var extrudedGeneric = Mesh.CreateRibbon(name, pathArray, rbCA, rbCP, 0, scene, updtbl, side);
             extrudedGeneric.pathArray = pathArray;
             extrudedGeneric.path3D = path3D;
+            extrudedGeneric.cap = cap;
             return extrudedGeneric;
         };
         // Plane & ground
@@ -12111,11 +12205,11 @@ var BABYLON;
             }, scene.database);
             return ground;
         };
-        Mesh.CreateTube = function (name, path, radius, tessellation, radiusFunction, scene, updatable, sideOrientation, tubeInstance) {
+        Mesh.CreateTube = function (name, path, radius, tessellation, radiusFunction, cap, scene, updatable, sideOrientation, tubeInstance) {
             if (sideOrientation === void 0) { sideOrientation = Mesh.DEFAULTSIDE; }
             if (tubeInstance === void 0) { tubeInstance = null; }
             // tube geometry
-            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction) {
+            var tubePathArray = function (path, path3D, circlePaths, radius, tessellation, radiusFunction, cap) {
                 var tangents = path3D.getTangents();
                 var normals = path3D.getNormals();
                 var distances = path3D.getDistances();
@@ -12133,30 +12227,57 @@ var BABYLON;
                     rad = radiusFunctionFinal(i, distances[i]); // current radius
                     circlePath = Array(); // current circle array
                     normal = normals[i]; // current normal  
-                    for (var ang = 0; ang < pi2; ang += step) {
-                        rotationMatrix = BABYLON.Matrix.RotationAxis(tangents[i], ang);
+                    for (var t = 0; t < tessellation; t++) {
+                        rotationMatrix = BABYLON.Matrix.RotationAxis(tangents[i], step * t);
                         rotated = BABYLON.Vector3.TransformCoordinates(normal, rotationMatrix).scaleInPlace(rad).add(path[i]);
                         circlePath.push(rotated);
                     }
+                    circlePath.push(circlePath[0]);
                     circlePaths[index] = circlePath;
                     index++;
                 }
+                // cap
+                var capPath = function (nbPoints, pathIndex) {
+                    var pointCap = Array();
+                    for (var i = 0; i < nbPoints; i++) {
+                        pointCap.push(path[pathIndex]);
+                    }
+                    return pointCap;
+                };
+                switch (cap) {
+                    case BABYLON.Mesh.NO_CAP:
+                        break;
+                    case BABYLON.Mesh.CAP_START:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        break;
+                    case BABYLON.Mesh.CAP_END:
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    case BABYLON.Mesh.CAP_ALL:
+                        circlePaths.unshift(capPath(tessellation + 1, 0));
+                        circlePaths.push(capPath(tessellation + 1, path.length - 1));
+                        break;
+                    default:
+                        break;
+                }
                 return circlePaths;
             };
             if (tubeInstance) {
                 var path3D = (tubeInstance.path3D).update(path);
-                var pathArray = tubePathArray(path, path3D, tubeInstance.pathArray, radius, tubeInstance.tessellation, radiusFunction);
+                var pathArray = tubePathArray(path, path3D, tubeInstance.pathArray, radius, tubeInstance.tessellation, radiusFunction, tubeInstance.cap);
                 tubeInstance = Mesh.CreateRibbon(null, pathArray, null, null, null, null, null, null, tubeInstance);
                 return tubeInstance;
             }
             // tube creation
             var path3D = new BABYLON.Path3D(path);
             var newPathArray = new Array();
-            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction);
+            cap = (cap < 0 || cap > 3) ? 0 : cap;
+            var pathArray = tubePathArray(path, path3D, newPathArray, radius, tessellation, radiusFunction, cap);
             var tube = Mesh.CreateRibbon(name, pathArray, false, true, 0, scene, updatable, sideOrientation);
             tube.pathArray = pathArray;
             tube.path3D = path3D;
             tube.tessellation = tessellation;
+            tube.cap = cap;
             return tube;
         };
         // Decals
@@ -12409,6 +12530,10 @@ var BABYLON;
         Mesh._BACKSIDE = 1;
         Mesh._DOUBLESIDE = 2;
         Mesh._DEFAULTSIDE = 0;
+        Mesh._NO_CAP = 0;
+        Mesh._CAP_START = 1;
+        Mesh._CAP_END = 2;
+        Mesh._CAP_ALL = 3;
         return Mesh;
     })(BABYLON.AbstractMesh);
     BABYLON.Mesh = Mesh;
@@ -23230,40 +23355,67 @@ var BABYLON;
          * @param {any} - normals   (number[] or Float32Array)
          */
         VertexData.ComputeNormals = function (positions, indices, normals) {
-            var positionVectors = [];
-            var facesOfVertices = [];
-            var index;
-            for (index = 0; index < positions.length; index += 3) {
-                var vector3 = new BABYLON.Vector3(positions[index], positions[index + 1], positions[index + 2]);
-                positionVectors.push(vector3);
-                facesOfVertices.push([]);
-            }
-            // Compute normals
-            var facesNormals = [];
-            for (index = 0; index < indices.length / 3; index++) {
+            var index = 0;
+            // temp Vector3
+            var p1 = BABYLON.Vector3.Zero();
+            var p2 = BABYLON.Vector3.Zero();
+            var p3 = BABYLON.Vector3.Zero();
+            var p1p2 = BABYLON.Vector3.Zero();
+            var p3p2 = BABYLON.Vector3.Zero();
+            var faceNormal = BABYLON.Vector3.Zero();
+            var vertexNormali1 = BABYLON.Vector3.Zero();
+            var vertexNormali2 = BABYLON.Vector3.Zero();
+            var vertexNormali3 = BABYLON.Vector3.Zero();
+            // indice triplet = 1 face
+            var nbFaces = indices.length / 3;
+            for (index = 0; index < nbFaces; index++) {
                 var i1 = indices[index * 3];
                 var i2 = indices[index * 3 + 1];
                 var i3 = indices[index * 3 + 2];
-                var p1 = positionVectors[i1];
-                var p2 = positionVectors[i2];
-                var p3 = positionVectors[i3];
-                var p1p2 = p1.subtract(p2);
-                var p3p2 = p3.subtract(p2);
-                facesNormals[index] = BABYLON.Vector3.Normalize(BABYLON.Vector3.Cross(p1p2, p3p2));
-                facesOfVertices[i1].push(index);
-                facesOfVertices[i2].push(index);
-                facesOfVertices[i3].push(index);
-            }
-            for (index = 0; index < positionVectors.length; index++) {
-                var faces = facesOfVertices[index];
-                var normal = BABYLON.Vector3.Zero();
-                for (var faceIndex = 0; faceIndex < faces.length; faceIndex++) {
-                    normal.addInPlace(facesNormals[faces[faceIndex]]);
-                }
-                normal = BABYLON.Vector3.Normalize(normal.scale(1.0 / faces.length));
-                normals[index * 3] = normal.x;
-                normals[index * 3 + 1] = normal.y;
-                normals[index * 3 + 2] = normal.z;
+                // setting the temp V3
+                BABYLON.Vector3.FromFloatsToRef(positions[i1 * 3], positions[i1 * 3 + 1], positions[i1 * 3 + 2], p1);
+                BABYLON.Vector3.FromFloatsToRef(positions[i2 * 3], positions[i2 * 3 + 1], positions[i2 * 3 + 2], p2);
+                BABYLON.Vector3.FromFloatsToRef(positions[i3 * 3], positions[i3 * 3 + 1], positions[i3 * 3 + 2], p3);
+                p1.subtractToRef(p2, p1p2);
+                p3.subtractToRef(p2, p3p2);
+                BABYLON.Vector3.CrossToRef(p1p2, p3p2, faceNormal);
+                faceNormal.normalize();
+                // All intermediate results are stored in the normals array :
+                // get the normals at i1, i2 and i3 indexes
+                normals[i1 * 3] = normals[i1 * 3] || 0.0;
+                normals[i1 * 3 + 1] = normals[i1 * 3 + 1] || 0.0;
+                normals[i1 * 3 + 2] = normals[i1 * 3 + 2] || 0.0;
+                normals[i2 * 3] = normals[i2 * 3] || 0.0;
+                normals[i2 * 3 + 1] = normals[i2 * 3 + 1] || 0.0;
+                normals[i2 * 3 + 2] = normals[i2 * 3 + 2] || 0.0;
+                normals[i3 * 3] = normals[i3 * 3] || 0.0;
+                normals[i3 * 3 + 1] = normals[i3 * 3 + 1] || 0.0;
+                normals[i3 * 3 + 2] = normals[i3 * 3 + 2] || 0.0;
+                // make intermediate vectors3 from normals values
+                BABYLON.Vector3.FromFloatsToRef(normals[i1 * 3], normals[i1 * 3 + 1], normals[i1 * 3 + 2], vertexNormali1);
+                BABYLON.Vector3.FromFloatsToRef(normals[i2 * 3], normals[i2 * 3 + 1], normals[i2 * 3 + 2], vertexNormali2);
+                BABYLON.Vector3.FromFloatsToRef(normals[i3 * 3], normals[i3 * 3 + 1], normals[i3 * 3 + 2], vertexNormali3);
+                // add the current face normals to these intermediate vectors3
+                vertexNormali1 = vertexNormali1.addInPlace(faceNormal);
+                vertexNormali2 = vertexNormali2.addInPlace(faceNormal);
+                vertexNormali3 = vertexNormali3.addInPlace(faceNormal);
+                // store back intermediate vectors3 into the normals array
+                normals[i1 * 3] = vertexNormali1.x;
+                normals[i1 * 3 + 1] = vertexNormali1.y;
+                normals[i1 * 3 + 2] = vertexNormali1.z;
+                normals[i2 * 3] = vertexNormali2.x;
+                normals[i2 * 3 + 1] = vertexNormali2.y;
+                normals[i2 * 3 + 2] = vertexNormali2.z;
+                normals[i3 * 3] = vertexNormali3.x;
+                normals[i3 * 3 + 1] = vertexNormali3.y;
+                normals[i3 * 3 + 2] = vertexNormali3.z;
+            }
+            for (index = 0; index < normals.length / 3; index++) {
+                BABYLON.Vector3.FromFloatsToRef(normals[index * 3], normals[index * 3 + 1], normals[index * 3 + 2], vertexNormali1);
+                vertexNormali1.normalize();
+                normals[index * 3] = vertexNormali1.x;
+                normals[index * 3 + 1] = vertexNormali1.y;
+                normals[index * 3 + 2] = vertexNormali1.z;
             }
         };
         VertexData._ComputeSides = function (sideOrientation, positions, indices, normals, uvs) {

文件差异内容过多而无法显示
+ 21 - 18
babylon.2.1-beta.js


+ 45 - 0
what's new - 2.1 - proposal.md

@@ -0,0 +1,45 @@
+- 2.1.0:
+ - **Major updates**
+ - SIMD.js support for math library. See [demo](http://www.babylonjs.com/simd.html) ([deltakosh](http://www.github.com/deltakosh))
+ - Unity 5  scene exporter. More info [here](https://github.com/BabylonJS/Babylon.js/tree/master/Exporters/Unity%205) ([davrous](http://www.github.com/davrous), [deltakosh](http://www.github.com/deltakosh))
+ - New ```Mesh.CreateDecal()``` function to create decals. See [demo](http://www.babylonjs.com/?DECALS). More info [here](http://doc.babylonjs.com/page.php?p=25094) ([deltakosh](http://www.github.com/deltakosh))
+ - New tool for debugLayer: You can now dump renderTargets to see their content ([deltakosh](http://www.github.com/deltakosh))
+ - Complete shadows code rework: New bias property for ShadowGenerator, new orthogonal shadows for directional shadows, 
+   automatic projection size for directional lights, new BlurVarianceShadowMap filter. See [demo](http://www.babylonjs.com/?SOFTSHADOWS). Documentation updated [here](http://babylondoc.azurewebsites.net/page.php?p=22151) ([deltakosh](http://www.github.com/deltakosh))
+ - New lens effects rendering pipeline. More info [here](http://doc.babylonjs.com/page.php?p=24841) ([jahow](https://github.com/jahow))
+ - New basic mesh type: Ribbons. See [demo](http://www.babylonjs.com/?RIBBONS). More info [here](http://doc.babylonjs.com/page.php?p=25088) ([jbousquie](https://github.com/jbousquie))
+ - New ```BABYLON.Mesh.ExtrudeShape``` and ```BABYLON.Mesh.ExtrudeShapeCustom```. More info [here](http://doc.babylonjs.com/page.php?p=24847) ([jbousquie](https://github.com/jbousquie))
+ - **Updates**
+ - Faster ```mesh.computeNormals()``` function ([jbousquie](https://github.com/jbousquie))
+ - Added the ability [to dynamically update or to morph](http://doc.babylonjs.com/page.php?p=25096) an mesh instance ([jbousquie](https://github.com/jbousquie))
+ - Added mesh.freezeWorldMatrix() for static meshes ([deltakosh](http://www.github.com/deltakosh))
+ - Added mesh.alwaysSelectAsActiveMesh to disable frustum clipping for a specific mesh ([deltakosh](http://www.github.com/deltakosh))
+ - Added updateMeshPositions method ([jbousquie](https://github.com/jbousquie))
+ - Callbacks for geometry changes ([raananw](http://www.github.com/raananw))
+ - Material onBind callback improvement ([jahow](http://www.github.com/jahow))
+ - New front/back/double side feature for meshes ([jbousquie](https://github.com/jbousquie)) 
+ - New basic mesh type: Disc ([jbousquie](https://github.com/jbousquie))
+ - New basic mesh type: Tube. More info [here](http://doc.babylonjs.com/page.php?p=24847) ([jbousquie](https://github.com/jbousquie))
+ - Allow ComputeNormals to work with Typed Arrays & the Typescript Level ([Palmer-JC](http://www.github.com/Palmer-JC))
+ - Added uniqueId for nodes ([raananw](http://www.github.com/raananw))
+ - Added ```Mesh.onLODLevelSelection(distance: number, mesh: Mesh, selectedLevel: Mesh)``` callback ([deltakosh](http://www.github.com/deltakosh))
+ - Added ```Material.zOffset``` to help reducing z-fighting ([deltakosh](http://www.github.com/deltakosh))
+ - Added excludeWithLayerMask to lights ([Palmer-JC](http://www.github.com/Palmer-JC))
+ - Added includeOnlyWithLayerMask for filtering meshes ([Palmer-JC](http://www.github.com/Palmer-JC))
+ - Mesh Default layerMask changed to allow more "special cameras filtering" ([Palmer-JC](http://www.github.com/Palmer-JC))
+ - Sprite can now have .width and .height properties ([deltakosh](http://www.github.com/deltakosh))
+ - Ability to register events based on mesh/camera/light addition and deletion ([raananw](http://www.github.com/raananw))
+ - New ```Curve``` object ([jbousquie](https://github.com/jbousquie)) More info [here](http://doc.babylonjs.com/page.php?p=25091)
+ - New ```Path3D``` object ([jbousquie](https://github.com/jbousquie)) More info [here](http://doc.babylonjs.com/page.php?p=25090)
+ - New 'color correction' post process ([jahow](http://www.github.com/jahow))
+ - Added sampling mode as a parameter for SpriteManager ([jahow](http://www.github.com/jahow))
+ - RenderTexture can now be saved to a file ([deltakosh](http://www.github.com/deltakosh))
+ - Better attributes management ([deltakosh](http://www.github.com/deltakosh))
+ - Source conform to typescript 1.4 ([raananw](http://www.github.com/raananw)) 
+ - Adding subMeshId property in PickingInfo structure ([deltakosh](http://www.github.com/deltakosh))
+ - No more error when a manifest is not found ([deltakosh](http://www.github.com/deltakosh))
+ - **Bug fixes**
+ - Initial quaternion fix for OIMO plugin ([raananw](http://www.github.com/raananw)) 
+ - ArcRotateCamera pinch zoom debug ([vousk](https://github.com/vousk)) 
+ - Fixing animatable bug when animation is over ([temechon](http://www.github.com/temechon))
+ - useCameraPostProcess whenever there is a special camera in use ([m0ppers](https://github.com/m0ppers))