XRHandModelFactory.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {
  2. Object3D
  3. } from 'three';
  4. import {
  5. XRHandPrimitiveModel
  6. } from './XRHandPrimitiveModel.js';
  7. import {
  8. XRHandMeshModel
  9. } from './XRHandMeshModel.js';
  10. /**
  11. * Represents a XR hand model.
  12. *
  13. * @augments Object3D
  14. */
  15. class XRHandModel extends Object3D {
  16. /**
  17. * Constructs a new XR hand model.
  18. *
  19. * @param {Group} controller - The hand controller.
  20. */
  21. constructor( controller ) {
  22. super();
  23. /**
  24. * The hand controller.
  25. *
  26. * @type {Group}
  27. */
  28. this.controller = controller;
  29. /**
  30. * The motion controller.
  31. *
  32. * @type {?MotionController}
  33. * @default null
  34. */
  35. this.motionController = null;
  36. /**
  37. * The controller's environment map.
  38. *
  39. * @type {?Texture}
  40. * @default null
  41. */
  42. this.envMap = null;
  43. /**
  44. * The model mesh.
  45. *
  46. * @type {Mesh}
  47. * @default null
  48. */
  49. this.mesh = null;
  50. }
  51. /**
  52. * Overwritten with a custom implementation. Makes sure the motion controller updates the mesh.
  53. *
  54. * @param {boolean} [force=false] - When set to `true`, a recomputation of world matrices is forced even
  55. * when {@link Object3D#matrixWorldAutoUpdate} is set to `false`.
  56. */
  57. updateMatrixWorld( force ) {
  58. super.updateMatrixWorld( force );
  59. if ( this.motionController ) {
  60. this.motionController.updateMesh();
  61. }
  62. }
  63. }
  64. /**
  65. * Similar to {@link XRControllerModelFactory}, this class allows to create hand models
  66. * for WebXR controllers that can be added as a visual representation to your scene.
  67. *
  68. * ```js
  69. * const handModelFactory = new XRHandModelFactory();
  70. *
  71. * const hand = renderer.xr.getHand( 0 );
  72. * hand.add( handModelFactory.createHandModel( hand ) );
  73. * scene.add( hand );
  74. * ```
  75. *
  76. * @three_import import { XRHandModelFactory } from 'three/addons/webxr/XRHandModelFactory.js';
  77. */
  78. class XRHandModelFactory {
  79. /**
  80. * Constructs a new XR hand model factory.
  81. *
  82. * @param {?GLTFLoader} [gltfLoader=null] - A glTF loader that is used to load hand models.
  83. * @param {?Function} [onLoad=null] - A callback that is executed when a hand model has been loaded.
  84. */
  85. constructor( gltfLoader = null, onLoad = null ) {
  86. /**
  87. * A glTF loader that is used to load hand models.
  88. *
  89. * @type {?GLTFLoader}
  90. * @default null
  91. */
  92. this.gltfLoader = gltfLoader;
  93. /**
  94. * The path to the model repository.
  95. *
  96. * @type {?string}
  97. * @default null
  98. */
  99. this.path = null;
  100. /**
  101. * A callback that is executed when a hand model has been loaded.
  102. *
  103. * @type {?Function}
  104. * @default null
  105. */
  106. this.onLoad = onLoad;
  107. }
  108. /**
  109. * Sets the path to the hand model repository.
  110. *
  111. * @param {string} path - The path to set.
  112. * @return {XRHandModelFactory} A reference to this instance.
  113. */
  114. setPath( path ) {
  115. this.path = path;
  116. return this;
  117. }
  118. /**
  119. * Creates a controller model for the given WebXR hand controller.
  120. *
  121. * @param {Group} controller - The hand controller.
  122. * @param {('spheres'|'boxes'|'mesh')} [profile] - The model profile that defines the model type.
  123. * @return {XRHandModel} The XR hand model.
  124. */
  125. createHandModel( controller, profile ) {
  126. const handModel = new XRHandModel( controller );
  127. controller.addEventListener( 'connected', ( event ) => {
  128. const xrInputSource = event.data;
  129. if ( xrInputSource.hand && ! handModel.motionController ) {
  130. handModel.xrInputSource = xrInputSource;
  131. // @todo Detect profile if not provided
  132. if ( profile === undefined || profile === 'spheres' ) {
  133. handModel.motionController = new XRHandPrimitiveModel( handModel, controller, this.path, xrInputSource.handedness, { primitive: 'sphere' } );
  134. } else if ( profile === 'boxes' ) {
  135. handModel.motionController = new XRHandPrimitiveModel( handModel, controller, this.path, xrInputSource.handedness, { primitive: 'box' } );
  136. } else if ( profile === 'mesh' ) {
  137. handModel.motionController = new XRHandMeshModel( handModel, controller, this.path, xrInputSource.handedness, this.gltfLoader, this.onLoad );
  138. }
  139. }
  140. controller.visible = true;
  141. } );
  142. controller.addEventListener( 'disconnected', () => {
  143. controller.visible = false;
  144. // handModel.motionController = null;
  145. // handModel.remove( scene );
  146. // scene = null;
  147. } );
  148. return handModel;
  149. }
  150. }
  151. export { XRHandModelFactory };