Skip to content

Commit

Permalink
3DOF support in tracked-controls, daydream-controls, head/arm model (#…
Browse files Browse the repository at this point in the history
…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
machenmusik authored and dmarcos committed Apr 7, 2017
1 parent 29d3e13 commit cb1399e
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 24 deletions.
46 changes: 46 additions & 0 deletions docs/components/daydream-controls.md
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)

5 changes: 3 additions & 2 deletions docs/components/hand-controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ parent_section: components
[tracked]: ./tracked-controls.md
[vive]: ./vive-controls.md
[oculustouch]: ./oculus-touch-controls.md
[daydream]: ./daydream-controls.md

The hand-controls gives tracked hands (using a prescribed model) with animated
gestures. hand-controls wraps the [vive-controls][vive] and
[oculus-touch-controls][oculustouch] components, which in turn wrap the
gestures. hand-controls wraps the [vive-controls][vive], [oculus-touch-controls][oculustouch],
and [daydream-controls][daydream] components, which in turn wrap the
[tracked-controls component][tracked]. The component gives extra events and
handles hand animations and poses.

Expand Down
24 changes: 17 additions & 7 deletions docs/components/tracked-controls.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ parent_section: components
[handcontrols]: ./hand-controls.md
[oculustouchcontrols]: ./oculus-touch-controls.md
[vivecontrols]: ./vive-controls.md
[daydreamcontrols]: ./daydream-controls.md

The tracked-controls component interfaces with tracked controllers.
tracked-controls uses the Gamepad API to handle tracked controllers, and is
abstracted by the [hand-controls component][handcontrols] as well as the
[vive-controls][vivecontrols] and [oculus-touch-controls][oculustouchcontrols]
[vive-controls][vivecontrols], [oculus-touch-controls][oculustouchcontrols], and
[daydream-controls][daydreamcontrols]
components. This component elects the appropriate controller, applies pose to
the entity, observes buttons state and emits appropriate events.
the entity, observes buttons state and emits appropriate events. For non-6DOF controllers
such as [daydream-controls][daydreamcontrols], a primitive arm model is used to emulate
positional data.

## Example

Expand All @@ -28,11 +32,17 @@ so using idPrefix for Vive / OpenVR controllers is recommended.

## Value

| Property | Description | Default Value |
|-------------|-------------------------------------------------------------- --|---------------|
| controller | Index of the controller in array returned by the Gamepad API. | 0 |
| id | Selects the controller from the Gamepad API using exact match. | |
| idPrefix | Selects the controller from the Gamepad API using prefix match. | |
| Property | Description | Default Value |
|-------------------|-------------------------------------------------------------- --|----------------------------|
| controller | Index of the controller in array returned by the Gamepad API. | 0 |
| id | Selects the controller from the Gamepad API using exact match. | |
| idPrefix | Selects the controller from the Gamepad API using prefix match. | |
| rotationOffset | Offset to add to model rotation. | 0 |
| headElement | Head element for arm model if needed (if not active camera). | |
| hand | Which hand to use, if arm model is needed. (left negates X) | right |
| eyesToElbow | Arm model vector from eyes to elbow as user height ratio. | {x:0.175, y:-0.3, z:-0.03} |
| forearm | Arm model vector for forearm as user height ratio. | {x:0, y:0, z:-0.175} |
| defaultUserHeight | Default user height (for cameras with zero). | 1.6 |

## Events

Expand Down
206 changes: 206 additions & 0 deletions src/components/daydream-controls.js
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);
}
});
1 change: 1 addition & 0 deletions src/components/hand-controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ module.exports.Component = registerComponent('hand-controls', {
}
el.setAttribute('vive-controls', controlConfiguration);
el.setAttribute('oculus-touch-controls', controlConfiguration);
el.setAttribute('daydream-controls', controlConfiguration);
el.setAttribute('blend-character-model', modelUrl);
},

Expand Down
1 change: 1 addition & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ require('./blend-character-model');
require('./camera');
require('./collada-model');
require('./cursor');
require('./daydream-controls');
require('./geometry');
require('./gltf-model');
require('./hand-controls');
Expand Down
Loading

0 comments on commit cb1399e

Please sign in to comment.