Browse Source

Merge pull request #8031 from PolygonalSun/polygonal-sun/device-source-manager

DeviceSourceManager
David Catuhe 5 years ago
parent
commit
c825335c6d

+ 2 - 0
dist/preview release/what's new.md

@@ -22,7 +22,9 @@
 - Added support for `material.disableColorWrite` ([Deltakosh](https://github.com/deltakosh))
 - The Mesh Asset Task also accepts File as sceneInput ([RaananW](https://github.com/RaananW))
 - Added support preserving vert colors for CSG objects ([PirateJC](https://github.com/PirateJC))
+- Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 - Added `boundingBoxRenderer.onBeforeBoxRenderingObservable` and `boundingBoxRenderer.onAfterBoxRenderingObservable` ([Deltakosh](https://github.com/deltakosh))
+- Added initial code for user facing DeviceSourceManager ([PolygonalSun](https://github.com/PolygonalSun))
 
 ### Engine
 

+ 188 - 0
src/DeviceInput/InputDevices/deviceEnums.ts

@@ -0,0 +1,188 @@
+/**
+ * Enum for Device Types
+ */
+export enum DeviceType {
+    /** Generic */
+    Generic = 0,
+    /** Keyboard */
+    Keyboard = 1,
+    /** Mouse */
+    Mouse = 2,
+    /** Touch Pointers */
+    Touch = 3,
+    /** PS4 Dual Shock */
+    DualShock = 4,
+    /** Xbox */
+    Xbox = 5,
+    /** Switch Controller */
+    Switch = 6
+}
+
+// Device Enums
+/**
+ * Enum for All Pointers (Touch/Mouse)
+ */
+export enum PointerInput {
+    /** Horizontal Axis */
+    Horizontal = 0,
+    /** Vertical Axis */
+    Vertical = 1,
+    /** Left Click or Touch */
+    LeftClick = 2,
+    /** Middle Click */
+    MiddleClick = 3,
+    /** Right Click */
+    RightClick = 4,
+    /** Browser Back */
+    BrowserBack = 5,
+    /** Browser Forward */
+    BrowserForward = 6
+}
+
+/**
+ * Enum for Dual Shock Gamepad
+ */
+export enum DualShockInput {
+    /** Cross */
+    Cross = 0,
+    /** Circle */
+    Circle = 1,
+    /** Square */
+    Square = 2,
+    /** Triangle */
+    Triangle = 3,
+    /** L1 */
+    L1 = 4,
+    /** R1 */
+    R1 = 5,
+    /** L2 */
+    L2 = 6,
+    /** R2 */
+    R2 = 7,
+    /** Share */
+    Share = 8,
+    /** Options */
+    Options = 9,
+    /** L3 */
+    L3 = 10,
+    /** R3 */
+    R3 = 11,
+    /** DPadUp */
+    DPadUp = 12,
+    /** DPadDown */
+    DPadDown = 13,
+    /** DPadLeft */
+    DPadLeft = 14,
+    /** DRight */
+    DPadRight = 15,
+    /** Home */
+    Home = 16,
+    /** TouchPad */
+    TouchPad = 17,
+    /** LStickXAxis */
+    LStickXAxis = 18,
+    /** LStickYAxis */
+    LStickYAxis = 19,
+    /** RStickXAxis */
+    RStickXAxis = 20,
+    /** RStickYAxis */
+    RStickYAxis = 21
+}
+
+/**
+ * Enum for Xbox Gamepad
+ */
+export enum XboxInput {
+    /** A */
+    A = 0,
+    /** B */
+    B = 1,
+    /** X */
+    X = 2,
+    /** Y */
+    Y = 3,
+    /** LB */
+    LB = 4,
+    /** RB */
+    RB = 5,
+    /** LT */
+    LT = 6,
+    /** RT */
+    RT = 7,
+    /** Back */
+    Back = 8,
+    /** Start */
+    Start = 9,
+    /** LS */
+    LS = 10,
+    /** RS */
+    RS = 11,
+    /** DPadUp */
+    DPadUp = 12,
+    /** DPadDown */
+    DPadDown = 13,
+    /** DPadLeft */
+    DPadLeft = 14,
+    /** DRight */
+    DPadRight = 15,
+    /** Home */
+    Home = 16,
+    /** LStickXAxis */
+    LStickXAxis = 17,
+    /** LStickYAxis */
+    LStickYAxis = 18,
+    /** RStickXAxis */
+    RStickXAxis = 19,
+    /** RStickYAxis */
+    RStickYAxis = 20
+}
+
+/**
+ * Enum for Switch (Pro/JoyCon L+R) Gamepad
+ */
+export enum SwitchInput {
+    /** B */
+    B = 0,
+    /** A */
+    A = 1,
+    /** Y */
+    Y = 2,
+    /** X */
+    X = 3,
+    /** L */
+    L = 4,
+    /** R */
+    R = 5,
+    /** ZL */
+    ZL = 6,
+    /** ZR */
+    ZR = 7,
+    /** Minus */
+    Minus = 8,
+    /** Plus */
+    Plus = 9,
+    /** LS */
+    LS = 10,
+    /** RS */
+    RS = 11,
+    /** DPadUp */
+    DPadUp = 12,
+    /** DPadDown */
+    DPadDown = 13,
+    /** DPadLeft */
+    DPadLeft = 14,
+    /** DRight */
+    DPadRight = 15,
+    /** Home */
+    Home = 16,
+    /** Capture */
+    Capture = 17,
+    /** LStickXAxis */
+    LStickXAxis = 18,
+    /** LStickYAxis */
+    LStickYAxis = 19,
+    /** RStickXAxis */
+    RStickXAxis = 20,
+    /** RStickYAxis */
+    RStickYAxis = 21
+}

+ 194 - 0
src/DeviceInput/InputDevices/deviceSourceManager.ts

@@ -0,0 +1,194 @@
+import { DeviceInputSystem } from '../deviceInputSystem';
+import { Engine } from '../../Engines/engine';
+import { IDisposable } from '../../scene';
+import { DeviceType } from './deviceEnums';
+import { Nullable } from '../../types';
+import { Observable } from '../../Misc/observable';
+import { DeviceInput } from './deviceTypes';
+
+/**
+ * Class that handles all input for a specific device
+ */
+export class DeviceSource<T extends DeviceType> {
+    // Public Members
+    /**
+     * Observable to handle device input changes per device
+     */
+    public readonly onInputChangedObservable = new Observable<{ inputIndex: DeviceInput<T>, previousState: Nullable<number>, currentState: Nullable<number> }>();
+
+    // Private Members
+    private readonly _deviceInputSystem: DeviceInputSystem;
+
+    /**
+     * Default Constructor
+     * @param deviceInputSystem Reference to DeviceInputSystem
+     * @param deviceType Type of device
+     * @param deviceSlot "Slot" or index that device is referenced in
+     */
+    constructor(deviceInputSystem: DeviceInputSystem,
+        /** Type of device */
+        public readonly deviceType: DeviceType,
+        /** "Slot" or index that device is referenced in */
+        public readonly deviceSlot: number = 0) {
+        this._deviceInputSystem = deviceInputSystem;
+    }
+
+    /**
+     * Get input for specific input
+     * @param inputIndex index of specific input on device
+     * @returns Input value from DeviceInputSystem
+     */
+    public getInput(inputIndex: DeviceInput<T>): Nullable<number> {
+        return this._deviceInputSystem.pollInput(this.deviceType, this.deviceSlot, inputIndex);
+    }
+}
+
+/**
+ * Class to keep track of devices
+ */
+export class DeviceSourceManager implements IDisposable {
+    // Public Members
+    /**
+     * Observable to be triggered when before a device is connected
+     */
+    public readonly onBeforeDeviceConnectedObservable = new Observable<{ deviceType: DeviceType, deviceSlot: number }>();
+
+    /**
+     * Observable to be triggered when before a device is disconnected
+     */
+    public readonly onBeforeDeviceDisconnectedObservable = new Observable<{ deviceType: DeviceType, deviceSlot: number }>();
+
+    /**
+     * Observable to be triggered when after a device is connected
+     */
+    public readonly onAfterDeviceConnectedObservable = new Observable<{ deviceType: DeviceType, deviceSlot: number }>();
+
+    /**
+     * Observable to be triggered when after a device is disconnected
+     */
+    public readonly onAfterDeviceDisconnectedObservable = new Observable<{ deviceType: DeviceType, deviceSlot: number }>();
+
+    // Private Members
+    private readonly _devices: Array<Array<DeviceSource<DeviceType>>>;
+    private readonly _firstDevice: Array<number>;
+    private readonly _deviceInputSystem: DeviceInputSystem;
+
+    /**
+     * Default Constructor
+     * @param engine engine to pull input element from
+     */
+    constructor(engine: Engine) {
+        const numberOfDeviceTypes = Object.keys(DeviceType).length / 2;
+        this._devices = new Array<Array<DeviceSource<DeviceType>>>(numberOfDeviceTypes);
+        this._firstDevice = new Array<number>(numberOfDeviceTypes);
+        this._deviceInputSystem = new DeviceInputSystem(engine);
+
+        this._deviceInputSystem.onDeviceConnected = (deviceType, deviceSlot) => {
+            this.onBeforeDeviceConnectedObservable.notifyObservers({ deviceType, deviceSlot });
+            this._addDevice(deviceType, deviceSlot);
+            this.onAfterDeviceConnectedObservable.notifyObservers({ deviceType, deviceSlot });
+        };
+        this._deviceInputSystem.onDeviceDisconnected = (deviceType, deviceSlot) => {
+            this.onBeforeDeviceDisconnectedObservable.notifyObservers({ deviceType, deviceSlot });
+            this._removeDevice(deviceType, deviceSlot);
+            this.onAfterDeviceDisconnectedObservable.notifyObservers({ deviceType, deviceSlot });
+        };
+
+        if (!this._deviceInputSystem.onInputChanged) {
+            this._deviceInputSystem.onInputChanged = (deviceType, deviceSlot, inputIndex, previousState, currentState) => {
+                this.getDeviceSource(deviceType, deviceSlot)?.onInputChangedObservable.notifyObservers({ inputIndex, previousState, currentState });
+            };
+        }
+    }
+
+    // Public Functions
+    /**
+     * Gets a DeviceSource, given a type and slot
+     * @param deviceType Enum specifying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
+     * @returns DeviceSource object
+     */
+    public getDeviceSource<T extends DeviceType>(deviceType: DeviceType, deviceSlot?: number): Nullable<DeviceSource<T>> {
+        if (deviceSlot === undefined) {
+            if (this._firstDevice[deviceType] === undefined) {
+                return null;
+            }
+
+            deviceSlot = this._firstDevice[deviceType];
+        }
+
+        if (!this._devices[deviceType] || this._devices[deviceType][deviceSlot] === undefined) {
+            return null;
+        }
+
+        return this._devices[deviceType][deviceSlot];
+    }
+
+    /**
+     * Gets an array of DeviceSource objects for a given device type
+     * @param deviceType Enum specifying device type
+     * @returns Array of DeviceSource objects
+     */
+    public getDeviceSources<T extends DeviceType>(deviceType: T): ReadonlyArray<DeviceSource<T>> {
+        return this._devices[deviceType];
+    }
+
+    /**
+     * Dispose of DeviceInputSystem and other parts
+     */
+    public dispose() {
+        this._deviceInputSystem.dispose();
+    }
+
+    // Private Functions
+    /**
+     * Function to add device name to device list
+     * @param deviceType Enum specifying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
+     */
+    private _addDevice(deviceType: DeviceType, deviceSlot: number) {
+        if (!this._devices[deviceType]) {
+            this._devices[deviceType] = new Array<DeviceSource<DeviceType>>();
+        }
+
+        this._devices[deviceType][deviceSlot] = new DeviceSource(this._deviceInputSystem, deviceType, deviceSlot);
+        this._updateFirstDevices(deviceType);
+    }
+
+    /**
+     * Function to remove device name to device list
+     * @param deviceType Enum specifying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
+     */
+    private _removeDevice(deviceType: DeviceType, deviceSlot: number) {
+        delete this._devices[deviceType][deviceSlot];
+        this._updateFirstDevices(deviceType);
+    }
+
+    /**
+     * Updates array storing first connected device of each type
+     * @param type Type of Device
+     */
+    private _updateFirstDevices(type: DeviceType) {
+        switch (type) {
+            case DeviceType.Keyboard:
+            case DeviceType.Mouse:
+                this._firstDevice[type] = 0;
+                break;
+            case DeviceType.Touch:
+            case DeviceType.DualShock:
+            case DeviceType.Xbox:
+            case DeviceType.Switch:
+            case DeviceType.Generic:
+                const devices = this._devices[type];
+                delete this._firstDevice[type];
+                for (let i = 0; i < devices.length; i++) {
+                    if (devices[i]) {
+                        this._firstDevice[type] = i;
+                        break;
+                    }
+                }
+                break;
+        }
+    }
+}

+ 12 - 0
src/DeviceInput/InputDevices/deviceTypes.ts

@@ -0,0 +1,12 @@
+import { DeviceType, PointerInput, DualShockInput, XboxInput, SwitchInput } from './deviceEnums';
+
+/**
+ * Type to handle enforcement of inputs
+ */
+export type DeviceInput<T extends DeviceType> =
+    T extends DeviceType.Keyboard | DeviceType.Generic ? number :
+    T extends DeviceType.Mouse | DeviceType.Touch ? PointerInput :
+    T extends DeviceType.DualShock ? DualShockInput :
+    T extends DeviceType.Xbox ? XboxInput :
+    T extends DeviceType.Switch ? SwitchInput :
+    never;

+ 133 - 67
src/DeviceInput/deviceInputSystem.ts

@@ -1,7 +1,7 @@
-import { Observable } from "../Misc/observable";
 import { Engine } from '../Engines/engine';
 import { IDisposable } from '../scene';
 import { Nullable } from '../types';
+import { DeviceType } from './InputDevices/deviceEnums';
 
 /**
  * This class will take all inputs from Keyboard, Pointer, and
@@ -10,25 +10,24 @@ import { Nullable } from '../types';
  * pointer device and one keyboard.
  */
 export class DeviceInputSystem implements IDisposable {
-    // Static
-    /** POINTER_DEVICE */
-    public static readonly POINTER_DEVICE: string = "Pointer";
-    /** KEYBOARD_DEVICE */
-    public static readonly KEYBOARD_DEVICE: string = "Keyboard";
+    /**
+     * Callback to be triggered when a device is connected
+     */
+    public onDeviceConnected: (deviceType: DeviceType, deviceSlot: number) => void = () => { };
 
     /**
-     * Observable to be triggered when a device is connected
+     * Callback to be triggered when a device is disconnected
      */
-    public onDeviceConnectedObservable = new Observable<string>();
+    public onDeviceDisconnected: (deviceType: DeviceType, deviceSlot: number) => void = () => { };
 
     /**
-     * Observable to be triggered when a device is disconnected
+     * Callback to be triggered when event driven input is updated
      */
-    public onDeviceDisconnectedObservable = new Observable<string>();
+    public onInputChanged: (deviceType: DeviceType, deviceSlot: number, inputIndex: number, previousState: Nullable<number>, currentState: Nullable<number>) => void;
 
     // Private Members
-    private _inputs: { [key: string]: Array<Nullable<number>> } = {};
-    private _gamepads: Array<string>;
+    private _inputs: Array<Array<Array<Nullable<number>>>> = [];
+    private _gamepads: Array<DeviceType>;
     private _keyboardActive: boolean = false;
     private _pointerActive: boolean = false;
     private _elementToAttachTo: HTMLElement;
@@ -43,7 +42,7 @@ export class DeviceInputSystem implements IDisposable {
     private _gamepadConnectedEvent = (evt: any) => { };
     private _gamepadDisconnectedEvent = (evt: any) => { };
 
-    private static _MAX_KEYCODES: number = 222;
+    private static _MAX_KEYCODES: number = 255;
     private static _MAX_POINTER_INPUTS: number = 7;
 
     /**
@@ -67,28 +66,34 @@ export class DeviceInputSystem implements IDisposable {
      * @param inputIndex Index of device input
      * @returns Current value of input
      */
-    public pollInput(deviceName: string, inputIndex: number): Nullable<number> {
-        const device = this._inputs[deviceName];
+
+    /**
+     * Checks for current device input value, given an id and input index
+     * @param deviceType Enum specifiying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
+     * @param inputIndex Id of input to be checked
+     * @returns Current value of input
+     */
+    public pollInput(deviceType: DeviceType, deviceSlot: number, inputIndex: number): Nullable<number> {
+        const device = this._inputs[deviceType][deviceSlot];
 
         if (!device) {
-            throw `Unable to find device ${deviceName}`;
+            throw `Unable to find device ${DeviceType[deviceType]}`;
         }
 
-        this._updateDevice(deviceName, inputIndex);
+        this._updateDevice(deviceType, deviceSlot, inputIndex);
 
         if (device[inputIndex] === undefined) {
-            throw `Unable to find input ${inputIndex} on device ${deviceName}`;
+            throw `Unable to find input ${inputIndex} for device ${DeviceType[deviceType]} in slot ${deviceSlot}`;
         }
+
         return device[inputIndex];
     }
 
     /**
-     * Dispose of all the eventlisteners and clears the observables
+     * Dispose of all the eventlisteners
      */
     public dispose() {
-        this.onDeviceConnectedObservable.clear();
-        this.onDeviceDisconnectedObservable.clear();
-
         // Keyboard Events
         if (this._keyboardActive) {
             window.removeEventListener("keydown", this._keyboardDownEvent);
@@ -109,31 +114,37 @@ export class DeviceInputSystem implements IDisposable {
 
     // Private functions
     /**
-     * Add device and inputs to device map
-     * @param deviceName Assigned name of device (may be SN)
+     * Add device and inputs to device array
+     * @param deviceType Enum specifiying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
      * @param numberOfInputs Number of input entries to create for given device
      */
-    private _registerDevice(deviceName: string, numberOfInputs: number) {
-        if (!this._inputs[deviceName]) {
+    private _registerDevice(deviceType: DeviceType, deviceSlot: number, numberOfInputs: number) {
+        if (!this._inputs[deviceType]) {
+            this._inputs[deviceType] = [];
+        }
+
+        if (!this._inputs[deviceType][deviceSlot]) {
             const device = new Array<Nullable<number>>(numberOfInputs);
 
             for (let i = 0; i < numberOfInputs; i++) {
                 device[i] = null;
             }
 
-            this._inputs[deviceName] = device;
-            this.onDeviceConnectedObservable.notifyObservers(deviceName);
+            this._inputs[deviceType][deviceSlot] = device;
+            this.onDeviceConnected(deviceType, deviceSlot);
         }
     }
 
     /**
      * Given a specific device name, remove that device from the device map
-     * @param deviceName Name of device to be removed
+     * @param deviceType Enum specifiying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
      */
-    private _unregisterDevice(deviceName: string) {
-        if (this._inputs[deviceName]) {
-            delete this._inputs[deviceName];
-            this.onDeviceDisconnectedObservable.notifyObservers(deviceName);
+    private _unregisterDevice(deviceType: DeviceType, deviceSlot: number) {
+        if (this._inputs[deviceType][deviceSlot]) {
+            delete this._inputs[deviceType][deviceSlot];
+            this.onDeviceDisconnected(deviceType, deviceSlot);
         }
     }
 
@@ -144,18 +155,24 @@ export class DeviceInputSystem implements IDisposable {
         this._keyboardDownEvent = ((evt) => {
             if (!this._keyboardActive) {
                 this._keyboardActive = true;
-                this._registerDevice(DeviceInputSystem.KEYBOARD_DEVICE, DeviceInputSystem._MAX_KEYCODES);
+                this._registerDevice(DeviceType.Keyboard, 0, DeviceInputSystem._MAX_KEYCODES);
             }
 
-            const kbKey = this._inputs[DeviceInputSystem.KEYBOARD_DEVICE];
+            const kbKey = this._inputs[DeviceType.Keyboard][0];
             if (kbKey) {
+                if (this.onInputChanged) {
+                    this.onInputChanged(DeviceType.Keyboard, 0, evt.keyCode, kbKey[evt.keyCode], 1);
+                }
                 kbKey[evt.keyCode] = 1;
             }
         });
 
         this._keyboardUpEvent = ((evt) => {
-            const kbKey = this._inputs[DeviceInputSystem.KEYBOARD_DEVICE];
+            const kbKey = this._inputs[DeviceType.Keyboard][0];
             if (kbKey) {
+                if (this.onInputChanged) {
+                    this.onInputChanged(DeviceType.Keyboard, 0, evt.keyCode, kbKey[evt.keyCode], 0);
+                }
                 kbKey[evt.keyCode] = 0;
             }
         });
@@ -169,29 +186,49 @@ export class DeviceInputSystem implements IDisposable {
      */
     private _handlePointerActions() {
         this._pointerMoveEvent = ((evt) => {
-            const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
-            if (!this._pointerActive) {
+            const deviceType = (evt.pointerType == "mouse") ? DeviceType.Mouse : DeviceType.Touch;
+            const deviceSlot = (evt.pointerType == "mouse") ? 0 : evt.pointerId;
+
+            if (!this._inputs[deviceType]) {
+                this._inputs[deviceType] = [];
+            }
+
+            if (!this._inputs[deviceType][deviceSlot]) {
                 this._pointerActive = true;
-                this._registerDevice(deviceName, DeviceInputSystem._MAX_POINTER_INPUTS);
+                this._registerDevice(deviceType, deviceSlot, DeviceInputSystem._MAX_POINTER_INPUTS);
             }
 
-            const pointer = this._inputs[deviceName];
+            const pointer = this._inputs[deviceType][deviceSlot];
             if (pointer) {
+                if (this.onInputChanged) {
+                    this.onInputChanged(deviceType, deviceSlot, 0, pointer[0], evt.clientX);
+                    this.onInputChanged(deviceType, deviceSlot, 1, pointer[1], evt.clientY);
+                }
                 pointer[0] = evt.clientX;
                 pointer[1] = evt.clientY;
             }
         });
 
         this._pointerDownEvent = ((evt) => {
+            const deviceType = (evt.pointerType == "mouse") ? DeviceType.Mouse : DeviceType.Touch;
+            const deviceSlot = (evt.pointerType == "mouse") ? 0 : evt.pointerId;
 
-            const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
-            if (!this._pointerActive) {
+            if (!this._inputs[deviceType]) {
+                this._inputs[deviceType] = [];
+            }
+
+            if (!this._inputs[deviceType][deviceSlot]) {
                 this._pointerActive = true;
-                this._registerDevice(deviceName, DeviceInputSystem._MAX_POINTER_INPUTS);
+                this._registerDevice(deviceType, deviceSlot, DeviceInputSystem._MAX_POINTER_INPUTS);
             }
 
-            const pointer = this._inputs[deviceName];
+            const pointer = this._inputs[deviceType][deviceSlot];
             if (pointer) {
+                if (this.onInputChanged) {
+                    this.onInputChanged(deviceType, deviceSlot, 0, pointer[0], evt.clientX);
+                    this.onInputChanged(deviceType, deviceSlot, 1, pointer[1], evt.clientY);
+                    this.onInputChanged(deviceType, deviceSlot, evt.button + 2, pointer[evt.button + 2], 1);
+                }
                 pointer[0] = evt.clientX;
                 pointer[1] = evt.clientY;
                 pointer[evt.button + 2] = 1;
@@ -199,15 +236,19 @@ export class DeviceInputSystem implements IDisposable {
         });
 
         this._pointerUpEvent = ((evt) => {
-            const deviceName = `${DeviceInputSystem.POINTER_DEVICE}-${evt.pointerId}`;
+            const deviceType = (evt.pointerType == "mouse") ? DeviceType.Mouse : DeviceType.Touch;
+            const deviceSlot = (evt.pointerType == "mouse") ? 0 : evt.pointerId;
 
-            const pointer = this._inputs[deviceName];
+            const pointer = this._inputs[deviceType][deviceSlot];
             if (pointer) {
+                if (this.onInputChanged) {
+                    this.onInputChanged(deviceType, deviceSlot, evt.button + 2, pointer[evt.button + 2], 0);
+                }
                 pointer[evt.button + 2] = 0;
             }
-            if (evt.pointerId != 1) // Don't unregister the mouse
-            {
-                this._unregisterDevice(deviceName);
+            // We don't want to unregister the mouse because we may miss input data when a mouse is moving after a click
+            if (evt.pointerType != "mouse") {
+                this._unregisterDevice(deviceType, deviceSlot);
             }
 
         });
@@ -222,16 +263,22 @@ export class DeviceInputSystem implements IDisposable {
      */
     private _handleGamepadActions() {
         this._gamepadConnectedEvent = ((evt: any) => {
-            const deviceName = `${evt.gamepad.id}-${evt.gamepad.index}`;
-            this._registerDevice(deviceName, evt.gamepad.buttons.length + evt.gamepad.axes.length);
+            const deviceType = this._getGamepadDeviceType(evt.gamepad.id);
+            const deviceSlot = evt.gamepad.index;
+
+            this._registerDevice(deviceType, deviceSlot, evt.gamepad.buttons.length + evt.gamepad.axes.length);
             this._gamepads = this._gamepads || new Array<string>(evt.gamepad.index + 1);
-            this._gamepads[evt.gamepad.index] = deviceName;
+            this._gamepads[deviceSlot] = deviceType;
         });
 
         this._gamepadDisconnectedEvent = ((evt: any) => {
-            const deviceName = this._gamepads[evt.gamepad.index];
-            this._unregisterDevice(deviceName);
-            delete this._gamepads[evt.gamepad.index];
+            if (this._gamepads) {
+                const deviceType = this._getGamepadDeviceType(evt.gamepad.id);
+                const deviceSlot = evt.gamepad.index;
+
+                this._unregisterDevice(deviceType, deviceSlot);
+                delete this._gamepads[deviceSlot];
+            }
         });
 
         window.addEventListener("gamepadconnected", this._gamepadConnectedEvent);
@@ -240,23 +287,42 @@ export class DeviceInputSystem implements IDisposable {
 
     /**
      * Update all non-event based devices with each frame
+     * @param deviceType Enum specifiying device type
+     * @param deviceSlot "Slot" or index that device is referenced in
+     * @param inputIndex Id of input to be checked
      */
-    private _updateDevice(deviceName: string, inputIndex: number) {
+    private _updateDevice(deviceType: DeviceType, deviceSlot: number, inputIndex: number) {
         // Gamepads
-        const gamepads = navigator.getGamepads();
+        const gp = navigator.getGamepads()[deviceSlot];
 
-        // Look for current gamepad and get updated values
-        for (const gp of gamepads) {
-            if (gp && deviceName == this._gamepads[gp.index]) {
-                const device = this._inputs[deviceName];
+        if (gp && deviceType == this._gamepads[deviceSlot]) {
+            const device = this._inputs[deviceType][deviceSlot];
 
-                if (inputIndex >= gp.buttons.length) {
-                    device[inputIndex] = gp.axes[inputIndex - gp.buttons.length].valueOf();
-                }
-                else {
-                    device[inputIndex] = gp.buttons[inputIndex].value;
-                }
+            if (inputIndex >= gp.buttons.length) {
+                device[inputIndex] = gp.axes[inputIndex - gp.buttons.length].valueOf();
+            }
+            else {
+                device[inputIndex] = gp.buttons[inputIndex].value;
             }
         }
     }
+
+    /**
+     * Gets DeviceType from the device name
+     * @param deviceName Name of Device from DeviceInputSystem
+     * @returns DeviceType enum value
+     */
+    private _getGamepadDeviceType(deviceName: string): DeviceType {
+        if (deviceName.indexOf("054c") !== -1) { // DualShock 4 Gamepad
+            return DeviceType.DualShock;
+        }
+        else if (deviceName.indexOf("Xbox One") !== -1 || deviceName.search("Xbox 360") !== -1 || deviceName.search("xinput") !== -1) { // Xbox Gamepad
+            return DeviceType.Xbox;
+        }
+        else if (deviceName.indexOf("057e") !== -1) { // Switch Gamepad
+            return DeviceType.Switch;
+        }
+
+        return DeviceType.Generic;
+    }
 }

+ 5 - 0
src/DeviceInput/index.ts

@@ -1 +1,6 @@
 export * from "./deviceInputSystem";
+export * from "./InputDevices/deviceEnums";
+export * from "./InputDevices/deviceTypes";
+
+import "./InputDevices/deviceSourceManager";
+export * from "./InputDevices/deviceSourceManager";