Spaces:
Sleeping
Sleeping
| import { BufferGeometry } from '../core/BufferGeometry.js'; | |
| import { Float32BufferAttribute } from '../core/BufferAttribute.js'; | |
| import { Vector3 } from '../math/Vector3.js'; | |
| import { Vector2 } from '../math/Vector2.js'; | |
| class CylinderGeometry extends BufferGeometry { | |
| constructor( | |
| radiusTop = 1, | |
| radiusBottom = 1, | |
| height = 1, | |
| radialSegments = 8, | |
| heightSegments = 1, | |
| openEnded = false, | |
| thetaStart = 0, | |
| thetaLength = Math.PI * 2 | |
| ) { | |
| super(); | |
| this.type = 'CylinderGeometry'; | |
| this.parameters = { | |
| radiusTop: radiusTop, | |
| radiusBottom: radiusBottom, | |
| height: height, | |
| radialSegments: radialSegments, | |
| heightSegments: heightSegments, | |
| openEnded: openEnded, | |
| thetaStart: thetaStart, | |
| thetaLength: thetaLength, | |
| }; | |
| const scope = this; | |
| radialSegments = Math.floor(radialSegments); | |
| heightSegments = Math.floor(heightSegments); | |
| // buffers | |
| const indices = []; | |
| const vertices = []; | |
| const normals = []; | |
| const uvs = []; | |
| // helper variables | |
| let index = 0; | |
| const indexArray = []; | |
| const halfHeight = height / 2; | |
| let groupStart = 0; | |
| // generate geometry | |
| generateTorso(); | |
| if (openEnded === false) { | |
| if (radiusTop > 0) generateCap(true); | |
| if (radiusBottom > 0) generateCap(false); | |
| } | |
| // build geometry | |
| this.setIndex(indices); | |
| this.setAttribute('position', new Float32BufferAttribute(vertices, 3)); | |
| this.setAttribute('normal', new Float32BufferAttribute(normals, 3)); | |
| this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); | |
| function generateTorso() { | |
| 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(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 | |
| scope.addGroup(groupStart, groupCount, 0); | |
| // calculate new start value for groups | |
| groupStart += groupCount; | |
| } | |
| function generateCap(top) { | |
| // save the index of the first center vertex | |
| const centerIndexStart = 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 | |
| index++; | |
| } | |
| // save the index of the last center vertex | |
| const centerIndexEnd = 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 | |
| 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 | |
| scope.addGroup(groupStart, groupCount, top === true ? 1 : 2); | |
| // calculate new start value for groups | |
| groupStart += groupCount; | |
| } | |
| } | |
| static fromJSON(data) { | |
| return new CylinderGeometry( | |
| data.radiusTop, | |
| data.radiusBottom, | |
| data.height, | |
| data.radialSegments, | |
| data.heightSegments, | |
| data.openEnded, | |
| data.thetaStart, | |
| data.thetaLength | |
| ); | |
| } | |
| } | |
| export { CylinderGeometry, CylinderGeometry as CylinderBufferGeometry }; | |