瀏覽代碼

Merge pull request #494 from RaananW/worker-collisions

Worker collisions
David Catuhe 10 年之前
父節點
當前提交
aa00ace4a6

+ 24 - 11
Babylon/Cameras/babylon.arcRotateCamera.js

@@ -10,6 +10,7 @@ var BABYLON;
     var ArcRotateCamera = (function (_super) {
         __extends(ArcRotateCamera, _super);
         function ArcRotateCamera(name, alpha, beta, radius, target, scene) {
+            var _this = this;
             _super.call(this, name, BABYLON.Vector3.Zero(), scene);
             this.alpha = alpha;
             this.beta = beta;
@@ -41,6 +42,21 @@ var BABYLON;
             this._previousPosition = BABYLON.Vector3.Zero();
             this._collisionVelocity = BABYLON.Vector3.Zero();
             this._newPosition = BABYLON.Vector3.Zero();
+            this._onCollisionPositionChange = function (collisionId, newPosition, collidedMesh) {
+                if (collidedMesh === void 0) { collidedMesh = null; }
+                if (collisionId != null || collisionId != undefined)
+                    newPosition.multiplyInPlace(_this._collider.radius);
+                if (!newPosition.equalsWithEpsilon(_this.position)) {
+                    _this.position.copyFrom(_this._previousPosition);
+                    _this.alpha = _this._previousAlpha;
+                    _this.beta = _this._previousBeta;
+                    _this.radius = _this._previousRadius;
+                    if (_this.onCollide && collidedMesh) {
+                        _this.onCollide(collidedMesh);
+                    }
+                }
+                _this._collisionTriggered = false;
+            };
             if (!this.target) {
                 this.target = BABYLON.Vector3.Zero();
             }
@@ -274,6 +290,10 @@ var BABYLON;
             }
         };
         ArcRotateCamera.prototype._update = function () {
+            //if (async) collision inspection was triggered, don't update the camera's position - until the collision callback was called.
+            if (this._collisionTriggered) {
+                return;
+            }
             for (var index = 0; index < this._keys.length; index++) {
                 var keyCode = this._keys[index];
                 if (this.keysLeft.indexOf(keyCode) !== -1) {
@@ -346,16 +366,8 @@ var BABYLON;
             if (this.checkCollisions) {
                 this._collider.radius = this.collisionRadius;
                 this.position.subtractToRef(this._previousPosition, this._collisionVelocity);
-                this.getScene()._getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, this._newPosition);
-                if (!this._newPosition.equalsWithEpsilon(this.position)) {
-                    this.position.copyFrom(this._previousPosition);
-                    this.alpha = this._previousAlpha;
-                    this.beta = this._previousBeta;
-                    this.radius = this._previousRadius;
-                    if (this.onCollide) {
-                        this.onCollide(this._collider.collidedMesh);
-                    }
-                }
+                this._collisionTriggered = true;
+                this.getScene().collisionCoordinator.getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
             }
             BABYLON.Matrix.LookAtLHToRef(this.position, target, this.upVector, this._viewMatrix);
             this._previousAlpha = this.alpha;
@@ -392,4 +404,5 @@ var BABYLON;
     })(BABYLON.Camera);
     BABYLON.ArcRotateCamera = ArcRotateCamera;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.arcRotateCamera.js.map
+
+//# sourceMappingURL=../Cameras/babylon.arcRotateCamera.js.map

+ 29 - 13
Babylon/Cameras/babylon.arcRotateCamera.ts

@@ -50,6 +50,8 @@
         private _previousAlpha: number;
         private _previousBeta: number;
         private _previousRadius: number;
+        //due to async collision inspection
+        private _collisionTriggered: boolean;
 
         constructor(name: string, public alpha: number, public beta: number, public radius: number, public target: any, scene: Scene) {
             super(name, Vector3.Zero(), scene);
@@ -344,6 +346,12 @@
         }
 
         public _update(): void {
+
+            //if (async) collision inspection was triggered, don't update the camera's position - until the collision callback was called.
+            if (this._collisionTriggered) {
+                return;
+            }
+
             // Keyboard
             for (var index = 0; index < this._keys.length; index++) {
                 var keyCode = this._keys[index];
@@ -431,19 +439,8 @@
                 this._collider.radius = this.collisionRadius;
                 this.position.subtractToRef(this._previousPosition, this._collisionVelocity);
 
-                this.getScene()._getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, this._newPosition);
-
-                if (!this._newPosition.equalsWithEpsilon(this.position)) {
-                    this.position.copyFrom(this._previousPosition);
-
-                    this.alpha = this._previousAlpha;
-                    this.beta = this._previousBeta;
-                    this.radius = this._previousRadius;
-
-                    if (this.onCollide) {
-                        this.onCollide(this._collider.collidedMesh);
-                    }
-                }
+                this._collisionTriggered = true;
+                this.getScene().collisionCoordinator.getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
             }
 
             Matrix.LookAtLHToRef(this.position, target, this.upVector, this._viewMatrix);
@@ -459,6 +456,25 @@
             return this._viewMatrix;
         }
 
+        private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: AbstractMesh = null) => {
+
+            if (collisionId != null || collisionId != undefined)
+               newPosition.multiplyInPlace(this._collider.radius);
+
+            if (!newPosition.equalsWithEpsilon(this.position)) {
+                this.position.copyFrom(this._previousPosition);
+
+                this.alpha = this._previousAlpha;
+                this.beta = this._previousBeta;
+                this.radius = this._previousRadius;
+
+                if (this.onCollide && collidedMesh) {
+                    this.onCollide(collidedMesh);
+                }
+            }
+            this._collisionTriggered = false;
+        }
+
         public zoomOn(meshes?: AbstractMesh[]): void {
             meshes = meshes || this.getScene().meshes;
 

+ 27 - 14
Babylon/Cameras/babylon.freeCamera.js

@@ -9,6 +9,7 @@ var BABYLON;
     var FreeCamera = (function (_super) {
         __extends(FreeCamera, _super);
         function FreeCamera(name, position, scene) {
+            var _this = this;
             _super.call(this, name, position, scene);
             this.ellipsoid = new BABYLON.Vector3(0.5, 1, 0.5);
             this.keysUp = [38];
@@ -24,6 +25,25 @@ var BABYLON;
             this._oldPosition = BABYLON.Vector3.Zero();
             this._diffPosition = BABYLON.Vector3.Zero();
             this._newPosition = BABYLON.Vector3.Zero();
+            this._onCollisionPositionChange = function (collisionId, newPosition, collidedMesh) {
+                if (collidedMesh === void 0) { collidedMesh = null; }
+                //TODO move this to the collision coordinator!
+                if (collisionId != null || collisionId != undefined)
+                    newPosition.multiplyInPlace(_this._collider.radius);
+                _this._newPosition.copyFrom(newPosition);
+                _this._newPosition.subtractToRef(_this._oldPosition, _this._diffPosition);
+                var oldPosition = _this.position.clone();
+                if (_this._diffPosition.length() > BABYLON.Engine.CollisionsEpsilon) {
+                    _this.position.addInPlace(_this._diffPosition);
+                    if (_this.onCollide && collidedMesh) {
+                        _this.onCollide(collidedMesh);
+                    }
+                }
+                //check if it is the gravity inspection
+                if (collisionId != _this.uniqueId) {
+                    _this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, _this.position) != 0);
+                }
+            };
         }
         // Controls
         FreeCamera.prototype.attachControl = function (element, noPreventDefault) {
@@ -141,7 +161,8 @@ var BABYLON;
                 this._reset();
             }
         };
-        FreeCamera.prototype._collideWithWorld = function (velocity) {
+        FreeCamera.prototype._collideWithWorld = function (velocity, gravityInspection) {
+            if (gravityInspection === void 0) { gravityInspection = false; }
             var globalPosition;
             if (this.parent) {
                 globalPosition = BABYLON.Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
@@ -151,14 +172,7 @@ var BABYLON;
             }
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
             this._collider.radius = this.ellipsoid;
-            this.getScene()._getNewPosition(this._oldPosition, velocity, this._collider, 3, this._newPosition);
-            this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
-            if (this._diffPosition.length() > BABYLON.Engine.CollisionsEpsilon) {
-                this.position.addInPlace(this._diffPosition);
-                if (this.onCollide) {
-                    this.onCollide(this._collider.collidedMesh);
-                }
-            }
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, velocity, this._collider, 3, null, this._onCollisionPositionChange, velocity.equals(this.getScene().gravity) ? this.uniqueId + 100000 : this.uniqueId);
         };
         FreeCamera.prototype._checkInputs = function () {
             if (!this._localDirection) {
@@ -190,11 +204,9 @@ var BABYLON;
         };
         FreeCamera.prototype._updatePosition = function () {
             if (this.checkCollisions && this.getScene().collisionsEnabled) {
-                this._collideWithWorld(this.cameraDirection);
+                this._collideWithWorld(this.cameraDirection, false);
                 if (this.applyGravity) {
-                    var oldPosition = this.position;
-                    this._collideWithWorld(this.getScene().gravity);
-                    this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, this.position) != 0);
+                    this._collideWithWorld(this.getScene().gravity, true);
                 }
             }
             else {
@@ -209,4 +221,5 @@ var BABYLON;
     })(BABYLON.TargetCamera);
     BABYLON.FreeCamera = FreeCamera;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.freeCamera.js.map
+
+//# sourceMappingURL=../Cameras/babylon.freeCamera.js.map

+ 22 - 9
Babylon/Cameras/babylon.freeCamera.ts

@@ -177,7 +177,7 @@
             }
         }
 
-        public _collideWithWorld(velocity: Vector3): void {
+        public _collideWithWorld(velocity: Vector3, gravityInspection: boolean = false): void {
             var globalPosition: Vector3;
 
             if (this.parent) {
@@ -189,17 +189,32 @@
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
             this._collider.radius = this.ellipsoid;
 
-            this.getScene()._getNewPosition(this._oldPosition, velocity, this._collider, 3, this._newPosition);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, velocity, this._collider, 3, null, this._onCollisionPositionChange, velocity.equals(this.getScene().gravity) ? this.uniqueId + 100000 : this.uniqueId);
+            
+        }
+
+        private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: AbstractMesh = null) => {
+            //TODO move this to the collision coordinator!
+            if (collisionId != null || collisionId != undefined)
+                newPosition.multiplyInPlace(this._collider.radius);
+
+            this._newPosition.copyFrom(newPosition);
+
             this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
 
+            var oldPosition = this.position.clone();
             if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
                 this.position.addInPlace(this._diffPosition);
-                if (this.onCollide) {
-                    this.onCollide(this._collider.collidedMesh);
+                if (this.onCollide && collidedMesh) {
+                    this.onCollide(collidedMesh);
                 }
             }
+            //check if it is the gravity inspection
+            if (collisionId != this.uniqueId) {
+                this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, this.position) != 0);
+            }
         }
-
+        
         public _checkInputs(): void {
             if (!this._localDirection) {
                 this._localDirection = BABYLON.Vector3.Zero();
@@ -233,11 +248,9 @@
 
         public _updatePosition(): void {
             if (this.checkCollisions && this.getScene().collisionsEnabled) {
-                this._collideWithWorld(this.cameraDirection);
+                this._collideWithWorld(this.cameraDirection, false);
                 if (this.applyGravity) {
-                    var oldPosition = this.position;
-                    this._collideWithWorld(this.getScene().gravity);
-                    this._needMoveForGravity = (BABYLON.Vector3.DistanceSquared(oldPosition, this.position) != 0);
+                    this._collideWithWorld(this.getScene().gravity, true);
                 }
             } else {
                 this.position.addInPlace(this.cameraDirection);

+ 11 - 11
Babylon/Collisions/babylon.collider.js

@@ -96,18 +96,19 @@ var BABYLON;
                 return false;
             return true;
         };
-        Collider.prototype._testTriangle = function (faceIndex, subMesh, p1, p2, p3) {
+        Collider.prototype._testTriangle = function (faceIndex, trianglePlaneArray, p1, p2, p3, hasMaterial) {
             var t0;
             var embeddedInPlane = false;
-            if (!subMesh._trianglePlanes) {
-                subMesh._trianglePlanes = [];
+            //defensive programming, actually not needed.
+            if (!trianglePlaneArray) {
+                trianglePlaneArray = [];
             }
-            if (!subMesh._trianglePlanes[faceIndex]) {
-                subMesh._trianglePlanes[faceIndex] = new BABYLON.Plane(0, 0, 0, 0);
-                subMesh._trianglePlanes[faceIndex].copyFromPoints(p1, p2, p3);
+            if (!trianglePlaneArray[faceIndex]) {
+                trianglePlaneArray[faceIndex] = new BABYLON.Plane(0, 0, 0, 0);
+                trianglePlaneArray[faceIndex].copyFromPoints(p1, p2, p3);
             }
-            var trianglePlane = subMesh._trianglePlanes[faceIndex];
-            if ((!subMesh.getMaterial()) && !trianglePlane.isFrontFacingTo(this.normalizedVelocity, 0))
+            var trianglePlane = trianglePlaneArray[faceIndex];
+            if ((!hasMaterial) && !trianglePlane.isFrontFacingTo(this.normalizedVelocity, 0))
                 return;
             var signedDistToTrianglePlane = trianglePlane.signedDistanceTo(this.basePoint);
             var normalDotVelocity = BABYLON.Vector3.Dot(trianglePlane.normal, this.velocity);
@@ -241,16 +242,15 @@ var BABYLON;
                     }
                     this.nearestDistance = distToCollision;
                     this.collisionFound = true;
-                    this.collidedMesh = subMesh.getMesh();
                 }
             }
         };
-        Collider.prototype._collide = function (subMesh, pts, indices, indexStart, indexEnd, decal) {
+        Collider.prototype._collide = function (trianglePlaneArray, pts, indices, indexStart, indexEnd, decal, hasMaterial) {
             for (var i = indexStart; i < indexEnd; i += 3) {
                 var p1 = pts[indices[i] - decal];
                 var p2 = pts[indices[i + 1] - decal];
                 var p3 = pts[indices[i + 2] - decal];
-                this._testTriangle(i, subMesh, p3, p2, p1);
+                this._testTriangle(i, trianglePlaneArray, p3, p2, p1, hasMaterial);
             }
         };
         Collider.prototype._getResponse = function (pos, vel) {

+ 12 - 12
Babylon/Collisions/babylon.collider.ts

@@ -68,7 +68,7 @@
         public initialPosition: Vector3;
         public nearestDistance: number;
         public intersectionPoint: Vector3;
-        public collidedMesh: AbstractMesh
+        public collidedMesh: AbstractMesh;
 
         private _collisionPoint = BABYLON.Vector3.Zero();
         private _planeIntersectionPoint = BABYLON.Vector3.Zero();
@@ -132,22 +132,23 @@
             return true;
         }
 
-        public _testTriangle(faceIndex: number, subMesh: SubMesh, p1: Vector3, p2: Vector3, p3: Vector3): void {
+        public _testTriangle(faceIndex: number, trianglePlaneArray: Array<Plane>, p1: Vector3, p2: Vector3, p3: Vector3, hasMaterial: boolean): void {
             var t0;
             var embeddedInPlane = false;
 
-            if (!subMesh._trianglePlanes) {
-                subMesh._trianglePlanes = [];
+            //defensive programming, actually not needed.
+            if (!trianglePlaneArray) {
+                trianglePlaneArray = [];
             }
 
-            if (!subMesh._trianglePlanes[faceIndex]) {
-                subMesh._trianglePlanes[faceIndex] = new BABYLON.Plane(0, 0, 0, 0);
-                subMesh._trianglePlanes[faceIndex].copyFromPoints(p1, p2, p3);
+            if (!trianglePlaneArray[faceIndex]) {
+                trianglePlaneArray[faceIndex] = new BABYLON.Plane(0, 0, 0, 0);
+                trianglePlaneArray[faceIndex].copyFromPoints(p1, p2, p3);
             }
 
-            var trianglePlane = subMesh._trianglePlanes[faceIndex];
+            var trianglePlane = trianglePlaneArray[faceIndex];
 
-            if ((!subMesh.getMaterial()) && !trianglePlane.isFrontFacingTo(this.normalizedVelocity, 0))
+            if ((!hasMaterial) && !trianglePlane.isFrontFacingTo(this.normalizedVelocity, 0))
                 return;
 
             var signedDistToTrianglePlane = trianglePlane.signedDistanceTo(this.basePoint);
@@ -310,18 +311,17 @@
                     }
                     this.nearestDistance = distToCollision;
                     this.collisionFound = true;
-                    this.collidedMesh = subMesh.getMesh();
                 }
             }
         }
 
-        public _collide(subMesh, pts: Vector3[], indices: number[], indexStart: number, indexEnd: number, decal: number): void {
+        public _collide(trianglePlaneArray: Array<Plane>, pts: Vector3[], indices: number[], indexStart: number, indexEnd: number, decal: number, hasMaterial: boolean): void {
             for (var i = indexStart; i < indexEnd; i += 3) {
                 var p1 = pts[indices[i] - decal];
                 var p2 = pts[indices[i + 1] - decal];
                 var p3 = pts[indices[i + 2] - decal];
 
-                this._testTriangle(i, subMesh, p3, p2, p1);
+                this._testTriangle(i, trianglePlaneArray, p3, p2, p1, hasMaterial);
             }
         }
 

+ 272 - 0
Babylon/Collisions/babylon.collisionCoordinator.js

@@ -0,0 +1,272 @@
+var BABYLON;
+(function (BABYLON) {
+    //WebWorker code will be inserted to this variable.
+    BABYLON.CollisionWorker = "";
+    (function (WorkerTaskType) {
+        WorkerTaskType[WorkerTaskType["INIT"] = 0] = "INIT";
+        WorkerTaskType[WorkerTaskType["UPDATE"] = 1] = "UPDATE";
+        WorkerTaskType[WorkerTaskType["COLLIDE"] = 2] = "COLLIDE";
+    })(BABYLON.WorkerTaskType || (BABYLON.WorkerTaskType = {}));
+    var WorkerTaskType = BABYLON.WorkerTaskType;
+    (function (WorkerReplyType) {
+        WorkerReplyType[WorkerReplyType["SUCCESS"] = 0] = "SUCCESS";
+        WorkerReplyType[WorkerReplyType["UNKNOWN_ERROR"] = 1] = "UNKNOWN_ERROR";
+    })(BABYLON.WorkerReplyType || (BABYLON.WorkerReplyType = {}));
+    var WorkerReplyType = BABYLON.WorkerReplyType;
+    var CollisionCoordinatorWorker = (function () {
+        function CollisionCoordinatorWorker() {
+            var _this = this;
+            this._scaledPosition = BABYLON.Vector3.Zero();
+            this._scaledVelocity = BABYLON.Vector3.Zero();
+            this.onMeshUpdated = function (mesh) {
+                _this._addUpdateMeshesList[mesh.uniqueId] = CollisionCoordinatorWorker.SerializeMesh(mesh);
+            };
+            this.onGeometryUpdated = function (geometry) {
+                _this._addUpdateGeometriesList[geometry.id] = CollisionCoordinatorWorker.SerializeGeometry(geometry);
+            };
+            this._afterRender = function () {
+                if (!_this._init)
+                    return;
+                if (_this._toRemoveGeometryArray.length == 0 && _this._toRemoveMeshesArray.length == 0 && Object.keys(_this._addUpdateGeometriesList).length == 0 && Object.keys(_this._addUpdateMeshesList).length == 0) {
+                    return;
+                }
+                //5 concurrent updates were sent to the web worker and were not yet processed. Abort next update.
+                //TODO make sure update runs as fast as possible to be able to update 60 FPS.
+                if (_this._runningUpdated > 4) {
+                    return;
+                }
+                ++_this._runningUpdated;
+                var payload = {
+                    updatedMeshes: _this._addUpdateMeshesList,
+                    updatedGeometries: _this._addUpdateGeometriesList,
+                    removedGeometries: _this._toRemoveGeometryArray,
+                    removedMeshes: _this._toRemoveMeshesArray
+                };
+                var message = {
+                    payload: payload,
+                    taskType: 1 /* UPDATE */
+                };
+                var serializable = [];
+                for (var id in payload.updatedGeometries) {
+                    if (payload.updatedGeometries.hasOwnProperty(id)) {
+                        //prepare transferables
+                        serializable.push(message.payload.updatedGeometries[id].indices.buffer);
+                        serializable.push(message.payload.updatedGeometries[id].normals.buffer);
+                        serializable.push(message.payload.updatedGeometries[id].positions.buffer);
+                    }
+                }
+                _this._worker.postMessage(message, serializable);
+                _this._addUpdateMeshesList = {};
+                _this._addUpdateGeometriesList = {};
+                _this._toRemoveGeometryArray = [];
+                _this._toRemoveMeshesArray = [];
+            };
+            this._onMessageFromWorker = function (e) {
+                var returnData = e.data;
+                if (returnData.error != 0 /* SUCCESS */) {
+                    //TODO what errors can be returned from the worker?
+                    BABYLON.Tools.Warn("error returned from worker!");
+                    return;
+                }
+                switch (returnData.taskType) {
+                    case 0 /* INIT */:
+                        _this._init = true;
+                        //Update the worked with ALL of the scene's current state
+                        _this._scene.meshes.forEach(function (mesh) {
+                            _this.onMeshAdded(mesh);
+                        });
+                        _this._scene.getGeometries().forEach(function (geometry) {
+                            _this.onGeometryAdded(geometry);
+                        });
+                        break;
+                    case 1 /* UPDATE */:
+                        _this._runningUpdated--;
+                        break;
+                    case 2 /* COLLIDE */:
+                        _this._runningCollisionTask = false;
+                        var returnPayload = returnData.payload;
+                        if (!_this._collisionsCallbackArray[returnPayload.collisionId])
+                            return;
+                        _this._collisionsCallbackArray[returnPayload.collisionId](returnPayload.collisionId, BABYLON.Vector3.FromArray(returnPayload.newPosition), _this._scene.getMeshByUniqueID(returnPayload.collidedMeshUniqueId));
+                        //cleanup
+                        _this._collisionsCallbackArray[returnPayload.collisionId] = undefined;
+                        break;
+                }
+            };
+            this._collisionsCallbackArray = [];
+            this._init = false;
+            this._runningUpdated = 0;
+            this._runningCollisionTask = false;
+            this._addUpdateMeshesList = {};
+            this._addUpdateGeometriesList = {};
+            this._toRemoveGeometryArray = [];
+            this._toRemoveMeshesArray = [];
+        }
+        CollisionCoordinatorWorker.prototype.getNewPosition = function (position, velocity, collider, maximumRetry, excludedMesh, onNewPosition, collisionIndex) {
+            if (!this._init)
+                ;
+            if (this._collisionsCallbackArray[collisionIndex])
+                return;
+            position.divideToRef(collider.radius, this._scaledPosition);
+            velocity.divideToRef(collider.radius, this._scaledVelocity);
+            this._collisionsCallbackArray[collisionIndex] = onNewPosition;
+            var payload = {
+                collider: {
+                    position: this._scaledPosition.asArray(),
+                    velocity: this._scaledVelocity.asArray(),
+                    radius: collider.radius.asArray()
+                },
+                collisionId: collisionIndex,
+                excludedMeshUniqueId: excludedMesh ? excludedMesh.uniqueId : null,
+                maximumRetry: maximumRetry
+            };
+            var message = {
+                payload: payload,
+                taskType: 2 /* COLLIDE */
+            };
+            this._worker.postMessage(message);
+        };
+        CollisionCoordinatorWorker.prototype.init = function (scene) {
+            this._scene = scene;
+            this._scene.registerAfterRender(this._afterRender);
+            var blobURL = URL.createObjectURL(new Blob([BABYLON.CollisionWorker], { type: 'application/javascript' }));
+            this._worker = new Worker(blobURL);
+            this._worker.onmessage = this._onMessageFromWorker;
+            var message = {
+                payload: {},
+                taskType: 0 /* INIT */
+            };
+            this._worker.postMessage(message);
+        };
+        CollisionCoordinatorWorker.prototype.destroy = function () {
+            this._scene.unregisterAfterRender(this._afterRender);
+            this._worker.terminate();
+        };
+        CollisionCoordinatorWorker.prototype.onMeshAdded = function (mesh) {
+            mesh.registerAfterWorldMatrixUpdate(this.onMeshUpdated);
+            this.onMeshUpdated(mesh);
+        };
+        CollisionCoordinatorWorker.prototype.onMeshRemoved = function (mesh) {
+            this._toRemoveMeshesArray.push(mesh.uniqueId);
+        };
+        CollisionCoordinatorWorker.prototype.onGeometryAdded = function (geometry) {
+            //TODO this will break if the user uses his own function. This should be an array of callbacks!
+            geometry.onGeometryUpdated = this.onGeometryUpdated;
+            this.onGeometryUpdated(geometry);
+        };
+        CollisionCoordinatorWorker.prototype.onGeometryDeleted = function (geometry) {
+            this._toRemoveGeometryArray.push(geometry.id);
+        };
+        CollisionCoordinatorWorker.SerializeMesh = function (mesh) {
+            var submeshes = [];
+            if (mesh.subMeshes) {
+                submeshes = mesh.subMeshes.map(function (sm, idx) {
+                    return {
+                        position: idx,
+                        verticesStart: sm.verticesStart,
+                        verticesCount: sm.verticesCount,
+                        indexStart: sm.indexStart,
+                        indexCount: sm.indexCount,
+                        hasMaterial: !!sm.getMaterial(),
+                        sphereCenter: sm.getBoundingInfo().boundingSphere.centerWorld.asArray(),
+                        sphereRadius: sm.getBoundingInfo().boundingSphere.radiusWorld,
+                        boxMinimum: sm.getBoundingInfo().boundingBox.minimumWorld.asArray(),
+                        boxMaximum: sm.getBoundingInfo().boundingBox.maximumWorld.asArray()
+                    };
+                });
+            }
+            var geometryId = mesh.geometry ? mesh.geometry.id : null;
+            return {
+                uniqueId: mesh.uniqueId,
+                id: mesh.id,
+                name: mesh.name,
+                geometryId: geometryId,
+                sphereCenter: mesh.getBoundingInfo().boundingSphere.centerWorld.asArray(),
+                sphereRadius: mesh.getBoundingInfo().boundingSphere.radiusWorld,
+                boxMinimum: mesh.getBoundingInfo().boundingBox.minimumWorld.asArray(),
+                boxMaximum: mesh.getBoundingInfo().boundingBox.maximumWorld.asArray(),
+                worldMatrixFromCache: mesh.worldMatrixFromCache.asArray(),
+                subMeshes: submeshes,
+                checkCollisions: mesh.checkCollisions
+            };
+        };
+        CollisionCoordinatorWorker.SerializeGeometry = function (geometry) {
+            return {
+                id: geometry.id,
+                positions: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind) || []),
+                normals: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.NormalKind) || []),
+                indices: new Int32Array(geometry.getIndices() || []),
+            };
+        };
+        return CollisionCoordinatorWorker;
+    })();
+    BABYLON.CollisionCoordinatorWorker = CollisionCoordinatorWorker;
+    var CollisionCoordinatorLegacy = (function () {
+        function CollisionCoordinatorLegacy() {
+            this._scaledPosition = BABYLON.Vector3.Zero();
+            this._scaledVelocity = BABYLON.Vector3.Zero();
+            this._finalPosition = BABYLON.Vector3.Zero();
+        }
+        CollisionCoordinatorLegacy.prototype.getNewPosition = function (position, velocity, collider, maximumRetry, excludedMesh, onNewPosition, collisionIndex) {
+            position.divideToRef(collider.radius, this._scaledPosition);
+            velocity.divideToRef(collider.radius, this._scaledVelocity);
+            collider.retry = 0;
+            collider.initialVelocity = this._scaledVelocity;
+            collider.initialPosition = this._scaledPosition;
+            this._collideWithWorld(this._scaledPosition, this._scaledVelocity, collider, maximumRetry, this._finalPosition, excludedMesh);
+            this._finalPosition.multiplyInPlace(collider.radius);
+            //run the callback
+            onNewPosition(null, this._finalPosition, collider.collidedMesh);
+        };
+        CollisionCoordinatorLegacy.prototype.init = function (scene) {
+            this._scene = scene;
+        };
+        CollisionCoordinatorLegacy.prototype.destroy = function () {
+            //Legacy need no destruction method.
+        };
+        //No update in legacy mode
+        CollisionCoordinatorLegacy.prototype.onMeshAdded = function (mesh) {
+        };
+        CollisionCoordinatorLegacy.prototype.onMeshUpdated = function (mesh) {
+        };
+        CollisionCoordinatorLegacy.prototype.onMeshRemoved = function (mesh) {
+        };
+        CollisionCoordinatorLegacy.prototype.onGeometryAdded = function (geometry) {
+        };
+        CollisionCoordinatorLegacy.prototype.onGeometryUpdated = function (geometry) {
+        };
+        CollisionCoordinatorLegacy.prototype.onGeometryDeleted = function (geometry) {
+        };
+        CollisionCoordinatorLegacy.prototype._collideWithWorld = function (position, velocity, collider, maximumRetry, finalPosition, excludedMesh) {
+            if (excludedMesh === void 0) { excludedMesh = null; }
+            var closeDistance = BABYLON.Engine.CollisionsEpsilon * 10.0;
+            if (collider.retry >= maximumRetry) {
+                finalPosition.copyFrom(position);
+                return;
+            }
+            collider._initialize(position, velocity, closeDistance);
+            for (var index = 0; index < this._scene.meshes.length; index++) {
+                var mesh = this._scene.meshes[index];
+                if (mesh.isEnabled() && mesh.checkCollisions && mesh.subMeshes && mesh !== excludedMesh) {
+                    mesh._checkCollision(collider);
+                }
+            }
+            if (!collider.collisionFound) {
+                position.addToRef(velocity, finalPosition);
+                return;
+            }
+            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
+                collider._getResponse(position, velocity);
+            }
+            if (velocity.length() <= closeDistance) {
+                finalPosition.copyFrom(position);
+                return;
+            }
+            collider.retry++;
+            this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
+        };
+        return CollisionCoordinatorLegacy;
+    })();
+    BABYLON.CollisionCoordinatorLegacy = CollisionCoordinatorLegacy;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.collisionCoordinator.js.map

+ 411 - 0
Babylon/Collisions/babylon.collisionCoordinator.ts

@@ -0,0 +1,411 @@
+module BABYLON {
+
+    //WebWorker code will be inserted to this variable.
+    export var CollisionWorker = "";
+
+    export interface ICollisionCoordinator {
+        getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void;
+        init(scene: Scene): void;
+        destroy(): void;
+
+        //Update meshes and geometries
+        onMeshAdded(mesh: AbstractMesh);
+        onMeshUpdated(mesh: AbstractMesh);
+        onMeshRemoved(mesh: AbstractMesh);
+        onGeometryAdded(geometry: Geometry);
+        onGeometryUpdated(geometry: Geometry);
+        onGeometryDeleted(geometry: Geometry);
+    }
+
+    export interface SerializedMesh {
+        id: string;
+        name: string;
+        uniqueId: number;
+        geometryId: string;
+        sphereCenter: Array<number>;
+        sphereRadius: number;
+        boxMinimum: Array<number>;
+        boxMaximum: Array<number>;
+        worldMatrixFromCache: any;
+        subMeshes: Array<SerializedSubMesh>;
+        checkCollisions: boolean;
+    }
+
+    export interface SerializedSubMesh {
+        position: number;
+        verticesStart: number;
+        verticesCount: number;
+        indexStart: number;
+        indexCount: number;
+        hasMaterial: boolean;
+        sphereCenter: Array<number>;
+        sphereRadius: number;
+        boxMinimum: Array<number>;
+        boxMaximum: Array<number>;
+    }
+
+    export interface SerializedGeometry {
+        id: string;
+        positions: Float32Array;
+        indices: Int32Array;
+        normals: Float32Array;
+        //uvs?: Float32Array;
+    }
+
+    export interface BabylonMessage {
+        taskType: WorkerTaskType;
+        payload: InitPayload|CollidePayload|UpdatePayload /*any for TS under 1.4*/;
+    }
+
+    export interface SerializedColliderToWorker {
+        position: Array<number>;
+        velocity: Array<number>;
+        radius: Array<number>;
+    }
+
+    export enum WorkerTaskType {
+        INIT,
+        UPDATE,
+        COLLIDE
+    }
+
+    export interface WorkerReply {
+        error: WorkerReplyType;
+        taskType: WorkerTaskType;
+        payload?: any;
+    }
+
+    export interface CollisionReplyPayload {
+        newPosition: Array<number>;
+        collisionId: number;
+        collidedMeshUniqueId: number;
+    }
+
+    export interface InitPayload {
+
+    }
+    
+    export interface CollidePayload {
+        collisionId: number;
+        collider: SerializedColliderToWorker;
+        maximumRetry: number;
+        excludedMeshUniqueId?: number;
+    }
+        
+    export interface UpdatePayload {
+        updatedMeshes: { [n: number]: SerializedMesh; };
+        updatedGeometries: { [s: string]: SerializedGeometry; };
+        removedMeshes: Array<number>;
+        removedGeometries: Array<string>;
+    }
+    
+    export enum WorkerReplyType {
+        SUCCESS,
+        UNKNOWN_ERROR
+    }
+
+    export class CollisionCoordinatorWorker implements ICollisionCoordinator {
+
+        private _scene: Scene;
+
+        private _scaledPosition = Vector3.Zero();
+        private _scaledVelocity = Vector3.Zero();
+
+        private _collisionsCallbackArray: Array<(collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void>;
+
+        private _init: boolean;
+        private _runningUpdated: number;
+        private _runningCollisionTask: boolean;
+        private _worker: Worker;
+
+        private _addUpdateMeshesList: { [n: number]: SerializedMesh; }
+        private _addUpdateGeometriesList: { [s: string]: SerializedGeometry; };
+        private _toRemoveMeshesArray: Array<number>;
+        private _toRemoveGeometryArray: Array<string>;
+
+        constructor() {
+            this._collisionsCallbackArray = [];
+            this._init = false;
+            this._runningUpdated = 0;
+            this._runningCollisionTask = false;
+
+            this._addUpdateMeshesList = {};
+            this._addUpdateGeometriesList = {};
+            this._toRemoveGeometryArray = [];
+            this._toRemoveMeshesArray = [];
+        }
+
+        public static SerializeMesh = function (mesh: BABYLON.AbstractMesh): SerializedMesh {
+            var submeshes : Array<SerializedSubMesh> = [];
+            if (mesh.subMeshes) {
+                submeshes = mesh.subMeshes.map(function (sm, idx) {
+                    return {
+                        position: idx,
+                        verticesStart: sm.verticesStart,
+                        verticesCount: sm.verticesCount,
+                        indexStart: sm.indexStart,
+                        indexCount: sm.indexCount,
+                        hasMaterial: !!sm.getMaterial(),
+                        sphereCenter: sm.getBoundingInfo().boundingSphere.centerWorld.asArray(),
+                        sphereRadius: sm.getBoundingInfo().boundingSphere.radiusWorld,
+                        boxMinimum: sm.getBoundingInfo().boundingBox.minimumWorld.asArray(),
+                        boxMaximum: sm.getBoundingInfo().boundingBox.maximumWorld.asArray()
+                    }
+                });
+            }
+
+            var geometryId = (<BABYLON.Mesh>mesh).geometry ? (<BABYLON.Mesh>mesh).geometry.id : null;
+
+            return {
+                uniqueId: mesh.uniqueId,
+                id: mesh.id,
+                name: mesh.name,
+                geometryId: geometryId,
+                sphereCenter: mesh.getBoundingInfo().boundingSphere.centerWorld.asArray(),
+                sphereRadius: mesh.getBoundingInfo().boundingSphere.radiusWorld,
+                boxMinimum: mesh.getBoundingInfo().boundingBox.minimumWorld.asArray(),
+                boxMaximum: mesh.getBoundingInfo().boundingBox.maximumWorld.asArray(),
+                worldMatrixFromCache: mesh.worldMatrixFromCache.asArray(),
+                subMeshes: submeshes,
+                checkCollisions: mesh.checkCollisions
+            }
+        }
+
+        public static SerializeGeometry = function (geometry: BABYLON.Geometry): SerializedGeometry {
+            return {
+                id: geometry.id,
+                positions: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.PositionKind) || []),
+                normals: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.NormalKind) || []),
+                indices: new Int32Array(geometry.getIndices() || []),
+                //uvs: new Float32Array(geometry.getVerticesData(BABYLON.VertexBuffer.UVKind) || [])
+            }
+        }
+
+        public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void {
+            if (!this._init);
+            if (this._collisionsCallbackArray[collisionIndex]) return;
+
+            position.divideToRef(collider.radius, this._scaledPosition);
+            velocity.divideToRef(collider.radius, this._scaledVelocity);
+
+            this._collisionsCallbackArray[collisionIndex] = onNewPosition;
+
+            var payload: CollidePayload = {
+                collider: {
+                    position: this._scaledPosition.asArray(),
+                    velocity: this._scaledVelocity.asArray(),
+                    radius: collider.radius.asArray()
+                },
+                collisionId: collisionIndex,
+                excludedMeshUniqueId: excludedMesh ? excludedMesh.uniqueId : null,
+                maximumRetry: maximumRetry
+            };
+            var message: BabylonMessage = {
+                payload: payload,
+                taskType: WorkerTaskType.COLLIDE
+            }
+            this._worker.postMessage(message);
+
+        }
+
+        public init(scene: Scene): void {
+            this._scene = scene;
+            this._scene.registerAfterRender(this._afterRender);
+            var blobURL = URL.createObjectURL(new Blob([BABYLON.CollisionWorker], { type: 'application/javascript' }));
+            this._worker = new Worker(blobURL);
+            this._worker.onmessage = this._onMessageFromWorker;
+            var message: BabylonMessage = {
+                payload: {},
+                taskType: WorkerTaskType.INIT
+            }
+            this._worker.postMessage(message);
+        }
+
+        public destroy(): void {
+            this._scene.unregisterAfterRender(this._afterRender);
+            this._worker.terminate();
+        }
+
+        public onMeshAdded(mesh: AbstractMesh) {
+            mesh.registerAfterWorldMatrixUpdate(this.onMeshUpdated);
+            this.onMeshUpdated(mesh);
+        }
+
+        public onMeshUpdated = (mesh: AbstractMesh) => {
+            this._addUpdateMeshesList[mesh.uniqueId] = CollisionCoordinatorWorker.SerializeMesh(mesh);
+        }
+
+        public onMeshRemoved(mesh: AbstractMesh) {
+            this._toRemoveMeshesArray.push(mesh.uniqueId);
+        }
+
+        public onGeometryAdded(geometry: Geometry) {
+            //TODO this will break if the user uses his own function. This should be an array of callbacks!
+            geometry.onGeometryUpdated = this.onGeometryUpdated;
+            this.onGeometryUpdated(geometry);
+        }
+
+        public onGeometryUpdated = (geometry: Geometry) => {
+            this._addUpdateGeometriesList[geometry.id] = CollisionCoordinatorWorker.SerializeGeometry(geometry);
+        }
+
+        public onGeometryDeleted(geometry: Geometry) {
+            this._toRemoveGeometryArray.push(geometry.id);
+        }
+
+        private _afterRender = () => {
+
+            if (!this._init) return;
+
+            if (this._toRemoveGeometryArray.length == 0 && this._toRemoveMeshesArray.length == 0 && Object.keys(this._addUpdateGeometriesList).length == 0 && Object.keys(this._addUpdateMeshesList).length == 0) {
+                return;
+            }
+
+            //5 concurrent updates were sent to the web worker and were not yet processed. Abort next update.
+            //TODO make sure update runs as fast as possible to be able to update 60 FPS.
+            if (this._runningUpdated > 4) {
+                return;
+            }
+
+            ++this._runningUpdated;
+
+            var payload: UpdatePayload = {
+                updatedMeshes: this._addUpdateMeshesList,
+                updatedGeometries: this._addUpdateGeometriesList,
+                removedGeometries: this._toRemoveGeometryArray,
+                removedMeshes: this._toRemoveMeshesArray
+            };
+            var message: BabylonMessage = {
+                payload: payload,
+                taskType: WorkerTaskType.UPDATE
+            }
+            var serializable = [];
+            for (var id in payload.updatedGeometries) {
+                if (payload.updatedGeometries.hasOwnProperty(id)) {
+                    //prepare transferables
+                    serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].indices.buffer);
+                    serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].normals.buffer);
+                    serializable.push((<UpdatePayload> message.payload).updatedGeometries[id].positions.buffer);
+                }
+            }
+
+            this._worker.postMessage(message, serializable);
+            this._addUpdateMeshesList = {};
+            this._addUpdateGeometriesList = {};
+            this._toRemoveGeometryArray = [];
+            this._toRemoveMeshesArray = [];
+        }
+
+        private _onMessageFromWorker = (e: MessageEvent) => {
+            var returnData = <WorkerReply> e.data;
+            if (returnData.error != WorkerReplyType.SUCCESS) {
+                //TODO what errors can be returned from the worker?
+                Tools.Warn("error returned from worker!");
+                return;
+            }
+
+            switch (returnData.taskType) {
+                case WorkerTaskType.INIT:
+                    this._init = true;
+                    //Update the worked with ALL of the scene's current state
+                    this._scene.meshes.forEach((mesh) => {
+                        this.onMeshAdded(mesh);
+                    });
+
+                    this._scene.getGeometries().forEach((geometry) => {
+                        this.onGeometryAdded(geometry);
+                    });
+
+                    break;
+                case WorkerTaskType.UPDATE:
+                    this._runningUpdated--;
+                    break;
+                case WorkerTaskType.COLLIDE:
+                    this._runningCollisionTask = false;
+                    var returnPayload: CollisionReplyPayload = returnData.payload;
+                    if (!this._collisionsCallbackArray[returnPayload.collisionId]) return;
+
+                    this._collisionsCallbackArray[returnPayload.collisionId](returnPayload.collisionId, Vector3.FromArray(returnPayload.newPosition), this._scene.getMeshByUniqueID(returnPayload.collidedMeshUniqueId));
+                    //cleanup
+                    this._collisionsCallbackArray[returnPayload.collisionId] = undefined;
+                    break;
+            }
+        }
+    }
+
+    export class CollisionCoordinatorLegacy implements ICollisionCoordinator {
+
+        private _scene: Scene;
+
+        private _scaledPosition = Vector3.Zero();
+        private _scaledVelocity = Vector3.Zero();
+
+        private _finalPosition = Vector3.Zero();
+
+        public getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh, onNewPosition: (collisionIndex: number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number): void {
+            position.divideToRef(collider.radius, this._scaledPosition);
+            velocity.divideToRef(collider.radius, this._scaledVelocity);
+            
+            collider.retry = 0;
+            collider.initialVelocity = this._scaledVelocity;
+            collider.initialPosition = this._scaledPosition;
+            this._collideWithWorld(this._scaledPosition, this._scaledVelocity, collider, maximumRetry, this._finalPosition, excludedMesh);
+
+            this._finalPosition.multiplyInPlace(collider.radius);
+            //run the callback
+            onNewPosition(null, this._finalPosition, collider.collidedMesh);
+        }
+
+        public init(scene: Scene): void {
+            this._scene = scene;
+        }
+
+        public destroy(): void {
+            //Legacy need no destruction method.
+        }
+
+        //No update in legacy mode
+        public onMeshAdded(mesh: AbstractMesh) { }
+        public onMeshUpdated(mesh: AbstractMesh) { }
+        public onMeshRemoved(mesh: AbstractMesh) { }
+        public onGeometryAdded(geometry: Geometry) { }
+        public onGeometryUpdated(geometry: Geometry) { }
+        public onGeometryDeleted(geometry: Geometry) { }
+
+        private _collideWithWorld(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: AbstractMesh = null): void {
+            var closeDistance = Engine.CollisionsEpsilon * 10.0;
+
+            if (collider.retry >= maximumRetry) {
+                finalPosition.copyFrom(position);
+                return;
+            }
+
+            collider._initialize(position, velocity, closeDistance);
+
+            // Check all meshes
+            for (var index = 0; index < this._scene.meshes.length; index++) {
+                var mesh = this._scene.meshes[index];
+                if (mesh.isEnabled() && mesh.checkCollisions && mesh.subMeshes && mesh !== excludedMesh) {
+                    mesh._checkCollision(collider);
+                }
+            }
+
+            if (!collider.collisionFound) {
+                position.addToRef(velocity, finalPosition);
+                return;
+            }
+
+            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
+                collider._getResponse(position, velocity);
+            }
+
+            if (velocity.length() <= closeDistance) {
+                finalPosition.copyFrom(position);
+                return;
+            }
+
+            collider.retry++;
+            this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
+        }
+    }
+}

+ 213 - 0
Babylon/Collisions/babylon.collisionWorker.js

@@ -0,0 +1,213 @@
+var BABYLON;
+(function (BABYLON) {
+    var CollisionCache = (function () {
+        function CollisionCache() {
+            this._meshes = {};
+            this._geometries = {};
+        }
+        CollisionCache.prototype.getMeshes = function () {
+            return this._meshes;
+        };
+        CollisionCache.prototype.getGeometries = function () {
+            return this._geometries;
+        };
+        CollisionCache.prototype.getMesh = function (id) {
+            return this._meshes[id];
+        };
+        CollisionCache.prototype.addMesh = function (mesh) {
+            this._meshes[mesh.uniqueId] = mesh;
+        };
+        CollisionCache.prototype.getGeometry = function (id) {
+            return this._geometries[id];
+        };
+        CollisionCache.prototype.addGeometry = function (geometry) {
+            this._geometries[geometry.id] = geometry;
+        };
+        return CollisionCache;
+    })();
+    BABYLON.CollisionCache = CollisionCache;
+    var CollideWorker = (function () {
+        function CollideWorker(collider, _collisionCache, finalPosition) {
+            this.collider = collider;
+            this._collisionCache = _collisionCache;
+            this.finalPosition = finalPosition;
+            this.collisionsScalingMatrix = BABYLON.Matrix.Zero();
+            this.collisionTranformationMatrix = BABYLON.Matrix.Zero();
+        }
+        CollideWorker.prototype.collideWithWorld = function (position, velocity, maximumRetry, excludedMeshUniqueId) {
+            //TODO CollisionsEpsilon should be defined here and not in the engine.
+            var closeDistance = 0.01;
+            //is initializing here correct? A quick look - looks like it is fine.
+            if (this.collider.retry >= maximumRetry) {
+                this.finalPosition.copyFrom(position);
+                return;
+            }
+            this.collider._initialize(position, velocity, closeDistance);
+            // Check all meshes
+            var meshes = this._collisionCache.getMeshes();
+            for (var uniqueId in meshes) {
+                if (meshes.hasOwnProperty(uniqueId) && parseInt(uniqueId) != excludedMeshUniqueId) {
+                    var mesh = meshes[uniqueId];
+                    if (mesh.checkCollisions)
+                        this.checkCollision(mesh);
+                }
+            }
+            if (!this.collider.collisionFound) {
+                position.addToRef(velocity, this.finalPosition);
+                return;
+            }
+            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
+                this.collider._getResponse(position, velocity);
+            }
+            if (velocity.length() <= closeDistance) {
+                this.finalPosition.copyFrom(position);
+                return;
+            }
+            this.collider.retry++;
+            this.collideWithWorld(position, velocity, maximumRetry, excludedMeshUniqueId);
+        };
+        CollideWorker.prototype.checkCollision = function (mesh) {
+            if (!this.collider._canDoCollision(BABYLON.Vector3.FromArray(mesh.sphereCenter), mesh.sphereRadius, BABYLON.Vector3.FromArray(mesh.boxMinimum), BABYLON.Vector3.FromArray(mesh.boxMaximum))) {
+                return;
+            }
+            ;
+            // Transformation matrix
+            BABYLON.Matrix.ScalingToRef(1.0 / this.collider.radius.x, 1.0 / this.collider.radius.y, 1.0 / this.collider.radius.z, this.collisionsScalingMatrix);
+            var worldFromCache = BABYLON.Matrix.FromArray(mesh.worldMatrixFromCache);
+            worldFromCache.multiplyToRef(this.collisionsScalingMatrix, this.collisionTranformationMatrix);
+            this.processCollisionsForSubMeshes(this.collisionTranformationMatrix, mesh);
+            //return colTransMat;
+        };
+        CollideWorker.prototype.processCollisionsForSubMeshes = function (transformMatrix, mesh) {
+            var len;
+            var subMeshes;
+            // No Octrees for now
+            //if (this._submeshesOctree && this.useOctreeForCollisions) {
+            //    var radius = collider.velocityWorldLength + Math.max(collider.radius.x, collider.radius.y, collider.radius.z);
+            //    var intersections = this._submeshesOctree.intersects(collider.basePointWorld, radius);
+            //    len = intersections.length;
+            //    subMeshes = intersections.data;
+            //} else {
+            subMeshes = mesh.subMeshes;
+            len = subMeshes.length;
+            //}
+            if (!mesh.geometryId) {
+                console.log("no mesh geometry id");
+                return;
+            }
+            var meshGeometry = this._collisionCache.getGeometry(mesh.geometryId);
+            if (!meshGeometry) {
+                console.log("couldn't find geometry", mesh.geometryId);
+                return;
+            }
+            for (var index = 0; index < len; index++) {
+                var subMesh = subMeshes[index];
+                // Bounding test
+                if (len > 1 && !this.checkSubmeshCollision(subMesh))
+                    continue;
+                //Unneeded
+                //subMesh['getMesh'] = function () {
+                //    return mesh.uniqueId;
+                //}
+                this.collideForSubMesh(subMesh, transformMatrix, meshGeometry);
+            }
+        };
+        CollideWorker.prototype.collideForSubMesh = function (subMesh, transformMatrix, meshGeometry) {
+            var positionsArray = [];
+            for (var i = 0; i < meshGeometry.positions.length; i = i + 3) {
+                var p = BABYLON.Vector3.FromArray([meshGeometry.positions[i], meshGeometry.positions[i + 1], meshGeometry.positions[i + 2]]);
+                positionsArray.push(p);
+            }
+            subMesh['_lastColliderTransformMatrix'] = transformMatrix.clone();
+            //The following two arrays should be initialized CORRECTLY to save some calculation time.
+            subMesh['_lastColliderWorldVertices'] = [];
+            subMesh['_trianglePlanes'] = [];
+            var start = subMesh.verticesStart;
+            var end = (subMesh.verticesStart + subMesh.verticesCount);
+            for (var i = start; i < end; i++) {
+                subMesh['_lastColliderWorldVertices'].push(BABYLON.Vector3.TransformCoordinates(positionsArray[i], transformMatrix));
+            }
+            //}
+            // Collide
+            this.collider._collide(subMesh['_trianglePlanes'] = [], subMesh['_lastColliderWorldVertices'], meshGeometry.indices, subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, subMesh.hasMaterial);
+        };
+        //TODO - this! :-)
+        CollideWorker.prototype.checkSubmeshCollision = function (subMesh) {
+            return this.collider._canDoCollision(BABYLON.Vector3.FromArray(subMesh.sphereCenter), subMesh.sphereRadius, BABYLON.Vector3.FromArray(subMesh.boxMinimum), BABYLON.Vector3.FromArray(subMesh.boxMaximum));
+        };
+        return CollideWorker;
+    })();
+    BABYLON.CollideWorker = CollideWorker;
+    var CollisionDetectorTransferable = (function () {
+        function CollisionDetectorTransferable() {
+        }
+        CollisionDetectorTransferable.prototype.onInit = function (payload) {
+            this._collisionCache = new CollisionCache();
+            var reply = {
+                error: 0 /* SUCCESS */,
+                taskType: 0 /* INIT */
+            };
+            postMessage(reply, undefined);
+        };
+        CollisionDetectorTransferable.prototype.onUpdate = function (payload) {
+            for (var id in payload.updatedGeometries) {
+                if (payload.updatedGeometries.hasOwnProperty(id)) {
+                    this._collisionCache.addGeometry(payload.updatedGeometries[id]);
+                }
+            }
+            for (var uniqueId in payload.updatedMeshes) {
+                if (payload.updatedMeshes.hasOwnProperty(uniqueId)) {
+                    this._collisionCache.addMesh(payload.updatedMeshes[uniqueId]);
+                }
+            }
+            var replay = {
+                error: 0 /* SUCCESS */,
+                taskType: 1 /* UPDATE */
+            };
+            postMessage(replay, undefined);
+        };
+        CollisionDetectorTransferable.prototype.onCollision = function (payload) {
+            var finalPosition = BABYLON.Vector3.Zero();
+            //create a new collider
+            var collider = new BABYLON.Collider();
+            collider.radius = BABYLON.Vector3.FromArray(payload.collider.radius);
+            var colliderWorker = new CollideWorker(collider, this._collisionCache, finalPosition);
+            colliderWorker.collideWithWorld(BABYLON.Vector3.FromArray(payload.collider.position), BABYLON.Vector3.FromArray(payload.collider.velocity), payload.maximumRetry, payload.excludedMeshUniqueId);
+            var replyPayload = {
+                collidedMeshUniqueId: collider.collidedMesh,
+                collisionId: payload.collisionId,
+                newPosition: finalPosition.asArray()
+            };
+            var reply = {
+                error: 0 /* SUCCESS */,
+                taskType: 2 /* COLLIDE */,
+                payload: replyPayload
+            };
+            postMessage(reply, undefined);
+        };
+        return CollisionDetectorTransferable;
+    })();
+    BABYLON.CollisionDetectorTransferable = CollisionDetectorTransferable;
+    //check if we are in a web worker, as this code should NOT run on the main UI thread
+    if (self && self instanceof WorkerGlobalScope) {
+        //Window hack to allow including babylonjs native code. the <any> is for typescript.
+        window = {};
+        var collisionDetector = new CollisionDetectorTransferable();
+        var onNewMessage = function (event) {
+            var message = event.data;
+            switch (message.taskType) {
+                case 0 /* INIT */:
+                    collisionDetector.onInit(message.payload);
+                    break;
+                case 2 /* COLLIDE */:
+                    collisionDetector.onCollision(message.payload);
+                    break;
+                case 1 /* UPDATE */:
+                    collisionDetector.onUpdate(message.payload);
+                    break;
+            }
+        };
+        self.onmessage = onNewMessage;
+    }
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.collisionWorker.js.map

+ 256 - 0
Babylon/Collisions/babylon.collisionWorker.ts

@@ -0,0 +1,256 @@
+module BABYLON {
+
+    export class CollisionCache {
+        private _meshes: { [n: number]: SerializedMesh; } = {};
+        private _geometries: { [s: number]: SerializedGeometry; } = {};
+
+        public getMeshes(): { [n: number]: SerializedMesh; } {
+            return this._meshes;
+        }
+
+        public getGeometries(): { [s: number]: SerializedGeometry; } {
+            return this._geometries;
+        }
+
+        public getMesh(id: any): SerializedMesh {
+            return this._meshes[id];
+        }
+
+        public addMesh(mesh: SerializedMesh) {
+            this._meshes[mesh.uniqueId] = mesh;
+        }
+
+        public getGeometry(id: string): SerializedGeometry {
+            return this._geometries[id];
+        }
+
+        public addGeometry(geometry: SerializedGeometry) {
+            this._geometries[geometry.id] = geometry;
+        }
+    }
+
+    export class CollideWorker {
+
+        private collisionsScalingMatrix = BABYLON.Matrix.Zero();
+        private collisionTranformationMatrix = BABYLON.Matrix.Zero();
+
+        constructor(public collider: BABYLON.Collider, private _collisionCache: CollisionCache, private finalPosition: BABYLON.Vector3) {
+
+        }
+
+        public collideWithWorld(position: BABYLON.Vector3, velocity: BABYLON.Vector3, maximumRetry: number, excludedMeshUniqueId?: number) {
+
+            //TODO CollisionsEpsilon should be defined here and not in the engine.
+            var closeDistance = /*BABYLON.Engine.CollisionsEpsilon * 10.0*/ 0.01;
+            //is initializing here correct? A quick look - looks like it is fine.
+            
+            if (this.collider.retry >= maximumRetry) {
+                this.finalPosition.copyFrom(position);
+                return;
+            }
+
+            this.collider._initialize(position, velocity, closeDistance);
+
+            // Check all meshes
+            var meshes = this._collisionCache.getMeshes();
+            for (var uniqueId in meshes) {
+                if (meshes.hasOwnProperty(uniqueId) && parseInt(uniqueId) != excludedMeshUniqueId) {
+                    var mesh: SerializedMesh = meshes[uniqueId];
+                    if (mesh.checkCollisions)
+                        this.checkCollision(mesh);
+                }
+            }
+
+            if (!this.collider.collisionFound) {
+                position.addToRef(velocity, this.finalPosition);
+                return;
+            }
+
+            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
+                this.collider._getResponse(position, velocity);
+            }
+
+            if (velocity.length() <= closeDistance) {
+                this.finalPosition.copyFrom(position);
+                return;
+            }
+
+            this.collider.retry++;
+            this.collideWithWorld(position, velocity, maximumRetry, excludedMeshUniqueId);
+        }
+
+        private checkCollision(mesh: SerializedMesh) {
+
+            if (!this.collider._canDoCollision(BABYLON.Vector3.FromArray(mesh.sphereCenter), mesh.sphereRadius, BABYLON.Vector3.FromArray(mesh.boxMinimum), BABYLON.Vector3.FromArray(mesh.boxMaximum))) {
+                return;
+            };
+
+            // Transformation matrix
+            BABYLON.Matrix.ScalingToRef(1.0 / this.collider.radius.x, 1.0 / this.collider.radius.y, 1.0 / this.collider.radius.z, this.collisionsScalingMatrix);
+            var worldFromCache = BABYLON.Matrix.FromArray(mesh.worldMatrixFromCache);
+            worldFromCache.multiplyToRef(this.collisionsScalingMatrix, this.collisionTranformationMatrix);
+
+            this.processCollisionsForSubMeshes(this.collisionTranformationMatrix, mesh);
+            //return colTransMat;
+        }
+
+        private processCollisionsForSubMeshes(transformMatrix: BABYLON.Matrix, mesh: SerializedMesh): void {
+            var len: number;
+            var subMeshes;
+
+            // No Octrees for now
+            //if (this._submeshesOctree && this.useOctreeForCollisions) {
+            //    var radius = collider.velocityWorldLength + Math.max(collider.radius.x, collider.radius.y, collider.radius.z);
+            //    var intersections = this._submeshesOctree.intersects(collider.basePointWorld, radius);
+
+            //    len = intersections.length;
+            //    subMeshes = intersections.data;
+            //} else {
+                subMeshes = mesh.subMeshes;
+                len = subMeshes.length;
+            //}
+
+            if (!mesh.geometryId) {
+                console.log("no mesh geometry id");
+                return;
+            }
+
+            var meshGeometry = this._collisionCache.getGeometry(mesh.geometryId);
+            if (!meshGeometry) {
+                console.log("couldn't find geometry", mesh.geometryId);
+                return;
+            }
+
+            for (var index = 0; index < len; index++) {
+                var subMesh = subMeshes[index];
+
+                // Bounding test
+                if (len > 1 && !this.checkSubmeshCollision(subMesh))
+                    continue;
+
+                //Unneeded
+                //subMesh['getMesh'] = function () {
+                //    return mesh.uniqueId;
+                //}
+                this.collideForSubMesh(subMesh, transformMatrix, meshGeometry);
+            }
+        }
+
+        private collideForSubMesh(subMesh: SerializedSubMesh, transformMatrix: BABYLON.Matrix, meshGeometry: SerializedGeometry): void {
+            var positionsArray = [];
+            for (var i = 0; i < meshGeometry.positions.length; i = i + 3) {
+                var p = BABYLON.Vector3.FromArray([meshGeometry.positions[i], meshGeometry.positions[i + 1], meshGeometry.positions[i + 2]]);
+                positionsArray.push(p);
+            }
+            subMesh['_lastColliderTransformMatrix'] = transformMatrix.clone();
+            //The following two arrays should be initialized CORRECTLY to save some calculation time.
+            subMesh['_lastColliderWorldVertices'] = [];
+            subMesh['_trianglePlanes'] = [];
+            var start = subMesh.verticesStart;
+            var end = (subMesh.verticesStart + subMesh.verticesCount);
+            for (var i = start; i < end; i++) {
+                subMesh['_lastColliderWorldVertices'].push(BABYLON.Vector3.TransformCoordinates(positionsArray[i], transformMatrix));
+            }
+        
+            //}
+            // Collide
+            this.collider._collide(subMesh['_trianglePlanes'] = [], subMesh['_lastColliderWorldVertices'], <any> meshGeometry.indices, subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, subMesh.hasMaterial);
+        }
+
+        //TODO - this! :-)
+        private checkSubmeshCollision(subMesh: SerializedSubMesh) : boolean {
+            return this.collider._canDoCollision(BABYLON.Vector3.FromArray(subMesh.sphereCenter), subMesh.sphereRadius, BABYLON.Vector3.FromArray(subMesh.boxMinimum), BABYLON.Vector3.FromArray(subMesh.boxMaximum));
+        }
+
+
+    }
+
+    export interface ICollisionDetector {
+        onInit(payload: InitPayload): void;
+        onUpdate(payload: UpdatePayload): void;
+        onCollision(payload: CollidePayload): void;
+    }
+
+    export class CollisionDetectorTransferable implements ICollisionDetector {
+        private _collisionCache: CollisionCache;
+
+        public onInit(payload: InitPayload) {
+            this._collisionCache = new CollisionCache();
+            var reply: WorkerReply = {
+                error: WorkerReplyType.SUCCESS,
+                taskType: WorkerTaskType.INIT
+            }
+            postMessage(reply, undefined);
+        }
+
+        public onUpdate(payload: UpdatePayload) {
+            for (var id in payload.updatedGeometries) {
+                if (payload.updatedGeometries.hasOwnProperty(id)) {
+                    this._collisionCache.addGeometry(payload.updatedGeometries[id]);
+                }
+            }
+            for (var uniqueId in payload.updatedMeshes) {
+                if (payload.updatedMeshes.hasOwnProperty(uniqueId)) {
+                    this._collisionCache.addMesh(payload.updatedMeshes[uniqueId]);
+                }
+            }
+
+            var replay: WorkerReply = {
+                error: WorkerReplyType.SUCCESS,
+                taskType: WorkerTaskType.UPDATE
+            }
+            postMessage(replay, undefined);
+        }
+
+        public onCollision(payload: CollidePayload) {
+            var finalPosition = BABYLON.Vector3.Zero();
+            //create a new collider
+            var collider = new BABYLON.Collider();
+            collider.radius = BABYLON.Vector3.FromArray(payload.collider.radius);
+
+            var colliderWorker = new CollideWorker(collider, this._collisionCache, finalPosition);
+            colliderWorker.collideWithWorld(BABYLON.Vector3.FromArray(payload.collider.position), BABYLON.Vector3.FromArray(payload.collider.velocity), payload.maximumRetry, payload.excludedMeshUniqueId);
+            var replyPayload: CollisionReplyPayload = {
+                collidedMeshUniqueId: <any> collider.collidedMesh,
+                collisionId: payload.collisionId,
+                newPosition: finalPosition.asArray()
+            }
+            var reply: WorkerReply = {
+                error: WorkerReplyType.SUCCESS,
+                taskType: WorkerTaskType.COLLIDE,
+                payload: replyPayload
+            }
+            postMessage(reply, undefined);
+        }
+    }
+
+    //TypeScript doesn't know WorkerGlobalScope
+    declare class WorkerGlobalScope { }
+
+    //check if we are in a web worker, as this code should NOT run on the main UI thread
+    if (self && self instanceof WorkerGlobalScope) {
+
+        //Window hack to allow including babylonjs native code. the <any> is for typescript.
+        window = <any> {};
+
+        var collisionDetector: ICollisionDetector = new CollisionDetectorTransferable();
+
+        var onNewMessage = function (event: MessageEvent) {
+            var message = <BabylonMessage> event.data;
+            switch (message.taskType) {
+                case WorkerTaskType.INIT:
+                    collisionDetector.onInit(<InitPayload> message.payload);
+                    break;
+                case WorkerTaskType.COLLIDE:
+                    collisionDetector.onCollision(<CollidePayload> message.payload);
+                    break;
+                case WorkerTaskType.UPDATE:
+                    collisionDetector.onUpdate(<UpdatePayload> message.payload);
+                    break;
+            }
+        }
+
+        self.onmessage = onNewMessage;
+    }
+
+}

+ 18 - 7
Babylon/Mesh/babylon.abstractMesh.js

@@ -9,6 +9,7 @@ var BABYLON;
     var AbstractMesh = (function (_super) {
         __extends(AbstractMesh, _super);
         function AbstractMesh(name, scene) {
+            var _this = this;
             _super.call(this, name, scene);
             // Properties
             this.definedFacingForward = true; // orientation for POV movement & rotation
@@ -71,6 +72,16 @@ var BABYLON;
             this._intersectionsInProgress = new Array();
             this._onAfterWorldMatrixUpdate = new Array();
             this._isWorldMatrixFrozen = false;
+            this._onCollisionPositionChange = function (collisionId, newPosition, collidedMesh) {
+                if (collidedMesh === void 0) { collidedMesh = null; }
+                //TODO move this to the collision coordinator!
+                if (collisionId != null || collisionId != undefined)
+                    newPosition.multiplyInPlace(_this._collider.radius);
+                newPosition.subtractToRef(_this._oldPositionForCollisions, _this._diffPositionForCollisions);
+                if (_this._diffPositionForCollisions.length() > BABYLON.Engine.CollisionsEpsilon) {
+                    _this.position.addInPlace(_this._diffPositionForCollisions);
+                }
+            };
             scene.addMesh(this);
         }
         Object.defineProperty(AbstractMesh, "BILLBOARDMODE_NONE", {
@@ -610,11 +621,7 @@ var BABYLON;
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPositionForCollisions);
             this._oldPositionForCollisions.addInPlace(this.ellipsoidOffset);
             this._collider.radius = this.ellipsoid;
-            this.getScene()._getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this._newPositionForCollisions, this);
-            this._newPositionForCollisions.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
-            if (this._diffPositionForCollisions.length() > BABYLON.Engine.CollisionsEpsilon) {
-                this.position.addInPlace(this._diffPositionForCollisions);
-            }
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
         };
         // Submeshes octree
         /**
@@ -648,7 +655,10 @@ var BABYLON;
                 }
             }
             // Collide
-            collider._collide(subMesh, subMesh._lastColliderWorldVertices, this.getIndices(), subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart);
+            collider._collide(subMesh._trianglePlanes, subMesh._lastColliderWorldVertices, this.getIndices(), subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, !!subMesh.getMaterial());
+            if (collider.collisionFound) {
+                collider.collidedMesh = this;
+            }
         };
         AbstractMesh.prototype._processCollisionsForSubMeshes = function (collider, transformMatrix) {
             var subMeshes;
@@ -814,4 +824,5 @@ var BABYLON;
     })(BABYLON.Node);
     BABYLON.AbstractMesh = AbstractMesh;
 })(BABYLON || (BABYLON = {}));
-//# sourceMappingURL=babylon.abstractMesh.js.map
+
+//# sourceMappingURL=../Mesh/babylon.abstractMesh.js.map

+ 14 - 4
Babylon/Mesh/babylon.abstractMesh.ts

@@ -719,8 +719,15 @@
             this._oldPositionForCollisions.addInPlace(this.ellipsoidOffset);
             this._collider.radius = this.ellipsoid;
 
-            this.getScene()._getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this._newPositionForCollisions, this);
-            this._newPositionForCollisions.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
+        }
+
+        private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: AbstractMesh = null) => {
+            //TODO move this to the collision coordinator!
+            if (collisionId != null || collisionId != undefined)
+                newPosition.multiplyInPlace(this._collider.radius);
+
+            newPosition.subtractToRef(this._oldPositionForCollisions, this._diffPositionForCollisions);
 
             if (this._diffPositionForCollisions.length() > Engine.CollisionsEpsilon) {
                 this.position.addInPlace(this._diffPositionForCollisions);
@@ -762,7 +769,10 @@
                 }
             }
             // Collide
-            collider._collide(subMesh, subMesh._lastColliderWorldVertices, this.getIndices(), subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart);
+            collider._collide(subMesh._trianglePlanes, subMesh._lastColliderWorldVertices, this.getIndices(), subMesh.indexStart, subMesh.indexStart + subMesh.indexCount, subMesh.verticesStart, !!subMesh.getMaterial());
+            if (collider.collisionFound) {
+                collider.collidedMesh = this;
+            }
         }
 
         public _processCollisionsForSubMeshes(collider: Collider, transformMatrix: Matrix): void {
@@ -957,4 +967,4 @@
             }
         }
     }
-}
+}

+ 1 - 0
Babylon/Mesh/babylon.subMesh.js

@@ -12,6 +12,7 @@ var BABYLON;
             this._mesh = mesh;
             this._renderingMesh = renderingMesh || mesh;
             mesh.subMeshes.push(this);
+            this._trianglePlanes = [];
             this._id = mesh.subMeshes.length - 1;
             if (createBoundingBox) {
                 this.refreshBoundingInfo();

+ 2 - 0
Babylon/Mesh/babylon.subMesh.ts

@@ -20,6 +20,8 @@
             this._renderingMesh = renderingMesh || <Mesh>mesh;
             mesh.subMeshes.push(this);
 
+            this._trianglePlanes = [];
+
             this._id = mesh.subMeshes.length - 1;
 
             if (createBoundingBox) {

+ 25 - 41
Babylon/babylon.scene.js

@@ -128,8 +128,6 @@ var BABYLON;
             this._activeBones = 0;
             this._activeAnimatables = new Array();
             this._transformMatrix = BABYLON.Matrix.Zero();
-            this._scaledPosition = BABYLON.Vector3.Zero();
-            this._scaledVelocity = BABYLON.Vector3.Zero();
             this._uniqueIdCounter = 0;
             this._engine = engine;
             engine.scenes.push(this);
@@ -143,6 +141,8 @@ var BABYLON;
             this.mainSoundTrack = new BABYLON.SoundTrack(this, { mainTrack: true });
             //simplification queue
             this.simplificationQueue = new BABYLON.SimplificationQueue();
+            //collision coordinator initialization.
+            this.workerCollisions = (!!Worker && !!BABYLON.CollisionWorker);
         }
         Object.defineProperty(Scene, "FOGMODE_NONE", {
             get: function () {
@@ -180,6 +180,21 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Scene.prototype, "workerCollisions", {
+            get: function () {
+                return this._workerCollisions;
+            },
+            set: function (enabled) {
+                this._workerCollisions = enabled;
+                if (this.collisionCoordinator) {
+                    this.collisionCoordinator.destroy();
+                }
+                this.collisionCoordinator = enabled ? new BABYLON.CollisionCoordinatorWorker() : new BABYLON.CollisionCoordinatorLegacy();
+                this.collisionCoordinator.init(this);
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Scene.prototype, "meshUnderPointer", {
             /**
              * The mesh that is currently under the pointer.
@@ -551,6 +566,8 @@ var BABYLON;
         Scene.prototype.addMesh = function (newMesh) {
             newMesh.uniqueId = this._uniqueIdCounter++;
             var position = this.meshes.push(newMesh);
+            //notify the collision coordinator
+            this.collisionCoordinator.onMeshAdded(newMesh);
             if (this.onNewMeshAdded) {
                 this.onNewMeshAdded(newMesh, position, this);
             }
@@ -561,6 +578,8 @@ var BABYLON;
                 // Remove from the scene if mesh found 
                 this.meshes.splice(index, 1);
             }
+            //notify the collision coordinator
+            this.collisionCoordinator.onMeshRemoved(toRemove);
             if (this.onMeshRemoved) {
                 this.onMeshRemoved(toRemove);
             }
@@ -763,6 +782,8 @@ var BABYLON;
                 return false;
             }
             this._geometries.push(geometry);
+            //notify the collision coordinator
+            this.collisionCoordinator.onGeometryAdded(geometry);
             if (this.onGeometryAdded) {
                 this.onGeometryAdded(geometry);
             }
@@ -777,6 +798,8 @@ var BABYLON;
             var index = this._geometries.indexOf(geometry);
             if (index > -1) {
                 this._geometries.splice(index, 1);
+                //notify the collision coordinator
+                this.collisionCoordinator.onGeometryDeleted(geometry);
                 if (this.onGeometryRemoved) {
                     this.onGeometryRemoved(geometry);
                 }
@@ -1488,45 +1511,6 @@ var BABYLON;
                 this.soundTracks[scIndex].dispose();
             }
         };
-        // Collisions
-        Scene.prototype._getNewPosition = function (position, velocity, collider, maximumRetry, finalPosition, excludedMesh) {
-            if (excludedMesh === void 0) { excludedMesh = null; }
-            position.divideToRef(collider.radius, this._scaledPosition);
-            velocity.divideToRef(collider.radius, this._scaledVelocity);
-            collider.retry = 0;
-            collider.initialVelocity = this._scaledVelocity;
-            collider.initialPosition = this._scaledPosition;
-            this._collideWithWorld(this._scaledPosition, this._scaledVelocity, collider, maximumRetry, finalPosition, excludedMesh);
-            finalPosition.multiplyInPlace(collider.radius);
-        };
-        Scene.prototype._collideWithWorld = function (position, velocity, collider, maximumRetry, finalPosition, excludedMesh) {
-            if (excludedMesh === void 0) { excludedMesh = null; }
-            var closeDistance = BABYLON.Engine.CollisionsEpsilon * 10.0;
-            if (collider.retry >= maximumRetry) {
-                finalPosition.copyFrom(position);
-                return;
-            }
-            collider._initialize(position, velocity, closeDistance);
-            for (var index = 0; index < this.meshes.length; index++) {
-                var mesh = this.meshes[index];
-                if (mesh.isEnabled() && mesh.checkCollisions && mesh.subMeshes && mesh !== excludedMesh) {
-                    mesh._checkCollision(collider);
-                }
-            }
-            if (!collider.collisionFound) {
-                position.addToRef(velocity, finalPosition);
-                return;
-            }
-            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
-                collider._getResponse(position, velocity);
-            }
-            if (velocity.length() <= closeDistance) {
-                finalPosition.copyFrom(position);
-                return;
-            }
-            collider.retry++;
-            this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
-        };
         // Octrees
         Scene.prototype.getWorldExtends = function () {
             var min = new BABYLON.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);

+ 34 - 52
Babylon/babylon.scene.ts

@@ -163,6 +163,8 @@
 
         // Collisions
         public collisionsEnabled = true;
+        private _workerCollisions;
+        public collisionCoordinator: ICollisionCoordinator;
         public gravity = new Vector3(0, -9.0, 0);
 
         // Postprocesses
@@ -247,9 +249,6 @@
         private _transformMatrix = Matrix.Zero();
         private _pickWithRayInverseMatrix: Matrix;
 
-        private _scaledPosition = Vector3.Zero();
-        private _scaledVelocity = Vector3.Zero();
-
         private _boundingBoxRenderer: BoundingBoxRenderer;
         private _outlineRenderer: OutlineRenderer;
 
@@ -292,6 +291,8 @@
 
             //simplification queue
             this.simplificationQueue = new SimplificationQueue();
+            //collision coordinator initialization.
+            this.workerCollisions = (!!Worker && !!BABYLON.CollisionWorker);
         }
 
         // Properties 
@@ -299,6 +300,21 @@
             return this._debugLayer;
         }
 
+        public set workerCollisions(enabled: boolean) {
+            this._workerCollisions = enabled;
+            if (this.collisionCoordinator) {
+                this.collisionCoordinator.destroy();
+            }
+
+            this.collisionCoordinator = enabled ? new CollisionCoordinatorWorker() : new CollisionCoordinatorLegacy();
+
+            this.collisionCoordinator.init(this);
+        }
+
+        public get workerCollisions() : boolean {
+            return this._workerCollisions;
+        }
+
         /**
          * The mesh that is currently under the pointer.
          * @return {BABYLON.AbstractMesh} mesh under the pointer/mouse cursor or null if none.
@@ -753,6 +769,10 @@
         public addMesh(newMesh: AbstractMesh) {
             newMesh.uniqueId = this._uniqueIdCounter++;
             var position = this.meshes.push(newMesh);
+
+            //notify the collision coordinator
+            this.collisionCoordinator.onMeshAdded(newMesh);
+
             if (this.onNewMeshAdded) {
                 this.onNewMeshAdded(newMesh, position, this);
             }
@@ -764,6 +784,9 @@
                 // Remove from the scene if mesh found 
                 this.meshes.splice(index, 1);
             }
+            //notify the collision coordinator
+            this.collisionCoordinator.onMeshRemoved(toRemove);
+
             if (this.onMeshRemoved) {
                 this.onMeshRemoved(toRemove);
             }
@@ -995,6 +1018,10 @@
             }
 
             this._geometries.push(geometry);
+
+            //notify the collision coordinator
+            this.collisionCoordinator.onGeometryAdded(geometry);
+
             if (this.onGeometryAdded) {
                 this.onGeometryAdded(geometry);
             }
@@ -1012,6 +1039,10 @@
 
             if (index > -1) {
                 this._geometries.splice(index, 1);
+
+                //notify the collision coordinator
+                this.collisionCoordinator.onGeometryDeleted(geometry);
+
                 if (this.onGeometryRemoved) {
                     this.onGeometryRemoved(geometry);
                 }
@@ -1892,55 +1923,6 @@
             }
         }
 
-        // Collisions
-        public _getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: AbstractMesh = null): void {
-            position.divideToRef(collider.radius, this._scaledPosition);
-            velocity.divideToRef(collider.radius, this._scaledVelocity);
-
-            collider.retry = 0;
-            collider.initialVelocity = this._scaledVelocity;
-            collider.initialPosition = this._scaledPosition;
-            this._collideWithWorld(this._scaledPosition, this._scaledVelocity, collider, maximumRetry, finalPosition, excludedMesh);
-
-            finalPosition.multiplyInPlace(collider.radius);
-        }
-
-        private _collideWithWorld(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: AbstractMesh = null): void {
-            var closeDistance = Engine.CollisionsEpsilon * 10.0;
-
-            if (collider.retry >= maximumRetry) {
-                finalPosition.copyFrom(position);
-                return;
-            }
-
-            collider._initialize(position, velocity, closeDistance);
-
-            // Check all meshes
-            for (var index = 0; index < this.meshes.length; index++) {
-                var mesh = this.meshes[index];
-                if (mesh.isEnabled() && mesh.checkCollisions && mesh.subMeshes && mesh !== excludedMesh) {
-                    mesh._checkCollision(collider);
-                }
-            }
-
-            if (!collider.collisionFound) {
-                position.addToRef(velocity, finalPosition);
-                return;
-            }
-
-            if (velocity.x !== 0 || velocity.y !== 0 || velocity.z !== 0) {
-                collider._getResponse(position, velocity);
-            }
-
-            if (velocity.length() <= closeDistance) {
-                finalPosition.copyFrom(position);
-                return;
-            }
-
-            collider.retry++;
-            this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
-        }
-
         // Octrees
         public getWorldExtends(): { min: Vector3; max: Vector3 } {
             var min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);

+ 1 - 0
Tools/BuildOurOwnBabylonJS/BuildOurOwnBabylonJS/babylonJS.xml

@@ -106,6 +106,7 @@
   <script src="Babylon/Cameras/babylon.targetCamera.js"></script>
   <script src="Babylon/Cameras/babylon.camera.js"></script>
   <script src="Babylon/Collisions/babylon.collider.js"></script>
+  <script src="Babylon/Collisions/babylon.collisionCoordinator.js"></script>
   <script src="Babylon/Lights//Shadows/babylon.shadowGenerator.js"></script>
   <script src="Babylon/Lights/babylon.directionalLight.js"></script>
   <script src="Babylon/Lights/babylon.hemisphericLight.js"></script>

+ 1 - 0
Tools/Gulp/.gitignore

@@ -14,3 +14,4 @@ nbproject
 *.sublime-project
 *.sublime-workspace
 .directory
+build

+ 167 - 0
Tools/Gulp/config.json

@@ -0,0 +1,167 @@
+{
+    "build" : {
+        "filename" : "babylon.js",
+        "minSuffix" : ".min",
+        "declarationFilename" : "babylon.d.ts",
+        "outputDirectory" : "build/"
+    },
+    "core" : {
+        "typescript" : [
+            "../../Babylon/**/*.ts",
+            "../../References/**/*.d.ts"
+        ],
+        "files" : [
+            "../../Babylon/Math/babylon.math.js",
+            "../../Babylon/Tools/babylon.database.js",
+            "../../Babylon/Tools/babylon.tools.tga.js",
+            "../../Babylon/Tools/babylon.tools.dds.js",
+            "../../Babylon/Tools/babylon.smartArray.js",
+            "../../Babylon/Tools/babylon.smartCollection.js",
+            "../../Babylon/Tools/babylon.tools.js",
+            "../../Babylon/babylon.engine.js",
+            "../../Babylon/babylon.node.js",
+            "../../Babylon/Tools/babylon.filesInput.js",
+            "../../Babylon/Collisions/babylon.pickingInfo.js",
+            "../../Babylon/Culling/babylon.boundingSphere.js",
+            "../../Babylon/Culling/babylon.boundingBox.js",
+            "../../Babylon/Culling/babylon.boundingInfo.js",
+            "../../Babylon/Mesh/babylon.abstractMesh.js",
+            "../../Babylon/Lights/babylon.light.js",
+            "../../Babylon/Lights/babylon.pointLight.js",
+            "../../Babylon/Lights/babylon.spotLight.js",
+            "../../Babylon/Lights/babylon.hemisphericLight.js",
+            "../../Babylon/Lights/babylon.directionalLight.js",
+            "../../Babylon/Lights/Shadows/babylon.shadowGenerator.js",
+            "../../Babylon/Collisions/babylon.collider.js",
+            "../../Babylon/Collisions/babylon.collisionCoordinator.js",
+            "../../Babylon/Cameras/babylon.camera.js",
+            "../../Babylon/Cameras/babylon.targetCamera.js",
+            "../../Babylon/Cameras/babylon.freeCamera.js",
+            "../../Babylon/Cameras/babylon.followCamera.js",
+            "../../Babylon/Cameras/babylon.touchCamera.js",
+            "../../Babylon/Cameras/babylon.arcRotateCamera.js",
+            "../../Babylon/Cameras/babylon.deviceOrientationCamera.js",
+            "../../Babylon/Rendering/babylon.renderingManager.js",
+            "../../Babylon/Rendering/babylon.renderingGroup.js",
+            "../../Babylon/babylon.scene.js",
+            "../../Babylon/Mesh/babylon.vertexBuffer.js",
+            "../../Babylon/Mesh/babylon.instancedMesh.js",
+            "../../Babylon/Mesh/babylon.mesh.js",
+            "../../Babylon/Mesh/babylon.subMesh.js",
+            "../../Babylon/Materials/Textures/babylon.baseTexture.js",
+            "../../Babylon/Materials/Textures/babylon.texture.js",
+            "../../Babylon/Materials/Textures/babylon.cubeTexture.js",
+            "../../Babylon/Materials/Textures/babylon.renderTargetTexture.js",
+            "../../Babylon/Materials/Textures/Procedurals/babylon.proceduralTexture.js",
+            "../../Babylon/Materials/Textures/babylon.mirrorTexture.js",
+            "../../Babylon/Materials/Textures/babylon.dynamicTexture.js",
+            "../../Babylon/Materials/Textures/babylon.videoTexture.js",
+            "../../Babylon/Materials/Textures/Procedurals/babylon.customProceduralTexture.js",
+            "../../Babylon/Materials/Textures/babylon.proceduralTexture.js",
+            "../../Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.js",
+            "../../Babylon/Materials/babylon.effect.js",
+            "../../Babylon/Materials/babylon.material.js",
+            "../../Babylon/Materials/babylon.standardMaterial.js",
+            "../../Babylon/Materials/babylon.multiMaterial.js",
+            "../../Babylon/Loading/babylon.sceneLoader.js",
+            "../../Babylon/Loading/Plugins/babylon.babylonFileLoader.js",
+            "../../Babylon/Sprites/babylon.spriteManager.js",
+            "../../Babylon/Sprites/babylon.sprite.js",
+            "../../Babylon/Layer/babylon.layer.js",
+            "../../Babylon/Particles/babylon.particle.js",
+            "../../Babylon/Particles/babylon.particleSystem.js",
+            "../../Babylon/Animations/babylon.animation.js",
+            "../../Babylon/Animations/babylon.animatable.js",
+            "../../Babylon/Animations/babylon.easing.js",
+            "../../Babylon/Culling/Octrees/babylon.octree.js",
+            "../../Babylon/Culling/Octrees/babylon.octreeBlock.js",
+            "../../Babylon/Bones/babylon.bone.js",
+            "../../Babylon/Bones/babylon.skeleton.js",
+            "../../Babylon/Bones/babylon.skeleton.js",
+            "../../Babylon/PostProcess/babylon.postProcess.js",
+            "../../Babylon/PostProcess/babylon.postProcessManager.js",
+            "../../Babylon/PostProcess/babylon.passPostProcess.js",
+            "../../Babylon/PostProcess/babylon.blurPostProcess.js",
+            "../../Babylon/PostProcess/babylon.refractionPostProcess.js",
+            "../../Babylon/PostProcess/babylon.blackAndWhitePostProcess.js",
+            "../../Babylon/PostProcess/babylon.convolutionPostProcess.js",
+            "../../Babylon/PostProcess/babylon.filterPostProcess.js",
+            "../../Babylon/PostProcess/babylon.fxaaPostProcess.js",
+            "../../Babylon/LensFlare/babylon.lensFlare.js",
+            "../../Babylon/LensFlare/babylon.lensFlareSystem.js",
+            "../../Babylon/Physics/Plugins/babylon.cannonJSPlugin.js",
+            "../../Babylon/Physics/Plugins/babylon.oimoJSPlugin.js",
+            "../../Babylon/Physics/babylon.physicsEngine.js",
+            "../../Babylon/Tools/babylon.sceneSerializer.js",
+            "../../Babylon/Mesh/babylon.csg.js",
+            "../../Babylon/PostProcess/babylon.oculusDistortionCorrectionPostProcess.js",
+            "../../Babylon/Tools/babylon.virtualJoystick.js",
+            "../../Babylon/Cameras/VR/babylon.oculusCamera.js",
+            "../../Babylon/Cameras/VR/babylon.oculusGamepadCamera.js",
+            "../../Babylon/Cameras/babylon.virtualJoysticksCamera.js",
+            "../../Babylon/Materials/babylon.shaderMaterial.js",
+            "../../Babylon/Mesh/babylon.mesh.vertexData.js",
+            "../../Babylon/Cameras/babylon.anaglyphCamera.js",
+            "../../Babylon/PostProcess/babylon.anaglyphPostProcess.js",
+            "../../Babylon/Tools/babylon.tags.js",
+            "../../Babylon/Tools/babylon.andOrNotEvaluator.js",
+            "../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPass.js",
+            "../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.js",
+            "../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPipeline.js",
+            "../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPipelineManager.js",
+            "../../Babylon/PostProcess/babylon.displayPassPostProcess.js",
+            "../../Babylon/Rendering/babylon.boundingBoxRenderer.js",
+            "../../Babylon/Actions/babylon.condition.js",
+            "../../Babylon/Actions/babylon.action.js",
+            "../../Babylon/Actions/babylon.actionManager.js",
+            "../../Babylon/Actions/babylon.interpolateValueAction.js",
+            "../../Babylon/Actions/babylon.directActions.js",
+            "../../Babylon/Mesh/babylon.geometry.js",
+            "../../Babylon/Mesh/babylon.groundMesh.js",
+            "../../Babylon/Mesh/babylon.instancedMesh.js",
+            "../../Babylon/Tools/babylon.gamepads.js",
+            "../../Babylon/Cameras/babylon.gamepadCamera.js",
+            "../../Babylon/Mesh/babylon.linesMesh.js",
+            "../../Babylon/Rendering/babylon.outlineRenderer.js",
+            "../../Babylon/Tools/babylon.assetsManager.js",
+            "../../Babylon/Cameras/VR/babylon.vrDeviceOrientationCamera.js",
+            "../../Babylon/Cameras/VR/babylon.webVRCamera.js",
+            "../../Babylon/Tools/babylon.sceneOptimizer.js",
+            "../../Babylon/Mesh/babylon.meshLODLevel.js",
+            "../../Babylon/Audio/babylon.audioEngine.js",
+            "../../Babylon/Audio/babylon.sound.js",
+            "../../Babylon/Audio/babylon.soundtrack.js",
+            "../../Babylon/Debug/babylon.debugLayer.js",
+            "../../Babylon/Materials/Textures/babylon.rawTexture.js",
+            "../../Babylon/Mesh/babylon.polygonMesh.js",
+            "../../Babylon/Mesh/babylon.meshSimplification.js",
+            "../../Babylon/Audio/babylon.analyser.js",
+            "../../Babylon/Rendering/babylon.depthRenderer.js",
+            "../../Babylon/PostProcess/babylon.ssaoRenderingPipeline.js",
+            "../../Babylon/PostProcess/babylon.volumetricLightScatteringPostProcess.js",
+            "../../Babylon/PostProcess/babylon.lensRenderingPipeline.js",
+            "../../Babylon/PostProcess/babylon.colorCorrectionPostProcess.js"
+        ]
+    },
+    "shadersDirectories" : [
+        {
+            "variable" : "BABYLON.Effect.ShadersStore",
+            "files" : "../../Babylon/Shaders/*.fx"
+        }
+    ],
+    "workers" : [
+        {
+            "variable" : "BABYLON.CollisionWorker",
+            "files" : [	
+                "../../Babylon/Collisions/babylon.collisionWorker.js",
+                "../../Babylon/Collisions/babylon.collider.js",
+                "../../Babylon/Collisions/babylon.collisionCoordinator.js",
+                "../../Babylon/Math/babylon.math.js"
+            ]
+        }
+    ],
+    "extras" : {
+        "files": [
+        ]
+    }
+}

+ 0 - 48
Tools/Gulp/gulp-shaders.js

@@ -1,48 +0,0 @@
-var through = require('through');
-var gutil = require('gulp-util');
-var PluginError = gutil.PluginError;
-var File = gutil.File;
-var path = require('path');
-
-
-var shaders = function shaders(name) {
-
-  var firstFile = null;
-  var content = {};
-
-  function bufferContents(file){
-    if (file.isNull()) return; // ignore
-    if (file.isStream()) return this.emit('error', new PluginError('gulp-shaders',  'Streaming not supported'));
-
-    if (!firstFile) firstFile = file;
-
-    var fileName = file.path
-      .replace(file.base, '')
-      .replace('.fragment', 'Pixel')
-      .replace('.vertex', 'Vertex')
-      .replace('.fx', 'Shader');
-
-    content[fileName] = file.contents.toString();
-  }
-
-  function endStream(){
-
-    var joinedPath = path.join(firstFile.base, name);
-
-    var joinedFile = new File({
-      cwd: firstFile.cwd,
-      base: firstFile.base,
-      path: joinedPath,
-      contents: new Buffer('BABYLON.Effect.ShadersStore='+JSON.stringify(content)+';')
-    });
-
-
-    this.emit('data', joinedFile);
-    this.emit('end');
-
-  }
-
-  return through(bufferContents, endStream);
-}
-
-module.exports = shaders;

+ 77 - 0
Tools/Gulp/gulp-srcToVariable.js

@@ -0,0 +1,77 @@
+var through = require('through2');
+var gutil = require('gulp-util');
+var PluginError = gutil.PluginError;
+var path = require('path');
+var File = gutil.File;
+
+// Consts
+const PLUGIN_NAME = 'gulp-srcToVariable';
+
+var srcToVariable = function srcToVariable(varName, asMap, namingCallback) {
+    
+    var content;
+    var firstFile;
+    
+    namingCallback = namingCallback || function(filename) { return filename; };
+    
+    function bufferContents(file, enc, cb) {
+    // ignore empty files
+    if (file.isNull()) {
+      cb();
+      return;
+    }
+
+    // no stream support, only files.
+    if (file.isStream()) {
+      this.emit('error', new PluginError('gulp-concat',  'Streaming not supported'));
+      cb();
+      return;
+    }
+
+    // set first file if not already set
+    if (!firstFile) {
+      firstFile = file;
+    }
+
+    // construct concat instance
+    if (!content) {
+      content = asMap ? {} : "";
+    }
+    // add file to concat instance
+    if(asMap) {
+        var name = namingCallback(file.relative);
+        
+        content[name] = file.contents.toString();
+    } else {
+        content += file.contents.toString();
+    }
+    cb();
+  }
+
+  function endStream(cb) {
+    if (!firstFile || !content) {
+      cb();
+      return;
+    }
+
+    
+    var joinedPath = path.join(firstFile.base, varName);
+
+    var joinedFile = new File({
+      cwd: firstFile.cwd,
+      base: firstFile.base,
+      path: joinedPath,
+      contents: new Buffer(varName + '=' + JSON.stringify(content) + ';')
+    });
+    
+    this.push(joinedFile);
+    
+    
+    cb();
+  }
+
+  return through.obj(bufferContents, endStream);
+    
+}
+
+module.exports = srcToVariable;

+ 69 - 218
Tools/Gulp/gulpfile.js

@@ -1,247 +1,98 @@
-var gulp = require('gulp-param')(require('gulp'), process.argv),
-    uglify = require('gulp-uglify'),
-    rename = require('gulp-rename'),
-    concat = require('gulp-concat'),
-    clean = require('gulp-clean'),
-    typescript = require('gulp-typescript'),
-	sourcemaps = require('gulp-sourcemaps'),
-    shaders = require('./gulp-shaders'),
-    gulpFilter = require('gulp-filter');
+var gulp = require("gulp");
+var uglify = require("gulp-uglify");
+var typescript = require("gulp-typescript");
+var sourcemaps = require("gulp-sourcemaps");
+var srcToVariable = require("./gulp-srcToVariable");
+var merge2 = require("merge2");
+var concat = require("gulp-concat");
+var rename = require("gulp-rename");
+var cleants = require('gulp-clean-ts-extends');
+var changed = require('gulp-changed');
+var runSequence = require('run-sequence');
 
-/**
- * Concat all fx files into one js file.
- */
-gulp.task('shaders', function() {
-  return gulp.src(['../../Babylon/Shaders/*.fx'])
-    .pipe(shaders('shaders.js'))
-    .pipe(gulp.dest('build/'))
+var config = require("./config.json");
 
-});
+var shadersStream;
+var workersStream;
 
-/**
- * Compile typescript files to their js respective files
- */
-gulp.task('typescript-to-js', function() {
-  //Compile all ts file into their respective js file.
-  
-  var tsResult = gulp.src(['../../Babylon/**/*.ts','../../References/**/*.d.ts'])
-                       .pipe(sourcemaps.init()) // This means sourcemaps will be generated
-                       .pipe(typescript({ noExternalResolve: true, target: 'ES5'}));
-  
-   return tsResult.js
-                .pipe(sourcemaps.write('.')) // Now the sourcemaps are added to the .js file
-                .pipe(gulp.dest('../../Babylon/'));
+//function to convert the shaders' filenames to variable names.
+function shadersName(filename) {
+    return filename.replace('.fragment', 'Pixel')
+      .replace('.vertex', 'Vertex')
+      .replace('.fx', 'Shader');
+}
+
+gulp.task("shaders", function(cb) {
+    shadersStream = config.shadersDirectories.map(function(shadersDef) {
+        return gulp.src(shadersDef.files).pipe(srcToVariable(shadersDef.variable, true, shadersName));
+    });
+    cb();
 });
 
-/**
- * Compile the declaration file babylon.d.ts
- */
-gulp.task('typescript-declaration', ['typescript-to-js'], function() {
-	
-	var tsResult = gulp.src(['../../Babylon/**/*.ts','../../References/**/*.d.ts'])
-                       .pipe(typescript({
-                           declarationFiles: true,
-                           noExternalResolve: true,
-						   target: 'ES5'
-                       }));
+gulp.task("workers", function(cb) {
+    workersStream = config.workers.map(function(workerDef) {
+        return gulp.src(workerDef.files).pipe(uglify()).pipe(srcToVariable(workerDef.variable));
+    });
+    cb();
+});
 
-    return tsResult.dts.pipe(concat('babylon.d.ts'))
-	.pipe(gulp.dest('build/'));
+/*
+Compiles all typescript files and creating a declaration file.
+*/
+gulp.task('typescript-compile', function() {  
+  var tsResult = gulp.src(config.core.typescript)
+                .pipe(sourcemaps.init()) // sourcemaps init. currently redundant directory def, waiting for this - https://github.com/floridoo/gulp-sourcemaps/issues/111
+                .pipe(typescript({ 
+                    noExternalResolve: true, 
+                    target: 'ES5', 
+                    declarationFiles: true,
+                    typescript: require('typescript')
+                }));
+    return merge2([
+        tsResult.dts
+            .pipe(concat(config.build.declarationFilename))
+            .pipe(gulp.dest(config.build.outputDirectory)),
+        tsResult.js
+            .pipe(sourcemaps.write("./")) // sourcemaps are written
+            .pipe(gulp.dest('../../Babylon/'))
+    ]);
 });
 
-/**
- * Concat all js files in order into one big js file and minify it.
- * The list is based on https://github.com/BabylonJS/Babylon.js/wiki/Creating-the-minified-version
- * Do not hesitate to update it if you need to add your own files.
- */
-gulp.task('scripts', ['shaders'] ,function() {
-  return gulp.src([
-      '../../Babylon/Math/babylon.math.js',
-      '../../Babylon/Tools/babylon.database.js',
-      '../../Babylon/Tools/babylon.tools.tga.js',
-      '../../Babylon/Tools/babylon.tools.dds.js',
-      '../../Babylon/Tools/babylon.smartArray.js',
-      '../../Babylon/Tools/babylon.smartCollection.js',
-      '../../Babylon/Tools/babylon.tools.js',
-      '../../Babylon/babylon.engine.js',
-      '../../Babylon/babylon.node.js',
-      '../../Babylon/Tools/babylon.filesInput.js',
-      '../../Babylon/Collisions/babylon.pickingInfo.js',
-      '../../Babylon/Culling/babylon.boundingSphere.js',
-      '../../Babylon/Culling/babylon.boundingBox.js',
-      '../../Babylon/Culling/babylon.boundingInfo.js',
-      '../../Babylon/Mesh/babylon.abstractMesh.js',
-      '../../Babylon/Lights/babylon.light.js',
-      '../../Babylon/Lights/babylon.pointLight.js',
-      '../../Babylon/Lights/babylon.spotLight.js',
-      '../../Babylon/Lights/babylon.hemisphericLight.js',
-      '../../Babylon/Lights/babylon.directionalLight.js',
-      '../../Babylon/Lights/Shadows/babylon.shadowGenerator.js',
-      '../../Babylon/Collisions/babylon.collider.js',
-      '../../Babylon/Cameras/babylon.camera.js',
-      '../../Babylon/Cameras/babylon.targetCamera.js',
-      '../../Babylon/Cameras/babylon.freeCamera.js',
-      '../../Babylon/Cameras/babylon.followCamera.js',
-      '../../Babylon/Cameras/babylon.touchCamera.js',
-      '../../Babylon/Cameras/babylon.arcRotateCamera.js',
-      '../../Babylon/Cameras/babylon.deviceOrientationCamera.js',
-      '../../Babylon/Rendering/babylon.renderingManager.js',
-      '../../Babylon/Rendering/babylon.renderingGroup.js',
-      '../../Babylon/babylon.scene.js',
-      '../../Babylon/Mesh/babylon.vertexBuffer.js',
-      '../../Babylon/Mesh/babylon.instancedMesh.js',
-      '../../Babylon/Mesh/babylon.mesh.js',
-      '../../Babylon/Mesh/babylon.subMesh.js',
-      '../../Babylon/Materials/Textures/babylon.baseTexture.js',
-      '../../Babylon/Materials/Textures/babylon.texture.js',
-      '../../Babylon/Materials/Textures/babylon.cubeTexture.js',
-      '../../Babylon/Materials/Textures/babylon.renderTargetTexture.js',
-      '../../Babylon/Materials/Textures/Procedurals/babylon.proceduralTexture.js',
-      '../../Babylon/Materials/Textures/babylon.mirrorTexture.js',
-      '../../Babylon/Materials/Textures/babylon.dynamicTexture.js',
-      '../../Babylon/Materials/Textures/babylon.videoTexture.js',
-      '../../Babylon/Materials/Textures/Procedurals/babylon.customProceduralTexture.js',
-      '../../Babylon/Materials/Textures/babylon.proceduralTexture.js',
-      '../../Babylon/Materials/Textures/Procedurals/babylon.standardProceduralTexture.js',
-      '../../Babylon/Materials/babylon.effect.js',
-      'build/shaders.js',
-      '../../Babylon/Materials/babylon.material.js',
-      '../../Babylon/Materials/babylon.standardMaterial.js',
-      '../../Babylon/Materials/babylon.multiMaterial.js',
-      '../../Babylon/Loading/babylon.sceneLoader.js',
-      '../../Babylon/Loading/Plugins/babylon.babylonFileLoader.js',
-      '../../Babylon/Sprites/babylon.spriteManager.js',
-      '../../Babylon/Sprites/babylon.sprite.js',
-      '../../Babylon/Layer/babylon.layer.js',
-      '../../Babylon/Particles/babylon.particle.js',
-      '../../Babylon/Particles/babylon.particleSystem.js',
-      '../../Babylon/Animations/babylon.animation.js',
-      '../../Babylon/Animations/babylon.animatable.js',
-      '../../Babylon/Animations/babylon.easing.js',
-      '../../Babylon/Culling/Octrees/babylon.octree.js',
-      '../../Babylon/Culling/Octrees/babylon.octreeBlock.js',
-      '../../Babylon/Bones/babylon.bone.js',
-      '../../Babylon/Bones/babylon.skeleton.js',
-      '../../Babylon/Bones/babylon.skeleton.js',
-      '../../Babylon/PostProcess/babylon.postProcess.js',
-      '../../Babylon/PostProcess/babylon.postProcessManager.js',
-      '../../Babylon/PostProcess/babylon.passPostProcess.js',
-      '../../Babylon/PostProcess/babylon.blurPostProcess.js',
-      '../../Babylon/PostProcess/babylon.refractionPostProcess.js',
-      '../../Babylon/PostProcess/babylon.blackAndWhitePostProcess.js',
-      '../../Babylon/PostProcess/babylon.convolutionPostProcess.js',
-      '../../Babylon/PostProcess/babylon.filterPostProcess.js',
-      '../../Babylon/PostProcess/babylon.fxaaPostProcess.js',
-      '../../Babylon/LensFlare/babylon.lensFlare.js',
-      '../../Babylon/LensFlare/babylon.lensFlareSystem.js',
-      '../../Babylon/Physics/Plugins/babylon.cannonJSPlugin.js',
-      '../../Babylon/Physics/Plugins/babylon.oimoJSPlugin.js',
-      '../../Babylon/Physics/babylon.physicsEngine.js',
-      '../../Babylon/Tools/babylon.sceneSerializer.js',
-      '../../Babylon/Mesh/babylon.csg.js',
-      '../../Babylon/PostProcess/babylon.oculusDistortionCorrectionPostProcess.js',
-      '../../Babylon/Tools/babylon.virtualJoystick.js',
-      '../../Babylon/Cameras/VR/babylon.oculusCamera.js',
-      '../../Babylon/Cameras/VR/babylon.oculusGamepadCamera.js',
-      '../../Babylon/Cameras/babylon.virtualJoysticksCamera.js',
-      '../../Babylon/Materials/babylon.shaderMaterial.js',
-      '../../Babylon/Mesh/babylon.mesh.vertexData.js',
-      '../../Babylon/Cameras/babylon.anaglyphCamera.js',
-      '../../Babylon/PostProcess/babylon.anaglyphPostProcess.js',
-      '../../Babylon/Tools/babylon.tags.js',
-      '../../Babylon/Tools/babylon.andOrNotEvaluator.js',
-      '../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPass.js',
-      '../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderEffect.js',
-      '../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPipeline.js',
-      '../../Babylon/PostProcess/RenderPipeline/babylon.postProcessRenderPipelineManager.js',
-      '../../Babylon/PostProcess/babylon.displayPassPostProcess.js',
-      '../../Babylon/Rendering/babylon.boundingBoxRenderer.js',
-      '../../Babylon/Actions/babylon.condition.js',
-      '../../Babylon/Actions/babylon.action.js',
-      '../../Babylon/Actions/babylon.actionManager.js',
-      '../../Babylon/Actions/babylon.interpolateValueAction.js',
-      '../../Babylon/Actions/babylon.directActions.js',
-      '../../Babylon/Mesh/babylon.geometry.js',
-      '../../Babylon/Mesh/babylon.groundMesh.js',
-      '../../Babylon/Mesh/babylon.instancedMesh.js',
-      '../../Babylon/Tools/babylon.gamepads.js',
-      '../../Babylon/Cameras/babylon.gamepadCamera.js',
-      '../../Babylon/Mesh/babylon.linesMesh.js',
-      '../../Babylon/Rendering/babylon.outlineRenderer.js',
-      '../../Babylon/Tools/babylon.assetsManager.js',
-      '../../Babylon/Cameras/VR/babylon.vrDeviceOrientationCamera.js',
-      '../../Babylon/Cameras/VR/babylon.webVRCamera.js',
-      '../../Babylon/Tools/babylon.sceneOptimizer.js',
-      '../../Babylon/Mesh/babylon.meshLODLevel.js',
-      '../../Babylon/Audio/babylon.audioEngine.js',
-      '../../Babylon/Audio/babylon.sound.js',
-      '../../Babylon/Audio/babylon.soundtrack.js',
-      '../../Babylon/Debug/babylon.debugLayer.js',
-      '../../Babylon/Materials/Textures/babylon.rawTexture.js',
-      '../../Babylon/Mesh/babylon.polygonMesh.js',
-      '../../Babylon/Mesh/babylon.meshSimplification.js',
-      '../../Babylon/Audio/babylon.analyser.js',
-      '../../Babylon/Rendering/babylon.depthRenderer.js',
-      '../../Babylon/PostProcess/babylon.ssaoRenderingPipeline.js',
-      '../../Babylon/PostProcess/babylon.volumetricLightScatteringPostProcess.js',
-      '../../Babylon/PostProcess/babylon.lensRenderingPipeline.js',
-	  '../../Babylon/PostProcess/babylon.colorCorrectionPostProcess.js'
-    ])
-    .pipe(concat('babylon.js'))
-    .pipe(gulp.dest('build/'))
-    .pipe(rename({suffix: '.min'}))
-    //.pipe(uglify({ outSourceMap: true })) //waiting for https://github.com/gulpjs/gulp/issues/356
+gulp.task("build", ["workers", "shaders"], function() {
+    return merge2(
+        gulp.src(config.core.files, config.extras.files),    
+        shadersStream,
+        workersStream
+    )
+    .pipe(concat(config.build.filename))
+    .pipe(cleants())
+    .pipe(gulp.dest(config.build.outputDirectory))
+    .pipe(rename({suffix: config.build.minSuffix}))
     .pipe(uglify())
-    .pipe(gulp.dest('build/'))
-
+    .pipe(gulp.dest(config.build.outputDirectory))
 });
 
-/**
- * Clean temporary files
- * Called after the task scripts
- */
-gulp.task('clean', ['scripts'], function() {
-  return gulp.src(['build/shaders.js'], {read: false})
-    .pipe(clean());
+gulp.task("typescript", function(cb) {
+    runSequence("typescript-compile", "build");
 });
 
 /**
- * The default task, call the tasks: shaders, scripts, clean
+ * The default task, call the tasks: build
  */
 gulp.task('default', function() {
-    gulp.start('shaders','scripts', 'clean');
-});
-
-/**
- * The default typescript task, call the tasks: shaders, scripts, clean AFTER the task typescript-to-js
- */
-gulp.task('typescript', ['typescript-declaration'], function() {
-    gulp.start('shaders','scripts', 'clean');
+    return gulp.start('build');
 });
 
 /**
  * Watch task, will call the default task if a js file is updated.
  */
 gulp.task('watch', function() {
-  gulp.watch('src/**/*.js', ['default']);
+  gulp.watch('../../Babylon/**/*.js', ['build']);
 });
 
 /**
  * Watch typescript task, will call the default typescript task if a typescript file is updated.
  */
 gulp.task('watch-typescript', function() {
-  gulp.watch('src/**/*.ts', ['typescript']);
-});
-
-/**
- * task to uglify a file passed as an argument
- */
-gulp.task('makeUgly' ,function(path, fileIn) {
-  if (path.lastIndexOf('/') + 1 !== path.length) { path  += '/'; }
-
-  return gulp.src([path + fileIn])
-    .pipe(rename({suffix: '.min'}))
-    .pipe(uglify())
-    .pipe(gulp.dest(path))
-
+  gulp.watch("../../Babylon/**/*.ts", ["typescript-compile", "build"]);
 });

+ 14 - 12
Tools/Gulp/package.json

@@ -1,19 +1,21 @@
 {
   "name": "BabylonJS",
-  "version": "1.9.0",
+  "version": "2.1.0",
   "description": "",
   "main": "",
   "devDependencies": {
-    "gulp": "^3.6.2",
-    "gulp-clean": "^0.3.0",
-    "gulp-concat": "^2.2.0",
-    "gulp-filter": "^0.4.1",
-    "gulp-rename": "^1.2.0",
-    "gulp-typescript" : "2.4.2",
-    "gulp-sourcemaps" : "1.3.0",
-    "gulp-uglify": "^0.3.0",
-    "gulp-util": "^2.2.14",
-    "through": "^2.3.4",
-    "gulp-param": "0.6.3"
+    "gulp": "^3.8.11",
+    "gulp-uglify": "~1.2.0",
+    "gulp-sourcemaps": "~1.5.2",
+    "typescript": "~1.4.0",
+    "gulp-typescript": "~2.6.0",
+    "through2": "~0.6.5",
+    "gulp-util": "~3.0.4",
+    "gulp-concat": "~2.5.2",
+    "merge2": "~0.3.5",
+    "gulp-rename": "~1.2.2",
+    "gulp-clean-ts-extends": "~0.1.1",
+    "gulp-changed": "~1.2.1",
+    "run-sequence": "~1.1.0"
   }
 }

+ 10 - 21
Tools/Gulp/readme.md

@@ -22,19 +22,13 @@ npm install
 npm update
 ```
 
-### Update gulpfile.js (task scripts) if you want to add your own files:
-```
-/**
- * Concat all js files in order into one big js file and minify it.
- * The list is based on https://github.com/BabylonJS/Babylon.js/wiki/Creating-the-minified-version
- * Do not hesistate to update it if you need to add your own files.
- */
-gulp.task('scripts', ['shaders'] ,function() {
-return gulp.src([
-      '../../Babylon/Math/babylon.math.js',
-      '../../Babylon/Math/babylon.axis.js',
-
-      ....
+### Update config.json if you want to add your own files:
+```
+"extras" : {
+    "files": [
+        "file1.js", "file2.js"
+    ]
+}
 ```
 ## From the javascript source
 ### Build Babylon.js from the javascript files:
@@ -69,14 +63,9 @@ Be aware that all js files content will be overwrite.
 gulp watch-typescript
 ```
 
-### Compile all the typscript files to their javascript respective files
+### Compile all the typscript files to their javascript respective files including declaration file
 ```
-gulp typescript-to-js
+gulp typescript-compile
 ```
 
-Be aware that all js files content will be overwrite.
-
-### Build the typescript declaration file
-```
-gulp typescript-declaration
-```
+Be aware that all js files content will be overwritten.