-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
3DOF support in tracked-controls, daydream-controls, head/arm model (#…
…2538) * add daydream-controls, using new 3DOF support in tracked-controls * add support for daydream-controls, using singlehand to determine which hand gets it * apply degenerate arm model when not 6DOF controller * if gamepad pose has no orientation, use camera * updated docs * add headElement to schema, so non-6DOF model can apply when head is not the active camera e.g. spectator view * remove arm model constants from schema; use default user height constant; hand => defaultHand * fix test case where controller was undefined * make default hand a constant, per discussion on PR * DEFAULT_HAND => DEFAULT_HANDEDNESS; daydream-controls should provide hand, so use it to filter, and attach to both hand-controls * move arm model into separate function
- Loading branch information
1 parent
29d3e13
commit cb1399e
Showing
8 changed files
with
357 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
--- | ||
title: daydream-controls | ||
type: components | ||
layout: docs | ||
parent_section: components | ||
--- | ||
|
||
[trackedcontrols]: ./tracked-controls.md | ||
|
||
The daydream-controls component interfaces with the Google Daydream controllers. | ||
It wraps the [tracked-controls component][trackedcontrols] while adding button | ||
mappings, events, and a Daydream controller model that highlights the touched | ||
and/or pressed buttons (trackpad). | ||
|
||
## Example | ||
|
||
```html | ||
<a-entity daydream-controls="hand: left"></a-entity> | ||
<a-entity daydream-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 (i.e., right, left). | right | | ||
| 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. | | ||
|
||
## 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) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
var registerComponent = require('../core/component').registerComponent; | ||
var bind = require('../utils/bind'); | ||
var isControllerPresent = require('../utils/tracked-controls').isControllerPresent; | ||
|
||
var DEFAULT_HANDEDNESS = require('../constants').DEFAULT_HANDEDNESS; | ||
|
||
var DAYDREAM_CONTROLLER_MODEL_BASE_URL = 'https://cdn.aframe.io/controllers/google/'; | ||
var DAYDREAM_CONTROLLER_MODEL_OBJ_URL = DAYDREAM_CONTROLLER_MODEL_BASE_URL + 'vr_controller_daydream.obj'; | ||
var DAYDREAM_CONTROLLER_MODEL_OBJ_MTL = DAYDREAM_CONTROLLER_MODEL_BASE_URL + 'vr_controller_daydream.mtl'; | ||
|
||
var GAMEPAD_ID_PREFIX = 'Daydream Controller'; | ||
|
||
/** | ||
* 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('daydream-controls', { | ||
schema: { | ||
hand: {default: DEFAULT_HANDEDNESS}, // 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 - menu ( never dispatched on this layer ) | ||
// 2 - system ( never dispatched on this layer ) | ||
mapping: { | ||
axis0: 'trackpad', | ||
axis1: 'trackpad', | ||
button0: 'trackpad', | ||
button1: 'menu', | ||
button2: 'system' | ||
}, | ||
|
||
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, {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; | ||
// this.removeControllersUpdateListener(); | ||
this.checkIfControllerPresent(); | ||
}, | ||
|
||
onGamepadDisconnected: 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; | ||
// 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, hand: data.hand, rotationOffset: data.rotationOffset}); | ||
if (!this.data.model) { return; } | ||
this.el.setAttribute('obj-model', { | ||
obj: DAYDREAM_CONTROLLER_MODEL_OBJ_URL, | ||
mtl: DAYDREAM_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.menu = controllerObject3D.getObjectByName('AppButton_AppButton_Cylinder.004'); | ||
buttonMeshes.system = controllerObject3D.getObjectByName('HomeButton_HomeButton_Cylinder.005'); | ||
buttonMeshes.trackpad = controllerObject3D.getObjectByName('TouchPad_TouchPad_Cylinder.003'); | ||
// Offset pivot point | ||
controllerObject3D.position.set(0, 0, -0.04); | ||
}, | ||
|
||
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); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.