瀏覽代碼

Merge pull request #1105 from nockawa/master

StringDictionary + misc
David Catuhe 9 年之前
父節點
當前提交
4b6867c02d

+ 220 - 211
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.js

@@ -1,211 +1,220 @@
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
-    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
-    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
-    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
-    return c > 3 && r && Object.defineProperty(target, key, r), r;
-};
-var BABYLON;
-(function (BABYLON) {
-    var eventPrefix = BABYLON.Tools.GetPointerPrefix();
-    var ArcRotateCameraPointersInput = (function () {
-        function ArcRotateCameraPointersInput() {
-            this.angularSensibilityX = 1000.0;
-            this.angularSensibilityY = 1000.0;
-            this.pinchPrecision = 6.0;
-            this.panningSensibility = 50.0;
-            this._isRightClick = false;
-            this._isCtrlPushed = false;
-            this.pinchInwards = true;
-        }
-        ArcRotateCameraPointersInput.prototype.attachControl = function (element, noPreventDefault) {
-            var _this = this;
-            var engine = this.camera.getEngine();
-            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointers = new BABYLON.SmartCollection();
-            var previousPinchDistance = 0;
-            this._pointerInput = function (p, s) {
-                var evt = p.event;
-                if (p.type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                    try {
-                        evt.srcElement.setPointerCapture(evt.pointerId);
-                    }
-                    catch (e) {
-                    }
-                    // Manage panning with right click
-                    _this._isRightClick = evt.button === 2;
-                    // manage pointers
-                    pointers.add(evt.pointerId, { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                    cacheSoloPointer = pointers.item(evt.pointerId);
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                }
-                else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
-                    try {
-                        evt.srcElement.releasePointerCapture(evt.pointerId);
-                    }
-                    catch (e) {
-                    }
-                    cacheSoloPointer = null;
-                    previousPinchDistance = 0;
-                    //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
-                    //but emptying completly pointers collection is required to fix a bug on iPhone : 
-                    //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
-                    //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                    pointers.empty();
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                }
-                else if (p.type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                    if (!noPreventDefault) {
-                        evt.preventDefault();
-                    }
-                    switch (pointers.count) {
-                        case 1:
-                            if (_this.panningSensibility !== 0 && ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) || (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
-                                _this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / _this.panningSensibility;
-                                _this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / _this.panningSensibility;
-                            }
-                            else {
-                                var offsetX = evt.clientX - cacheSoloPointer.x;
-                                var offsetY = evt.clientY - cacheSoloPointer.y;
-                                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
-                                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
-                            }
-                            cacheSoloPointer.x = evt.clientX;
-                            cacheSoloPointer.y = evt.clientY;
-                            break;
-                        case 2:
-                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                            pointers.item(evt.pointerId).x = evt.clientX;
-                            pointers.item(evt.pointerId).y = evt.clientY;
-                            var direction = _this.pinchInwards ? 1 : -1;
-                            var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
-                            var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
-                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                            if (previousPinchDistance === 0) {
-                                previousPinchDistance = pinchSquaredDistance;
-                                return;
-                            }
-                            if (pinchSquaredDistance !== previousPinchDistance) {
-                                _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (_this.pinchPrecision * ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) * direction);
-                                previousPinchDistance = pinchSquaredDistance;
-                            }
-                            break;
-                        default:
-                            if (pointers.item(evt.pointerId)) {
-                                pointers.item(evt.pointerId).x = evt.clientX;
-                                pointers.item(evt.pointerId).y = evt.clientY;
-                            }
-                    }
-                }
-            };
-            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, BABYLON.PointerEventTypes.POINTERDOWN | BABYLON.PointerEventTypes.POINTERUP | BABYLON.PointerEventTypes.POINTERMOVE);
-            this._onContextMenu = function (evt) {
-                evt.preventDefault();
-            };
-            if (!this.camera._useCtrlForPanning) {
-                element.addEventListener("contextmenu", this._onContextMenu, false);
-            }
-            this._onLostFocus = function () {
-                //this._keys = [];
-                pointers.empty();
-                previousPinchDistance = 0;
-                cacheSoloPointer = null;
-            };
-            this._onKeyDown = function (evt) {
-                _this._isCtrlPushed = evt.ctrlKey;
-            };
-            this._onKeyUp = function (evt) {
-                _this._isCtrlPushed = evt.ctrlKey;
-            };
-            this._onMouseMove = function (evt) {
-                if (!engine.isPointerLock) {
-                    return;
-                }
-                var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
-                var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
-                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
-                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
-                if (!noPreventDefault) {
-                    evt.preventDefault();
-                }
-            };
-            this._onGestureStart = function (e) {
-                if (window.MSGesture === undefined) {
-                    return;
-                }
-                if (!_this._MSGestureHandler) {
-                    _this._MSGestureHandler = new MSGesture();
-                    _this._MSGestureHandler.target = element;
-                }
-                _this._MSGestureHandler.addPointer(e.pointerId);
-            };
-            this._onGesture = function (e) {
-                _this.camera.radius *= e.scale;
-                if (e.preventDefault) {
-                    if (!noPreventDefault) {
-                        e.stopPropagation();
-                        e.preventDefault();
-                    }
-                }
-            };
-            element.addEventListener("mousemove", this._onMouseMove, false);
-            element.addEventListener("MSPointerDown", this._onGestureStart, false);
-            element.addEventListener("MSGestureChange", this._onGesture, false);
-            BABYLON.Tools.RegisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
-                { name: "blur", handler: this._onLostFocus }
-            ]);
-        };
-        ArcRotateCameraPointersInput.prototype.detachControl = function (element) {
-            if (element && this._observer) {
-                this.camera.getScene().onPointerObservable.remove(this._observer);
-                this._observer = null;
-                element.removeEventListener("contextmenu", this._onContextMenu);
-                element.removeEventListener("mousemove", this._onMouseMove);
-                element.removeEventListener("MSPointerDown", this._onGestureStart);
-                element.removeEventListener("MSGestureChange", this._onGesture);
-                this._isRightClick = false;
-                this._isCtrlPushed = false;
-                this.pinchInwards = true;
-                this._onKeyDown = null;
-                this._onKeyUp = null;
-                this._onMouseMove = null;
-                this._onGestureStart = null;
-                this._onGesture = null;
-                this._MSGestureHandler = null;
-                this._onLostFocus = null;
-                this._onContextMenu = null;
-            }
-            BABYLON.Tools.UnregisterTopRootEvents([
-                { name: "keydown", handler: this._onKeyDown },
-                { name: "keyup", handler: this._onKeyUp },
-                { name: "blur", handler: this._onLostFocus }
-            ]);
-        };
-        ArcRotateCameraPointersInput.prototype.getTypeName = function () {
-            return "ArcRotateCameraPointersInput";
-        };
-        ArcRotateCameraPointersInput.prototype.getSimpleName = function () {
-            return "pointers";
-        };
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityX", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityY", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
-        __decorate([
-            BABYLON.serialize()
-        ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
-        return ArcRotateCameraPointersInput;
-    })();
-    BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
-    BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
-})(BABYLON || (BABYLON = {}));
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var BABYLON;
+(function (BABYLON) {
+    var eventPrefix = BABYLON.Tools.GetPointerPrefix();
+    var ArcRotateCameraPointersInput = (function () {
+        function ArcRotateCameraPointersInput() {
+            this.angularSensibilityX = 1000.0;
+            this.angularSensibilityY = 1000.0;
+            this.pinchPrecision = 6.0;
+            this.panningSensibility = 50.0;
+            this._isRightClick = false;
+            this._isCtrlPushed = false;
+            this.pinchInwards = true;
+        }
+        ArcRotateCameraPointersInput.prototype.attachControl = function (element, noPreventDefault) {
+            var _this = this;
+            var engine = this.camera.getEngine();
+            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
+            var pointA, pointB;
+            var previousPinchDistance = 0;
+            this._pointerInput = function (p, s) {
+                var evt = p.event;
+                if (p.type === BABYLON.PointerEventTypes.POINTERDOWN) {
+                    try {
+                        evt.srcElement.setPointerCapture(evt.pointerId);
+                    }
+                    catch (e) {
+                    }
+                    // Manage panning with right click
+                    _this._isRightClick = evt.button === 2;
+                    // manage pointers
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                }
+                else if (p.type === BABYLON.PointerEventTypes.POINTERUP) {
+                    try {
+                        evt.srcElement.releasePointerCapture(evt.pointerId);
+                    }
+                    catch (e) {
+                    }
+                    cacheSoloPointer = null;
+                    previousPinchDistance = 0;
+                    //would be better to use pointers.remove(evt.pointerId) for multitouch gestures, 
+                    //but emptying completly pointers collection is required to fix a bug on iPhone : 
+                    //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
+                    //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
+                    pointA = pointB = undefined;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                }
+                else if (p.type === BABYLON.PointerEventTypes.POINTERMOVE) {
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (_this.panningSensibility !== 0 &&
+                            ((_this._isCtrlPushed && _this.camera._useCtrlForPanning) ||
+                                (!_this.camera._useCtrlForPanning && _this._isRightClick))) {
+                            _this.camera
+                                .inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / _this.panningSensibility;
+                            _this.camera
+                                .inertialPanningY += (evt.clientY - cacheSoloPointer.y) / _this.panningSensibility;
+                        }
+                        else {
+                            var offsetX = evt.clientX - cacheSoloPointer.x;
+                            var offsetY = evt.clientY - cacheSoloPointer.y;
+                            _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
+                            _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                        var ed = (pointA.pointerId === evt.pointerId) ? pointA : pointB;
+                        ed.x = evt.clientX;
+                        ed.y = evt.clientY;
+                        var direction = _this.pinchInwards ? 1 : -1;
+                        var distX = pointA.x - pointB.x;
+                        var distY = pointA.y - pointB.y;
+                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            _this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (_this.pinchPrecision *
+                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
+                    }
+                }
+            };
+            this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, BABYLON.PointerEventTypes.POINTERDOWN | BABYLON.PointerEventTypes.POINTERUP | BABYLON.PointerEventTypes.POINTERMOVE);
+            this._onContextMenu = function (evt) {
+                evt.preventDefault();
+            };
+            if (!this.camera._useCtrlForPanning) {
+                element.addEventListener("contextmenu", this._onContextMenu, false);
+            }
+            this._onLostFocus = function () {
+                //this._keys = [];
+                pointA = pointB = undefined;
+                previousPinchDistance = 0;
+                cacheSoloPointer = null;
+            };
+            this._onKeyDown = function (evt) {
+                _this._isCtrlPushed = evt.ctrlKey;
+            };
+            this._onKeyUp = function (evt) {
+                _this._isCtrlPushed = evt.ctrlKey;
+            };
+            this._onMouseMove = function (evt) {
+                if (!engine.isPointerLock) {
+                    return;
+                }
+                var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+                var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+                _this.camera.inertialAlphaOffset -= offsetX / _this.angularSensibilityX;
+                _this.camera.inertialBetaOffset -= offsetY / _this.angularSensibilityY;
+                if (!noPreventDefault) {
+                    evt.preventDefault();
+                }
+            };
+            this._onGestureStart = function (e) {
+                if (window.MSGesture === undefined) {
+                    return;
+                }
+                if (!_this._MSGestureHandler) {
+                    _this._MSGestureHandler = new MSGesture();
+                    _this._MSGestureHandler.target = element;
+                }
+                _this._MSGestureHandler.addPointer(e.pointerId);
+            };
+            this._onGesture = function (e) {
+                _this.camera.radius *= e.scale;
+                if (e.preventDefault) {
+                    if (!noPreventDefault) {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }
+                }
+            };
+            element.addEventListener("mousemove", this._onMouseMove, false);
+            element.addEventListener("MSPointerDown", this._onGestureStart, false);
+            element.addEventListener("MSGestureChange", this._onGesture, false);
+            BABYLON.Tools.RegisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        };
+        ArcRotateCameraPointersInput.prototype.detachControl = function (element) {
+            if (element && this._observer) {
+                this.camera.getScene().onPointerObservable.remove(this._observer);
+                this._observer = null;
+                element.removeEventListener("contextmenu", this._onContextMenu);
+                element.removeEventListener("mousemove", this._onMouseMove);
+                element.removeEventListener("MSPointerDown", this._onGestureStart);
+                element.removeEventListener("MSGestureChange", this._onGesture);
+                this._isRightClick = false;
+                this._isCtrlPushed = false;
+                this.pinchInwards = true;
+                this._onKeyDown = null;
+                this._onKeyUp = null;
+                this._onMouseMove = null;
+                this._onGestureStart = null;
+                this._onGesture = null;
+                this._MSGestureHandler = null;
+                this._onLostFocus = null;
+                this._onContextMenu = null;
+            }
+            BABYLON.Tools.UnregisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        };
+        ArcRotateCameraPointersInput.prototype.getTypeName = function () {
+            return "ArcRotateCameraPointersInput";
+        };
+        ArcRotateCameraPointersInput.prototype.getSimpleName = function () {
+            return "pointers";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityX", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "angularSensibilityY", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
+        return ArcRotateCameraPointersInput;
+    }());
+    BABYLON.ArcRotateCameraPointersInput = ArcRotateCameraPointersInput;
+    BABYLON.CameraInputTypes["ArcRotateCameraPointersInput"] = ArcRotateCameraPointersInput;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.arcrotatecamera.input.pointers.js.map

+ 53 - 45
src/Cameras/Inputs/babylon.arcrotatecamera.input.pointers.ts

@@ -33,8 +33,8 @@ module BABYLON {
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             var engine = this.camera.getEngine();
-            var cacheSoloPointer; // cache pointer object for better perf on camera rotation
-            var pointers = new SmartCollection();
+            var cacheSoloPointer: { x: number, y: number, pointerId: number, type: any }; // cache pointer object for better perf on camera rotation
+            var pointA: { x: number, y: number, pointerId: number, type: any }, pointB: { x: number, y: number, pointerId: number, type: any };
             var previousPinchDistance = 0;
 
             this._pointerInput = (p, s) => {
@@ -51,8 +51,13 @@ module BABYLON {
                     this._isRightClick = evt.button === 2;
 
                     // manage pointers
-                    pointers.add(evt.pointerId, { x: evt.clientX, y: evt.clientY, type: evt.pointerType });
-                    cacheSoloPointer = pointers.item(evt.pointerId);
+                    cacheSoloPointer = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId, type: evt.pointerType };
+                    if (pointA === undefined) {
+                        pointA = cacheSoloPointer;
+                    }
+                    else if (pointB === undefined) {
+                        pointB = cacheSoloPointer;
+                    }
                     if (!noPreventDefault) {
                         evt.preventDefault();
                     }
@@ -70,7 +75,7 @@ module BABYLON {
                     //but emptying completly pointers collection is required to fix a bug on iPhone : 
                     //when changing orientation while pinching camera, one pointer stay pressed forever if we don't release all pointers  
                     //will be ok to put back pointers.remove(evt.pointerId); when iPhone bug corrected
-                    pointers.empty();
+                    pointA = pointB = undefined;
 
                     if (!noPreventDefault) {
                         evt.preventDefault();
@@ -80,45 +85,48 @@ module BABYLON {
                         evt.preventDefault();
                     }
 
-                    switch (pointers.count) {
-                        case 1: //normal camera rotation
-                            if (this.panningSensibility !== 0 && ((this._isCtrlPushed && this.camera._useCtrlForPanning) || (!this.camera._useCtrlForPanning && this._isRightClick))) {
-                                this.camera.inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
-                                this.camera.inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
-                            } else {
-                                var offsetX = evt.clientX - cacheSoloPointer.x;
-                                var offsetY = evt.clientY - cacheSoloPointer.y;
-                                this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
-                                this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
-                            }
-                            cacheSoloPointer.x = evt.clientX;
-                            cacheSoloPointer.y = evt.clientY;
-                            break;
-
-                        case 2: //pinch
-                            //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
-                            pointers.item(evt.pointerId).x = evt.clientX;
-                            pointers.item(evt.pointerId).y = evt.clientY;
-                            var direction = this.pinchInwards ? 1 : -1;
-                            var distX = pointers.getItemByIndex(0).x - pointers.getItemByIndex(1).x;
-                            var distY = pointers.getItemByIndex(0).y - pointers.getItemByIndex(1).y;
-                            var pinchSquaredDistance = (distX * distX) + (distY * distY);
-                            if (previousPinchDistance === 0) {
-                                previousPinchDistance = pinchSquaredDistance;
-                                return;
-                            }
-
-                            if (pinchSquaredDistance !== previousPinchDistance) {
-                                this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) / (this.pinchPrecision * ((this.angularSensibilityX + this.angularSensibilityY) / 2) * direction);
-                                previousPinchDistance = pinchSquaredDistance;
-                            }
-                            break;
-
-                        default:
-                            if (pointers.item(evt.pointerId)) {
-                                pointers.item(evt.pointerId).x = evt.clientX;
-                                pointers.item(evt.pointerId).y = evt.clientY;
-                            }
+                    // One button down
+                    if (pointA && pointB === undefined) {
+                        if (this.panningSensibility !== 0 &&
+                        ((this._isCtrlPushed && this.camera._useCtrlForPanning) ||
+                        (!this.camera._useCtrlForPanning && this._isRightClick))) {
+                            this.camera
+                                .inertialPanningX += -(evt.clientX - cacheSoloPointer.x) / this.panningSensibility;
+                            this.camera
+                                .inertialPanningY += (evt.clientY - cacheSoloPointer.y) / this.panningSensibility;
+                        } else {
+                            var offsetX = evt.clientX - cacheSoloPointer.x;
+                            var offsetY = evt.clientY - cacheSoloPointer.y;
+                            this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX;
+                            this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY;
+                        }
+                        cacheSoloPointer.x = evt.clientX;
+                        cacheSoloPointer.y = evt.clientY;
+                    }
+
+                   // Two buttons down: pinch
+                    else if (pointA && pointB) {
+                        //if (noPreventDefault) { evt.preventDefault(); } //if pinch gesture, could be usefull to force preventDefault to avoid html page scroll/zoom in some mobile browsers
+                        var ed = (pointA.pointerId===evt.pointerId) ? pointA : pointB;
+                        ed.x = evt.clientX;
+                        ed.y = evt.clientY;
+                        var direction = this.pinchInwards ? 1 : -1;
+                        var distX = pointA.x - pointB.x;
+                        var distY = pointA.y - pointB.y;
+                        var pinchSquaredDistance = (distX * distX) + (distY * distY);
+                        if (previousPinchDistance === 0) {
+                            previousPinchDistance = pinchSquaredDistance;
+                            return;
+                        }
+
+                        if (pinchSquaredDistance !== previousPinchDistance) {
+                            this.camera
+                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchDistance) /
+                                (this.pinchPrecision *
+                                    ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
+                                    direction);
+                            previousPinchDistance = pinchSquaredDistance;
+                        }
                     }
                 }
             }
@@ -135,7 +143,7 @@ module BABYLON {
 
             this._onLostFocus = () => {
                 //this._keys = [];
-                pointers.empty();
+                pointA = pointB = undefined;
                 previousPinchDistance = 0;
                 cacheSoloPointer = null;
             };

+ 0 - 100
src/Tools/babylon.smartCollection.js

@@ -1,100 +0,0 @@
-var BABYLON;
-(function (BABYLON) {
-    var SmartCollection = (function () {
-        function SmartCollection(capacity) {
-            if (capacity === void 0) { capacity = 10; }
-            this.count = 0;
-            this._initialCapacity = capacity;
-            this.items = {};
-            this._keys = new Array(this._initialCapacity);
-        }
-        SmartCollection.prototype.add = function (key, item) {
-            if (this.items[key] != undefined) {
-                return -1;
-            }
-            this.items[key] = item;
-            //literal keys are always strings, but we keep source type of key in _keys array
-            this._keys[this.count++] = key;
-            if (this.count > this._keys.length) {
-                this._keys.length *= 2;
-            }
-            return this.count;
-        };
-        SmartCollection.prototype.remove = function (key) {
-            if (this.items[key] == undefined) {
-                return -1;
-            }
-            return this.removeItemOfIndex(this.indexOf(key));
-        };
-        SmartCollection.prototype.removeItemOfIndex = function (index) {
-            if (index < this.count && index > -1) {
-                delete this.items[this._keys[index]];
-                //here, shifting by hand is better optimised than .splice
-                while (index < this.count) {
-                    this._keys[index] = this._keys[index + 1];
-                    index++;
-                }
-            }
-            else {
-                return -1;
-            }
-            return --this.count;
-        };
-        SmartCollection.prototype.indexOf = function (key) {
-            for (var i = 0; i !== this.count; i++) {
-                if (this._keys[i] === key) {
-                    return i;
-                }
-            }
-            return -1;
-        };
-        SmartCollection.prototype.item = function (key) {
-            return this.items[key];
-        };
-        SmartCollection.prototype.getAllKeys = function () {
-            if (this.count > 0) {
-                var keys = new Array(this.count);
-                for (var i = 0; i < this.count; i++) {
-                    keys[i] = this._keys[i];
-                }
-                return keys;
-            }
-            else {
-                return undefined;
-            }
-        };
-        SmartCollection.prototype.getKeyByIndex = function (index) {
-            if (index < this.count && index > -1) {
-                return this._keys[index];
-            }
-            else {
-                return undefined;
-            }
-        };
-        SmartCollection.prototype.getItemByIndex = function (index) {
-            if (index < this.count && index > -1) {
-                return this.items[this._keys[index]];
-            }
-            else {
-                return undefined;
-            }
-        };
-        SmartCollection.prototype.empty = function () {
-            if (this.count > 0) {
-                this.count = 0;
-                this.items = {};
-                this._keys = new Array(this._initialCapacity);
-            }
-        };
-        SmartCollection.prototype.forEach = function (block) {
-            var key;
-            for (key in this.items) {
-                if (this.items.hasOwnProperty(key)) {
-                    block(this.items[key]);
-                }
-            }
-        };
-        return SmartCollection;
-    })();
-    BABYLON.SmartCollection = SmartCollection;
-})(BABYLON || (BABYLON = {}));

+ 0 - 115
src/Tools/babylon.smartCollection.ts

@@ -1,115 +0,0 @@
-module BABYLON {
-    export class SmartCollection {
-        public count = 0;
-        public items: any;
-
-        private _keys: string[];
-        private _initialCapacity: number;
-
-        constructor(capacity: number = 10) {
-            this._initialCapacity = capacity;
-            this.items = {};
-            this._keys = new Array(this._initialCapacity);
-        }
-
-        public add(key: any, item: any): number {
-            if (this.items[key] != undefined) {
-                return -1;
-            }
-            this.items[key] = item;
-            
-            //literal keys are always strings, but we keep source type of key in _keys array
-            this._keys[this.count++] = key;
-            if (this.count > this._keys.length) {
-                this._keys.length *= 2;
-            }
-
-            return this.count;
-        }
-
-        public remove(key: any): number {
-            if (this.items[key] == undefined) {
-                return -1;
-            }
-
-            return this.removeItemOfIndex(this.indexOf(key));
-        }
-
-        public removeItemOfIndex(index: number): number {
-            if (index < this.count && index > -1) {
-                delete this.items[this._keys[index]];
-                    
-                //here, shifting by hand is better optimised than .splice
-                while (index < this.count) {
-                    this._keys[index] = this._keys[index + 1]; index++;
-                }
-            }
-            else {
-                return -1;
-            }
-
-            return --this.count;
-        }
-
-        public indexOf(key: any): number {
-            for (var i = 0; i !== this.count; i++) {
-                if (this._keys[i] === key) {
-                    return i;
-                }
-            }
-            return -1;
-        }
-
-        public item(key: any): any {
-            return this.items[key];
-        }
-
-        public getAllKeys(): any[] {
-            if (this.count > 0) {
-                var keys = new Array(this.count);
-                for (var i = 0; i < this.count; i++) {
-                    keys[i] = this._keys[i];
-                }
-                return keys;
-            }
-            else {
-                return undefined;
-            }
-        }
-
-        public getKeyByIndex(index: number): any {
-            if (index < this.count && index > -1) {
-                return this._keys[index];
-            }
-            else {
-                return undefined;
-            }
-        }
-
-        public getItemByIndex(index: number): any {
-            if (index < this.count && index > -1) {
-                return this.items[this._keys[index]];
-            }
-            else {
-                return undefined;
-            }
-        }
-
-        public empty(): void {
-            if (this.count > 0) {
-                this.count = 0;
-                this.items = {};
-                this._keys = new Array(this._initialCapacity);
-            }
-        }
-
-        public forEach(block: (item: any) => void) {
-            var key: string;
-            for (key in this.items) {
-                if (this.items.hasOwnProperty(key)) {
-                    block(this.items[key]);
-                }
-            }
-        }
-    }
-} 

+ 138 - 0
src/Tools/babylon.stringDictionary.js

@@ -0,0 +1,138 @@
+var BABYLON;
+(function (BABYLON) {
+    /**
+     * This class implement a typical dictionary using a string as key and the generic type T as value.
+     * The underlying implemetation relies on an associative array to ensure the best performances.
+     * The value can be anything including 'null' but except 'undefined'
+     */
+    var StringDictionary = (function () {
+        function StringDictionary() {
+            this._count = 0;
+            this._data = {};
+        }
+        /**
+         * Get a value based from its key
+         * @param key the given key to get the matching value from
+         * @return the value if found, otherwise undefined is returned
+         */
+        StringDictionary.prototype.get = function (key) {
+            var val = this._data[key];
+            if (val !== undefined) {
+                return val;
+            }
+            return undefined;
+        };
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matchin value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        StringDictionary.prototype.getOrAddWithFactory = function (key, factory) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+            val = factory(key);
+            if (val) {
+                this.add(key, val);
+            }
+            return val;
+        };
+        /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        StringDictionary.prototype.getOrAdd = function (key, val) {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+            this.add(key, val);
+            return val;
+        };
+        /**
+         * Check if there's a given key in the dictionary
+         * @param key the key to check for
+         * @return true if the key is present, false otherwise
+         */
+        StringDictionary.prototype.contains = function (key) {
+            return this._data[key] !== undefined;
+        };
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        StringDictionary.prototype.add = function (key, value) {
+            if (this._data[key] !== undefined) {
+                return false;
+            }
+            this._data[key] = value;
+            ++this._count;
+            return true;
+        };
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        StringDictionary.prototype.remove = function (key) {
+            if (this.contains(key)) {
+                delete this._data[key];
+                --this._count;
+                return true;
+            }
+            return false;
+        };
+        /**
+         * Clear the whole content of the dictionary
+         */
+        StringDictionary.prototype.clear = function () {
+            this._data = {};
+            this._count = 0;
+        };
+        Object.defineProperty(StringDictionary.prototype, "count", {
+            get: function () {
+                return this._count;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Execute a callback on each key/val of the dictionary.
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute on a given key/value pair
+         */
+        StringDictionary.prototype.forEach = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                callback(cur, val);
+            }
+        };
+        /**
+         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
+         * If the callback returns null or undefined the method will iterate to the next key/value pair
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
+         */
+        StringDictionary.prototype.first = function (callback) {
+            for (var cur in this._data) {
+                var val = this._data[cur];
+                var res = callback(cur, val);
+                if (res) {
+                    return res;
+                }
+            }
+            return null;
+        };
+        return StringDictionary;
+    }());
+    BABYLON.StringDictionary = StringDictionary;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.stringDictionary.js.map

+ 141 - 0
src/Tools/babylon.stringDictionary.ts

@@ -0,0 +1,141 @@
+module BABYLON {
+    /**
+     * This class implement a typical dictionary using a string as key and the generic type T as value.
+     * The underlying implemetation relies on an associative array to ensure the best performances.
+     * The value can be anything including 'null' but except 'undefined'
+     */
+    export class StringDictionary<T> {
+        /**
+         * Get a value based from its key
+         * @param key the given key to get the matching value from
+         * @return the value if found, otherwise undefined is returned
+         */
+        public get(key: string): T {
+            var val = this._data[key];
+            if (val !== undefined) {
+                return val;
+            }
+            return undefined;
+        }
+
+        /**
+         * Get a value from its key or add it if it doesn't exist.
+         * This method will ensure you that a given key/data will be present in the dictionary.
+         * @param key the given key to get the matchin value from
+         * @param factory the factory that will create the value if the key is not present in the dictionary.
+         * The factory will only be invoked if there's no data for the given key.
+         * @return the value corresponding to the key.
+         */
+        public getOrAddWithFactory(key: string, factory: (key: string) => T): T {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+
+            val = factory(key);
+            if (val) {
+                this.add(key, val);
+            }
+
+            return val;
+        }
+
+        /**
+         * Get a value from its key if present in the dictionary otherwise add it
+         * @param key the key to get the value from
+         * @param val if there's no such key/value pair in the dictionary add it with this value
+         * @return the value corresponding to the key
+         */
+        public getOrAdd(key: string, val: T): T {
+            var val = this.get(key);
+            if (val !== undefined) {
+                return val;
+            }
+
+            this.add(key, val);
+            return val;
+        }
+
+        /**
+         * Check if there's a given key in the dictionary
+         * @param key the key to check for
+         * @return true if the key is present, false otherwise
+         */
+        public contains(key): boolean {
+            return this._data[key] !== undefined;
+        }
+
+        /**
+         * Add a new key and its corresponding value
+         * @param key the key to add
+         * @param value the value corresponding to the key
+         * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
+         */
+        public add(key: string, value: T): boolean {
+            if (this._data[key] !== undefined) {
+                return false;
+            }
+            this._data[key] = value;
+            ++this._count;
+            return true;
+        }
+
+        /**
+         * Remove a key/value from the dictionary.
+         * @param key the key to remove
+         * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
+         */
+        public remove(key: string): boolean {
+            if (this.contains(key)) {
+                delete this._data[key];
+                --this._count;
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Clear the whole content of the dictionary
+         */
+        public clear() {
+            this._data = {}
+            this._count = 0;
+        }
+
+        public get count() {
+            return this._count;
+        }
+
+        /**
+         * Execute a callback on each key/val of the dictionary.
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute on a given key/value pair
+         */
+        public forEach(callback: (key: string, val: T) => void) {
+            for (let cur in this._data) {
+                var val = this._data[cur];
+                callback(cur, val);
+            }
+        }
+
+        /**
+         * Execute a callback on every occurence of the dictionary until it returns a valid TRes object.
+         * If the callback returns null or undefined the method will iterate to the next key/value pair
+         * Note that you can remove any element in this dictionary in the callback implementation
+         * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
+         */
+        public first<TRes>(callback: (key: string, val: T) => TRes) {
+            for (let cur in this._data) {
+                var val = this._data[cur];
+                var res = callback(cur, val);
+                if (res) {
+                    return res;
+                }
+            }
+            return null;
+        }
+
+        private _count = 0;
+        private _data = {};
+    }
+}

+ 6 - 0
src/Tools/babylon.tools.ts

@@ -698,6 +698,12 @@
             return false;
         }
 
+        public static getClassName(obj): string {
+            var funcNameRegex = /function (.{1,})\(/;
+            var results = (funcNameRegex).exec((obj).constructor.toString());
+            return (results && results.length > 1) ? results[1] : "";
+        }
+
         // Logs
         private static _NoneLogLevel = 0;
         private static _MessageLogLevel = 1;

+ 298 - 296
src/Tools/babylon.virtualJoystick.js

@@ -1,296 +1,298 @@
-// Mainly based on these 2 articles : 
-// Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
-// & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/ 
-var BABYLON;
-(function (BABYLON) {
-    (function (JoystickAxis) {
-        JoystickAxis[JoystickAxis["X"] = 0] = "X";
-        JoystickAxis[JoystickAxis["Y"] = 1] = "Y";
-        JoystickAxis[JoystickAxis["Z"] = 2] = "Z";
-    })(BABYLON.JoystickAxis || (BABYLON.JoystickAxis = {}));
-    var JoystickAxis = BABYLON.JoystickAxis;
-    var VirtualJoystick = (function () {
-        function VirtualJoystick(leftJoystick) {
-            var _this = this;
-            if (leftJoystick) {
-                this._leftJoystick = true;
-            }
-            else {
-                this._leftJoystick = false;
-            }
-            this._joystickIndex = VirtualJoystick._globalJoystickIndex;
-            VirtualJoystick._globalJoystickIndex++;
-            // By default left & right arrow keys are moving the X
-            // and up & down keys are moving the Y
-            this._axisTargetedByLeftAndRight = JoystickAxis.X;
-            this._axisTargetedByUpAndDown = JoystickAxis.Y;
-            this.reverseLeftRight = false;
-            this.reverseUpDown = false;
-            // collections of pointers
-            this._touches = new BABYLON.SmartCollection();
-            this.deltaPosition = BABYLON.Vector3.Zero();
-            this._joystickSensibility = 25;
-            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
-            this._rotationSpeed = 25;
-            this._inverseRotationSpeed = 1 / (this._rotationSpeed / 1000);
-            this._rotateOnAxisRelativeToMesh = false;
-            this._onResize = function (evt) {
-                VirtualJoystick.vjCanvasWidth = window.innerWidth;
-                VirtualJoystick.vjCanvasHeight = window.innerHeight;
-                VirtualJoystick.vjCanvas.width = VirtualJoystick.vjCanvasWidth;
-                VirtualJoystick.vjCanvas.height = VirtualJoystick.vjCanvasHeight;
-                VirtualJoystick.halfWidth = VirtualJoystick.vjCanvasWidth / 2;
-                VirtualJoystick.halfHeight = VirtualJoystick.vjCanvasHeight / 2;
-            };
-            // injecting a canvas element on top of the canvas 3D game
-            if (!VirtualJoystick.vjCanvas) {
-                window.addEventListener("resize", this._onResize, false);
-                VirtualJoystick.vjCanvas = document.createElement("canvas");
-                VirtualJoystick.vjCanvasWidth = window.innerWidth;
-                VirtualJoystick.vjCanvasHeight = window.innerHeight;
-                VirtualJoystick.vjCanvas.width = window.innerWidth;
-                VirtualJoystick.vjCanvas.height = window.innerHeight;
-                VirtualJoystick.vjCanvas.style.width = "100%";
-                VirtualJoystick.vjCanvas.style.height = "100%";
-                VirtualJoystick.vjCanvas.style.position = "absolute";
-                VirtualJoystick.vjCanvas.style.backgroundColor = "transparent";
-                VirtualJoystick.vjCanvas.style.top = "0px";
-                VirtualJoystick.vjCanvas.style.left = "0px";
-                VirtualJoystick.vjCanvas.style.zIndex = "5";
-                VirtualJoystick.vjCanvas.style.msTouchAction = "none";
-                // Support for jQuery PEP polyfill
-                VirtualJoystick.vjCanvas.setAttribute("touch-action", "none");
-                VirtualJoystick.vjCanvasContext = VirtualJoystick.vjCanvas.getContext('2d');
-                VirtualJoystick.vjCanvasContext.strokeStyle = "#ffffff";
-                VirtualJoystick.vjCanvasContext.lineWidth = 2;
-                document.body.appendChild(VirtualJoystick.vjCanvas);
-            }
-            VirtualJoystick.halfWidth = VirtualJoystick.vjCanvas.width / 2;
-            VirtualJoystick.halfHeight = VirtualJoystick.vjCanvas.height / 2;
-            this.pressed = false;
-            // default joystick color
-            this._joystickColor = "cyan";
-            this._joystickPointerID = -1;
-            // current joystick position
-            this._joystickPointerPos = new BABYLON.Vector2(0, 0);
-            this._joystickPreviousPointerPos = new BABYLON.Vector2(0, 0);
-            // origin joystick position
-            this._joystickPointerStartPos = new BABYLON.Vector2(0, 0);
-            this._deltaJoystickVector = new BABYLON.Vector2(0, 0);
-            this._onPointerDownHandlerRef = function (evt) {
-                _this._onPointerDown(evt);
-            };
-            this._onPointerMoveHandlerRef = function (evt) {
-                _this._onPointerMove(evt);
-            };
-            this._onPointerOutHandlerRef = function (evt) {
-                _this._onPointerUp(evt);
-            };
-            this._onPointerUpHandlerRef = function (evt) {
-                _this._onPointerUp(evt);
-            };
-            VirtualJoystick.vjCanvas.addEventListener('pointerdown', this._onPointerDownHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointermove', this._onPointerMoveHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointerup', this._onPointerUpHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener('pointerout', this._onPointerUpHandlerRef, false);
-            VirtualJoystick.vjCanvas.addEventListener("contextmenu", function (evt) {
-                evt.preventDefault(); // Disables system menu
-            }, false);
-            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
-        }
-        VirtualJoystick.prototype.setJoystickSensibility = function (newJoystickSensibility) {
-            this._joystickSensibility = newJoystickSensibility;
-            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
-        };
-        VirtualJoystick.prototype._onPointerDown = function (e) {
-            var positionOnScreenCondition;
-            e.preventDefault();
-            if (this._leftJoystick === true) {
-                positionOnScreenCondition = (e.clientX < VirtualJoystick.halfWidth);
-            }
-            else {
-                positionOnScreenCondition = (e.clientX > VirtualJoystick.halfWidth);
-            }
-            if (positionOnScreenCondition && this._joystickPointerID < 0) {
-                // First contact will be dedicated to the virtual joystick
-                this._joystickPointerID = e.pointerId;
-                this._joystickPointerStartPos.x = e.clientX;
-                this._joystickPointerStartPos.y = e.clientY;
-                this._joystickPointerPos = this._joystickPointerStartPos.clone();
-                this._joystickPreviousPointerPos = this._joystickPointerStartPos.clone();
-                this._deltaJoystickVector.x = 0;
-                this._deltaJoystickVector.y = 0;
-                this.pressed = true;
-                this._touches.add(e.pointerId.toString(), e);
-            }
-            else {
-                // You can only trigger the action buttons with a joystick declared
-                if (VirtualJoystick._globalJoystickIndex < 2 && this._action) {
-                    this._action();
-                    this._touches.add(e.pointerId.toString(), { x: e.clientX, y: e.clientY, prevX: e.clientX, prevY: e.clientY });
-                }
-            }
-        };
-        VirtualJoystick.prototype._onPointerMove = function (e) {
-            // If the current pointer is the one associated to the joystick (first touch contact)
-            if (this._joystickPointerID == e.pointerId) {
-                this._joystickPointerPos.x = e.clientX;
-                this._joystickPointerPos.y = e.clientY;
-                this._deltaJoystickVector = this._joystickPointerPos.clone();
-                this._deltaJoystickVector = this._deltaJoystickVector.subtract(this._joystickPointerStartPos);
-                var directionLeftRight = this.reverseLeftRight ? -1 : 1;
-                var deltaJoystickX = directionLeftRight * this._deltaJoystickVector.x / this._inversedSensibility;
-                switch (this._axisTargetedByLeftAndRight) {
-                    case JoystickAxis.X:
-                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                    case JoystickAxis.Y:
-                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                    case JoystickAxis.Z:
-                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickX));
-                        break;
-                }
-                var directionUpDown = this.reverseUpDown ? 1 : -1;
-                var deltaJoystickY = directionUpDown * this._deltaJoystickVector.y / this._inversedSensibility;
-                switch (this._axisTargetedByUpAndDown) {
-                    case JoystickAxis.X:
-                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                    case JoystickAxis.Y:
-                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                    case JoystickAxis.Z:
-                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickY));
-                        break;
-                }
-            }
-            else {
-                if (this._touches.item(e.pointerId.toString())) {
-                    this._touches.item(e.pointerId.toString()).x = e.clientX;
-                    this._touches.item(e.pointerId.toString()).y = e.clientY;
-                }
-            }
-        };
-        VirtualJoystick.prototype._onPointerUp = function (e) {
-            if (this._joystickPointerID == e.pointerId) {
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);
-                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
-                this._joystickPointerID = -1;
-                this.pressed = false;
-            }
-            else {
-                var touch = this._touches.item(e.pointerId.toString());
-                if (touch) {
-                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
-                }
-            }
-            this._deltaJoystickVector.x = 0;
-            this._deltaJoystickVector.y = 0;
-            this._touches.remove(e.pointerId.toString());
-        };
-        /**
-        * Change the color of the virtual joystick
-        * @param newColor a string that must be a CSS color value (like "red") or the hexa value (like "#FF0000")
-        */
-        VirtualJoystick.prototype.setJoystickColor = function (newColor) {
-            this._joystickColor = newColor;
-        };
-        VirtualJoystick.prototype.setActionOnTouch = function (action) {
-            this._action = action;
-        };
-        // Define which axis you'd like to control for left & right 
-        VirtualJoystick.prototype.setAxisForLeftRight = function (axis) {
-            switch (axis) {
-                case JoystickAxis.X:
-                case JoystickAxis.Y:
-                case JoystickAxis.Z:
-                    this._axisTargetedByLeftAndRight = axis;
-                    break;
-                default:
-                    this._axisTargetedByLeftAndRight = JoystickAxis.X;
-                    break;
-            }
-        };
-        // Define which axis you'd like to control for up & down 
-        VirtualJoystick.prototype.setAxisForUpDown = function (axis) {
-            switch (axis) {
-                case JoystickAxis.X:
-                case JoystickAxis.Y:
-                case JoystickAxis.Z:
-                    this._axisTargetedByUpAndDown = axis;
-                    break;
-                default:
-                    this._axisTargetedByUpAndDown = JoystickAxis.Y;
-                    break;
-            }
-        };
-        VirtualJoystick.prototype._clearCanvas = function () {
-            if (this._leftJoystick) {
-                VirtualJoystick.vjCanvasContext.clearRect(0, 0, VirtualJoystick.vjCanvasWidth / 2, VirtualJoystick.vjCanvasHeight);
-            }
-            else {
-                VirtualJoystick.vjCanvasContext.clearRect(VirtualJoystick.vjCanvasWidth / 2, 0, VirtualJoystick.vjCanvasWidth, VirtualJoystick.vjCanvasHeight);
-            }
-        };
-        VirtualJoystick.prototype._drawVirtualJoystick = function () {
-            var _this = this;
-            if (this.pressed) {
-                this._touches.forEach(function (touch) {
-                    if (touch.pointerId === _this._joystickPointerID) {
-                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPointerStartPos.x - 63, _this._joystickPointerStartPos.y - 63, 126, 126);
-                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPreviousPointerPos.x - 41, _this._joystickPreviousPointerPos.y - 41, 82, 82);
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.lineWidth = 2;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 60, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
-                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerPos.x, _this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        _this._joystickPreviousPointerPos = _this._joystickPointerPos.clone();
-                    }
-                    else {
-                        VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.fillStyle = "white";
-                        VirtualJoystick.vjCanvasContext.beginPath();
-                        VirtualJoystick.vjCanvasContext.strokeStyle = "red";
-                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
-                        VirtualJoystick.vjCanvasContext.arc(touch.x, touch.y, 40, 0, Math.PI * 2, true);
-                        VirtualJoystick.vjCanvasContext.stroke();
-                        VirtualJoystick.vjCanvasContext.closePath();
-                        touch.prevX = touch.x;
-                        touch.prevY = touch.y;
-                    }
-                    ;
-                });
-            }
-            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
-        };
-        VirtualJoystick.prototype.releaseCanvas = function () {
-            if (VirtualJoystick.vjCanvas) {
-                VirtualJoystick.vjCanvas.removeEventListener('pointerdown', this._onPointerDownHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointermove', this._onPointerMoveHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointerup', this._onPointerUpHandlerRef);
-                VirtualJoystick.vjCanvas.removeEventListener('pointerout', this._onPointerUpHandlerRef);
-                window.removeEventListener("resize", this._onResize);
-                document.body.removeChild(VirtualJoystick.vjCanvas);
-                VirtualJoystick.vjCanvas = null;
-            }
-        };
-        // Used to draw the virtual joystick inside a 2D canvas on top of the WebGL rendering canvas
-        VirtualJoystick._globalJoystickIndex = 0;
-        return VirtualJoystick;
-    })();
-    BABYLON.VirtualJoystick = VirtualJoystick;
-})(BABYLON || (BABYLON = {}));
+// Mainly based on these 2 articles : 
+// Creating an universal virtual touch joystick working for all Touch models thanks to Hand.JS : http://blogs.msdn.com/b/davrous/archive/2013/02/22/creating-an-universal-virtual-touch-joystick-working-for-all-touch-models-thanks-to-hand-js.aspx
+// & on Seb Lee-Delisle original work: http://seb.ly/2011/04/multi-touch-game-controller-in-javascripthtml5-for-ipad/ 
+var BABYLON;
+(function (BABYLON) {
+    (function (JoystickAxis) {
+        JoystickAxis[JoystickAxis["X"] = 0] = "X";
+        JoystickAxis[JoystickAxis["Y"] = 1] = "Y";
+        JoystickAxis[JoystickAxis["Z"] = 2] = "Z";
+    })(BABYLON.JoystickAxis || (BABYLON.JoystickAxis = {}));
+    var JoystickAxis = BABYLON.JoystickAxis;
+    var VirtualJoystick = (function () {
+        function VirtualJoystick(leftJoystick) {
+            var _this = this;
+            if (leftJoystick) {
+                this._leftJoystick = true;
+            }
+            else {
+                this._leftJoystick = false;
+            }
+            this._joystickIndex = VirtualJoystick._globalJoystickIndex;
+            VirtualJoystick._globalJoystickIndex++;
+            // By default left & right arrow keys are moving the X
+            // and up & down keys are moving the Y
+            this._axisTargetedByLeftAndRight = JoystickAxis.X;
+            this._axisTargetedByUpAndDown = JoystickAxis.Y;
+            this.reverseLeftRight = false;
+            this.reverseUpDown = false;
+            // collections of pointers
+            this._touches = new BABYLON.StringDictionary();
+            this.deltaPosition = BABYLON.Vector3.Zero();
+            this._joystickSensibility = 25;
+            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
+            this._rotationSpeed = 25;
+            this._inverseRotationSpeed = 1 / (this._rotationSpeed / 1000);
+            this._rotateOnAxisRelativeToMesh = false;
+            this._onResize = function (evt) {
+                VirtualJoystick.vjCanvasWidth = window.innerWidth;
+                VirtualJoystick.vjCanvasHeight = window.innerHeight;
+                VirtualJoystick.vjCanvas.width = VirtualJoystick.vjCanvasWidth;
+                VirtualJoystick.vjCanvas.height = VirtualJoystick.vjCanvasHeight;
+                VirtualJoystick.halfWidth = VirtualJoystick.vjCanvasWidth / 2;
+                VirtualJoystick.halfHeight = VirtualJoystick.vjCanvasHeight / 2;
+            };
+            // injecting a canvas element on top of the canvas 3D game
+            if (!VirtualJoystick.vjCanvas) {
+                window.addEventListener("resize", this._onResize, false);
+                VirtualJoystick.vjCanvas = document.createElement("canvas");
+                VirtualJoystick.vjCanvasWidth = window.innerWidth;
+                VirtualJoystick.vjCanvasHeight = window.innerHeight;
+                VirtualJoystick.vjCanvas.width = window.innerWidth;
+                VirtualJoystick.vjCanvas.height = window.innerHeight;
+                VirtualJoystick.vjCanvas.style.width = "100%";
+                VirtualJoystick.vjCanvas.style.height = "100%";
+                VirtualJoystick.vjCanvas.style.position = "absolute";
+                VirtualJoystick.vjCanvas.style.backgroundColor = "transparent";
+                VirtualJoystick.vjCanvas.style.top = "0px";
+                VirtualJoystick.vjCanvas.style.left = "0px";
+                VirtualJoystick.vjCanvas.style.zIndex = "5";
+                VirtualJoystick.vjCanvas.style.msTouchAction = "none";
+                // Support for jQuery PEP polyfill
+                VirtualJoystick.vjCanvas.setAttribute("touch-action", "none");
+                VirtualJoystick.vjCanvasContext = VirtualJoystick.vjCanvas.getContext('2d');
+                VirtualJoystick.vjCanvasContext.strokeStyle = "#ffffff";
+                VirtualJoystick.vjCanvasContext.lineWidth = 2;
+                document.body.appendChild(VirtualJoystick.vjCanvas);
+            }
+            VirtualJoystick.halfWidth = VirtualJoystick.vjCanvas.width / 2;
+            VirtualJoystick.halfHeight = VirtualJoystick.vjCanvas.height / 2;
+            this.pressed = false;
+            // default joystick color
+            this._joystickColor = "cyan";
+            this._joystickPointerID = -1;
+            // current joystick position
+            this._joystickPointerPos = new BABYLON.Vector2(0, 0);
+            this._joystickPreviousPointerPos = new BABYLON.Vector2(0, 0);
+            // origin joystick position
+            this._joystickPointerStartPos = new BABYLON.Vector2(0, 0);
+            this._deltaJoystickVector = new BABYLON.Vector2(0, 0);
+            this._onPointerDownHandlerRef = function (evt) {
+                _this._onPointerDown(evt);
+            };
+            this._onPointerMoveHandlerRef = function (evt) {
+                _this._onPointerMove(evt);
+            };
+            this._onPointerOutHandlerRef = function (evt) {
+                _this._onPointerUp(evt);
+            };
+            this._onPointerUpHandlerRef = function (evt) {
+                _this._onPointerUp(evt);
+            };
+            VirtualJoystick.vjCanvas.addEventListener('pointerdown', this._onPointerDownHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointermove', this._onPointerMoveHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointerup', this._onPointerUpHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener('pointerout', this._onPointerUpHandlerRef, false);
+            VirtualJoystick.vjCanvas.addEventListener("contextmenu", function (evt) {
+                evt.preventDefault(); // Disables system menu
+            }, false);
+            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
+        }
+        VirtualJoystick.prototype.setJoystickSensibility = function (newJoystickSensibility) {
+            this._joystickSensibility = newJoystickSensibility;
+            this._inversedSensibility = 1 / (this._joystickSensibility / 1000);
+        };
+        VirtualJoystick.prototype._onPointerDown = function (e) {
+            var positionOnScreenCondition;
+            e.preventDefault();
+            if (this._leftJoystick === true) {
+                positionOnScreenCondition = (e.clientX < VirtualJoystick.halfWidth);
+            }
+            else {
+                positionOnScreenCondition = (e.clientX > VirtualJoystick.halfWidth);
+            }
+            if (positionOnScreenCondition && this._joystickPointerID < 0) {
+                // First contact will be dedicated to the virtual joystick
+                this._joystickPointerID = e.pointerId;
+                this._joystickPointerStartPos.x = e.clientX;
+                this._joystickPointerStartPos.y = e.clientY;
+                this._joystickPointerPos = this._joystickPointerStartPos.clone();
+                this._joystickPreviousPointerPos = this._joystickPointerStartPos.clone();
+                this._deltaJoystickVector.x = 0;
+                this._deltaJoystickVector.y = 0;
+                this.pressed = true;
+                this._touches.add(e.pointerId.toString(), e);
+            }
+            else {
+                // You can only trigger the action buttons with a joystick declared
+                if (VirtualJoystick._globalJoystickIndex < 2 && this._action) {
+                    this._action();
+                    this._touches.add(e.pointerId.toString(), { x: e.clientX, y: e.clientY, prevX: e.clientX, prevY: e.clientY });
+                }
+            }
+        };
+        VirtualJoystick.prototype._onPointerMove = function (e) {
+            // If the current pointer is the one associated to the joystick (first touch contact)
+            if (this._joystickPointerID == e.pointerId) {
+                this._joystickPointerPos.x = e.clientX;
+                this._joystickPointerPos.y = e.clientY;
+                this._deltaJoystickVector = this._joystickPointerPos.clone();
+                this._deltaJoystickVector = this._deltaJoystickVector.subtract(this._joystickPointerStartPos);
+                var directionLeftRight = this.reverseLeftRight ? -1 : 1;
+                var deltaJoystickX = directionLeftRight * this._deltaJoystickVector.x / this._inversedSensibility;
+                switch (this._axisTargetedByLeftAndRight) {
+                    case JoystickAxis.X:
+                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                    case JoystickAxis.Y:
+                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                    case JoystickAxis.Z:
+                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickX));
+                        break;
+                }
+                var directionUpDown = this.reverseUpDown ? 1 : -1;
+                var deltaJoystickY = directionUpDown * this._deltaJoystickVector.y / this._inversedSensibility;
+                switch (this._axisTargetedByUpAndDown) {
+                    case JoystickAxis.X:
+                        this.deltaPosition.x = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                    case JoystickAxis.Y:
+                        this.deltaPosition.y = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                    case JoystickAxis.Z:
+                        this.deltaPosition.z = Math.min(1, Math.max(-1, deltaJoystickY));
+                        break;
+                }
+            }
+            else {
+                var data = this._touches.get(e.pointerId.toString());
+                if (data) {
+                    data.x = e.clientX;
+                    data.y = e.clientY;
+                }
+            }
+        };
+        VirtualJoystick.prototype._onPointerUp = function (e) {
+            if (this._joystickPointerID == e.pointerId) {
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPointerStartPos.x - 63, this._joystickPointerStartPos.y - 63, 126, 126);
+                VirtualJoystick.vjCanvasContext.clearRect(this._joystickPreviousPointerPos.x - 41, this._joystickPreviousPointerPos.y - 41, 82, 82);
+                this._joystickPointerID = -1;
+                this.pressed = false;
+            }
+            else {
+                var touch = this._touches.get(e.pointerId.toString());
+                if (touch) {
+                    VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                }
+            }
+            this._deltaJoystickVector.x = 0;
+            this._deltaJoystickVector.y = 0;
+            this._touches.remove(e.pointerId.toString());
+        };
+        /**
+        * Change the color of the virtual joystick
+        * @param newColor a string that must be a CSS color value (like "red") or the hexa value (like "#FF0000")
+        */
+        VirtualJoystick.prototype.setJoystickColor = function (newColor) {
+            this._joystickColor = newColor;
+        };
+        VirtualJoystick.prototype.setActionOnTouch = function (action) {
+            this._action = action;
+        };
+        // Define which axis you'd like to control for left & right 
+        VirtualJoystick.prototype.setAxisForLeftRight = function (axis) {
+            switch (axis) {
+                case JoystickAxis.X:
+                case JoystickAxis.Y:
+                case JoystickAxis.Z:
+                    this._axisTargetedByLeftAndRight = axis;
+                    break;
+                default:
+                    this._axisTargetedByLeftAndRight = JoystickAxis.X;
+                    break;
+            }
+        };
+        // Define which axis you'd like to control for up & down 
+        VirtualJoystick.prototype.setAxisForUpDown = function (axis) {
+            switch (axis) {
+                case JoystickAxis.X:
+                case JoystickAxis.Y:
+                case JoystickAxis.Z:
+                    this._axisTargetedByUpAndDown = axis;
+                    break;
+                default:
+                    this._axisTargetedByUpAndDown = JoystickAxis.Y;
+                    break;
+            }
+        };
+        VirtualJoystick.prototype._clearCanvas = function () {
+            if (this._leftJoystick) {
+                VirtualJoystick.vjCanvasContext.clearRect(0, 0, VirtualJoystick.vjCanvasWidth / 2, VirtualJoystick.vjCanvasHeight);
+            }
+            else {
+                VirtualJoystick.vjCanvasContext.clearRect(VirtualJoystick.vjCanvasWidth / 2, 0, VirtualJoystick.vjCanvasWidth, VirtualJoystick.vjCanvasHeight);
+            }
+        };
+        VirtualJoystick.prototype._drawVirtualJoystick = function () {
+            var _this = this;
+            if (this.pressed) {
+                this._touches.forEach(function (touch) {
+                    if (touch.pointerId === _this._joystickPointerID) {
+                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPointerStartPos.x - 63, _this._joystickPointerStartPos.y - 63, 126, 126);
+                        VirtualJoystick.vjCanvasContext.clearRect(_this._joystickPreviousPointerPos.x - 41, _this._joystickPreviousPointerPos.y - 41, 82, 82);
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.lineWidth = 2;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerStartPos.x, _this._joystickPointerStartPos.y, 60, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = _this._joystickColor;
+                        VirtualJoystick.vjCanvasContext.arc(_this._joystickPointerPos.x, _this._joystickPointerPos.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        _this._joystickPreviousPointerPos = _this._joystickPointerPos.clone();
+                    }
+                    else {
+                        VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.fillStyle = "white";
+                        VirtualJoystick.vjCanvasContext.beginPath();
+                        VirtualJoystick.vjCanvasContext.strokeStyle = "red";
+                        VirtualJoystick.vjCanvasContext.lineWidth = 6;
+                        VirtualJoystick.vjCanvasContext.arc(touch.x, touch.y, 40, 0, Math.PI * 2, true);
+                        VirtualJoystick.vjCanvasContext.stroke();
+                        VirtualJoystick.vjCanvasContext.closePath();
+                        touch.prevX = touch.x;
+                        touch.prevY = touch.y;
+                    }
+                    ;
+                });
+            }
+            requestAnimationFrame(function () { _this._drawVirtualJoystick(); });
+        };
+        VirtualJoystick.prototype.releaseCanvas = function () {
+            if (VirtualJoystick.vjCanvas) {
+                VirtualJoystick.vjCanvas.removeEventListener('pointerdown', this._onPointerDownHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointermove', this._onPointerMoveHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointerup', this._onPointerUpHandlerRef);
+                VirtualJoystick.vjCanvas.removeEventListener('pointerout', this._onPointerUpHandlerRef);
+                window.removeEventListener("resize", this._onResize);
+                document.body.removeChild(VirtualJoystick.vjCanvas);
+                VirtualJoystick.vjCanvas = null;
+            }
+        };
+        // Used to draw the virtual joystick inside a 2D canvas on top of the WebGL rendering canvas
+        VirtualJoystick._globalJoystickIndex = 0;
+        return VirtualJoystick;
+    }());
+    BABYLON.VirtualJoystick = VirtualJoystick;
+})(BABYLON || (BABYLON = {}));
+//# sourceMappingURL=babylon.virtualJoystick.js.map

+ 7 - 6
src/Tools/babylon.virtualJoystick.ts

@@ -40,7 +40,7 @@ module BABYLON {
         private _deltaJoystickVector: Vector2;
         private _leftJoystick: boolean;
         private _joystickIndex: number;
-        private _touches: SmartCollection;
+        private _touches: StringDictionary<{ x: number, y: number, prevX: number, prevY: number } | PointerEvent>;
 
         private _onPointerDownHandlerRef: (e: PointerEvent) => any;
         private _onPointerMoveHandlerRef: (e: PointerEvent) => any;
@@ -68,7 +68,7 @@ module BABYLON {
             this.reverseUpDown = false;
 
             // collections of pointers
-            this._touches = new SmartCollection();
+            this._touches = new StringDictionary<{ x: number, y: number, prevX: number, prevY: number } | PointerEvent>();
             this.deltaPosition = Vector3.Zero();
 
             this._joystickSensibility = 25;
@@ -220,9 +220,10 @@ module BABYLON {
                 }
             }
             else {
-                if (this._touches.item(e.pointerId.toString())) {
-                    this._touches.item(e.pointerId.toString()).x = e.clientX;
-                    this._touches.item(e.pointerId.toString()).y = e.clientY;                     
+                let data = this._touches.get(e.pointerId.toString());
+                if (data) {
+                    data.x = e.clientX;
+                    data.y = e.clientY;                     
                 }
             }
         }
@@ -235,7 +236,7 @@ module BABYLON {
                 this.pressed = false;
             }
             else {
-                var touch = this._touches.item(e.pointerId.toString());
+                var touch = <{ x: number, y: number, prevX: number, prevY: number }>this._touches.get(e.pointerId.toString());
                 if (touch) {
                     VirtualJoystick.vjCanvasContext.clearRect(touch.prevX - 43, touch.prevY - 43, 86, 86);
                 }