WebVRManager.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /**
  2. * @author mrdoob / http://mrdoob.com/
  3. */
  4. import { Group } from '../../objects/Group.js';
  5. import { Matrix4 } from '../../math/Matrix4.js';
  6. import { Vector2 } from '../../math/Vector2.js';
  7. import { Vector3 } from '../../math/Vector3.js';
  8. import { Vector4 } from '../../math/Vector4.js';
  9. import { Quaternion } from '../../math/Quaternion.js';
  10. import { ArrayCamera } from '../../cameras/ArrayCamera.js';
  11. import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
  12. import { WebGLAnimation } from '../webgl/WebGLAnimation.js';
  13. import { setProjectionFromUnion } from './WebVRUtils.js';
  14. function WebVRManager( renderer ) {
  15. var renderWidth, renderHeight;
  16. var scope = this;
  17. var device = null;
  18. var frameData = null;
  19. var poseTarget = null;
  20. var controllers = [];
  21. var standingMatrix = new Matrix4();
  22. var standingMatrixInverse = new Matrix4();
  23. var framebufferScaleFactor = 1.0;
  24. var frameOfReferenceType = 'stage';
  25. if ( typeof window !== 'undefined' && 'VRFrameData' in window ) {
  26. frameData = new window.VRFrameData();
  27. window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );
  28. }
  29. var matrixWorldInverse = new Matrix4();
  30. var tempQuaternion = new Quaternion();
  31. var tempPosition = new Vector3();
  32. var cameraL = new PerspectiveCamera();
  33. cameraL.viewport = new Vector4();
  34. cameraL.layers.enable( 1 );
  35. var cameraR = new PerspectiveCamera();
  36. cameraR.viewport = new Vector4();
  37. cameraR.layers.enable( 2 );
  38. var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
  39. cameraVR.layers.enable( 1 );
  40. cameraVR.layers.enable( 2 );
  41. //
  42. function isPresenting() {
  43. return device !== null && device.isPresenting === true;
  44. }
  45. var currentSize = new Vector2(), currentPixelRatio;
  46. function onVRDisplayPresentChange() {
  47. if ( isPresenting() ) {
  48. var eyeParameters = device.getEyeParameters( 'left' );
  49. renderWidth = eyeParameters.renderWidth * framebufferScaleFactor;
  50. renderHeight = eyeParameters.renderHeight * framebufferScaleFactor;
  51. currentPixelRatio = renderer.getPixelRatio();
  52. renderer.getSize( currentSize );
  53. renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );
  54. cameraL.viewport.set( 0, 0, renderWidth, renderHeight );
  55. cameraR.viewport.set( renderWidth, 0, renderWidth, renderHeight );
  56. animation.start();
  57. } else {
  58. if ( scope.enabled ) {
  59. renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio );
  60. }
  61. animation.stop();
  62. }
  63. }
  64. //
  65. var triggers = [];
  66. function findGamepad( id ) {
  67. var gamepads = navigator.getGamepads && navigator.getGamepads();
  68. for ( var i = 0, j = 0, l = gamepads.length; i < l; i ++ ) {
  69. var gamepad = gamepads[ i ];
  70. if ( gamepad && ( gamepad.id === 'Daydream Controller' ||
  71. gamepad.id === 'Gear VR Controller' || gamepad.id === 'Oculus Go Controller' ||
  72. gamepad.id === 'OpenVR Gamepad' || gamepad.id.startsWith( 'Oculus Touch' ) ||
  73. gamepad.id.startsWith( 'Spatial Controller' ) ) ) {
  74. if ( j === id ) return gamepad;
  75. j ++;
  76. }
  77. }
  78. }
  79. function updateControllers() {
  80. for ( var i = 0; i < controllers.length; i ++ ) {
  81. var controller = controllers[ i ];
  82. var gamepad = findGamepad( i );
  83. if ( gamepad !== undefined && gamepad.pose !== undefined ) {
  84. if ( gamepad.pose === null ) return;
  85. // Pose
  86. var pose = gamepad.pose;
  87. if ( pose.hasPosition === false ) controller.position.set( 0.2, - 0.6, - 0.05 );
  88. if ( pose.position !== null ) controller.position.fromArray( pose.position );
  89. if ( pose.orientation !== null ) controller.quaternion.fromArray( pose.orientation );
  90. controller.matrix.compose( controller.position, controller.quaternion, controller.scale );
  91. controller.matrix.premultiply( standingMatrix );
  92. controller.matrix.decompose( controller.position, controller.quaternion, controller.scale );
  93. controller.matrixWorldNeedsUpdate = true;
  94. controller.visible = true;
  95. // Trigger
  96. var buttonId = gamepad.id === 'Daydream Controller' ? 0 : 1;
  97. if ( triggers[ i ] === undefined ) triggers[ i ] = false;
  98. if ( triggers[ i ] !== gamepad.buttons[ buttonId ].pressed ) {
  99. triggers[ i ] = gamepad.buttons[ buttonId ].pressed;
  100. if ( triggers[ i ] === true ) {
  101. controller.dispatchEvent( { type: 'selectstart' } );
  102. } else {
  103. controller.dispatchEvent( { type: 'selectend' } );
  104. controller.dispatchEvent( { type: 'select' } );
  105. }
  106. }
  107. } else {
  108. controller.visible = false;
  109. }
  110. }
  111. }
  112. function updateViewportFromBounds( viewport, bounds ) {
  113. if ( bounds !== null && bounds.length === 4 ) {
  114. viewport.set( bounds[ 0 ] * renderWidth, bounds[ 1 ] * renderHeight, bounds[ 2 ] * renderWidth, bounds[ 3 ] * renderHeight );
  115. }
  116. }
  117. //
  118. this.enabled = false;
  119. this.getController = function ( id ) {
  120. var controller = controllers[ id ];
  121. if ( controller === undefined ) {
  122. controller = new Group();
  123. controller.matrixAutoUpdate = false;
  124. controller.visible = false;
  125. controllers[ id ] = controller;
  126. }
  127. return controller;
  128. };
  129. this.getDevice = function () {
  130. return device;
  131. };
  132. this.setDevice = function ( value ) {
  133. if ( value !== undefined ) device = value;
  134. animation.setContext( value );
  135. };
  136. this.setFramebufferScaleFactor = function ( value ) {
  137. framebufferScaleFactor = value;
  138. };
  139. this.setFrameOfReferenceType = function ( value ) {
  140. frameOfReferenceType = value;
  141. };
  142. this.setPoseTarget = function ( object ) {
  143. if ( object !== undefined ) poseTarget = object;
  144. };
  145. this.getCamera = function ( camera ) {
  146. var userHeight = frameOfReferenceType === 'stage' ? 1.6 : 0;
  147. if ( isPresenting() === false ) {
  148. camera.position.set( 0, userHeight, 0 );
  149. camera.rotation.set( 0, 0, 0 );
  150. return camera;
  151. }
  152. device.depthNear = camera.near;
  153. device.depthFar = camera.far;
  154. device.getFrameData( frameData );
  155. //
  156. if ( frameOfReferenceType === 'stage' ) {
  157. var stageParameters = device.stageParameters;
  158. if ( stageParameters ) {
  159. standingMatrix.fromArray( stageParameters.sittingToStandingTransform );
  160. } else {
  161. standingMatrix.makeTranslation( 0, userHeight, 0 );
  162. }
  163. }
  164. var pose = frameData.pose;
  165. var poseObject = poseTarget !== null ? poseTarget : camera;
  166. // We want to manipulate poseObject by its position and quaternion components since users may rely on them.
  167. poseObject.matrix.copy( standingMatrix );
  168. poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale );
  169. if ( pose.orientation !== null ) {
  170. tempQuaternion.fromArray( pose.orientation );
  171. poseObject.quaternion.multiply( tempQuaternion );
  172. }
  173. if ( pose.position !== null ) {
  174. tempQuaternion.setFromRotationMatrix( standingMatrix );
  175. tempPosition.fromArray( pose.position );
  176. tempPosition.applyQuaternion( tempQuaternion );
  177. poseObject.position.add( tempPosition );
  178. }
  179. poseObject.updateMatrixWorld();
  180. //
  181. cameraL.near = camera.near;
  182. cameraR.near = camera.near;
  183. cameraL.far = camera.far;
  184. cameraR.far = camera.far;
  185. cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix );
  186. cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix );
  187. // TODO (mrdoob) Double check this code
  188. standingMatrixInverse.getInverse( standingMatrix );
  189. if ( frameOfReferenceType === 'stage' ) {
  190. cameraL.matrixWorldInverse.multiply( standingMatrixInverse );
  191. cameraR.matrixWorldInverse.multiply( standingMatrixInverse );
  192. }
  193. var parent = poseObject.parent;
  194. if ( parent !== null ) {
  195. matrixWorldInverse.getInverse( parent.matrixWorld );
  196. cameraL.matrixWorldInverse.multiply( matrixWorldInverse );
  197. cameraR.matrixWorldInverse.multiply( matrixWorldInverse );
  198. }
  199. // envMap and Mirror needs camera.matrixWorld
  200. cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse );
  201. cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse );
  202. cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix );
  203. cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix );
  204. setProjectionFromUnion( cameraVR, cameraL, cameraR );
  205. //
  206. var layers = device.getLayers();
  207. if ( layers.length ) {
  208. var layer = layers[ 0 ];
  209. updateViewportFromBounds( cameraL.viewport, layer.leftBounds );
  210. updateViewportFromBounds( cameraR.viewport, layer.rightBounds );
  211. }
  212. updateControllers();
  213. return cameraVR;
  214. };
  215. this.getStandingMatrix = function () {
  216. return standingMatrix;
  217. };
  218. this.isPresenting = isPresenting;
  219. // Animation Loop
  220. var animation = new WebGLAnimation();
  221. this.setAnimationLoop = function ( callback ) {
  222. animation.setAnimationLoop( callback );
  223. if ( isPresenting() ) animation.start();
  224. };
  225. this.submitFrame = function () {
  226. if ( isPresenting() ) device.submitFrame();
  227. };
  228. this.dispose = function () {
  229. if ( typeof window !== 'undefined' ) {
  230. window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange );
  231. }
  232. };
  233. }
  234. export { WebVRManager };
粤ICP备19079148号