| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- import {AGeometryGenerator} from '../AGeometryGenerator'
- import {Vector2, Vector3} from 'threepipe'
-
-
- export interface CylinderGeometryGeneratorParams {
- radiusTop: number,
- radiusBottom: number,
- height: number,
- radialSegments: number,
- heightSegments: number,
- openEnded: boolean,
- thetaStart: number,
- thetaLength: number
- }
-
- export class CylinderGeometryGenerator extends AGeometryGenerator<CylinderGeometryGeneratorParams> {
-
- defaultParams = {
- radiusTop: 1,
- radiusBottom: 1,
- height: 1,
- radialSegments: 32,
- heightSegments: 1,
- openEnded: false,
- thetaStart: 0,
- thetaLength: Math.PI * 2,
- }
-
- protected _generateTorso(state: any) {
- const {radiusTop, radiusBottom, height,
- radialSegments, heightSegments,
- thetaStart, thetaLength, indexArray, indices, groups,
- vertices, normals, uvs, groupStart, halfHeight} = state
-
- const normal = new Vector3()
- const vertex = new Vector3()
-
- let groupCount = 0
-
- // this will be used to calculate the normal
- const slope = (radiusBottom - radiusTop) / height
-
- // generate vertices, normals and uvs
-
- for (let y = 0; y <= heightSegments; y++) {
- const indexRow = []
- const v = y / heightSegments
- // calculate the radius of the current row
- const radius = v * (radiusBottom - radiusTop) + radiusTop
- for (let x = 0; x <= radialSegments; x++) {
- const u = x / radialSegments
- const theta = u * thetaLength + thetaStart
- const sinTheta = Math.sin(theta)
- const cosTheta = Math.cos(theta)
- // vertex
- vertex.x = radius * sinTheta
- vertex.y = -v * height + halfHeight
- vertex.z = radius * cosTheta
- vertices.push(vertex.x, vertex.y, vertex.z)
- // normal
- normal.set(sinTheta, slope, cosTheta).normalize()
- normals.push(normal.x, normal.y, normal.z)
- // uv
- uvs.push(u, 1 - v)
- // save index of vertex in respective row
- indexRow.push(state.index++)
- }
- // now save vertices of the row in our index array
- indexArray.push(indexRow)
- }
- // generate indices
- for (let x = 0; x < radialSegments; x++) {
- for (let y = 0; y < heightSegments; y++) {
- // we use the index array to access the correct indices
- const a = indexArray[ y ][ x ]
- const b = indexArray[ y + 1 ][ x ]
- const c = indexArray[ y + 1 ][ x + 1 ]
- const d = indexArray[ y ][ x + 1 ]
- // faces
- indices.push(a, b, d)
- indices.push(b, c, d)
- // update group counter
- groupCount += 6
- }
- }
- // add a group to the geometry. this will ensure multi material support
- groups.push({start: groupStart, count: groupCount, materialIndex: 0})
- // calculate new start value for groups
- state.groupStart += groupCount
- }
- protected _generateCap(state: any, top: boolean) {
- const {radiusTop, radiusBottom,
- radialSegments,
- thetaStart, thetaLength, indices, groups,
- vertices, normals, uvs, groupStart, halfHeight} = state
- // save the index of the first center vertex
- const centerIndexStart = state.index
- const uv = new Vector2()
- const vertex = new Vector3()
- let groupCount = 0
- const radius = top === true ? radiusTop : radiusBottom
- const sign = top === true ? 1 : -1
- // first we generate the center vertex data of the cap.
- // because the geometry needs one set of uvs per face,
- // we must generate a center vertex per face/segment
- for (let x = 1; x <= radialSegments; x++) {
- // vertex
- vertices.push(0, halfHeight * sign, 0)
- // normal
- normals.push(0, sign, 0)
- // uv
- uvs.push(0.5, 0.5)
- // increase index
- state.index++
- }
- // save the index of the last center vertex
- const centerIndexEnd = state.index
- // now we generate the surrounding vertices, normals and uvs
- for (let x = 0; x <= radialSegments; x++) {
- const u = x / radialSegments
- const theta = u * thetaLength + thetaStart
- const cosTheta = Math.cos(theta)
- const sinTheta = Math.sin(theta)
- // vertex
- vertex.x = radius * sinTheta
- vertex.y = halfHeight * sign
- vertex.z = radius * cosTheta
- vertices.push(vertex.x, vertex.y, vertex.z)
- // normal
- normals.push(0, sign, 0)
- // uv
- uv.x = cosTheta * 0.5 + 0.5
- uv.y = sinTheta * 0.5 * sign + 0.5
- uvs.push(uv.x, uv.y)
- // increase index
- state.index++
- }
- // generate indices
- for (let x = 0; x < radialSegments; x++) {
- const c = centerIndexStart + x
- const i = centerIndexEnd + x
- if (top === true) {
- // face top
- indices.push(i, i + 1, c)
- } else {
- // face bottom
- indices.push(i + 1, i, c)
- }
- groupCount += 3
- }
- // add a group to the geometry. this will ensure multi material support
- groups.push({start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2})
- // calculate new start value for groups
- state.groupStart += groupCount
- }
-
- protected _generateData(params: CylinderGeometryGeneratorParams) {
- let {radialSegments, heightSegments} = params
-
- radialSegments = Math.floor(radialSegments)
- heightSegments = Math.floor(heightSegments)
-
- const state = {
- indices: [],
- vertices: [],
- normals: [],
- uvs: [],
- numberOfVertices: 0,
- groupStart: 0,
- groups: [],
- index: 0,
- indexArray: [],
- halfHeight: params.height / 2,
- ...params,
- radialSegments,
- heightSegments,
- }
-
- // generate geometry
-
- this._generateTorso(state)
-
- if (params.openEnded === false) {
-
- if (params.radiusTop > 0) this._generateCap(state, true)
- if (params.radiusBottom > 0) this._generateCap(state, false)
-
- }
-
- return state
- }
-
- }
|