Explorar o código

Restructure and add functionality

The collision detector is now an interface.
Callbacks for mesh/geometry add/update/remove were registered.
Coordinator for worker is set to send the worker the information needed
for update/collide.
AfterRender function is registered to update the worker database state.
Raanan Weber %!s(int64=10) %!d(string=hai) anos
pai
achega
d6b7df3cc5

+ 1 - 1
Babylon/Cameras/babylon.arcRotateCamera.js

@@ -365,7 +365,7 @@ var BABYLON;
                 this._collider.radius = this.collisionRadius;
                 this.position.subtractToRef(this._previousPosition, this._collisionVelocity);
                 this._collisionTriggered = true;
-                this.getScene().collisionCoordinator._getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+                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;

+ 1 - 1
Babylon/Cameras/babylon.arcRotateCamera.ts

@@ -440,7 +440,7 @@
                 this.position.subtractToRef(this._previousPosition, this._collisionVelocity);
 
                 this._collisionTriggered = true;
-                this.getScene().collisionCoordinator._getNewPosition(this._previousPosition, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+                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);

+ 1 - 1
Babylon/Cameras/babylon.freeCamera.js

@@ -168,7 +168,7 @@ var BABYLON;
             }
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
             this._collider.radius = this.ellipsoid;
-            this.getScene().collisionCoordinator._getNewPosition(this._oldPosition, velocity, this._collider, 3, null, this._onCollisionPositionChange, velocity.equals(this.getScene().gravity) ? this.uniqueId + 100000 : this.uniqueId);
+            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) {

+ 1 - 1
Babylon/Cameras/babylon.freeCamera.ts

@@ -189,7 +189,7 @@
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
             this._collider.radius = this.ellipsoid;
 
-            this.getScene().collisionCoordinator._getNewPosition(this._oldPosition, velocity, this._collider, 3, null, this._onCollisionPositionChange, velocity.equals(this.getScene().gravity) ? this.uniqueId + 100000 : this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, velocity, this._collider, 3, null, this._onCollisionPositionChange, velocity.equals(this.getScene().gravity) ? this.uniqueId + 100000 : this.uniqueId);
             
         }
 

+ 181 - 22
Babylon/Collisions/babylon.collisionCoordinator.js

@@ -1,33 +1,192 @@
 var BABYLON;
 (function (BABYLON) {
-    var CollisionCoordinator = (function () {
-        function CollisionCoordinator(_scene) {
-            this._scene = _scene;
+    (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._finalPosition = 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 () {
+                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 variable is here only in case the update takes longer than a frame! 
+                _this._runningUpdated++;
+                _this._worker.postMessage(message, serializable);
+                _this._addUpdateMeshesList = {};
+                _this._addUpdateGeometriesList = {};
+                _this._toRemoveGeometryArray = [];
+                _this._toRemoveMeshesArray = [];
+            };
+            this._onMessageFromWorker = function (e) {
+                var returnData = e.data;
+                switch (returnData.taskType) {
+                    case 0 /* INIT */:
+                        //TODO is init required after worker is done initializing?
+                        _this._init = true;
+                        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 = [];
-            //TODO initialize worker here
+            this._init = false;
+            this._runningUpdated = 0;
+            this._runningCollisionTask = false;
+            this._addUpdateMeshesList = {};
+            this._addUpdateGeometriesList = {};
+            this._toRemoveGeometryArray = [];
+            this._toRemoveMeshesArray = [];
         }
-        // Collisions
-        CollisionCoordinator.prototype._getNewPosition = function (position, velocity, collider, maximumRetry, excludedMesh, onNewPosition, collisionIndex) {
-            if (excludedMesh === void 0) { excludedMesh = null; }
-            if (collisionIndex === void 0) { collisionIndex = 0; }
+        CollisionCoordinatorWorker.prototype.getNewPosition = function (position, velocity, collider, maximumRetry, excludedMesh, onNewPosition, collisionIndex) {
+            if (this._collisionsCallbackArray[collisionIndex])
+                return;
             position.divideToRef(collider.radius, this._scaledPosition);
             velocity.divideToRef(collider.radius, this._scaledVelocity);
-            if (this._scene.workerCollisions) {
-            }
-            else {
-                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(collisionIndex, this._finalPosition, collider.collidedMesh);
+            this._collisionsCallbackArray[collisionIndex] = onNewPosition;
+        };
+        CollisionCoordinatorWorker.prototype.init = function (scene) {
+            this._scene = scene;
+            this._scene.registerAfterRender(this._afterRender);
+            //TODO init worker
+        };
+        CollisionCoordinatorWorker.prototype.destroy = function () {
+            this._scene.unregisterAfterRender(this._afterRender);
+            //TODO destroy worker
+        };
+        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 on 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
+                    };
+                });
             }
+            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(collisionIndex, 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) {
         };
-        CollisionCoordinator.prototype._collideWithWorld = function (position, velocity, collider, maximumRetry, finalPosition, excludedMesh) {
+        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) {
@@ -55,8 +214,8 @@ var BABYLON;
             collider.retry++;
             this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
         };
-        return CollisionCoordinator;
+        return CollisionCoordinatorLegacy;
     })();
-    BABYLON.CollisionCoordinator = CollisionCoordinator;
+    BABYLON.CollisionCoordinatorLegacy = CollisionCoordinatorLegacy;
 })(BABYLON || (BABYLON = {}));
 //# sourceMappingURL=babylon.collisionCoordinator.js.map

+ 298 - 22
Babylon/Collisions/babylon.collisionCoordinator.ts

@@ -1,38 +1,316 @@
 module BABYLON {
+
+    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;
+    }
+
+    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 class CollisionCoordinator {
+    export interface CollidePayload {
+        collisionId: number;
+        collider: SerializedColliderToWorker;
+        maximumRetry: number;
+        excludedMeshUniqueId?: number;
+    }
         
-        private _collisionsCallbackArray: Array<Function>;
+    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 _finalPosition = Vector3.Zero();
-        
-        constructor(private _scene:Scene) {
+        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 = [];
-            //TODO initialize worker here
-        }    
+            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 = [];
+            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
+                    }
+                });
+            }
+
+            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._collisionsCallbackArray[collisionIndex]) return;
 
-        // Collisions
-        public _getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, excludedMesh: AbstractMesh = null, onNewPosition?: (collisionId : number, newPosition: BABYLON.Vector3, collidedMesh?: BABYLON.AbstractMesh) => void, collisionIndex: number = 0): void {
             position.divideToRef(collider.radius, this._scaledPosition);
             velocity.divideToRef(collider.radius, this._scaledVelocity);
 
-            if (this._scene.workerCollisions) {
-
-            } else {
-                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(collisionIndex, this._finalPosition, collider.collidedMesh);
+            this._collisionsCallbackArray[collisionIndex] = onNewPosition;
+
+        }
+
+        public init(scene: Scene): void {
+            this._scene = scene;
+            this._scene.registerAfterRender(this._afterRender);
+            //TODO init worker
+
+        }
+
+        public destroy(): void {
+            this._scene.unregisterAfterRender(this._afterRender);
+            //TODO destroy worker
+        }
+
+        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 on 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 = () => {
+            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 variable is here only in case the update takes longer than a frame! 
+            this._runningUpdated++;
+
+            this._worker.postMessage(message, serializable);
+            this._addUpdateMeshesList = {};
+            this._addUpdateGeometriesList = {};
+            this._toRemoveGeometryArray = [];
+            this._toRemoveMeshesArray = [];
         }
 
+        private _onMessageFromWorker = (e: MessageEvent) => {
+            var returnData = <WorkerReply> e.data;
+
+            switch (returnData.taskType) {
+                case WorkerTaskType.INIT:
+                    //TODO is init required after worker is done initializing?
+                    this._init = true;
+                    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(collisionIndex, 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;
 
@@ -68,7 +346,5 @@ module BABYLON {
             collider.retry++;
             this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
         }
-
     }
-
 }

+ 3 - 3
Babylon/Mesh/babylon.abstractMesh.js

@@ -196,7 +196,7 @@ var BABYLON;
                 this.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
                 this.rotation = BABYLON.Vector3.Zero();
             }
-            if (!space || space === 0 /* LOCAL */) {
+            if (!space || space === BABYLON.Space.LOCAL) {
                 var rotationQuaternion = BABYLON.Quaternion.RotationAxis(axis, amount);
                 this.rotationQuaternion = this.rotationQuaternion.multiply(rotationQuaternion);
             }
@@ -212,7 +212,7 @@ var BABYLON;
         };
         AbstractMesh.prototype.translate = function (axis, distance, space) {
             var displacementVector = axis.scale(distance);
-            if (!space || space === 0 /* LOCAL */) {
+            if (!space || space === BABYLON.Space.LOCAL) {
                 var tempV3 = this.getPositionExpressedInLocalSpace().add(displacementVector);
                 this.setPositionWithLocalVector(tempV3);
             }
@@ -610,7 +610,7 @@ var BABYLON;
             globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPositionForCollisions);
             this._oldPositionForCollisions.addInPlace(this.ellipsoidOffset);
             this._collider.radius = this.ellipsoid;
-            this.getScene().collisionCoordinator._getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
         };
         // Submeshes octree
         /**

+ 1 - 1
Babylon/Mesh/babylon.abstractMesh.ts

@@ -714,7 +714,7 @@
             this._oldPositionForCollisions.addInPlace(this.ellipsoidOffset);
             this._collider.radius = this.ellipsoid;
 
-            this.getScene().collisionCoordinator._getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPositionForCollisions, velocity, this._collider, 3, this, this._onCollisionPositionChange, this.uniqueId);
         }
 
         private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: AbstractMesh = null) => {

+ 25 - 3
Babylon/babylon.scene.js

@@ -86,7 +86,6 @@ var BABYLON;
             this.lensFlareSystems = new Array();
             // Collisions
             this.collisionsEnabled = true;
-            this.workerCollisions = true;
             this.gravity = new BABYLON.Vector3(0, -9.0, 0);
             // Postprocesses
             this.postProcessesEnabled = true;
@@ -142,8 +141,8 @@ var BABYLON;
             this.mainSoundTrack = new BABYLON.SoundTrack(this, { mainTrack: true });
             //simplification queue
             this.simplificationQueue = new BABYLON.SimplificationQueue();
-            //collision coordinator
-            this.collisionCoordinator = new BABYLON.CollisionCoordinator(this);
+            //collision coordinator initialization
+            this.workerCollisions = true;
         }
         Object.defineProperty(Scene, "FOGMODE_NONE", {
             get: function () {
@@ -181,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.
@@ -552,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);
             }
@@ -562,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);
             }
@@ -764,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);
             }
@@ -778,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);
                 }

+ 34 - 4
Babylon/babylon.scene.ts

@@ -163,8 +163,8 @@
 
         // Collisions
         public collisionsEnabled = true;
-        public workerCollisions = true;
-        public collisionCoordinator: CollisionCoordinator;
+        private _workerCollisions;
+        public collisionCoordinator: ICollisionCoordinator;
         public gravity = new Vector3(0, -9.0, 0);
 
         // Postprocesses
@@ -291,8 +291,8 @@
 
             //simplification queue
             this.simplificationQueue = new SimplificationQueue();
-            //collision coordinator
-            this.collisionCoordinator = new CollisionCoordinator(this);
+            //collision coordinator initialization
+            this.workerCollisions = true;
         }
 
         // Properties 
@@ -300,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.
@@ -754,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);
             }
@@ -765,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);
             }
@@ -996,6 +1018,10 @@
             }
 
             this._geometries.push(geometry);
+
+            //notify the collision coordinator
+            this.collisionCoordinator.onGeometryAdded(geometry);
+
             if (this.onGeometryAdded) {
                 this.onGeometryAdded(geometry);
             }
@@ -1013,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);
                 }