TubeGeometry.js 6.8 KB

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