threepipe
Version:
A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.
95 lines (81 loc) • 3.34 kB
text/typescript
import {Vector3} from 'three'
import {AGeometryGenerator} from '../AGeometryGenerator'
export interface SphereGeometryGeneratorParams {
radius: number,
widthSegments: number,
heightSegments: number,
phiStart: number,
phiLength: number,
thetaStart: number,
thetaLength: number,
}
export class SphereGeometryGenerator<Tt extends string= 'sphere'> extends AGeometryGenerator<SphereGeometryGeneratorParams, Tt> {
constructor(type: Tt = 'sphere' as Tt, defaultParams?: Partial<SphereGeometryGeneratorParams>) {
super(type)
if (defaultParams) Object.assign(this.defaultParams, defaultParams)
}
defaultParams: SphereGeometryGeneratorParams = {
radius: 1,
widthSegments: 32,
heightSegments: 16,
phiStart: 0,
phiLength: Math.PI * 2,
thetaStart: 0,
thetaLength: Math.PI,
}
protected _generateData(params: SphereGeometryGeneratorParams) {
const {radius, phiStart, phiLength, thetaStart, thetaLength} = params
let {widthSegments, heightSegments} = params
widthSegments = Math.max(3, Math.floor(widthSegments))
heightSegments = Math.max(2, Math.floor(heightSegments))
const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI)
let index = 0
const grid = []
const vertex = new Vector3()
const normal = new Vector3()
// buffers
const indices = []
const vertices = []
const normals = []
const uvs = []
// generate vertices, normals and uvs
for (let iy = 0; iy <= heightSegments; iy++) {
const verticesRow = []
const v = iy / heightSegments
// special case for the poles
let uOffset = 0
if (iy === 0 && thetaStart === 0) {
uOffset = 0.5 / widthSegments
} else if (iy === heightSegments && thetaEnd === Math.PI) {
uOffset = -0.5 / widthSegments
}
for (let ix = 0; ix <= widthSegments; ix++) {
const u = ix / widthSegments
// vertex
vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength)
vertex.y = radius * Math.cos(thetaStart + v * thetaLength)
vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength)
vertices.push(vertex.x, vertex.y, vertex.z)
// normal
normal.copy(vertex).normalize()
normals.push(normal.x, normal.y, normal.z)
// uv
uvs.push(u + uOffset, 1 - v)
verticesRow.push(index++)
}
grid.push(verticesRow)
}
// indices
for (let iy = 0; iy < heightSegments; iy++) {
for (let ix = 0; ix < widthSegments; ix++) {
const a = grid[iy][ix + 1]
const b = grid[iy][ix]
const c = grid[iy + 1][ix]
const d = grid[iy + 1][ix + 1]
if (iy !== 0 || thetaStart > 0) indices.push(a, b, d)
if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d)
}
}
return {indices, vertices, normals, uvs}
}
}