Selaa lähdekoodia

composable camera

gleborgne 9 vuotta sitten
vanhempi
commit
c850b65e2f

+ 71 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.deviceorientation.js

@@ -0,0 +1,71 @@
+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 ComposableCameraDeviceOrientationInput = (function () {
+        function ComposableCameraDeviceOrientationInput() {
+            this._offsetX = null;
+            this._offsetY = null;
+            this._orientationGamma = 0;
+            this._orientationBeta = 0;
+            this._initialOrientationGamma = 0;
+            this._initialOrientationBeta = 0;
+            this.angularSensibility = 10000.0;
+            this.moveSensibility = 50.0;
+            this._resetOrientationGamma = this.resetOrientationGamma.bind(this);
+            this._orientationChanged = this.orientationChanged.bind(this);
+        }
+        ComposableCameraDeviceOrientationInput.prototype.attachCamera = function (camera) {
+            this.camera = camera;
+            window.addEventListener("resize", this._resetOrientationGamma, false);
+            window.addEventListener("deviceorientation", this._orientationChanged);
+        };
+        ComposableCameraDeviceOrientationInput.prototype.resetOrientationGamma = function () {
+            this._initialOrientationGamma = null;
+        };
+        ComposableCameraDeviceOrientationInput.prototype.orientationChanged = function (evt) {
+            if (!this._initialOrientationGamma) {
+                this._initialOrientationGamma = evt.gamma;
+                this._initialOrientationBeta = evt.beta;
+            }
+            this._orientationGamma = evt.gamma;
+            this._orientationBeta = evt.beta;
+            this._offsetY = (this._initialOrientationBeta - this._orientationBeta);
+            this._offsetX = (this._initialOrientationGamma - this._orientationGamma);
+        };
+        ComposableCameraDeviceOrientationInput.prototype.detach = function () {
+            window.removeEventListener("resize", this._resetOrientationGamma);
+            window.removeEventListener("deviceorientation", this._orientationChanged);
+            this._orientationGamma = 0;
+            this._orientationBeta = 0;
+            this._initialOrientationGamma = 0;
+            this._initialOrientationBeta = 0;
+        };
+        ComposableCameraDeviceOrientationInput.prototype.checkInputs = function () {
+            if (!this._offsetX) {
+                return;
+            }
+            var camera = this.camera;
+            camera.cameraRotation.y -= this._offsetX / this.angularSensibility;
+            var speed = camera._computeLocalCameraSpeed();
+            var direction = new BABYLON.Vector3(0, 0, speed * this._offsetY / this.moveSensibility);
+            BABYLON.Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
+            camera.cameraDirection.addInPlace(BABYLON.Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+        };
+        ComposableCameraDeviceOrientationInput.prototype.getTypeName = function () {
+            return "deviceorientation";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraDeviceOrientationInput.prototype, "angularSensibility", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraDeviceOrientationInput.prototype, "moveSensibility", void 0);
+        return ComposableCameraDeviceOrientationInput;
+    }());
+    BABYLON.ComposableCameraDeviceOrientationInput = ComposableCameraDeviceOrientationInput;
+})(BABYLON || (BABYLON = {}));

+ 78 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.deviceorientation.ts

@@ -0,0 +1,78 @@
+module BABYLON {
+    export class ComposableCameraDeviceOrientationInput implements IComposableCameraInput {
+        camera: ComposableCamera;
+
+        private _offsetX: number = null;
+        private _offsetY: number = null;
+        private _orientationGamma: number = 0;
+        private _orientationBeta: number = 0;
+        private _initialOrientationGamma: number = 0;
+        private _initialOrientationBeta: number = 0;
+        private _orientationChanged: (e: DeviceOrientationEvent) => any;
+        private _resetOrientationGamma: () => any;
+
+        @serialize()
+        public angularSensibility: number = 10000.0;
+
+        @serialize()
+        public moveSensibility: number = 50.0;
+
+        constructor() {
+            this._resetOrientationGamma = this.resetOrientationGamma.bind(this);
+            this._orientationChanged = this.orientationChanged.bind(this);
+        }
+
+        attachCamera(camera: ComposableCamera) {
+            this.camera = camera;
+
+            window.addEventListener("resize", this._resetOrientationGamma, false);
+            window.addEventListener("deviceorientation", this._orientationChanged);
+        }
+
+        resetOrientationGamma() {
+            this._initialOrientationGamma = null;
+        }
+
+        orientationChanged(evt) {
+            if (!this._initialOrientationGamma) {
+                this._initialOrientationGamma = evt.gamma;
+                this._initialOrientationBeta = evt.beta;
+            }
+
+            this._orientationGamma = evt.gamma;
+            this._orientationBeta = evt.beta;
+
+            this._offsetY = (this._initialOrientationBeta - this._orientationBeta);
+            this._offsetX = (this._initialOrientationGamma - this._orientationGamma);
+        }
+
+        detach() {
+            window.removeEventListener("resize", this._resetOrientationGamma);
+            window.removeEventListener("deviceorientation", this._orientationChanged);
+            
+            this._orientationGamma = 0;
+            this._orientationBeta = 0;
+            this._initialOrientationGamma = 0;
+            this._initialOrientationBeta = 0;
+        }
+
+        public checkInputs() {
+            if (!this._offsetX) {
+                return;
+            }
+            
+            var camera = this.camera;
+            camera.cameraRotation.y -= this._offsetX / this.angularSensibility;
+
+            var speed = camera._computeLocalCameraSpeed();
+            var direction = new Vector3(0, 0, speed * this._offsetY / this.moveSensibility);
+
+            Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
+            camera.cameraDirection.addInPlace(Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+        }
+
+        getTypeName(): string {
+            return "deviceorientation";
+        }
+    }
+}

+ 59 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.gamepad.js

@@ -0,0 +1,59 @@
+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 ComposableCameraGamepadInput = (function () {
+        function ComposableCameraGamepadInput() {
+            var _this = this;
+            this.gamepadAngularSensibility = 200;
+            this.gamepadMoveSensibility = 40;
+            this._gamepads = new BABYLON.Gamepads(function (gamepad) { _this._onNewGameConnected(gamepad); });
+        }
+        ComposableCameraGamepadInput.prototype.attachCamera = function (camera) {
+            this.camera = camera;
+        };
+        ComposableCameraGamepadInput.prototype.detach = function () {
+        };
+        ComposableCameraGamepadInput.prototype.checkInputs = function () {
+            if (this.gamepad) {
+                var camera = this.camera;
+                var LSValues = this.gamepad.leftStick;
+                var normalizedLX = LSValues.x / this.gamepadMoveSensibility;
+                var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
+                LSValues.x = Math.abs(normalizedLX) > 0.005 ? 0 + normalizedLX : 0;
+                LSValues.y = Math.abs(normalizedLY) > 0.005 ? 0 + normalizedLY : 0;
+                var RSValues = this.gamepad.rightStick;
+                var normalizedRX = RSValues.x / this.gamepadAngularSensibility;
+                var normalizedRY = RSValues.y / this.gamepadAngularSensibility;
+                RSValues.x = Math.abs(normalizedRX) > 0.001 ? 0 + normalizedRX : 0;
+                RSValues.y = Math.abs(normalizedRY) > 0.001 ? 0 + normalizedRY : 0;
+                var cameraTransform = BABYLON.Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
+                var speed = camera._computeLocalCameraSpeed() * 50.0;
+                var deltaTransform = BABYLON.Vector3.TransformCoordinates(new BABYLON.Vector3(LSValues.x * speed, 0, -LSValues.y * speed), cameraTransform);
+                camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
+                camera.cameraRotation = camera.cameraRotation.add(new BABYLON.Vector2(RSValues.y, RSValues.x));
+            }
+        };
+        ComposableCameraGamepadInput.prototype._onNewGameConnected = function (gamepad) {
+            // Only the first gamepad can control the camera
+            if (gamepad.index === 0) {
+                this.gamepad = gamepad;
+            }
+        };
+        ComposableCameraGamepadInput.prototype.getTypeName = function () {
+            return "gamepad";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraGamepadInput.prototype, "gamepadAngularSensibility", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraGamepadInput.prototype, "gamepadMoveSensibility", void 0);
+        return ComposableCameraGamepadInput;
+    }());
+    BABYLON.ComposableCameraGamepadInput = ComposableCameraGamepadInput;
+})(BABYLON || (BABYLON = {}));

+ 61 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.gamepad.ts

@@ -0,0 +1,61 @@
+module BABYLON {       
+    export class ComposableCameraGamepadInput implements IComposableCameraInput {
+        camera : ComposableCamera;
+        
+        public gamepad: Gamepad;
+        private _gamepads: Gamepads;
+
+        @serialize()
+        public gamepadAngularSensibility = 200;
+
+        @serialize()
+        public gamepadMoveSensibility = 40;
+        
+        constructor(){
+            this._gamepads = new Gamepads((gamepad: Gamepad) => { this._onNewGameConnected(gamepad); });
+        }
+        
+        attachCamera(camera : ComposableCamera){
+            this.camera = camera;
+        }
+        
+        detach(){
+            
+        }
+        
+        checkInputs(){
+            if (this.gamepad) {
+                var camera = this.camera;
+                var LSValues = this.gamepad.leftStick;
+                var normalizedLX = LSValues.x / this.gamepadMoveSensibility;
+                var normalizedLY = LSValues.y / this.gamepadMoveSensibility;
+                LSValues.x = Math.abs(normalizedLX) > 0.005 ? 0 + normalizedLX : 0;
+                LSValues.y = Math.abs(normalizedLY) > 0.005 ? 0 + normalizedLY : 0;
+
+                var RSValues = this.gamepad.rightStick;
+                var normalizedRX = RSValues.x / this.gamepadAngularSensibility;
+                var normalizedRY = RSValues.y / this.gamepadAngularSensibility;
+                RSValues.x = Math.abs(normalizedRX) > 0.001 ? 0 + normalizedRX : 0;
+                RSValues.y = Math.abs(normalizedRY) > 0.001 ? 0 + normalizedRY : 0;
+
+                var cameraTransform = Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
+
+                var speed = camera._computeLocalCameraSpeed() * 50.0;
+                var deltaTransform = Vector3.TransformCoordinates(new Vector3(LSValues.x * speed, 0, -LSValues.y * speed), cameraTransform);
+                camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
+                camera.cameraRotation = camera.cameraRotation.add(new Vector2(RSValues.y, RSValues.x));
+            }
+        }
+        
+        private _onNewGameConnected(gamepad: Gamepad) {
+            // Only the first gamepad can control the camera
+            if (gamepad.index === 0) {
+                this.gamepad = gamepad;
+            }
+        }
+        
+        getTypeName(): string{
+            return "gamepad";
+        }
+    }
+}

+ 107 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.keyboard.js

@@ -0,0 +1,107 @@
+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 ComposableCameraKeyboardMoveInput = (function () {
+        function ComposableCameraKeyboardMoveInput() {
+            this._keys = [];
+            this.keysUp = [38];
+            this.keysDown = [40];
+            this.keysLeft = [37];
+            this.keysRight = [39];
+        }
+        ComposableCameraKeyboardMoveInput.prototype.attachCamera = function (camera) {
+            var _this = this;
+            this.camera = camera;
+            if (this._onKeyDown === undefined) {
+                this._onKeyDown = function (evt) {
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index === -1) {
+                            _this._keys.push(evt.keyCode);
+                        }
+                        if (!camera._noPreventDefault) {
+                            evt.preventDefault();
+                        }
+                    }
+                };
+                this._onKeyUp = function (evt) {
+                    if (_this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        _this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = _this._keys.indexOf(evt.keyCode);
+                        if (index >= 0) {
+                            _this._keys.splice(index, 1);
+                        }
+                        if (!camera._noPreventDefault) {
+                            evt.preventDefault();
+                        }
+                    }
+                };
+                BABYLON.Tools.RegisterTopRootEvents([
+                    { name: "keydown", handler: this._onKeyDown },
+                    { name: "keyup", handler: this._onKeyUp },
+                    { name: "blur", handler: this._onLostFocus }
+                ]);
+            }
+        };
+        ComposableCameraKeyboardMoveInput.prototype.detach = function () {
+            BABYLON.Tools.UnregisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        };
+        ComposableCameraKeyboardMoveInput.prototype.checkInputs = function () {
+            var camera = this.camera;
+            // Keyboard
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                var speed = camera._computeLocalCameraSpeed();
+                if (this.keysLeft.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(-speed, 0, 0);
+                }
+                else if (this.keysUp.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, speed);
+                }
+                else if (this.keysRight.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(speed, 0, 0);
+                }
+                else if (this.keysDown.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, -speed);
+                }
+                camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
+                BABYLON.Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
+                camera.cameraDirection.addInPlace(camera._transformedDirection);
+            }
+        };
+        ComposableCameraKeyboardMoveInput.prototype.getTypeName = function () {
+            return "keyboardmove";
+        };
+        ComposableCameraKeyboardMoveInput.prototype._onLostFocus = function (e) {
+            this._keys = [];
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraKeyboardMoveInput.prototype, "keysUp", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraKeyboardMoveInput.prototype, "keysDown", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraKeyboardMoveInput.prototype, "keysLeft", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraKeyboardMoveInput.prototype, "keysRight", void 0);
+        return ComposableCameraKeyboardMoveInput;
+    }());
+    BABYLON.ComposableCameraKeyboardMoveInput = ComposableCameraKeyboardMoveInput;
+})(BABYLON || (BABYLON = {}));

+ 104 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.keyboard.ts

@@ -0,0 +1,104 @@
+module BABYLON {
+    export class ComposableCameraKeyboardMoveInput implements IComposableCameraInput {
+        camera: ComposableCamera;
+        private _keys = [];
+        private _onKeyDown: (e: KeyboardEvent) => any;
+        private _onKeyUp: (e: KeyboardEvent) => any;
+
+        @serialize()
+        public keysUp = [38];
+
+        @serialize()
+        public keysDown = [40];
+
+        @serialize()
+        public keysLeft = [37];
+
+        @serialize()
+        public keysRight = [39];
+
+        attachCamera(camera: ComposableCamera) {
+            this.camera = camera;
+            
+            if (this._onKeyDown === undefined) {
+
+                this._onKeyDown = evt => {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index === -1) {
+                            this._keys.push(evt.keyCode);
+                        }
+                        if (!camera._noPreventDefault) {
+                            evt.preventDefault();
+                        }
+                    }
+                };
+
+                this._onKeyUp = evt => {
+                    if (this.keysUp.indexOf(evt.keyCode) !== -1 ||
+                        this.keysDown.indexOf(evt.keyCode) !== -1 ||
+                        this.keysLeft.indexOf(evt.keyCode) !== -1 ||
+                        this.keysRight.indexOf(evt.keyCode) !== -1) {
+                        var index = this._keys.indexOf(evt.keyCode);
+
+                        if (index >= 0) {
+                            this._keys.splice(index, 1);
+                        }
+                        if (!camera._noPreventDefault) {
+                            evt.preventDefault();
+                        }
+                    }
+                };
+
+                Tools.RegisterTopRootEvents([
+                    { name: "keydown", handler: this._onKeyDown },
+                    { name: "keyup", handler: this._onKeyUp },
+                    { name: "blur", handler: this._onLostFocus }
+                ]);
+            }
+        }
+
+        detach() {
+            Tools.UnregisterTopRootEvents([
+                { name: "keydown", handler: this._onKeyDown },
+                { name: "keyup", handler: this._onKeyUp },
+                { name: "blur", handler: this._onLostFocus }
+            ]);
+        }
+        
+        public checkInputs() {
+            var camera = this.camera;
+            // Keyboard
+            for (var index = 0; index < this._keys.length; index++) {
+                var keyCode = this._keys[index];
+                var speed = camera._computeLocalCameraSpeed();
+
+                if (this.keysLeft.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(-speed, 0, 0);
+                } else if (this.keysUp.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, speed);
+                } else if (this.keysRight.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(speed, 0, 0);
+                } else if (this.keysDown.indexOf(keyCode) !== -1) {
+                    camera._localDirection.copyFromFloats(0, 0, -speed);
+                }
+
+                camera.getViewMatrix().invertToRef(camera._cameraTransformMatrix);
+                Vector3.TransformNormalToRef(camera._localDirection, camera._cameraTransformMatrix, camera._transformedDirection);
+                camera.cameraDirection.addInPlace(camera._transformedDirection);
+            }
+        }
+
+        getTypeName(): string {
+            return "keyboardmove";
+        }
+
+        public _onLostFocus(e: FocusEvent): void {
+            this._keys = [];
+        }
+    }
+}

+ 98 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.mouse.js

@@ -0,0 +1,98 @@
+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 ComposableCameraMouseInput = (function () {
+        function ComposableCameraMouseInput() {
+            this.angularSensibility = 2000.0;
+        }
+        ComposableCameraMouseInput.prototype.attachCamera = function (camera) {
+            this.camera = camera;
+        };
+        ComposableCameraMouseInput.prototype.attachElement = function (element, noPreventDefault) {
+            var _this = this;
+            var previousPosition;
+            this.attachedElement = element;
+            if (this._onMouseDown === undefined) {
+                var camera = this.camera;
+                var engine = this.camera.getEngine();
+                this._onMouseDown = function (evt) {
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+                this._onMouseUp = function (evt) {
+                    previousPosition = null;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+                this._onMouseOut = function (evt) {
+                    previousPosition = null;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+                this._onMouseMove = function (evt) {
+                    if (!previousPosition && !engine.isPointerLock) {
+                        return;
+                    }
+                    var offsetX;
+                    var offsetY;
+                    if (!engine.isPointerLock) {
+                        offsetX = evt.clientX - previousPosition.x;
+                        offsetY = evt.clientY - previousPosition.y;
+                    }
+                    else {
+                        offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+                        offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+                    }
+                    camera.cameraRotation.y += offsetX / _this.angularSensibility;
+                    camera.cameraRotation.x += offsetY / _this.angularSensibility;
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+            }
+            element.addEventListener("mousedown", this._onMouseDown, false);
+            element.addEventListener("mouseup", this._onMouseUp, false);
+            element.addEventListener("mouseout", this._onMouseOut, false);
+            element.addEventListener("mousemove", this._onMouseMove, false);
+        };
+        ComposableCameraMouseInput.prototype.detachElement = function (element) {
+            if (this.attachedElement !== element) {
+                return;
+            }
+            element.removeEventListener("mousedown", this._onMouseDown);
+            element.removeEventListener("mouseup", this._onMouseUp);
+            element.removeEventListener("mouseout", this._onMouseOut);
+            element.removeEventListener("mousemove", this._onMouseMove);
+            this.attachedElement = null;
+        };
+        ComposableCameraMouseInput.prototype.detach = function () {
+            if (this.attachedElement) {
+                this.detachElement(this.attachedElement);
+            }
+        };
+        ComposableCameraMouseInput.prototype.getTypeName = function () {
+            return "mouse";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraMouseInput.prototype, "angularSensibility", void 0);
+        return ComposableCameraMouseInput;
+    }());
+    BABYLON.ComposableCameraMouseInput = ComposableCameraMouseInput;
+})(BABYLON || (BABYLON = {}));

+ 111 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.mouse.ts

@@ -0,0 +1,111 @@
+module BABYLON {       
+    export class ComposableCameraMouseInput implements IComposableCameraInput {
+        camera : ComposableCamera;
+        attachedElement : HTMLElement;
+        
+        @serialize()
+        public angularSensibility = 2000.0;
+        
+        private _onMouseDown: (e: MouseEvent) => any;
+        private _onMouseUp: (e: MouseEvent) => any;
+        private _onMouseOut: (e: MouseEvent) => any;
+        private _onMouseMove: (e: MouseEvent) => any;
+        
+        attachCamera(camera : ComposableCamera){
+            this.camera = camera;
+        }
+        
+        attachElement(element: HTMLElement, noPreventDefault?: boolean){     
+            var previousPosition;
+            this.attachedElement = element;
+                
+            if (this._onMouseDown === undefined) {
+                var camera = this.camera;
+                var engine = this.camera.getEngine();
+                
+                this._onMouseDown = evt => {
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+
+                this._onMouseUp = evt => {
+                    previousPosition = null;
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+
+                this._onMouseOut = evt => {
+                    previousPosition = null;
+                    
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+
+                this._onMouseMove = evt => {
+                    if (!previousPosition && !engine.isPointerLock) {
+                        return;
+                    }
+
+                    var offsetX;
+                    var offsetY;
+
+                    if (!engine.isPointerLock) {
+                        offsetX = evt.clientX - previousPosition.x;
+                        offsetY = evt.clientY - previousPosition.y;
+                    } else {
+                        offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
+                        offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
+                    }
+
+                    camera.cameraRotation.y += offsetX / this.angularSensibility;
+                    camera.cameraRotation.x += offsetY / this.angularSensibility;
+
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                    
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                };
+            }
+
+            element.addEventListener("mousedown", this._onMouseDown, false);
+            element.addEventListener("mouseup", this._onMouseUp, false);
+            element.addEventListener("mouseout", this._onMouseOut, false);
+            element.addEventListener("mousemove", this._onMouseMove, false);
+   
+        }
+        
+        detachElement(element : HTMLElement){   
+            if (this.attachedElement !== element) {
+                return;
+            }
+
+            element.removeEventListener("mousedown", this._onMouseDown);
+            element.removeEventListener("mouseup", this._onMouseUp);
+            element.removeEventListener("mouseout", this._onMouseOut);
+            element.removeEventListener("mousemove", this._onMouseMove); 
+            this.attachedElement = null;        
+        }
+        
+        detach(){          
+            if (this.attachedElement){
+                this.detachElement(this.attachedElement);
+            }  
+        }
+        
+        getTypeName(): string{
+            return "mouse";
+        }
+    }
+}

+ 129 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.touch.js

@@ -0,0 +1,129 @@
+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 ComposableCameraTouchInput = (function () {
+        function ComposableCameraTouchInput() {
+            this._offsetX = null;
+            this._offsetY = null;
+            this._pointerCount = 0;
+            this._pointerPressed = [];
+            this.touchAngularSensibility = 200000.0;
+            this.touchMoveSensibility = 250.0;
+        }
+        ComposableCameraTouchInput.prototype.attachCamera = function (camera) {
+            this.camera = camera;
+        };
+        ComposableCameraTouchInput.prototype.attachElement = function (element, noPreventDefault) {
+            var _this = this;
+            var previousPosition;
+            if (this._attachedElement) {
+                return;
+            }
+            this._attachedElement = element;
+            if (this._onPointerDown === undefined) {
+                this._onPointerDown = function (evt) {
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                    _this._pointerPressed.push(evt.pointerId);
+                    if (_this._pointerPressed.length !== 1) {
+                        return;
+                    }
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                };
+                this._onPointerUp = function (evt) {
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                    var index = _this._pointerPressed.indexOf(evt.pointerId);
+                    if (index === -1) {
+                        return;
+                    }
+                    _this._pointerPressed.splice(index, 1);
+                    if (index != 0) {
+                        return;
+                    }
+                    previousPosition = null;
+                    _this._offsetX = null;
+                    _this._offsetY = null;
+                };
+                this._onPointerMove = function (evt) {
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+                    if (!previousPosition) {
+                        return;
+                    }
+                    var index = _this._pointerPressed.indexOf(evt.pointerId);
+                    if (index != 0) {
+                        return;
+                    }
+                    _this._offsetX = evt.clientX - previousPosition.x;
+                    _this._offsetY = -(evt.clientY - previousPosition.y);
+                };
+            }
+            element.addEventListener("pointerdown", this._onPointerDown);
+            element.addEventListener("pointerup", this._onPointerUp);
+            element.addEventListener("pointerout", this._onPointerUp);
+            element.addEventListener("pointermove", this._onPointerMove);
+        };
+        ComposableCameraTouchInput.prototype.detachElement = function (element) {
+            if (this._attachedElement !== element) {
+                return;
+            }
+            element.removeEventListener("pointerdown", this._onPointerDown);
+            element.removeEventListener("pointerup", this._onPointerUp);
+            element.removeEventListener("pointerout", this._onPointerUp);
+            element.removeEventListener("pointermove", this._onPointerMove);
+            this._attachedElement = null;
+        };
+        ComposableCameraTouchInput.prototype.checkInputs = function () {
+            if (this._offsetX) {
+                var camera = this.camera;
+                camera.cameraRotation.y += this._offsetX / this.touchAngularSensibility;
+                if (this._pointerPressed.length > 1) {
+                    camera.cameraRotation.x += -this._offsetY / this.touchAngularSensibility;
+                }
+                else {
+                    var speed = camera._computeLocalCameraSpeed();
+                    var direction = new BABYLON.Vector3(0, 0, speed * this._offsetY / this.touchMoveSensibility);
+                    BABYLON.Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
+                    camera.cameraDirection.addInPlace(BABYLON.Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+                }
+            }
+        };
+        ComposableCameraTouchInput.prototype.detach = function () {
+            if (this._attachedElement) {
+                this.detachElement(this._attachedElement);
+            }
+        };
+        ComposableCameraTouchInput.prototype.getTypeName = function () {
+            return "touch";
+        };
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraTouchInput.prototype, "touchAngularSensibility", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCameraTouchInput.prototype, "touchMoveSensibility", void 0);
+        return ComposableCameraTouchInput;
+    }());
+    BABYLON.ComposableCameraTouchInput = ComposableCameraTouchInput;
+})(BABYLON || (BABYLON = {}));

+ 154 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.touch.ts

@@ -0,0 +1,154 @@
+module BABYLON {
+    export class ComposableCameraTouchInput implements IComposableCameraInput {
+        camera: ComposableCamera;
+
+        private _offsetX: number = null;
+        private _offsetY: number = null;
+        private _pointerCount: number = 0;
+        private _pointerPressed = [];
+        private _attachedElement: HTMLElement;
+        private _onPointerDown: (e: PointerEvent) => any;
+        private _onPointerUp: (e: PointerEvent) => any;
+        private _onPointerMove: (e: PointerEvent) => any;
+
+        @serialize()
+        public touchAngularSensibility: number = 200000.0;
+
+        @serialize()
+        public touchMoveSensibility: number = 250.0;
+
+        attachCamera(camera: ComposableCamera) {
+            this.camera = camera;
+        }
+
+        attachElement(element: HTMLElement, noPreventDefault?: boolean) {
+            var previousPosition;
+
+            if (this._attachedElement) {
+                return;
+            }
+
+            this._attachedElement = element;
+
+            if (this._onPointerDown === undefined) {
+
+                this._onPointerDown = (evt) => {
+
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+
+                    this._pointerPressed.push(evt.pointerId);
+
+                    if (this._pointerPressed.length !== 1) {
+                        return;
+                    }
+
+                    previousPosition = {
+                        x: evt.clientX,
+                        y: evt.clientY
+                    };
+                };
+
+                this._onPointerUp = (evt) => {
+
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+
+                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
+
+                    if (index === -1) {
+                        return;
+                    }
+                    this._pointerPressed.splice(index, 1);
+
+                    if (index != 0) {
+                        return;
+                    }
+                    previousPosition = null;
+                    this._offsetX = null;
+                    this._offsetY = null;
+                };
+
+                this._onPointerMove = (evt) => {
+
+                    if (evt.pointerType === "mouse") {
+                        return;
+                    }
+
+                    if (!noPreventDefault) {
+                        evt.preventDefault();
+                    }
+
+                    if (!previousPosition) {
+                        return;
+                    }
+
+                    var index: number = this._pointerPressed.indexOf(evt.pointerId);
+
+                    if (index != 0) {
+                        return;
+                    }
+
+                    this._offsetX = evt.clientX - previousPosition.x;
+                    this._offsetY = -(evt.clientY - previousPosition.y);
+                };
+
+
+            }
+
+            element.addEventListener("pointerdown", this._onPointerDown);
+            element.addEventListener("pointerup", this._onPointerUp);
+            element.addEventListener("pointerout", this._onPointerUp);
+            element.addEventListener("pointermove", this._onPointerMove);
+        }
+
+        detachElement(element: HTMLElement) {
+            if (this._attachedElement !== element) {
+                return;
+            }
+
+            element.removeEventListener("pointerdown", this._onPointerDown);
+            element.removeEventListener("pointerup", this._onPointerUp);
+            element.removeEventListener("pointerout", this._onPointerUp);
+            element.removeEventListener("pointermove", this._onPointerMove);
+            this._attachedElement = null;
+        }
+
+        checkInputs() {
+            if (this._offsetX) {
+                var camera = this.camera;
+                camera.cameraRotation.y += this._offsetX / this.touchAngularSensibility;
+
+                if (this._pointerPressed.length > 1) {
+                    camera.cameraRotation.x += -this._offsetY / this.touchAngularSensibility;
+                } else {
+                    var speed = camera._computeLocalCameraSpeed();
+                    var direction = new Vector3(0, 0, speed * this._offsetY / this.touchMoveSensibility);
+
+                    Matrix.RotationYawPitchRollToRef(camera.rotation.y, camera.rotation.x, 0, camera._cameraRotationMatrix);
+                    camera.cameraDirection.addInPlace(Vector3.TransformCoordinates(direction, camera._cameraRotationMatrix));
+                }
+            }
+        }
+
+        detach() {
+            if (this._attachedElement) {
+                this.detachElement(this._attachedElement);
+            }
+        }
+
+        getTypeName(): string {
+            return "touch";
+        }
+    }
+}

+ 48 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.virtualjoystick.js

@@ -0,0 +1,48 @@
+var BABYLON;
+(function (BABYLON) {
+    var ComposableCameraVirtualJoystickInput = (function () {
+        function ComposableCameraVirtualJoystickInput() {
+        }
+        ComposableCameraVirtualJoystickInput.prototype.getLeftJoystick = function () {
+            return this._leftjoystick;
+        };
+        ComposableCameraVirtualJoystickInput.prototype.getRightJoystick = function () {
+            return this._rightjoystick;
+        };
+        ComposableCameraVirtualJoystickInput.prototype.checkInputs = function () {
+            var camera = this.camera;
+            var speed = camera._computeLocalCameraSpeed() * 50;
+            var cameraTransform = BABYLON.Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
+            var deltaTransform = BABYLON.Vector3.TransformCoordinates(new BABYLON.Vector3(this._leftjoystick.deltaPosition.x * speed, this._leftjoystick.deltaPosition.y * speed, this._leftjoystick.deltaPosition.z * speed), cameraTransform);
+            camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
+            camera.cameraRotation = camera.cameraRotation.addVector3(this._rightjoystick.deltaPosition);
+            if (!this._leftjoystick.pressed) {
+                this._leftjoystick.deltaPosition = this._leftjoystick.deltaPosition.scale(0.9);
+            }
+            if (!this._rightjoystick.pressed) {
+                this._rightjoystick.deltaPosition = this._rightjoystick.deltaPosition.scale(0.9);
+            }
+        };
+        ComposableCameraVirtualJoystickInput.prototype.attachCamera = function (camera) {
+            this.camera = camera;
+            this._leftjoystick = new BABYLON.VirtualJoystick(true);
+            this._leftjoystick.setAxisForUpDown(BABYLON.JoystickAxis.Z);
+            this._leftjoystick.setAxisForLeftRight(BABYLON.JoystickAxis.X);
+            this._leftjoystick.setJoystickSensibility(0.15);
+            this._rightjoystick = new BABYLON.VirtualJoystick(false);
+            this._rightjoystick.setAxisForUpDown(BABYLON.JoystickAxis.X);
+            this._rightjoystick.setAxisForLeftRight(BABYLON.JoystickAxis.Y);
+            this._rightjoystick.reverseUpDown = true;
+            this._rightjoystick.setJoystickSensibility(0.05);
+            this._rightjoystick.setJoystickColor("yellow");
+        };
+        ComposableCameraVirtualJoystickInput.prototype.detach = function () {
+            this._leftjoystick.releaseCanvas();
+        };
+        ComposableCameraVirtualJoystickInput.prototype.getTypeName = function () {
+            return "touch";
+        };
+        return ComposableCameraVirtualJoystickInput;
+    }());
+    BABYLON.ComposableCameraVirtualJoystickInput = ComposableCameraVirtualJoystickInput;
+})(BABYLON || (BABYLON = {}));

+ 55 - 0
src/Cameras/Composable/Inputs/babylon.camerainput.virtualjoystick.ts

@@ -0,0 +1,55 @@
+module BABYLON {
+    export class ComposableCameraVirtualJoystickInput implements IComposableCameraInput {
+        camera: ComposableCamera;
+
+        private _leftjoystick: VirtualJoystick;
+        private _rightjoystick: VirtualJoystick;
+        
+        public getLeftJoystick(): VirtualJoystick {
+            return this._leftjoystick;
+        }
+
+        public getRightJoystick(): VirtualJoystick {
+            return this._rightjoystick;
+        }
+
+        public checkInputs() {
+            var camera = this.camera;
+            var speed = camera._computeLocalCameraSpeed() * 50;
+            var cameraTransform = Matrix.RotationYawPitchRoll(camera.rotation.y, camera.rotation.x, 0);
+            var deltaTransform = Vector3.TransformCoordinates(new Vector3(this._leftjoystick.deltaPosition.x * speed, this._leftjoystick.deltaPosition.y * speed, this._leftjoystick.deltaPosition.z * speed), cameraTransform);
+            camera.cameraDirection = camera.cameraDirection.add(deltaTransform);
+            camera.cameraRotation = camera.cameraRotation.addVector3(this._rightjoystick.deltaPosition);
+            
+            if (!this._leftjoystick.pressed) {
+                this._leftjoystick.deltaPosition = this._leftjoystick.deltaPosition.scale(0.9);
+            }
+            if (!this._rightjoystick.pressed) {
+                this._rightjoystick.deltaPosition = this._rightjoystick.deltaPosition.scale(0.9);
+            }
+        }
+        
+        attachCamera(camera: ComposableCamera) {
+            this.camera = camera;
+            
+            this._leftjoystick = new VirtualJoystick(true);
+            this._leftjoystick.setAxisForUpDown(JoystickAxis.Z);
+            this._leftjoystick.setAxisForLeftRight(JoystickAxis.X);
+            this._leftjoystick.setJoystickSensibility(0.15);
+            this._rightjoystick = new VirtualJoystick(false);
+            this._rightjoystick.setAxisForUpDown(JoystickAxis.X);
+            this._rightjoystick.setAxisForLeftRight(JoystickAxis.Y);
+            this._rightjoystick.reverseUpDown = true;
+            this._rightjoystick.setJoystickSensibility(0.05);
+            this._rightjoystick.setJoystickColor("yellow");
+        }
+
+        detach() {
+            this._leftjoystick.releaseCanvas();
+        }
+
+        getTypeName(): string {
+            return "touch";
+        }
+    }
+}

+ 68 - 0
src/Cameras/Composable/babylon.cameraInputsManager.js

@@ -0,0 +1,68 @@
+var BABYLON;
+(function (BABYLON) {
+    var ComposableCameraInputsManager = (function () {
+        function ComposableCameraInputsManager(camera) {
+            this.inputs = {};
+            this.camera = camera;
+            this.checkInputs = function () { };
+        }
+        ComposableCameraInputsManager.prototype.add = function (input) {
+            var type = input.getTypeName();
+            if (this.inputs[type]) {
+                BABYLON.Tools.Warn("camera input of type " + type + " already exists on camera");
+                return;
+            }
+            this.inputs[type] = input;
+            input.attachCamera(this.camera);
+            //for checkInputs, we are dynamically creating a function
+            //the goal is to avoid the performance penalty of looping for inputs in the render loop
+            if (input.checkInputs) {
+                this.checkInputs = this._addCheckInputs(input.checkInputs);
+            }
+            if (this.camera._attachedElement && input.attachElement) {
+                input.attachElement(this.camera._attachedElement, this.camera._noPreventDefault);
+            }
+        };
+        ComposableCameraInputsManager.prototype._addCheckInputs = function (fn) {
+            var current = this.checkInputs;
+            return function () {
+                current();
+                fn();
+            };
+        };
+        ComposableCameraInputsManager.prototype.attachElement = function (element, noPreventDefault) {
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.attachElement)
+                    this.inputs[cam].attachElement(element, noPreventDefault);
+            }
+        };
+        ComposableCameraInputsManager.prototype.detachElement = function (element) {
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.detachElement)
+                    this.inputs[cam].detachElement(element);
+            }
+        };
+        ComposableCameraInputsManager.prototype.rebuildInputCheck = function (element) {
+            this.checkInputs = function () { };
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.checkInputs) {
+                    this.checkInputs = function () {
+                        input.checkInputs();
+                    };
+                }
+            }
+        };
+        ComposableCameraInputsManager.prototype.clear = function () {
+            for (var cam in this.inputs) {
+                this.inputs[cam].detach();
+            }
+            this.inputs = {};
+            this.checkInputs = function () { };
+        };
+        return ComposableCameraInputsManager;
+    }());
+    BABYLON.ComposableCameraInputsManager = ComposableCameraInputsManager;
+})(BABYLON || (BABYLON = {}));

+ 96 - 0
src/Cameras/Composable/babylon.cameraInputsManager.ts

@@ -0,0 +1,96 @@
+module BABYLON {
+    export interface IComposableCameraInput {
+        camera: ComposableCamera;
+        attachCamera(camera: ComposableCamera);
+        detach();        
+        getTypeName(): string;
+        
+        attachElement? : (element: HTMLElement, noPreventDefault?: boolean) => void;
+        detachElement? : (element: HTMLElement) => void;
+        checkInputs?: () => void;
+    }
+
+    export interface ComposableCameraInputsMap {
+        [name: string]: IComposableCameraInput;
+        [idx: number]: IComposableCameraInput;
+    }
+
+    export class ComposableCameraInputsManager {
+        inputs: ComposableCameraInputsMap;
+        camera: ComposableCamera;
+        checkInputs: () => void;
+
+        constructor(camera: ComposableCamera) {
+            this.inputs = {};
+            this.camera = camera;
+            this.checkInputs = () => { };
+        }
+
+        add(input: IComposableCameraInput) {
+            var type = input.getTypeName();
+            if (this.inputs[type]) {
+                Tools.Warn("camera input of type " + type + " already exists on camera");
+                return;
+            }
+
+            this.inputs[type] = input;
+            input.attachCamera(this.camera);
+            
+            //for checkInputs, we are dynamically creating a function
+            //the goal is to avoid the performance penalty of looping for inputs in the render loop
+            if (input.checkInputs) {
+                this.checkInputs = this._addCheckInputs(input.checkInputs);
+            }
+
+            if (this.camera._attachedElement && input.attachElement) {
+                input.attachElement(this.camera._attachedElement, this.camera._noPreventDefault);
+            }
+        }
+        
+        private _addCheckInputs(fn){
+            var current = this.checkInputs;
+            return () => {
+                current();
+                fn();
+            }
+        }
+
+        attachElement(element: HTMLElement, noPreventDefault?: boolean) {
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.attachElement)
+                    this.inputs[cam].attachElement(element, noPreventDefault);
+            }
+        }
+
+        detachElement(element: HTMLElement) {
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.detachElement)
+                    this.inputs[cam].detachElement(element);
+            }
+        }
+
+        rebuildInputCheck(element: HTMLElement) {
+            this.checkInputs = () => { };
+
+            for (var cam in this.inputs) {
+                var input = this.inputs[cam];
+                if (input.checkInputs) {
+                    this.checkInputs = () => {
+                        input.checkInputs();
+                    }
+                }
+            }
+        }
+
+        clear() {
+            for (var cam in this.inputs) {
+                this.inputs[cam].detach();
+            }
+
+            this.inputs = {};
+            this.checkInputs = () => { };
+        }
+    }
+} 

+ 122 - 0
src/Cameras/Composable/babylon.composableCamera.js

@@ -0,0 +1,122 @@
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+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 ComposableCamera = (function (_super) {
+        __extends(ComposableCamera, _super);
+        function ComposableCamera(name, position, scene) {
+            var _this = this;
+            _super.call(this, name, position, scene);
+            this.ellipsoid = new BABYLON.Vector3(0.5, 1, 0.5);
+            this.checkCollisions = false;
+            this.applyGravity = false;
+            this._collider = new BABYLON.Collider();
+            this._needMoveForGravity = false;
+            this._oldPosition = BABYLON.Vector3.Zero();
+            this._diffPosition = BABYLON.Vector3.Zero();
+            this._newPosition = BABYLON.Vector3.Zero();
+            this._onCollisionPositionChange = function (collisionId, newPosition, collidedMesh) {
+                if (collidedMesh === void 0) { collidedMesh = null; }
+                //TODO move this to the collision coordinator!
+                if (_this.getScene().workerCollisions)
+                    newPosition.multiplyInPlace(_this._collider.radius);
+                var updatePosition = function (newPos) {
+                    _this._newPosition.copyFrom(newPos);
+                    _this._newPosition.subtractToRef(_this._oldPosition, _this._diffPosition);
+                    var oldPosition = _this.position.clone();
+                    if (_this._diffPosition.length() > BABYLON.Engine.CollisionsEpsilon) {
+                        _this.position.addInPlace(_this._diffPosition);
+                        if (_this.onCollide && collidedMesh) {
+                            _this.onCollide(collidedMesh);
+                        }
+                    }
+                };
+                updatePosition(newPosition);
+            };
+            this.inputs = new BABYLON.ComposableCameraInputsManager(this);
+        }
+        // Controls
+        ComposableCamera.prototype.attachControl = function (element, noPreventDefault) {
+            if (this._attachedElement) {
+                return;
+            }
+            this._noPreventDefault = noPreventDefault;
+            this._attachedElement = element;
+            this.inputs.attachElement(element, noPreventDefault);
+        };
+        ComposableCamera.prototype.detachControl = function (element) {
+            if (this._attachedElement !== element) {
+                return;
+            }
+            this.inputs.detachElement(this._attachedElement);
+            this._attachedElement = null;
+            this.cameraDirection = new BABYLON.Vector3(0, 0, 0);
+            this.cameraRotation = new BABYLON.Vector2(0, 0);
+        };
+        ComposableCamera.prototype._collideWithWorld = function (velocity) {
+            var globalPosition;
+            if (this.parent) {
+                globalPosition = BABYLON.Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
+            }
+            else {
+                globalPosition = this.position;
+            }
+            globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
+            this._collider.radius = this.ellipsoid;
+            //no need for clone, as long as gravity is not on.
+            var actualVelocity = velocity;
+            //add gravity to the velocity to prevent the dual-collision checking
+            if (this.applyGravity) {
+                //this prevents mending with cameraDirection, a global variable of the free camera class.
+                actualVelocity = velocity.add(this.getScene().gravity);
+            }
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+        };
+        ComposableCamera.prototype._checkInputs = function () {
+            if (!this._localDirection) {
+                this._localDirection = BABYLON.Vector3.Zero();
+                this._transformedDirection = BABYLON.Vector3.Zero();
+            }
+            this.inputs.checkInputs();
+            _super.prototype._checkInputs.call(this);
+        };
+        ComposableCamera.prototype._decideIfNeedsToMove = function () {
+            return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
+        };
+        ComposableCamera.prototype._updatePosition = function () {
+            if (this.checkCollisions && this.getScene().collisionsEnabled) {
+                this._collideWithWorld(this.cameraDirection);
+            }
+            else {
+                this.position.addInPlace(this.cameraDirection);
+            }
+        };
+        ComposableCamera.prototype.dispose = function () {
+            this.inputs.clear();
+            _super.prototype.dispose.call(this);
+        };
+        ComposableCamera.prototype.getTypeName = function () {
+            return "FreeCamera";
+        };
+        __decorate([
+            BABYLON.serializeAsVector3()
+        ], ComposableCamera.prototype, "ellipsoid", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCamera.prototype, "checkCollisions", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ComposableCamera.prototype, "applyGravity", void 0);
+        return ComposableCamera;
+    }(BABYLON.TargetCamera));
+    BABYLON.ComposableCamera = ComposableCamera;
+})(BABYLON || (BABYLON = {}));

+ 134 - 0
src/Cameras/Composable/babylon.composableCamera.ts

@@ -0,0 +1,134 @@
+module BABYLON {       
+    export class ComposableCamera extends TargetCamera {
+        @serializeAsVector3()
+        public ellipsoid = new Vector3(0.5, 1, 0.5);
+
+        @serialize()
+        public checkCollisions = false;
+
+        @serialize()
+        public applyGravity = false;
+        
+        public inputs : ComposableCameraInputsManager;
+
+        public onCollide: (collidedMesh: AbstractMesh) => void;
+        
+        private _collider = new Collider();
+        private _needMoveForGravity = false;
+        private _oldPosition = Vector3.Zero();
+        private _diffPosition = Vector3.Zero();
+        private _newPosition = Vector3.Zero();
+        public _attachedElement: HTMLElement;
+        public _noPreventDefault: boolean;
+        
+        public _localDirection: Vector3;
+        public _transformedDirection: Vector3;        
+        
+        constructor(name: string, position: Vector3, scene: Scene) {
+            super(name, position, scene);
+            this.inputs = new ComposableCameraInputsManager(this);
+        }
+
+        // Controls
+        public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
+            if (this._attachedElement) {
+                return;
+            }
+            this._noPreventDefault = noPreventDefault;
+            this._attachedElement = element;
+
+            this.inputs.attachElement(element, noPreventDefault);
+        }        
+
+        public detachControl(element: HTMLElement): void {
+            if (this._attachedElement !== element) {
+                return;
+            }
+
+            this.inputs.detachElement(this._attachedElement);
+            this._attachedElement = null;
+            
+            this.cameraDirection = new Vector3(0, 0, 0);
+            this.cameraRotation = new Vector2(0, 0);
+        }
+
+        public _collideWithWorld(velocity: Vector3): void {
+            var globalPosition: Vector3;
+
+            if (this.parent) {
+                globalPosition = Vector3.TransformCoordinates(this.position, this.parent.getWorldMatrix());
+            } else {
+                globalPosition = this.position;
+            }
+
+            globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
+            this._collider.radius = this.ellipsoid;
+
+            //no need for clone, as long as gravity is not on.
+            var actualVelocity = velocity;
+			
+            //add gravity to the velocity to prevent the dual-collision checking
+            if (this.applyGravity) {
+                //this prevents mending with cameraDirection, a global variable of the free camera class.
+                actualVelocity = velocity.add(this.getScene().gravity);
+            }
+
+            this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+
+        }
+
+        private _onCollisionPositionChange = (collisionId: number, newPosition: Vector3, collidedMesh: AbstractMesh = null) => {
+            //TODO move this to the collision coordinator!
+            if (this.getScene().workerCollisions)
+                newPosition.multiplyInPlace(this._collider.radius);
+
+            var updatePosition = (newPos) => {
+                this._newPosition.copyFrom(newPos);
+
+                this._newPosition.subtractToRef(this._oldPosition, this._diffPosition);
+
+                var oldPosition = this.position.clone();
+                if (this._diffPosition.length() > Engine.CollisionsEpsilon) {
+                    this.position.addInPlace(this._diffPosition);
+                    if (this.onCollide && collidedMesh) {
+                        this.onCollide(collidedMesh);
+                    }
+                }
+            }
+
+            updatePosition(newPosition);
+        }
+
+        public _checkInputs(): void {
+            if (!this._localDirection) {
+                this._localDirection = Vector3.Zero();
+                this._transformedDirection = Vector3.Zero();
+            }
+
+            this.inputs.checkInputs();
+
+            super._checkInputs();
+        }
+
+        public _decideIfNeedsToMove(): boolean {
+            return this._needMoveForGravity || Math.abs(this.cameraDirection.x) > 0 || Math.abs(this.cameraDirection.y) > 0 || Math.abs(this.cameraDirection.z) > 0;
+        }
+
+        public _updatePosition(): void {
+            if (this.checkCollisions && this.getScene().collisionsEnabled) {
+                this._collideWithWorld(this.cameraDirection);
+            } else {
+                this.position.addInPlace(this.cameraDirection);
+            }
+        }
+
+        public dispose(): void {
+            this.inputs.clear();
+            super.dispose();
+        }
+        
+        public getTypeName(): string {
+            return "FreeCamera";
+        }
+    }    
+}