-
-
Notifications
You must be signed in to change notification settings - Fork 4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support GearVR 3DOF controller #2545
Changes from all commits
d89a2ff
70996d2
62b03eb
20e8ec8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
--- | ||
title: gearvr-controls | ||
type: components | ||
layout: docs | ||
parent_section: components | ||
--- | ||
|
||
[trackedcontrols]: ./tracked-controls.md | ||
|
||
The gearvr-controls component interfaces with the Samsung/Oculus Gear VR controllers. | ||
It wraps the [tracked-controls component][trackedcontrols] while adding button | ||
mappings, events, and a Gear VR controller model that highlights the touched | ||
and/or pressed buttons (trackpad, trigger). | ||
|
||
## Example | ||
|
||
```html | ||
<!-- Match Gear VR controller if present, regardless of hand. --> | ||
<a-entity gearvr-controls></a-entity> | ||
|
||
<!-- Match Gear VR controller if present and for specified hand. --> | ||
<a-entity gearvr-controls="hand: left"></a-entity> | ||
<a-entity gearvr-controls="hand: right"></a-entity> | ||
``` | ||
|
||
## Value | ||
|
||
| Property | Description | Default | | ||
|----------------------|----------------------------------------------------|---------| | ||
| buttonColor | Button colors when not pressed. | #000000 | | ||
| buttonTouchedColor | Button colors when touched. | #777777 | | ||
| buttonHighlightColor | Button colors when pressed and active. | #FFFFFF | | ||
| hand | The hand that will be tracked (e.g., right, left). | | | ||
| model | Whether the Daydream controller model is loaded. | true | | ||
| rotationOffset | Offset to apply to model rotation. | 0 | | ||
|
||
## Events | ||
|
||
| Event Name | Description | | ||
| ---------- | ----------- | | ||
| trackpaddown | Trackpad pressed. | | ||
| trackpadup | Trackpad released. | | ||
| trackpadtouchstart | Trackpad touched. | | ||
| trackpadtouchend | Trackpad not touched. | | ||
| triggerdown | Trigger pressed. | | ||
| triggerup | Trigger released. | | ||
|
||
## Assets | ||
|
||
- [Controller OBJ](https://cdn.aframe.io/controllers/google/vr_controller_daydream.obj) | ||
- [Controller MTL](https://cdn.aframe.io/controllers/google/vr_controller_daydream.mtl) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
var registerComponent = require('../core/component').registerComponent; | ||
var bind = require('../utils/bind'); | ||
var isControllerPresent = require('../utils/tracked-controls').isControllerPresent; | ||
|
||
var GEARVR_CONTROLLER_MODEL_BASE_URL = 'https://cdn.aframe.io/controllers/samsung/'; | ||
var GEARVR_CONTROLLER_MODEL_OBJ_URL = GEARVR_CONTROLLER_MODEL_BASE_URL + 'gear_vr_controller.obj'; | ||
var GEARVR_CONTROLLER_MODEL_OBJ_MTL = GEARVR_CONTROLLER_MODEL_BASE_URL + 'gear_vr_controller.mtl'; | ||
|
||
var GAMEPAD_ID_PREFIX = 'Gear VR'; | ||
|
||
/** | ||
* Vive Controls Component | ||
* Interfaces with vive controllers and maps Gamepad events to | ||
* common controller buttons: trackpad, trigger, grip, menu and system | ||
* It loads a controller model and highlights the pressed buttons | ||
*/ | ||
module.exports.Component = registerComponent('gearvr-controls', { | ||
schema: { | ||
hand: {default: ''}, // This informs the degenerate arm model. | ||
buttonColor: {type: 'color', default: '#000000'}, | ||
buttonTouchedColor: {type: 'color', default: '#777777'}, | ||
buttonHighlightColor: {type: 'color', default: '#FFFFFF'}, | ||
model: {default: true}, | ||
rotationOffset: {default: 0} // use -999 as sentinel value to auto-determine based on hand | ||
}, | ||
|
||
// buttonId | ||
// 0 - trackpad | ||
// 1 - triggeri | ||
mapping: { | ||
axis0: 'trackpad', | ||
axis1: 'trackpad', | ||
button0: 'trackpad', | ||
button1: 'trigger' | ||
}, | ||
|
||
bindMethods: function () { | ||
this.onModelLoaded = bind(this.onModelLoaded, this); | ||
this.onControllersUpdate = bind(this.onControllersUpdate, this); | ||
this.checkIfControllerPresent = bind(this.checkIfControllerPresent, this); | ||
this.removeControllersUpdateListener = bind(this.removeControllersUpdateListener, this); | ||
this.onGamepadConnected = bind(this.onGamepadConnected, this); | ||
this.onGamepadDisconnected = bind(this.onGamepadDisconnected, this); | ||
}, | ||
|
||
init: function () { | ||
var self = this; | ||
this.animationActive = 'pointing'; | ||
this.onButtonDown = function (evt) { self.onButtonEvent(evt.detail.id, 'down'); }; | ||
this.onButtonUp = function (evt) { self.onButtonEvent(evt.detail.id, 'up'); }; | ||
this.onButtonTouchStart = function (evt) { self.onButtonEvent(evt.detail.id, 'touchstart'); }; | ||
this.onButtonTouchEnd = function (evt) { self.onButtonEvent(evt.detail.id, 'touchend'); }; | ||
this.onAxisMoved = bind(this.onAxisMoved, this); | ||
this.controllerPresent = false; | ||
this.everGotGamepadEvent = false; | ||
this.lastControllerCheck = 0; | ||
this.bindMethods(); | ||
this.isControllerPresent = isControllerPresent; // to allow mock | ||
}, | ||
|
||
addEventListeners: function () { | ||
var el = this.el; | ||
el.addEventListener('buttondown', this.onButtonDown); | ||
el.addEventListener('buttonup', this.onButtonUp); | ||
el.addEventListener('touchstart', this.onButtonTouchStart); | ||
el.addEventListener('touchend', this.onButtonTouchEnd); | ||
el.addEventListener('model-loaded', this.onModelLoaded); | ||
el.addEventListener('axismove', this.onAxisMoved); | ||
}, | ||
|
||
removeEventListeners: function () { | ||
var el = this.el; | ||
el.removeEventListener('buttondown', this.onButtonDown); | ||
el.removeEventListener('buttonup', this.onButtonUp); | ||
el.removeEventListener('touchstart', this.onButtonTouchStart); | ||
el.removeEventListener('touchend', this.onButtonTouchEnd); | ||
el.removeEventListener('model-loaded', this.onModelLoaded); | ||
el.removeEventListener('axismove', this.onAxisMoved); | ||
}, | ||
|
||
checkIfControllerPresent: function () { | ||
var isPresent = this.isControllerPresent(this.el.sceneEl, GAMEPAD_ID_PREFIX, this.data.hand ? {hand: this.data.hand} : {}); | ||
if (isPresent === this.controllerPresent) { return; } | ||
this.controllerPresent = isPresent; | ||
if (isPresent) { this.injectTrackedControls(); } // inject track-controls | ||
}, | ||
|
||
onGamepadConnected: function (evt) { | ||
// for now, don't disable controller update listening, due to | ||
// apparent issue with FF Nightly only sending one event and seeing one controller; | ||
// this.everGotGamepadEvent = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we want to set this flag? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// this.removeControllersUpdateListener(); | ||
this.checkIfControllerPresent(); | ||
}, | ||
|
||
onGamepadDisconnected: function (evt) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this method needed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// for now, don't disable controller update listening, due to | ||
// apparent issue with FF Nightly only sending one event and seeing one controller; | ||
// this.everGotGamepadEvent = true; | ||
// this.removeControllersUpdateListener(); | ||
this.checkIfControllerPresent(); | ||
}, | ||
|
||
play: function () { | ||
this.checkIfControllerPresent(); | ||
window.addEventListener('gamepadconnected', this.onGamepadConnected, false); | ||
window.addEventListener('gamepaddisconnected', this.onGamepadDisconnected, false); | ||
this.addControllersUpdateListener(); | ||
this.addEventListeners(); | ||
}, | ||
|
||
pause: function () { | ||
window.removeEventListener('gamepadconnected', this.onGamepadConnected, false); | ||
window.removeEventListener('gamepaddisconnected', this.onGamepadDisconnected, false); | ||
this.removeControllersUpdateListener(); | ||
this.removeEventListeners(); | ||
}, | ||
|
||
injectTrackedControls: function () { | ||
var el = this.el; | ||
var data = this.data; | ||
el.setAttribute('tracked-controls', {idPrefix: GAMEPAD_ID_PREFIX, rotationOffset: data.rotationOffset}); | ||
if (!this.data.model) { return; } | ||
this.el.setAttribute('obj-model', { | ||
obj: GEARVR_CONTROLLER_MODEL_OBJ_URL, | ||
mtl: GEARVR_CONTROLLER_MODEL_OBJ_MTL | ||
}); | ||
}, | ||
|
||
addControllersUpdateListener: function () { | ||
this.el.sceneEl.addEventListener('controllersupdated', this.onControllersUpdate, false); | ||
}, | ||
|
||
removeControllersUpdateListener: function () { | ||
this.el.sceneEl.removeEventListener('controllersupdated', this.onControllersUpdate, false); | ||
}, | ||
|
||
onControllersUpdate: function () { | ||
if (!this.everGotGamepadEvent) { this.checkIfControllerPresent(); } | ||
}, | ||
|
||
// No need for onButtonChanged, since Daydream controller has no analog buttons. | ||
|
||
onModelLoaded: function (evt) { | ||
var controllerObject3D = evt.detail.model; | ||
var buttonMeshes; | ||
if (!this.data.model) { return; } | ||
buttonMeshes = this.buttonMeshes = {}; | ||
buttonMeshes.trigger = controllerObject3D.getObjectByName('Trigger'); | ||
buttonMeshes.trackpad = controllerObject3D.getObjectByName('Touchpad'); | ||
}, | ||
|
||
onAxisMoved: function (evt) { | ||
if (evt.detail.axis[0] === 0 && evt.detail.axis[1] === 0) { return; } | ||
this.el.emit('trackpadmoved', { x: evt.detail.axis[0], y: evt.detail.axis[1] }); | ||
}, | ||
|
||
onButtonEvent: function (id, evtName) { | ||
var buttonName = this.mapping['button' + id]; | ||
var i; | ||
if (Array.isArray(buttonName)) { | ||
for (i = 0; i < buttonName.length; i++) { | ||
this.el.emit(buttonName[i] + evtName); | ||
} | ||
} else { | ||
this.el.emit(buttonName + evtName); | ||
} | ||
this.updateModel(buttonName, evtName); | ||
}, | ||
|
||
updateModel: function (buttonName, evtName) { | ||
var i; | ||
if (!this.data.model) { return; } | ||
if (Array.isArray(buttonName)) { | ||
for (i = 0; i < buttonName.length; i++) { | ||
this.updateButtonModel(buttonName[i], evtName); | ||
} | ||
} else { | ||
this.updateButtonModel(buttonName, evtName); | ||
} | ||
}, | ||
|
||
updateButtonModel: function (buttonName, state) { | ||
var buttonMeshes = this.buttonMeshes; | ||
if (!buttonMeshes || !buttonMeshes[buttonName]) { return; } | ||
var color; | ||
switch (state) { | ||
case 'down': | ||
color = this.data.buttonHighlightColor; | ||
break; | ||
case 'touchstart': | ||
color = this.data.buttonTouchedColor; | ||
break; | ||
default: | ||
color = this.data.buttonColor; | ||
} | ||
buttonMeshes[buttonName].material.color.set(color); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not this follow the convention established by #2513? https://github.com/aframevr/aframe/pull/2513/files#diff-ae2d3da90e49853ba9d7c7ea05862707R35
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, but you haven't merged it yet! from 5 days ago: