Bläddra i källkod

Merge pull request #357 from ElemarJR/master

Basic animation info support using paths
David Catuhe 10 år sedan
förälder
incheckning
5095568432

+ 123 - 7
Babylon/Math/babylon.math.js

@@ -575,7 +575,7 @@
         };
 
         Vector3.prototype.equalsWithEpsilon = function (otherVector) {
-            return Math.abs(this.x - otherVector.x) < BABYLON.Engine.Epsilon && Math.abs(this.y - otherVector.y) < BABYLON.Engine.Epsilon && Math.abs(this.z - otherVector.z) < BABYLON.Engine.Epsilon;
+            return Math.abs(this.x - otherVector.x) < Engine.Epsilon && Math.abs(this.y - otherVector.y) < Engine.Epsilon && Math.abs(this.z - otherVector.z) < Engine.Epsilon;
         };
 
         Vector3.prototype.equalsToFloats = function (x, y, z) {
@@ -858,7 +858,7 @@
             var vector = Vector3.TransformCoordinates(source, matrix);
             var num = source.x * matrix.m[3] + source.y * matrix.m[7] + source.z * matrix.m[11] + matrix.m[15];
 
-            if (BABYLON.Tools.WithinEpsilon(num, 1.0)) {
+            if (Tools.WithinEpsilon(num, 1.0)) {
                 vector = vector.scale(1.0 / num);
             }
 
@@ -873,7 +873,7 @@
             var vector = Vector3.TransformCoordinates(source, matrix);
             var num = source.x * matrix.m[3] + source.y * matrix.m[7] + source.z * matrix.m[11] + matrix.m[15];
 
-            if (BABYLON.Tools.WithinEpsilon(num, 1.0)) {
+            if (Tools.WithinEpsilon(num, 1.0)) {
                 vector = vector.scale(1.0 / num);
             }
 
@@ -1020,7 +1020,7 @@
         };
 
         Vector4.prototype.equalsWithEpsilon = function (otherVector) {
-            return Math.abs(this.x - otherVector.x) < BABYLON.Engine.Epsilon && Math.abs(this.y - otherVector.y) < BABYLON.Engine.Epsilon && Math.abs(this.z - otherVector.z) < BABYLON.Engine.Epsilon && Math.abs(this.w - otherVector.w) < BABYLON.Engine.Epsilon;
+            return Math.abs(this.x - otherVector.x) < Engine.Epsilon && Math.abs(this.y - otherVector.y) < Engine.Epsilon && Math.abs(this.z - otherVector.z) < Engine.Epsilon && Math.abs(this.w - otherVector.w) < Engine.Epsilon;
         };
 
         Vector4.prototype.equalsToFloats = function (x, y, z, w) {
@@ -2465,7 +2465,7 @@
                 return null;
             }
 
-            return new BABYLON.IntersectionInfo(bu, bv, distance);
+            return new IntersectionInfo(bu, bv, distance);
         };
 
         // Statics
@@ -2622,18 +2622,90 @@
     })();
     BABYLON.Arc2 = Arc2;
 
+    var PathCursor = (function () {
+        function PathCursor(path) {
+            this.path = path;
+            this._onchange = new Array();
+            this.value = 0;
+            this.animations = new Array();
+        }
+        PathCursor.prototype.getPoint = function () {
+            var point = this.path.getPointAtLengthPosition(this.value);
+            return new Vector3(point.x, 0, point.y);
+        };
+
+        PathCursor.prototype.moveAhead = function (step) {
+            if (typeof step === "undefined") { step = 0.002; }
+            this.move(step);
+        };
+
+        PathCursor.prototype.moveBack = function (step) {
+            if (typeof step === "undefined") { step = 0.002; }
+            this.move(-step);
+        };
+
+        PathCursor.prototype.move = function (step) {
+            if (Math.abs(step) > 1) {
+                throw "step size should be less than 1.";
+            }
+
+            this.value += step;
+            this.ensureLimits();
+            this.raiseOnChange();
+        };
+
+        PathCursor.prototype.ensureLimits = function () {
+            while (this.value > 1) {
+                this.value -= 1;
+            }
+            while (this.value < 0) {
+                this.value += 1;
+            }
+        };
+
+        // used by animation engine
+        PathCursor.prototype.markAsDirty = function (propertyName) {
+            this.ensureLimits();
+            this.raiseOnChange();
+        };
+
+        PathCursor.prototype.raiseOnChange = function () {
+            var _this = this;
+            this._onchange.forEach(function (f) {
+                return f(_this);
+            });
+        };
+
+        PathCursor.prototype.onchange = function (f) {
+            this._onchange.push(f);
+        };
+        return PathCursor;
+    })();
+    BABYLON.PathCursor = PathCursor;
+
     var Path2 = (function () {
         function Path2(x, y) {
             this._points = [];
+            this._length = 0;
+            this.closed = false;
             this._points.push(new Vector2(x, y));
         }
         Path2.prototype.addLineTo = function (x, y) {
-            this._points.push(new Vector2(x, y));
+            if (closed) {
+                throw "cannot add lines to closed paths";
+            }
+            var newPoint = new Vector2(x, y);
+            var previousPoint = this._points[this._points.length - 1];
+            this._points.push(newPoint);
+            this._length += newPoint.subtract(previousPoint).length();
             return this;
         };
 
         Path2.prototype.addArcTo = function (midX, midY, endX, endY, numberOfSegments) {
             if (typeof numberOfSegments === "undefined") { numberOfSegments = 36; }
+            if (closed) {
+                throw "cannot add arcs to closed paths";
+            }
             var startPoint = this._points[this._points.length - 1];
             var midPoint = new Vector2(midX, midY);
             var endPoint = new Vector2(endX, endY);
@@ -2655,9 +2727,54 @@
         };
 
         Path2.prototype.close = function () {
+            this.closed = true;
+            return this;
+        };
+
+        Path2.prototype.length = function () {
+            var result = this._length;
+
+            if (!this.closed) {
+                var lastPoint = this._points[this._points.length - 1];
+                var firstPoint = this._points[0];
+                result += (firstPoint.subtract(lastPoint).length());
+            }
+
+            return result;
+        };
+
+        Path2.prototype.getPoints = function () {
             return this._points;
         };
 
+        Path2.prototype.getPointAtLengthPosition = function (normalizedLengthPosition) {
+            if (normalizedLengthPosition < 0 || normalizedLengthPosition > 1) {
+                throw "normalized length position should be between 0 and 1.";
+            }
+
+            var lengthPosition = normalizedLengthPosition * this.length();
+
+            var previousOffset = 0;
+            for (var i = 0; i < this._points.length; i++) {
+                var j = (i + 1) % this._points.length;
+
+                var a = this._points[i];
+                var b = this._points[j];
+                var bToA = b.subtract(a);
+
+                var nextOffset = (bToA.length() + previousOffset);
+                if (lengthPosition >= previousOffset && lengthPosition <= nextOffset) {
+                    var dir = bToA.normalize();
+                    var localOffset = lengthPosition - previousOffset;
+
+                    return new Vector2(a.x + (dir.x * localOffset), a.y + (dir.y * localOffset));
+                }
+                previousOffset = nextOffset;
+            }
+
+            throw "internal error";
+        };
+
         Path2.StartingAt = function (x, y) {
             return new Path2(x, y);
         };
@@ -2665,4 +2782,3 @@
     })();
     BABYLON.Path2 = Path2;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.math.js.map

+ 119 - 2
Babylon/Math/babylon.math.ts

@@ -2611,19 +2611,88 @@
         }
     }
 
+    export class PathCursor {
+        private _onchange = new Array <(cursor: PathCursor) => void>();
+
+        value: number = 0;
+        animations = new Array<BABYLON.Animation>();
+
+        constructor(private path: Path2) {
+        }
+
+        getPoint() : Vector3 {
+            var point = this.path.getPointAtLengthPosition(this.value);
+            return new Vector3(point.x, 0, point.y);
+        }
+
+        moveAhead(step: number = 0.002) {
+            this.move(step);
+            
+        }
+
+        moveBack(step: number = 0.002) {
+            this.move(-step);
+        }
+
+        move(step: number) {
+            
+            if (Math.abs(step) > 1) {
+                throw "step size should be less than 1.";
+            }
+
+            this.value += step;
+            this.ensureLimits();
+            this.raiseOnChange();
+        }
+
+        private ensureLimits() {
+            while (this.value > 1) {
+                this.value -= 1;
+            } 
+            while (this.value < 0) {
+                this.value += 1;
+            }
+        }
+
+        // used by animation engine
+        private markAsDirty(propertyName: string) {
+            this.ensureLimits();
+            this.raiseOnChange();
+        }
+
+        private raiseOnChange() {
+            this._onchange.forEach(f => f(this));
+        }
+
+        onchange(f: (cursor: PathCursor) => void) {
+            this._onchange.push(f);
+        }
+    }
+
     export class Path2 {
         private _points: Vector2[] = [];
+        private _length: number = 0;
+        closed: boolean = false;
 
         constructor(x: number, y: number) {
             this._points.push(new Vector2(x, y));
         }
 
         addLineTo(x: number, y: number): Path2 {
-            this._points.push(new Vector2(x, y));
+            if (closed) {
+                throw "cannot add lines to closed paths";
+            }
+            var newPoint = new Vector2(x, y);
+            var previousPoint = this._points[this._points.length - 1];
+            this._points.push(newPoint);
+            this._length += newPoint.subtract(previousPoint).length();
             return this;
         }
 
         addArcTo(midX: number, midY: number, endX: number, endY: number, numberOfSegments = 36): Path2 {
+            if (closed) {
+                throw "cannot add arcs to closed paths";
+            }
             var startPoint = this._points[this._points.length - 1];
             var midPoint = new Vector2(midX, midY);
             var endPoint = new Vector2(endX, endY);
@@ -2643,10 +2712,58 @@
             return this;
         }
 
-        close(): Vector2[] {
+        close(): Path2 {
+            this.closed = true;
+            return this;
+        }
+
+        length(): number {
+            var result = this._length;
+
+            if (!this.closed) {
+                var lastPoint = this._points[this._points.length - 1];
+                var firstPoint = this._points[0];
+                result += (firstPoint.subtract(lastPoint).length());
+            }
+
+            return result;
+        }
+
+        getPoints(): Vector2[] {
             return this._points;
         }
 
+        getPointAtLengthPosition(normalizedLengthPosition: number): Vector2 {
+            if (normalizedLengthPosition < 0 || normalizedLengthPosition > 1) {
+                throw "normalized length position should be between 0 and 1.";
+            }
+
+            var lengthPosition = normalizedLengthPosition * this.length();
+
+            var previousOffset = 0;
+            for (var i = 0; i < this._points.length; i++) {
+                var j = (i + 1) % this._points.length;
+
+                var a = this._points[i];
+                var b = this._points[j];
+                var bToA = b.subtract(a);
+
+                var nextOffset = (bToA.length() + previousOffset);
+                if (lengthPosition >= previousOffset && lengthPosition <= nextOffset) {
+                    var dir = bToA.normalize();
+                    var localOffset = lengthPosition - previousOffset;
+
+                    return new Vector2(
+                        a.x + (dir.x * localOffset),
+                        a.y + (dir.y * localOffset)
+                        );
+                }
+                previousOffset = nextOffset;
+            }
+
+            throw "internal error";
+        }
+
         static StartingAt(x: number, y: number): Path2 {
             return new Path2(x, y);
         }

+ 33 - 18
Babylon/Mesh/babylon.polygonmesh.js

@@ -13,7 +13,15 @@ var BABYLON;
             this.index = index;
         }
         return IndexedVector2;
-    })(BABYLON.Vector2);
+    })(Vector2);
+
+    function nearlyEqual(a, b, epsilon) {
+        if (typeof epsilon === "undefined") { epsilon = 0.0001; }
+        if (a === b) {
+            return true;
+        }
+        return Math.abs(a - b) < epsilon;
+    }
 
     var PolygonPoints = (function () {
         function PolygonPoints() {
@@ -23,7 +31,7 @@ var BABYLON;
             var _this = this;
             var result = new Array();
             originalPoints.forEach(function (point) {
-                if (result.length === 0 || !(BABYLON.Tools.WithinEpsilon(point.x, result[0].x) && BABYLON.Tools.WithinEpsilon(point.y, result[0].y))) {
+                if (result.length === 0 || !(nearlyEqual(point.x, result[0].x) && nearlyEqual(point.y, result[0].y))) {
                     var newPoint = new IndexedVector2(point, _this.elements.length);
                     result.push(newPoint);
                     _this.elements.push(newPoint);
@@ -34,8 +42,8 @@ var BABYLON;
         };
 
         PolygonPoints.prototype.computeBounds = function () {
-            var lmin = new BABYLON.Vector2(this.elements[0].x, this.elements[0].y);
-            var lmax = new BABYLON.Vector2(this.elements[0].x, this.elements[0].y);
+            var lmin = new Vector2(this.elements[0].x, this.elements[0].y);
+            var lmax = new Vector2(this.elements[0].x, this.elements[0].y);
 
             this.elements.forEach(function (point) {
                 // x
@@ -68,10 +76,10 @@ var BABYLON;
         }
         Polygon.Rectangle = function (xmin, ymin, xmax, ymax) {
             return [
-                new BABYLON.Vector2(xmin, ymin),
-                new BABYLON.Vector2(xmax, ymin),
-                new BABYLON.Vector2(xmax, ymax),
-                new BABYLON.Vector2(xmin, ymax)
+                new Vector2(xmin, ymin),
+                new Vector2(xmax, ymin),
+                new Vector2(xmax, ymax),
+                new Vector2(xmin, ymax)
             ];
         };
 
@@ -85,7 +93,7 @@ var BABYLON;
             var increment = (Math.PI * 2) / numberOfSides;
 
             for (var i = 0; i < numberOfSides; i++) {
-                result.push(new BABYLON.Vector2(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius));
+                result.push(new Vector2(cx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius));
                 angle -= increment;
             }
 
@@ -104,7 +112,7 @@ var BABYLON;
         };
 
         Polygon.StartingAt = function (x, y) {
-            return BABYLON.Path2.StartingAt(x, y);
+            return Path2.StartingAt(x, y);
         };
         return Polygon;
     })();
@@ -112,14 +120,22 @@ var BABYLON;
 
     var PolygonMeshBuilder = (function () {
         function PolygonMeshBuilder(name, contours, scene) {
-            this.name = name;
-            this.scene = scene;
             this._points = new PolygonPoints();
             if (!("poly2tri" in window)) {
                 throw "PolygonMeshBuilder cannot be used because poly2tri is not referenced";
             }
 
-            this._swctx = new poly2tri.SweepContext(this._points.add(contours));
+            this._name = name;
+            this._scene = scene;
+
+            var points;
+            if (contours instanceof Path2) {
+                points = contours.getPoints();
+            } else {
+                points = contours;
+            }
+
+            this._swctx = new poly2tri.SweepContext(this._points.add(points));
         }
         PolygonMeshBuilder.prototype.addHole = function (hole) {
             this._swctx.addHole(this._points.add(hole));
@@ -128,7 +144,7 @@ var BABYLON;
 
         PolygonMeshBuilder.prototype.build = function (updatable) {
             if (typeof updatable === "undefined") { updatable = false; }
-            var result = new BABYLON.Mesh(this.name, this.scene);
+            var result = new Mesh(this._name, this._scene);
 
             var normals = [];
             var positions = [];
@@ -150,9 +166,9 @@ var BABYLON;
                 });
             });
 
-            result.setVerticesData(positions, BABYLON.VertexBuffer.PositionKind, updatable);
-            result.setVerticesData(normals, BABYLON.VertexBuffer.NormalKind, updatable);
-            result.setVerticesData(uvs, BABYLON.VertexBuffer.UVKind, updatable);
+            result.setVerticesData(positions, VertexBuffer.PositionKind, updatable);
+            result.setVerticesData(normals, VertexBuffer.NormalKind, updatable);
+            result.setVerticesData(uvs, VertexBuffer.UVKind, updatable);
             result.setIndices(indices);
 
             return result;
@@ -161,4 +177,3 @@ var BABYLON;
     })();
     BABYLON.PolygonMeshBuilder = PolygonMeshBuilder;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.polygonMesh.js.map

+ 27 - 4
Babylon/Mesh/babylon.polygonmesh.ts

@@ -5,6 +5,14 @@
         }
     }
 
+    function nearlyEqual(a: number, b: number, epsilon: number = 0.0001): boolean {
+        if (a === b) {
+            return true;
+        }
+        return Math.abs(a - b) < epsilon;
+    }
+
+
     class PolygonPoints {
         elements = new Array<IndexedVector2>();
 
@@ -12,7 +20,7 @@
 
             var result = new Array<IndexedVector2>();
             originalPoints.forEach(point => {
-                if (result.length === 0 || !(Tools.WithinEpsilon(point.x, result[0].x) && Tools.WithinEpsilon(point.y, result[0].y))) {
+                if (result.length === 0 || !(nearlyEqual(point.x, result[0].x) && nearlyEqual(point.y, result[0].y))) {
                     var newPoint = new IndexedVector2(point, this.elements.length);
                     result.push(newPoint);
                     this.elements.push(newPoint);
@@ -101,12 +109,27 @@
         private _swctx: poly2tri.SweepContext;
         private _points = new PolygonPoints();
 
-        constructor(private name: string, contours: Vector2[], private scene: Scene) {
+        private _name: string;
+        private _scene: Scene;
+
+        constructor(name: string, contours: Path2, scene: Scene) 
+        constructor(name: string, contours: Vector2[], scene: Scene)
+        constructor(name: string, contours: any, scene: Scene) {
             if (!("poly2tri" in window)) {
                 throw "PolygonMeshBuilder cannot be used because poly2tri is not referenced";
             }
 
-            this._swctx = new poly2tri.SweepContext(this._points.add(contours));
+            this._name = name;
+            this._scene = scene;
+
+            var points: Vector2[];
+            if (contours instanceof Path2) {
+                points = (<Path2>contours).getPoints();
+            } else {
+                points = (<Vector2[]>contours);
+            }
+
+            this._swctx = new poly2tri.SweepContext(this._points.add(points));
         }
 
         addHole(hole: Vector2[]): PolygonMeshBuilder {
@@ -115,7 +138,7 @@
         }
 
         build(updatable: boolean = false): Mesh {
-            var result = new Mesh(this.name, this.scene);
+            var result = new Mesh(this._name, this._scene);
 
             var normals = [];
             var positions = [];