PositionalAudioHelper.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import {
  2. BufferGeometry,
  3. BufferAttribute,
  4. LineBasicMaterial,
  5. Line,
  6. MathUtils
  7. } from 'three';
  8. /**
  9. * This helper displays the directional cone of a positional audio.
  10. *
  11. * `PositionalAudioHelper` must be added as a child of the positional audio.
  12. *
  13. * ```js
  14. * const positionalAudio = new THREE.PositionalAudio( listener );
  15. * positionalAudio.setDirectionalCone( 180, 230, 0.1 );
  16. * scene.add( positionalAudio );
  17. *
  18. * const helper = new PositionalAudioHelper( positionalAudio );
  19. * positionalAudio.add( helper );
  20. * ```
  21. *
  22. * @augments Line
  23. */
  24. class PositionalAudioHelper extends Line {
  25. /**
  26. * Constructs a new positional audio helper.
  27. *
  28. * @param {PositionalAudio} audio - The audio to visualize.
  29. * @param {number} [range=1] - The range of the directional cone.
  30. * @param {number} [divisionsInnerAngle=16] - The number of divisions of the inner part of the directional cone.
  31. * @param {number} [divisionsOuterAngle=2] The number of divisions of the outer part of the directional cone.
  32. */
  33. constructor( audio, range = 1, divisionsInnerAngle = 16, divisionsOuterAngle = 2 ) {
  34. const geometry = new BufferGeometry();
  35. const divisions = divisionsInnerAngle + divisionsOuterAngle * 2;
  36. const positions = new Float32Array( ( divisions * 3 + 3 ) * 3 );
  37. geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
  38. const materialInnerAngle = new LineBasicMaterial( { color: 0x00ff00 } );
  39. const materialOuterAngle = new LineBasicMaterial( { color: 0xffff00 } );
  40. super( geometry, [ materialOuterAngle, materialInnerAngle ] );
  41. /**
  42. * The audio to visualize.
  43. *
  44. * @type {PositionalAudio}
  45. */
  46. this.audio = audio;
  47. /**
  48. * The range of the directional cone.
  49. *
  50. * @type {number}
  51. * @default 1
  52. */
  53. this.range = range;
  54. /**
  55. * The number of divisions of the inner part of the directional cone.
  56. *
  57. * @type {number}
  58. * @default 16
  59. */
  60. this.divisionsInnerAngle = divisionsInnerAngle;
  61. /**
  62. * The number of divisions of the outer part of the directional cone.
  63. *
  64. * @type {number}
  65. * @default 2
  66. */
  67. this.divisionsOuterAngle = divisionsOuterAngle;
  68. this.type = 'PositionalAudioHelper';
  69. this.update();
  70. }
  71. /**
  72. * Updates the helper. This method must be called whenever the directional cone
  73. * of the positional audio is changed.
  74. */
  75. update() {
  76. const audio = this.audio;
  77. const range = this.range;
  78. const divisionsInnerAngle = this.divisionsInnerAngle;
  79. const divisionsOuterAngle = this.divisionsOuterAngle;
  80. const coneInnerAngle = MathUtils.degToRad( audio.panner.coneInnerAngle );
  81. const coneOuterAngle = MathUtils.degToRad( audio.panner.coneOuterAngle );
  82. const halfConeInnerAngle = coneInnerAngle / 2;
  83. const halfConeOuterAngle = coneOuterAngle / 2;
  84. let start = 0;
  85. let count = 0;
  86. let i;
  87. let stride;
  88. const geometry = this.geometry;
  89. const positionAttribute = geometry.attributes.position;
  90. geometry.clearGroups();
  91. //
  92. function generateSegment( from, to, divisions, materialIndex ) {
  93. const step = ( to - from ) / divisions;
  94. positionAttribute.setXYZ( start, 0, 0, 0 );
  95. count ++;
  96. for ( i = from; i < to; i += step ) {
  97. stride = start + count;
  98. positionAttribute.setXYZ( stride, Math.sin( i ) * range, 0, Math.cos( i ) * range );
  99. positionAttribute.setXYZ( stride + 1, Math.sin( Math.min( i + step, to ) ) * range, 0, Math.cos( Math.min( i + step, to ) ) * range );
  100. positionAttribute.setXYZ( stride + 2, 0, 0, 0 );
  101. count += 3;
  102. }
  103. geometry.addGroup( start, count, materialIndex );
  104. start += count;
  105. count = 0;
  106. }
  107. //
  108. generateSegment( - halfConeOuterAngle, - halfConeInnerAngle, divisionsOuterAngle, 0 );
  109. generateSegment( - halfConeInnerAngle, halfConeInnerAngle, divisionsInnerAngle, 1 );
  110. generateSegment( halfConeInnerAngle, halfConeOuterAngle, divisionsOuterAngle, 0 );
  111. //
  112. positionAttribute.needsUpdate = true;
  113. if ( coneInnerAngle === coneOuterAngle ) this.material[ 0 ].visible = false;
  114. }
  115. /**
  116. * Frees the GPU-related resources allocated by this instance. Call this
  117. * method whenever this instance is no longer used in your app.
  118. */
  119. dispose() {
  120. this.geometry.dispose();
  121. this.material[ 0 ].dispose();
  122. this.material[ 1 ].dispose();
  123. }
  124. }
  125. export { PositionalAudioHelper };
粤ICP备19079148号