TubeGeometry.js 6.8 KB

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