TubeGeometry.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. /**
  2. * @author WestLangley / https://github.com/WestLangley
  3. * @author zz85 / https://github.com/zz85
  4. * @author miningold / https://github.com/miningold
  5. *
  6. * Modified from the TorusKnotGeometry by @oosmoxiecode
  7. *
  8. * Creates a tube which extrudes along a 3d spline
  9. *
  10. * Uses parallel transport frames as described in
  11. * http://www.cs.indiana.edu/pub/techreports/TR425.pdf
  12. */
  13. THREE.TubeGeometry = function( path, segments, radius, segmentsRadius, closed, debug ) {
  14. THREE.Geometry.call( this );
  15. this.path = path;
  16. this.segments = segments || 64;
  17. this.radius = radius || 1;
  18. this.segmentsRadius = segmentsRadius || 8;
  19. this.closed = closed || false;
  20. if ( debug ) this.debug = new THREE.Object3D();
  21. this.grid = [];
  22. var scope = this,
  23. tangent,
  24. normal,
  25. binormal,
  26. numpoints = this.segments + 1,
  27. x, y, z,
  28. tx, ty, tz,
  29. u, v,
  30. cx, cy,
  31. pos, pos2 = new THREE.Vector3(),
  32. i, j,
  33. ip, jp,
  34. a, b, c, d,
  35. uva, uvb, uvc, uvd;
  36. var frames = new THREE.TubeGeometry.FrenetFrames(path, segments, closed),
  37. tangents = frames.tangents,
  38. normals = frames.normals,
  39. binormals = frames.binormals;
  40. // proxy internals
  41. this.tangents = tangents;
  42. this.normals = normals;
  43. this.binormals = binormals;
  44. function vert( x, y, z ) {
  45. return scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ) - 1;
  46. }
  47. // consruct the grid
  48. for ( i = 0; i < numpoints; i++ ) {
  49. this.grid[ i ] = [];
  50. u = i / ( numpoints - 1 );
  51. pos = path.getPointAt( u );
  52. tangent = tangents[ i ];
  53. normal = normals[ i ];
  54. binormal = binormals[ i ];
  55. if ( this.debug ) {
  56. this.debug.add(new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff));
  57. this.debug.add(new THREE.ArrowHelper(normal, pos, radius, 0xff0000));
  58. this.debug.add(new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00));
  59. }
  60. for ( j = 0; j < this.segmentsRadius; j++ ) {
  61. v = j / this.segmentsRadius * 2 * Math.PI;
  62. cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
  63. cy = this.radius * Math.sin( v );
  64. pos2.copy( pos );
  65. pos2.x += cx * normal.x + cy * binormal.x;
  66. pos2.y += cx * normal.y + cy * binormal.y;
  67. pos2.z += cx * normal.z + cy * binormal.z;
  68. this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
  69. }
  70. }
  71. // construct the mesh
  72. for ( i = 0; i < this.segments; i++ ) {
  73. for ( j = 0; j < this.segmentsRadius; j++ ) {
  74. ip = ( closed ) ? (i + 1) % this.segments : i + 1;
  75. jp = (j + 1) % this.segmentsRadius;
  76. a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
  77. b = this.grid[ ip ][ j ];
  78. c = this.grid[ ip ][ jp ];
  79. d = this.grid[ i ][ jp ];
  80. uva = new THREE.UV( i / this.segments, j / this.segmentsRadius );
  81. uvb = new THREE.UV( ( i + 1 ) / this.segments, j / this.segmentsRadius );
  82. uvc = new THREE.UV( ( i + 1 ) / this.segments, ( j + 1 ) / this.segmentsRadius );
  83. uvd = new THREE.UV( i / this.segments, ( j + 1 ) / this.segmentsRadius );
  84. this.faces.push( new THREE.Face4( a, b, c, d ) );
  85. this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc, uvd ] );
  86. }
  87. }
  88. this.computeCentroids();
  89. this.computeFaceNormals();
  90. this.computeVertexNormals();
  91. };
  92. THREE.TubeGeometry.prototype = new THREE.Geometry();
  93. THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
  94. // For computing of Frenet frames, exposing the tangents, normals and binormals the spline
  95. THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) {
  96. var
  97. tangent = new THREE.Vector3(),
  98. normal = new THREE.Vector3(),
  99. binormal = new THREE.Vector3(),
  100. tangents = [],
  101. normals = [],
  102. binormals = [],
  103. vec = new THREE.Vector3(),
  104. mat = new THREE.Matrix4(),
  105. numpoints = segments + 1,
  106. theta,
  107. epsilon = 0.0001,
  108. smallest,
  109. tx, ty, tz,
  110. i, u, v;
  111. // expose internals
  112. this.tangents = tangents;
  113. this.normals = normals;
  114. this.binormals = binormals;
  115. // compute the tangent vectors for each segment on the path
  116. for ( i = 0; i < numpoints; i++ ) {
  117. u = i / ( numpoints - 1 );
  118. tangents[ i ] = path.getTangentAt( u );
  119. tangents[ i ].normalize();
  120. }
  121. initialNormal3();
  122. function initialNormal1(lastBinormal) {
  123. // fixed start binormal. Has dangers of 0 vectors
  124. normals[ 0 ] = new THREE.Vector3();
  125. binormals[ 0 ] = new THREE.Vector3();
  126. if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
  127. normals[ 0 ].cross( lastBinormal, tangents[ 0 ] ).normalize();
  128. binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize();
  129. }
  130. function initialNormal2() {
  131. // This uses the Frenet-Serret formula for deriving binormal
  132. var t2 = path.getTangentAt( epsilon );
  133. normals[ 0 ] = new THREE.Vector3().sub( t2, tangents[ 0 ] ).normalize();
  134. binormals[ 0 ] = new THREE.Vector3().cross( tangents[ 0 ], normals[ 0 ] );
  135. normals[ 0 ].cross( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
  136. binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize();
  137. }
  138. function initialNormal3() {
  139. // select an initial normal vector perpenicular to the first tangent vector,
  140. // and in the direction of the smallest tangent xyz component
  141. normals[ 0 ] = new THREE.Vector3();
  142. binormals[ 0 ] = new THREE.Vector3();
  143. smallest = Number.MAX_VALUE;
  144. tx = Math.abs( tangents[ 0 ].x );
  145. ty = Math.abs( tangents[ 0 ].y );
  146. tz = Math.abs( tangents[ 0 ].z );
  147. if ( tx <= smallest ) {
  148. smallest = tx;
  149. normal.set( 1, 0, 0 );
  150. }
  151. if ( ty <= smallest ) {
  152. smallest = ty;
  153. normal.set( 0, 1, 0 );
  154. }
  155. if ( tz <= smallest ) {
  156. normal.set( 0, 0, 1 );
  157. }
  158. vec.cross( tangents[ 0 ], normal ).normalize();
  159. normals[ 0 ].cross( tangents[ 0 ], vec );
  160. binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] );
  161. }
  162. // compute the slowly-varying normal and binormal vectors for each segment on the path
  163. for ( i = 1; i < numpoints; i++ ) {
  164. normals[ i ] = normals[ i-1 ].clone();
  165. binormals[ i ] = binormals[ i-1 ].clone();
  166. vec.cross( tangents[ i-1 ], tangents[ i ] );
  167. if ( vec.length() > epsilon ) {
  168. vec.normalize();
  169. theta = Math.acos( tangents[ i-1 ].dot( tangents[ i ] ) );
  170. mat.setRotationAxis( vec, theta ).multiplyVector3( normals[ i ] );
  171. }
  172. binormals[ i ].cross( tangents[ i ], normals[ i ] );
  173. }
  174. // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
  175. if ( closed ) {
  176. theta = Math.acos( normals[ 0 ].dot( normals[ numpoints-1 ] ) );
  177. theta /= ( numpoints - 1 );
  178. if ( tangents[ 0 ].dot( vec.cross( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
  179. theta = -theta;
  180. }
  181. for ( i = 1; i < numpoints; i++ ) {
  182. // twist a little...
  183. mat.setRotationAxis( tangents[ i ], theta * i ).multiplyVector3( normals[ i ] );
  184. binormals[ i ].cross( tangents[ i ], normals[ i ] );
  185. }
  186. }
  187. };