11var registerComponent = require ( '../core/component' ) . registerComponent ;
2- var bind = require ( '../utils/bind' ) ;
3- var checkControllerPresentAndSetup = require ( '../utils/tracked-controls' ) . checkControllerPresentAndSetup ;
4- var emitIfAxesChanged = require ( '../utils/tracked-controls' ) . emitIfAxesChanged ;
2+ var utils = require ( '../utils/' ) ;
3+
4+ var bind = utils . bind ;
5+ var checkControllerPresentAndSetup = utils . trackedControls . checkControllerPresentAndSetup ;
6+ var emitIfAxesChanged = utils . trackedControls . emitIfAxesChanged ;
57
68var VIVE_CONTROLLER_MODEL_OBJ_URL = 'https://cdn.aframe.io/controllers/vive/vr_controller_vive.obj' ;
79var VIVE_CONTROLLER_MODEL_OBJ_MTL = 'https://cdn.aframe.io/controllers/vive/vr_controller_vive.mtl' ;
810
911var GAMEPAD_ID_PREFIX = 'OpenVR ' ;
1012
1113/**
12- * Vive Controls Component
13- * Interfaces with vive controllers and maps Gamepad events to
14- * common controller buttons: trackpad, trigger, grip, menu and system
15- * It loads a controller model and highlights the pressed buttons
14+ * Vive controls.
15+ * Interface with Vive controllers and map Gamepad events to controller buttons:
16+ * trackpad, trigger, grip, menu, system
17+ * Load a controller model and highlight the pressed buttons.
1618 */
1719module . exports . Component = registerComponent ( 'vive-controls' , {
1820 schema : {
1921 hand : { default : 'left' } ,
2022 buttonColor : { type : 'color' , default : '#FAFAFA' } , // Off-white.
2123 buttonHighlightColor : { type : 'color' , default : '#22D1EE' } , // Light blue.
2224 model : { default : true } ,
23- rotationOffset : { default : 0 } // use -999 as sentinel value to auto-determine based on hand
25+ rotationOffset : { default : 0 }
2426 } ,
2527
26- // buttonId
27- // 0 - trackpad
28- // 1 - trigger ( intensity value from 0.5 to 1 )
29- // 2 - grip
30- // 3 - menu ( dispatch but better for menu options )
31- // 4 - system ( never dispatched on this layer )
28+ /**
29+ * Button IDs:
30+ * 0 - trackpad
31+ * 1 - trigger (intensity value from 0.5 to 1)
32+ * 2 - grip
33+ * 3 - menu (dispatch but better for menu options)
34+ * 4 - system (never dispatched on this layer)
35+ */
3236 mapping : {
3337 axes : { 'trackpad' : [ 0 , 1 ] } ,
3438 buttons : [ 'trackpad' , 'trigger' , 'grip' , 'menu' , 'system' ]
3539 } ,
3640
37- // Use these labels for detail on axis events such as thumbstickmoved.
38- // e.g. for thumbstickmoved detail, the first axis returned is labeled x, and the second is labeled y.
41+ /**
42+ * Labels for detail on axis events such as `thumbstickmoved`.
43+ * For example, on `thumbstickmoved` detail, the first axis returned is labeled x, and the
44+ * second is labeled y.
45+ */
3946 axisLabels : [ 'x' , 'y' , 'z' , 'w' ] ,
4047
41- bindMethods : function ( ) {
42- this . onModelLoaded = bind ( this . onModelLoaded , this ) ;
43- this . onControllersUpdate = bind ( this . onControllersUpdate , this ) ;
44- this . checkIfControllerPresent = bind ( this . checkIfControllerPresent , this ) ;
45- this . removeControllersUpdateListener = bind ( this . removeControllersUpdateListener , this ) ;
46- this . onAxisMoved = bind ( this . onAxisMoved , this ) ;
47- } ,
48-
4948 init : function ( ) {
5049 var self = this ;
5150 this . animationActive = 'pointing' ;
51+ this . checkControllerPresentAndSetup = checkControllerPresentAndSetup ; // To allow mock.
52+ this . controllerPresent = false ;
53+ this . emitIfAxesChanged = emitIfAxesChanged ; // To allow mock.
54+ this . lastControllerCheck = 0 ;
5255 this . onButtonChanged = bind ( this . onButtonChanged , this ) ;
5356 this . onButtonDown = function ( evt ) { self . onButtonEvent ( evt . detail . id , 'down' ) ; } ;
5457 this . onButtonUp = function ( evt ) { self . onButtonEvent ( evt . detail . id , 'up' ) ; } ;
55- this . onButtonTouchStart = function ( evt ) { self . onButtonEvent ( evt . detail . id , 'touchstart' ) ; } ;
5658 this . onButtonTouchEnd = function ( evt ) { self . onButtonEvent ( evt . detail . id , 'touchend' ) ; } ;
59+ this . onButtonTouchStart = function ( evt ) { self . onButtonEvent ( evt . detail . id , 'touchstart' ) ; } ;
5760 this . onAxisMoved = bind ( this . onAxisMoved , this ) ;
58- this . controllerPresent = false ;
59- this . lastControllerCheck = 0 ;
6061 this . previousButtonValues = { } ;
62+
6163 this . bindMethods ( ) ;
62- this . checkControllerPresentAndSetup = checkControllerPresentAndSetup ; // to allow mock
63- this . emitIfAxesChanged = emitIfAxesChanged ; // to allow mock
64+ } ,
65+
66+ play : function ( ) {
67+ this . checkIfControllerPresent ( ) ;
68+ this . addControllersUpdateListener ( ) ;
69+ // Note that due to gamepadconnected event propagation issues, we don't rely on events.
70+ window . addEventListener ( 'gamepaddisconnected' , this . checkIfControllerPresent , false ) ;
71+ } ,
72+
73+ pause : function ( ) {
74+ this . removeEventListeners ( ) ;
75+ this . removeControllersUpdateListener ( ) ;
76+ // Note that due to gamepadconnected event propagation issues, we don't rely on events.
77+ window . removeEventListener ( 'gamepaddisconnected' , this . checkIfControllerPresent , false ) ;
78+ } ,
79+
80+ bindMethods : function ( ) {
81+ this . onModelLoaded = bind ( this . onModelLoaded , this ) ;
82+ this . onControllersUpdate = bind ( this . onControllersUpdate , this ) ;
83+ this . checkIfControllerPresent = bind ( this . checkIfControllerPresent , this ) ;
84+ this . removeControllersUpdateListener = bind ( this . removeControllersUpdateListener , this ) ;
85+ this . onAxisMoved = bind ( this . onAxisMoved , this ) ;
6486 } ,
6587
6688 addEventListeners : function ( ) {
6789 var el = this . el ;
6890 el . addEventListener ( 'buttonchanged' , this . onButtonChanged ) ;
6991 el . addEventListener ( 'buttondown' , this . onButtonDown ) ;
7092 el . addEventListener ( 'buttonup' , this . onButtonUp ) ;
71- el . addEventListener ( 'touchstart' , this . onButtonTouchStart ) ;
72- el . addEventListener ( 'touchend' , this . onButtonTouchEnd ) ;
7393 el . addEventListener ( 'model-loaded' , this . onModelLoaded ) ;
7494 el . addEventListener ( 'axismove' , this . onAxisMoved ) ;
7595 } ,
@@ -79,42 +99,35 @@ module.exports.Component = registerComponent('vive-controls', {
7999 el . removeEventListener ( 'buttonchanged' , this . onButtonChanged ) ;
80100 el . removeEventListener ( 'buttondown' , this . onButtonDown ) ;
81101 el . removeEventListener ( 'buttonup' , this . onButtonUp ) ;
82- el . removeEventListener ( 'touchstart' , this . onButtonTouchStart ) ;
83- el . removeEventListener ( 'touchend' , this . onButtonTouchEnd ) ;
84102 el . removeEventListener ( 'model-loaded' , this . onModelLoaded ) ;
85103 el . removeEventListener ( 'axismove' , this . onAxisMoved ) ;
86104 } ,
87105
106+ /**
107+ * Once OpenVR returns correct hand data in supporting browsers, we can use hand property.
108+ * var isPresent = this.checkControllerPresentAndSetup(this.el.sceneEl, GAMEPAD_ID_PREFIX,
109+ { hand: data.hand });
110+ * Until then, use hardcoded index.
111+ */
88112 checkIfControllerPresent : function ( ) {
89113 var data = this . data ;
90- // Once OpenVR / SteamVR return correct hand data in the supporting browsers, we can use hand property.
91- // var isPresent = this.checkControllerPresentAndSetup(this.el.sceneEl, GAMEPAD_ID_PREFIX, { hand: data.hand });
92- // Until then, use hardcoded index.
93114 var controllerIndex = data . hand === 'right' ? 0 : data . hand === 'left' ? 1 : 2 ;
94- this . checkControllerPresentAndSetup ( this , GAMEPAD_ID_PREFIX , { index : controllerIndex } ) ;
95- } ,
96-
97- play : function ( ) {
98- this . checkIfControllerPresent ( ) ;
99- this . addControllersUpdateListener ( ) ;
100- // Note that due to gamepadconnected event propagation issues, we don't rely on events.
101- window . addEventListener ( 'gamepaddisconnected' , this . checkIfControllerPresent , false ) ;
102- } ,
103-
104- pause : function ( ) {
105- this . removeEventListeners ( ) ;
106- this . removeControllersUpdateListener ( ) ;
107- // Note that due to gamepadconnected event propagation issues, we don't rely on events.
108- window . removeEventListener ( 'gamepaddisconnected' , this . checkIfControllerPresent , false ) ;
115+ this . checkControllerPresentAndSetup ( this , GAMEPAD_ID_PREFIX , { index : controllerIndex } ) ;
109116 } ,
110117
111118 injectTrackedControls : function ( ) {
112119 var el = this . el ;
113120 var data = this . data ;
114- // handId: 0 - right, 1 - left, 2 - anything else...
115- var controller = data . hand === 'right' ? 0 : data . hand === 'left' ? 1 : 2 ;
116- // if we have an OpenVR Gamepad, use the fixed mapping
117- el . setAttribute ( 'tracked-controls' , { idPrefix : GAMEPAD_ID_PREFIX , controller : controller , rotationOffset : data . rotationOffset } ) ;
121+
122+ // If we have an OpenVR Gamepad, use the fixed mapping.
123+ el . setAttribute ( 'tracked-controls' , {
124+ idPrefix : GAMEPAD_ID_PREFIX ,
125+ // Hand IDs: 0 = right, 1 = left, 2 = anything else.
126+ controller : data . hand === 'right' ? 0 : data . hand === 'left' ? 1 : 2 ,
127+ rotationOffset : data . rotationOffset
128+ } ) ;
129+
130+ // Load model.
118131 if ( ! this . data . model ) { return ; }
119132 this . el . setAttribute ( 'obj-model' , {
120133 obj : VIVE_CONTROLLER_MODEL_OBJ_URL ,
@@ -130,17 +143,23 @@ module.exports.Component = registerComponent('vive-controls', {
130143 this . el . sceneEl . removeEventListener ( 'controllersupdated' , this . onControllersUpdate , false ) ;
131144 } ,
132145
133- onControllersUpdate : function ( ) { this . checkIfControllerPresent ( ) ; } ,
146+ onControllersUpdate : function ( ) {
147+ this . checkIfControllerPresent ( ) ;
148+ } ,
134149
150+ /**
151+ * Rotate the trigger button based on how hard the trigger is pressed.
152+ */
135153 onButtonChanged : function ( evt ) {
136154 var button = this . mapping . buttons [ evt . detail . id ] ;
137155 var buttonMeshes = this . buttonMeshes ;
138156 var analogValue ;
157+
139158 if ( ! button ) { return ; }
140159
141160 if ( button === 'trigger' ) {
142161 analogValue = evt . detail . state . value ;
143- // Update button mesh, if any .
162+ // Update trigger rotation depending on button value .
144163 if ( buttonMeshes && buttonMeshes . trigger ) {
145164 buttonMeshes . trigger . rotation . x = - analogValue * ( Math . PI / 12 ) ;
146165 }
@@ -151,9 +170,13 @@ module.exports.Component = registerComponent('vive-controls', {
151170 } ,
152171
153172 onModelLoaded : function ( evt ) {
154- var controllerObject3D = evt . detail . model ;
155173 var buttonMeshes ;
174+ var controllerObject3D = evt . detail . model ;
175+ var self = this ;
176+
156177 if ( ! this . data . model ) { return ; }
178+
179+ // Store button meshes object to be able to change their colors.
157180 buttonMeshes = this . buttonMeshes = { } ;
158181 buttonMeshes . grip = {
159182 left : controllerObject3D . getObjectByName ( 'leftgrip' ) ,
@@ -163,41 +186,60 @@ module.exports.Component = registerComponent('vive-controls', {
163186 buttonMeshes . system = controllerObject3D . getObjectByName ( 'systembutton' ) ;
164187 buttonMeshes . trackpad = controllerObject3D . getObjectByName ( 'touchpad' ) ;
165188 buttonMeshes . trigger = controllerObject3D . getObjectByName ( 'trigger' ) ;
166- // Offset pivot point
189+
190+ // Set default colors.
191+ Object . keys ( buttonMeshes ) . forEach ( function ( buttonName ) {
192+ self . setButtonColor ( buttonName , self . data . buttonColor ) ;
193+ } ) ;
194+
195+ // Offset pivot point.
167196 controllerObject3D . position . set ( 0 , - 0.015 , 0.04 ) ;
168197 } ,
169198
170- onAxisMoved : function ( evt ) { this . emitIfAxesChanged ( this , this . mapping . axes , evt ) ; } ,
199+ onAxisMoved : function ( evt ) {
200+ this . emitIfAxesChanged ( this , this . mapping . axes , evt ) ;
201+ } ,
171202
172203 onButtonEvent : function ( id , evtName ) {
173204 var buttonName = this . mapping . buttons [ id ] ;
205+ var color ;
174206 var i ;
207+ var isTouch = evtName . indexOf ( 'touch' ) !== - 1 ;
208+
209+ // Only trackpad has touch. Ignore for the rest, even if Gamepad API says touched.
210+ if ( isTouch && buttonName !== 'trackpad' ) { return ; }
211+
212+ // Emit events.
175213 if ( Array . isArray ( buttonName ) ) {
176214 for ( i = 0 ; i < buttonName . length ; i ++ ) {
177215 this . el . emit ( buttonName [ i ] + evtName ) ;
178216 }
179217 } else {
180218 this . el . emit ( buttonName + evtName ) ;
181219 }
182- this . updateModel ( buttonName , evtName ) ;
183- } ,
184220
185- updateModel : function ( buttonName , evtName ) {
186- var i ;
187221 if ( ! this . data . model ) { return ; }
222+
223+ // Don't change color for trackpad touch.
224+ if ( isTouch ) { return ; }
225+
226+ // Update colors.
227+ color = evtName === 'up' ? this . data . buttonColor : this . data . buttonHighlightColor ;
188228 if ( Array . isArray ( buttonName ) ) {
189229 for ( i = 0 ; i < buttonName . length ; i ++ ) {
190- this . updateButtonModel ( buttonName [ i ] , evtName ) ;
230+ this . setButtonColor ( buttonName [ i ] , color ) ;
191231 }
192232 } else {
193- this . updateButtonModel ( buttonName , evtName ) ;
233+ this . setButtonColor ( buttonName , color ) ;
194234 }
195235 } ,
196236
197- updateButtonModel : function ( buttonName , state ) {
198- var color = state === 'up' ? this . data . buttonColor : this . data . buttonHighlightColor ;
237+ setButtonColor : function ( buttonName , color ) {
199238 var buttonMeshes = this . buttonMeshes ;
239+
200240 if ( ! buttonMeshes ) { return ; }
241+
242+ // Need to do both left and right sides for grip.
201243 if ( buttonName === 'grip' ) {
202244 buttonMeshes . grip . left . material . color . set ( color ) ;
203245 buttonMeshes . grip . right . material . color . set ( color ) ;
0 commit comments