Source: geometry/SphereGeometry.js

import Class from '../core/Class';
import Geometry from './Geometry';
import GeometryData from './GeometryData';

const centerData = [0, 0, 0]; // eslint-disable-line no-unused-vars
/**
 * 球形几何体
 * @class
 * @extends Geometry
 */
const SphereGeometry = Class.create(/** @lends SphereGeometry.prototype */ {
    Extends: Geometry,
    /**
     * @default true
     * @type {boolean}
     */
    isSphereGeometry: true,
    /**
     * @default SphereGeometry
     * @type {string}
     */
    className: 'SphereGeometry',
    /**
     * 半径
     * @default 1
     * @type {number}
     */
    radius: 1,
    /**
     * 垂直分割面的数量
     * @default 16
     * @type {number}
     */
    heightSegments: 16,
    /**
     * 水平分割面的数量
     * @default 32
     * @type {number}
     */
    widthSegments: 32,
    /**
     * @constructs
     * @param {Object} [params] 创建对象的属性参数。可包含此类的所有属性。
     * @param {number} [params.radius=1] 半径
     * @param {number} [params.heightSegments=16] 垂直分割面的数量
     * @param {number} [params.widthSegments=32] 水平分割面的数量
     * @param {any} [params.[value:string]] 其它属性
     */
    constructor(params) {
        SphereGeometry.superclass.constructor.call(this, params);
        this.build();
    },
    build() {
        const radius = this.radius;
        const heightSegments = this.heightSegments;
        const widthSegments = this.widthSegments;

        const count = (widthSegments + 1) * (heightSegments + 1);
        const gridCount = widthSegments * heightSegments;
        const vertices = new Float32Array(count * 3);
        const tangents = new Float32Array(count * 4);
        const uvs = new Float32Array(count * 2);
        const indices = new Uint16Array(gridCount * 6);

        let indexId = 0;
        let vertexId = 0;
        let tangentId = 0;
        let uvId = 0;
        let pointId = 0;
        const ANGLE_360 = Math.PI * 2;
        const ANGLE_180 = Math.PI;

        for (let h = 0; h <= heightSegments; h++) {
            const v = h / heightSegments;
            const pitchAngle = ANGLE_180 * v;
            const y = Math.cos(pitchAngle) * radius;
            const yawRadius = Math.sin(pitchAngle) * radius;

            for (let w = 0; w <= widthSegments; w++) {
                const u = w / widthSegments;
                const yawAngle = ANGLE_360 * u;
                const yawCos = Math.cos(yawAngle);
                const yawSin = Math.sin(yawAngle);
                const x = yawCos * yawRadius;
                const z = yawSin * yawRadius;

                let tangentX = yawSin;
                let tangentY = 0;
                let tangentZ = -yawCos;

                tangents[tangentId++] = tangentX;
                tangents[tangentId++] = tangentY;
                tangents[tangentId++] = tangentZ;
                tangents[tangentId++] = 1;

                vertices[vertexId++] = x;
                vertices[vertexId++] = y;
                vertices[vertexId++] = z;

                uvs[uvId++] = u;
                uvs[uvId++] = v;

                if (h > 0 && w > 0) {
                    const a = pointId;
                    const b = a - 1;
                    const c = b - widthSegments - 1;
                    const d = a - widthSegments - 1;

                    indices[indexId++] = c;
                    indices[indexId++] = d;
                    indices[indexId++] = a;
                    indices[indexId++] = c;
                    indices[indexId++] = a;
                    indices[indexId++] = b;
                }
                pointId++;
            }
        }
        this.vertices = new GeometryData(vertices, 3);
        this.indices = new GeometryData(indices, 1);
        this.uvs = new GeometryData(uvs, 2);
        this.tangents = new GeometryData(tangents, 4);
        this.normals = new GeometryData(new Float32Array(vertices), 3);
    },
    _raycast(ray, side) {
        // TODO:optimize
        return SphereGeometry.superclass._raycast.call(this, ray, side);
    }
});

export default SphereGeometry;