CylinderGeometry.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import { BufferGeometry } from '../core/BufferGeometry.js';
  2. import { Float32BufferAttribute } from '../core/BufferAttribute.js';
  3. import { Vector3 } from '../math/Vector3.js';
  4. import { Vector2 } from '../math/Vector2.js';
  5. class CylinderGeometry extends BufferGeometry {
  6. constructor( radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 ) {
  7. super();
  8. this.type = 'CylinderGeometry';
  9. this.parameters = {
  10. radiusTop: radiusTop,
  11. radiusBottom: radiusBottom,
  12. height: height,
  13. radialSegments: radialSegments,
  14. heightSegments: heightSegments,
  15. openEnded: openEnded,
  16. thetaStart: thetaStart,
  17. thetaLength: thetaLength
  18. };
  19. const scope = this;
  20. radialSegments = Math.floor( radialSegments );
  21. heightSegments = Math.floor( heightSegments );
  22. // buffers
  23. const indices = [];
  24. const vertices = [];
  25. const normals = [];
  26. const uvs = [];
  27. // helper variables
  28. let index = 0;
  29. const indexArray = [];
  30. const halfHeight = height / 2;
  31. let groupStart = 0;
  32. // generate geometry
  33. generateTorso();
  34. if ( openEnded === false ) {
  35. if ( radiusTop > 0 ) generateCap( true );
  36. if ( radiusBottom > 0 ) generateCap( false );
  37. }
  38. // build geometry
  39. this.setIndex( indices );
  40. this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
  41. this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
  42. this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
  43. function generateTorso() {
  44. const normal = new Vector3();
  45. const vertex = new Vector3();
  46. let groupCount = 0;
  47. // this will be used to calculate the normal
  48. const slope = ( radiusBottom - radiusTop ) / height;
  49. // generate vertices, normals and uvs
  50. for ( let y = 0; y <= heightSegments; y ++ ) {
  51. const indexRow = [];
  52. const v = y / heightSegments;
  53. // calculate the radius of the current row
  54. const radius = v * ( radiusBottom - radiusTop ) + radiusTop;
  55. for ( let x = 0; x <= radialSegments; x ++ ) {
  56. const u = x / radialSegments;
  57. const theta = u * thetaLength + thetaStart;
  58. const sinTheta = Math.sin( theta );
  59. const cosTheta = Math.cos( theta );
  60. // vertex
  61. vertex.x = radius * sinTheta;
  62. vertex.y = - v * height + halfHeight;
  63. vertex.z = radius * cosTheta;
  64. vertices.push( vertex.x, vertex.y, vertex.z );
  65. // normal
  66. normal.set( sinTheta, slope, cosTheta ).normalize();
  67. normals.push( normal.x, normal.y, normal.z );
  68. // uv
  69. uvs.push( u, 1 - v );
  70. // save index of vertex in respective row
  71. indexRow.push( index ++ );
  72. }
  73. // now save vertices of the row in our index array
  74. indexArray.push( indexRow );
  75. }
  76. // generate indices
  77. for ( let x = 0; x < radialSegments; x ++ ) {
  78. for ( let y = 0; y < heightSegments; y ++ ) {
  79. // we use the index array to access the correct indices
  80. const a = indexArray[ y ][ x ];
  81. const b = indexArray[ y + 1 ][ x ];
  82. const c = indexArray[ y + 1 ][ x + 1 ];
  83. const d = indexArray[ y ][ x + 1 ];
  84. // faces
  85. indices.push( a, b, d );
  86. indices.push( b, c, d );
  87. // update group counter
  88. groupCount += 6;
  89. }
  90. }
  91. // add a group to the geometry. this will ensure multi material support
  92. scope.addGroup( groupStart, groupCount, 0 );
  93. // calculate new start value for groups
  94. groupStart += groupCount;
  95. }
  96. function generateCap( top ) {
  97. // save the index of the first center vertex
  98. const centerIndexStart = index;
  99. const uv = new Vector2();
  100. const vertex = new Vector3();
  101. let groupCount = 0;
  102. const radius = ( top === true ) ? radiusTop : radiusBottom;
  103. const sign = ( top === true ) ? 1 : - 1;
  104. // first we generate the center vertex data of the cap.
  105. // because the geometry needs one set of uvs per face,
  106. // we must generate a center vertex per face/segment
  107. for ( let x = 1; x <= radialSegments; x ++ ) {
  108. // vertex
  109. vertices.push( 0, halfHeight * sign, 0 );
  110. // normal
  111. normals.push( 0, sign, 0 );
  112. // uv
  113. uvs.push( 0.5, 0.5 );
  114. // increase index
  115. index ++;
  116. }
  117. // save the index of the last center vertex
  118. const centerIndexEnd = index;
  119. // now we generate the surrounding vertices, normals and uvs
  120. for ( let x = 0; x <= radialSegments; x ++ ) {
  121. const u = x / radialSegments;
  122. const theta = u * thetaLength + thetaStart;
  123. const cosTheta = Math.cos( theta );
  124. const sinTheta = Math.sin( theta );
  125. // vertex
  126. vertex.x = radius * sinTheta;
  127. vertex.y = halfHeight * sign;
  128. vertex.z = radius * cosTheta;
  129. vertices.push( vertex.x, vertex.y, vertex.z );
  130. // normal
  131. normals.push( 0, sign, 0 );
  132. // uv
  133. uv.x = ( cosTheta * 0.5 ) + 0.5;
  134. uv.y = ( sinTheta * 0.5 * sign ) + 0.5;
  135. uvs.push( uv.x, uv.y );
  136. // increase index
  137. index ++;
  138. }
  139. // generate indices
  140. for ( let x = 0; x < radialSegments; x ++ ) {
  141. const c = centerIndexStart + x;
  142. const i = centerIndexEnd + x;
  143. if ( top === true ) {
  144. // face top
  145. indices.push( i, i + 1, c );
  146. } else {
  147. // face bottom
  148. indices.push( i + 1, i, c );
  149. }
  150. groupCount += 3;
  151. }
  152. // add a group to the geometry. this will ensure multi material support
  153. scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );
  154. // calculate new start value for groups
  155. groupStart += groupCount;
  156. }
  157. }
  158. copy( source ) {
  159. super.copy( source );
  160. this.parameters = Object.assign( {}, source.parameters );
  161. return this;
  162. }
  163. static fromJSON( data ) {
  164. return new CylinderGeometry( data.radiusTop, data.radiusBottom, data.height, data.radialSegments, data.heightSegments, data.openEnded, data.thetaStart, data.thetaLength );
  165. }
  166. }
  167. export { CylinderGeometry };