threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CylinderGeometryGenerator.ts 6.6KB

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