Jelajahi Sumber

extended gamepad implementation
Moved the WebVR controllers to the gamepad extention class.
It is now possible to attach a mesh to the gamepad.
There are now two implementation for each WebVR controller (vive, and oculus)

Raanan Weber 8 tahun lalu
induk
melakukan
c51beeb818
2 mengubah file dengan 300 tambahan dan 80 penghapusan
  1. 295 0
      src/Tools/babylon.extendedGamepad.ts
  2. 5 80
      src/Tools/babylon.gamepads.ts

+ 295 - 0
src/Tools/babylon.extendedGamepad.ts

@@ -0,0 +1,295 @@
+
+module BABYLON {
+
+    export enum PoseEnabledControllerType {
+        VIVE,
+        OCULUS,
+        GENERIC
+    }
+
+    export interface MutableGamepadButton {
+        value: number;
+        touched: boolean;
+        pressed: boolean;
+    }
+
+    export class PoseEnabledControllerHelper {
+        public static InitiateController(vrGamepad: any) {
+            // for now, only Oculus and Vive are supported
+            if (vrGamepad.id.indexOf('Oculus Touch') !== -1) {
+                return new OculusTouchController(vrGamepad);
+            } else {
+                return new ViveController(vrGamepad);
+            }
+        }
+    }
+
+    export class PoseEnabledController extends Gamepad {
+        public position: Vector3;
+        public rotationQuaternion: Quaternion;
+        public controllerType: PoseEnabledControllerType;
+        /**
+         * Vive mapping:
+         * 0: touchpad
+         * 1: trigger
+         * 2: left AND right buttons
+         * 3: menu button
+         */
+
+        /**
+         * 
+         *oculus:
+
+         
+         */
+        public rawPose: GamepadPose;
+
+        private _mesh: AbstractMesh; // a node that will be attached to this Gamepad
+
+        constructor(public vrGamepad) {
+            super(vrGamepad.id, vrGamepad.index, vrGamepad);
+            this.type = Gamepad.POSE_ENABLED;
+            this.controllerType = PoseEnabledControllerType.GENERIC;
+            this.position = Vector3.Zero();
+            this.rotationQuaternion = new Quaternion();
+        }
+
+        public update() {
+            super.update();
+            var pose: GamepadPose = this.vrGamepad.pose;
+            if (pose) {
+                this.rawPose = pose;
+                if (pose.hasPosition) {
+                    this.position.copyFromFloats(pose.position[0], pose.position[1], -pose.position[2]);
+                }
+                if (pose.hasOrientation) {
+                    this.rotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
+                }
+            }
+            if (this._mesh) {
+                this._mesh.position.copyFrom(this.position);
+                this._mesh.rotationQuaternion.copyFrom(this.rotationQuaternion);
+            }
+        }
+
+        public attachToMesh(mesh: AbstractMesh) {
+            this._mesh = mesh;
+            if (!this._mesh.rotationQuaternion) {
+                this._mesh.rotationQuaternion = new Quaternion();
+            }
+        }
+    }
+
+    export interface GamepadButtonChanges {
+        changed: boolean;
+        pressChanged: boolean;
+        touchChanged: boolean;
+        valueChanged: boolean;
+    }
+
+    export abstract class WebVRController extends PoseEnabledController {
+
+        public onTriggerChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        public onMainButtonStateChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        public onSecondaryButtonStateChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        public onPadStateChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+        public onPadValuesChangedObservable = new Observable<StickValues>();
+
+        protected _buttons: Array<MutableGamepadButton>;
+
+        private _onButtonStateChange: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void;
+
+        public onButtonStateChange(callback: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void) {
+            this._onButtonStateChange = callback;
+        }
+
+        public hand: string; // 'left' or 'right', see https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this._buttons = new Array<ExtendedGamepadButton>(vrGamepad.buttons.length);
+            this.hand = vrGamepad.hand;
+            // stick values changed?
+            this.onleftstickchanged = () => {
+                this.onPadValuesChangedObservable.notifyObservers(this.pad);
+            }
+        }
+
+        public get pad() {
+            return this.leftStick;
+        }
+
+        public update() {
+            super.update();
+            for (var index = 0; index < this._buttons.length; index++) {
+                this._setButtonValue(this.vrGamepad.buttons[index], this._buttons[index], index);
+            };
+        }
+
+        protected abstract handleButtonChange(buttonIdx: number, value: ExtendedGamepadButton, changes: GamepadButtonChanges);
+
+        private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
+            if (!currentState) {
+                this._buttons[buttonIndex] = {
+                    pressed: newState.pressed,
+                    touched: newState.touched,
+                    value: newState.value
+                }
+            }
+            this._checkChanges(newState, currentState);
+            if (this._changes.changed) {
+                this._onButtonStateChange && this._onButtonStateChange(this.index, buttonIndex, newState);
+
+                this.handleButtonChange(buttonIndex, newState, this._changes);
+            }
+            this._buttons[buttonIndex].pressed = newState.pressed;
+            this._buttons[buttonIndex].touched = newState.touched;
+            // oculus triggers are never 0, thou not touched.
+            this._buttons[buttonIndex].value = newState.value < 0.00000001 ? 0 : newState.value;
+        }
+
+        // avoid GC, store state in a tmp object
+        private _changes: GamepadButtonChanges;
+
+        private _checkChanges(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton) {
+            this._changes.pressChanged = newState.pressed !== currentState.pressed;
+            this._changes.touchChanged = newState.touched !== currentState.touched;
+            this._changes.valueChanged = newState.value !== currentState.value;
+            this._changes.changed = this._changes.pressChanged || this._changes.touchChanged || this._changes.valueChanged;
+            return this._changes;
+        }
+    }
+
+    export class OculusTouchController extends WebVRController {
+
+        public onSecondaryTriggerChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        public onThumbRestChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.OCULUS;
+        }
+
+        // helper getters for left and right hand.
+        public get onAButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No A button on left hand');
+            }
+        }
+
+        public get onBButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No B button on left hand');
+            }
+        }
+
+        public get onXButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No X button on right hand');
+            }
+        }
+
+        public get onYButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No Y button on right hand');
+            }
+        }
+
+        /*
+         0) thumb stick (touch, press, value = pressed (0,1)). value is in this.leftStick
+         1) index trigger (touch (?), press (only when value > 0.1), value 0 to 1)
+         2) secondary trigger (same)
+         3) A (right) X (left), touch, pressed = value
+         4) B / Y 
+         5) thumb rest
+        */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = { state: state, changes: changes };
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                case 1: // index trigger
+                    this.onTriggerChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // secondary trigger
+                    this.onSecondaryTriggerChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 4:
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 5:
+                    this.onThumbRestChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+
+    }
+
+    export class ViveController extends WebVRController {
+
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.VIVE;
+        }
+
+        public get onLeftButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onRightButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onMenuButtonStateChangedObservable() {
+            return this.onSecondaryButtonStateChangedObservable;
+        }
+
+        /**
+         * Vive mapping:
+         * 0: touchpad
+         * 1: trigger
+         * 2: left AND right buttons
+         * 3: menu button
+         */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = { state: state, changes: changes };
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                case 1: // index trigger
+                    this.onTriggerChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // left AND right button
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+    }
+
+
+}
+
+interface ExtendedGamepadButton extends GamepadButton {
+    readonly pressed: boolean;
+    readonly touched: boolean;
+    readonly value: number;
+}

+ 5 - 80
src/Tools/babylon.gamepads.ts

@@ -74,8 +74,10 @@
             if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
                 newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
             }
-            else if ((<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1) {
-                newGamepad = new WebVRController(gamepad.id, gamepad.index, gamepad);
+            // (<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1
+            // if pose is supported, use the (WebVR) pose enabled controller
+            else if (gamepad.pose) {
+                newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
             }
             else {
                 newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
@@ -171,7 +173,7 @@
         public static GAMEPAD = 0;
         public static GENERIC = 1;
         public static XBOX = 2;
-        public static WEBVR = 3;
+        public static POSE_ENABLED = 3;
 
         constructor(public id: string, public index: number, public browserGamepad, leftStickX: number = 0, leftStickY: number = 1, rightStickX: number = 2, rightStickY: number = 3) {
             this.type = Gamepad.GAMEPAD;
@@ -224,83 +226,6 @@
         }
     }
 
-    export interface VRButtonState {
-        pressed: boolean;
-        touched: boolean;
-        value: number;
-    }
-
-    export class WebVRController extends Gamepad {
-        public position: Vector3;
-        public rotationQuaternion: Quaternion;
-        /**
-         * Vive mapping:
-         * 0: touchpad
-         * 1: trigger
-         * 2: left AND right buttons
-         * 3: menu button
-         */
-        private _buttons: Array<VRButtonState>;
-
-        public rawPose: GamepadPose;
-
-        private _onButtonStateChange: (controlledIndex: number, buttonIndex: number, state: VRButtonState) => void;
-
-        public onButtonStateChange(callback: (controlledIndex: number, buttonIndex: number, state: VRButtonState) => void) {
-            this._onButtonStateChange = callback;
-        }
-
-        public getButtonAxis(index: number = 0): StickValues {
-            if (index) {
-                return this.rightStick;
-            }
-            return this.leftStick;
-        }
-
-        constructor(public id: string, public index: number, public vrGamepad) {
-            super(id, index, vrGamepad);
-            this.type = Gamepad.WEBVR;
-            this._buttons = new Array(vrGamepad.buttons.length);
-            this.position = Vector3.Zero();
-            this.rotationQuaternion = new Quaternion();
-        }
-
-        private _setButtonValue(newState: VRButtonState, currentState: VRButtonState, buttonIndex: number): VRButtonState {
-            if (!currentState) {
-                return newState;
-            }
-            if (
-                newState.pressed !== currentState.pressed ||
-                newState.touched !== currentState.touched ||
-                newState.value !== currentState.value) {
-
-                this._onButtonStateChange && this._onButtonStateChange(this.index, buttonIndex, newState);
-            }
-            return {
-                pressed: newState.pressed,
-                touched: newState.touched,
-                value: newState.value
-            };
-        }
-
-        public update() {
-            super.update();
-            for (var index = 0; index < this._buttons.length; index++) {
-                this._buttons[index] = this._setButtonValue(this.vrGamepad.buttons[index], this._buttons[index], index);
-            };
-            var pose: GamepadPose = this.vrGamepad.pose;
-            if (pose) {
-                this.rawPose = pose;
-                if (pose.hasPosition) {
-                    this.position.copyFromFloats(pose.position[0], pose.position[1], -pose.position[2]);
-                }
-                if (pose.hasOrientation) {
-                    this.rotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
-                }
-            }
-        }
-    }
-
     export class GenericPad extends Gamepad {
         private _buttons: Array<number>;
         private _onbuttondown: (buttonPressed: number) => void;