Source: geometry/CylinderGeometry.js

  1. import { Geometry } from './Geometry.js';
  2. import { BufferAttribute } from './BufferAttribute.js';
  3. import { Vector2 } from '../math/Vector2.js';
  4. import { Vector3 } from '../math/Vector3.js';
  5. /**
  6. * A class for generating cylinder geometries.
  7. * @constructor
  8. * @memberof zen3d
  9. * @extends zen3d.Geometry
  10. * @param {number} [radiusTop=1] — Radius of the cylinder at the top.
  11. * @param {number} [radiusBottom=1] — Radius of the cylinder at the bottom.
  12. * @param {number} [height=1] — Height of the cylinder.
  13. * @param {Integer} [radialSegments=8] — Number of segmented faces around the circumference of the cylinder.
  14. * @param {Integer} [heightSegments=1] — Number of rows of faces along the height of the cylinder.
  15. * @param {number} [openEnded=false] — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.
  16. * @param {number} [thetaStart=0] — Start angle for first segment, default = 0 (three o'clock position).
  17. * @param {number} [thetaLength=2*Pi] — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.
  18. */
  19. function CylinderGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
  20. Geometry.call(this);
  21. this.buildGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength);
  22. }
  23. CylinderGeometry.prototype = Object.assign(Object.create(Geometry.prototype), {
  24. constructor: CylinderGeometry,
  25. buildGeometry: function(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
  26. var scope = this;
  27. radiusTop = radiusTop !== undefined ? radiusTop : 1;
  28. radiusBottom = radiusBottom !== undefined ? radiusBottom : 1;
  29. height = height || 1;
  30. radialSegments = Math.floor(radialSegments) || 8;
  31. heightSegments = Math.floor(heightSegments) || 1;
  32. openEnded = openEnded !== undefined ? openEnded : false;
  33. thetaStart = thetaStart !== undefined ? thetaStart : 0.0;
  34. thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;
  35. // buffers
  36. var indices = [];
  37. var vertices = [];
  38. var normals = [];
  39. var uvs = [];
  40. // helper variables
  41. var index = 0;
  42. var indexArray = [];
  43. var halfHeight = height / 2;
  44. var groupStart = 0;
  45. // generate geometry
  46. generateTorso();
  47. if (openEnded === false) {
  48. if (radiusTop > 0) generateCap(true);
  49. if (radiusBottom > 0) generateCap(false);
  50. }
  51. // build geometry
  52. this.setIndex(indices);
  53. this.addAttribute('a_Position', new BufferAttribute(new Float32Array(vertices), 3));
  54. this.addAttribute('a_Normal', new BufferAttribute(new Float32Array(normals), 3));
  55. this.addAttribute('a_Uv', new BufferAttribute(new Float32Array(uvs), 2));
  56. function generateTorso() {
  57. var x, y;
  58. var normal = new Vector3();
  59. var vertex = new Vector3();
  60. var groupCount = 0;
  61. // this will be used to calculate the normal
  62. var slope = (radiusBottom - radiusTop) / height;
  63. // generate vertices, normals and uvs
  64. for (y = 0; y <= heightSegments; y++) {
  65. var indexRow = [];
  66. var v = y / heightSegments;
  67. // calculate the radius of the current row
  68. var radius = v * (radiusBottom - radiusTop) + radiusTop;
  69. for (x = 0; x <= radialSegments; x++) {
  70. var u = x / radialSegments;
  71. var theta = u * thetaLength + thetaStart;
  72. var sinTheta = Math.sin(theta);
  73. var cosTheta = Math.cos(theta);
  74. // vertex
  75. vertex.x = radius * sinTheta;
  76. vertex.y = -v * height + halfHeight;
  77. vertex.z = radius * cosTheta;
  78. vertices.push(vertex.x, vertex.y, vertex.z);
  79. // normal
  80. normal.set(sinTheta, slope, cosTheta).normalize();
  81. normals.push(normal.x, normal.y, normal.z);
  82. // uv
  83. uvs.push(u, 1 - v);
  84. // save index of vertex in respective row
  85. indexRow.push(index++);
  86. }
  87. // now save vertices of the row in our index array
  88. indexArray.push(indexRow);
  89. }
  90. // generate indices
  91. for (x = 0; x < radialSegments; x++) {
  92. for (y = 0; y < heightSegments; y++) {
  93. // we use the index array to access the correct indices
  94. var a = indexArray[y][x];
  95. var b = indexArray[y + 1][x];
  96. var c = indexArray[y + 1][x + 1];
  97. var d = indexArray[y][x + 1];
  98. // faces
  99. indices.push(a, b, d);
  100. indices.push(b, c, d);
  101. // update group counter
  102. groupCount += 6;
  103. }
  104. }
  105. // add a group to the geometry. this will ensure multi material support
  106. scope.addGroup(groupStart, groupCount, 0);
  107. // calculate new start value for groups
  108. groupStart += groupCount;
  109. }
  110. function generateCap(top) {
  111. var x, centerIndexStart, centerIndexEnd;
  112. var uv = new Vector2();
  113. var vertex = new Vector3();
  114. var groupCount = 0;
  115. var radius = (top === true) ? radiusTop : radiusBottom;
  116. var sign = (top === true) ? 1 : -1;
  117. // save the index of the first center vertex
  118. centerIndexStart = index;
  119. // first we generate the center vertex data of the cap.
  120. // because the geometry needs one set of uvs per face,
  121. // we must generate a center vertex per face/segment
  122. for (x = 1; x <= radialSegments; x++) {
  123. // vertex
  124. vertices.push(0, halfHeight * sign, 0);
  125. // normal
  126. normals.push(0, sign, 0);
  127. // uv
  128. uvs.push(0.5, 0.5);
  129. // increase index
  130. index++;
  131. }
  132. // save the index of the last center vertex
  133. centerIndexEnd = index;
  134. // now we generate the surrounding vertices, normals and uvs
  135. for (x = 0; x <= radialSegments; x++) {
  136. var u = x / radialSegments;
  137. var theta = u * thetaLength + thetaStart;
  138. var cosTheta = Math.cos(theta);
  139. var sinTheta = Math.sin(theta);
  140. // vertex
  141. vertex.x = radius * sinTheta;
  142. vertex.y = halfHeight * sign;
  143. vertex.z = radius * cosTheta;
  144. vertices.push(vertex.x, vertex.y, vertex.z);
  145. // normal
  146. normals.push(0, sign, 0);
  147. // uv
  148. uv.x = (cosTheta * 0.5) + 0.5;
  149. uv.y = (sinTheta * 0.5 * sign) + 0.5;
  150. uvs.push(uv.x, uv.y);
  151. // increase index
  152. index++;
  153. }
  154. // generate indices
  155. for (x = 0; x < radialSegments; x++) {
  156. var c = centerIndexStart + x;
  157. var i = centerIndexEnd + x;
  158. if (top === true) {
  159. // face top
  160. indices.push(i, i + 1, c);
  161. } else {
  162. // face bottom
  163. indices.push(i + 1, i, c);
  164. }
  165. groupCount += 3;
  166. }
  167. // add a group to the geometry. this will ensure multi material support
  168. scope.addGroup(groupStart, groupCount, top === true ? 1 : 2);
  169. // calculate new start value for groups
  170. groupStart += groupCount;
  171. }
  172. this.computeBoundingBox();
  173. this.computeBoundingSphere();
  174. }
  175. });
  176. export { CylinderGeometry };