TubePainter.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import {
  2. BufferAttribute,
  3. BufferGeometry,
  4. Color,
  5. DynamicDrawUsage,
  6. Matrix4,
  7. Mesh,
  8. MeshStandardMaterial,
  9. Vector3
  10. } from 'three';
  11. /**
  12. * @classdesc This module can be used to paint tube-like meshes
  13. * along a sequence of points. This module is used in a XR
  14. * painter demo.
  15. *
  16. * ```js
  17. * const painter = new TubePainter();
  18. * scene.add( painter.mesh );
  19. * ```
  20. *
  21. * @name TubePainter
  22. * @class
  23. * @three_import import { TubePainter } from 'three/addons/misc/TubePainter.js';
  24. */
  25. function TubePainter() {
  26. const BUFFER_SIZE = 1000000 * 3;
  27. const positions = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
  28. positions.usage = DynamicDrawUsage;
  29. const normals = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
  30. normals.usage = DynamicDrawUsage;
  31. const colors = new BufferAttribute( new Float32Array( BUFFER_SIZE ), 3 );
  32. colors.usage = DynamicDrawUsage;
  33. const geometry = new BufferGeometry();
  34. geometry.setAttribute( 'position', positions );
  35. geometry.setAttribute( 'normal', normals );
  36. geometry.setAttribute( 'color', colors );
  37. geometry.drawRange.count = 0;
  38. const material = new MeshStandardMaterial( {
  39. vertexColors: true
  40. } );
  41. const mesh = new Mesh( geometry, material );
  42. mesh.frustumCulled = false;
  43. //
  44. function getPoints( size ) {
  45. const PI2 = Math.PI * 2;
  46. const sides = 10;
  47. const array = [];
  48. const radius = 0.01 * size;
  49. for ( let i = 0; i < sides; i ++ ) {
  50. const angle = ( i / sides ) * PI2;
  51. array.push( new Vector3( Math.sin( angle ) * radius, Math.cos( angle ) * radius, 0 ) );
  52. }
  53. return array;
  54. }
  55. //
  56. const vector1 = new Vector3();
  57. const vector2 = new Vector3();
  58. const vector3 = new Vector3();
  59. const vector4 = new Vector3();
  60. const color = new Color( 0xffffff );
  61. let size = 1;
  62. function stroke( position1, position2, matrix1, matrix2 ) {
  63. if ( position1.distanceToSquared( position2 ) === 0 ) return;
  64. let count = geometry.drawRange.count;
  65. const points = getPoints( size );
  66. for ( let i = 0, il = points.length; i < il; i ++ ) {
  67. const vertex1 = points[ i ];
  68. const vertex2 = points[ ( i + 1 ) % il ];
  69. // positions
  70. vector1.copy( vertex1 ).applyMatrix4( matrix2 ).add( position2 );
  71. vector2.copy( vertex2 ).applyMatrix4( matrix2 ).add( position2 );
  72. vector3.copy( vertex2 ).applyMatrix4( matrix1 ).add( position1 );
  73. vector4.copy( vertex1 ).applyMatrix4( matrix1 ).add( position1 );
  74. vector1.toArray( positions.array, ( count + 0 ) * 3 );
  75. vector2.toArray( positions.array, ( count + 1 ) * 3 );
  76. vector4.toArray( positions.array, ( count + 2 ) * 3 );
  77. vector2.toArray( positions.array, ( count + 3 ) * 3 );
  78. vector3.toArray( positions.array, ( count + 4 ) * 3 );
  79. vector4.toArray( positions.array, ( count + 5 ) * 3 );
  80. // normals
  81. vector1.copy( vertex1 ).applyMatrix4( matrix2 ).normalize();
  82. vector2.copy( vertex2 ).applyMatrix4( matrix2 ).normalize();
  83. vector3.copy( vertex2 ).applyMatrix4( matrix1 ).normalize();
  84. vector4.copy( vertex1 ).applyMatrix4( matrix1 ).normalize();
  85. vector1.toArray( normals.array, ( count + 0 ) * 3 );
  86. vector2.toArray( normals.array, ( count + 1 ) * 3 );
  87. vector4.toArray( normals.array, ( count + 2 ) * 3 );
  88. vector2.toArray( normals.array, ( count + 3 ) * 3 );
  89. vector3.toArray( normals.array, ( count + 4 ) * 3 );
  90. vector4.toArray( normals.array, ( count + 5 ) * 3 );
  91. // colors
  92. color.toArray( colors.array, ( count + 0 ) * 3 );
  93. color.toArray( colors.array, ( count + 1 ) * 3 );
  94. color.toArray( colors.array, ( count + 2 ) * 3 );
  95. color.toArray( colors.array, ( count + 3 ) * 3 );
  96. color.toArray( colors.array, ( count + 4 ) * 3 );
  97. color.toArray( colors.array, ( count + 5 ) * 3 );
  98. count += 6;
  99. }
  100. geometry.drawRange.count = count;
  101. }
  102. //
  103. const up = new Vector3( 0, 1, 0 );
  104. const point1 = new Vector3();
  105. const point2 = new Vector3();
  106. const matrix1 = new Matrix4();
  107. const matrix2 = new Matrix4();
  108. function moveTo( position ) {
  109. point1.copy( position );
  110. matrix1.lookAt( point2, point1, up );
  111. point2.copy( position );
  112. matrix2.copy( matrix1 );
  113. }
  114. function lineTo( position ) {
  115. point1.copy( position );
  116. matrix1.lookAt( point2, point1, up );
  117. stroke( point1, point2, matrix1, matrix2 );
  118. point2.copy( point1 );
  119. matrix2.copy( matrix1 );
  120. }
  121. function setSize( value ) {
  122. size = value;
  123. }
  124. //
  125. let count = 0;
  126. function update() {
  127. const start = count;
  128. const end = geometry.drawRange.count;
  129. if ( start === end ) return;
  130. positions.addUpdateRange( start * 3, ( end - start ) * 3 );
  131. positions.needsUpdate = true;
  132. normals.addUpdateRange( start * 3, ( end - start ) * 3 );
  133. normals.needsUpdate = true;
  134. colors.addUpdateRange( start * 3, ( end - start ) * 3 );
  135. colors.needsUpdate = true;
  136. count = geometry.drawRange.count;
  137. }
  138. return {
  139. /**
  140. * The "painted" tube mesh. Must be added to the scene.
  141. *
  142. * @name TubePainter#mesh
  143. * @type {Mesh}
  144. */
  145. mesh: mesh,
  146. /**
  147. * Moves the current painting position to the given value.
  148. *
  149. * @method
  150. * @name TubePainter#moveTo
  151. * @param {Vector3} position The new painting position.
  152. */
  153. moveTo: moveTo,
  154. /**
  155. * Draw a stroke from the current position to the given one.
  156. * This method extends the tube while drawing with the XR
  157. * controllers.
  158. *
  159. * @method
  160. * @name TubePainter#lineTo
  161. * @param {Vector3} position The destination position.
  162. */
  163. lineTo: lineTo,
  164. /**
  165. * Sets the size of newly rendered tube segments.
  166. *
  167. * @method
  168. * @name TubePainter#setSize
  169. * @param {number} size The size.
  170. */
  171. setSize: setSize,
  172. /**
  173. * Updates the internal geometry buffers so the new painted
  174. * segments are rendered.
  175. *
  176. * @method
  177. * @name TubePainter#update
  178. */
  179. update: update
  180. };
  181. }
  182. export { TubePainter };