function customGeometry(r = 1, numCols = 30, numRows = 15) {
const vertices = [];
const indices = [];
const uvs = [];
const halfPi = Math.PI / 2;
for (let col = 0; col <= numCols; col++) {
const t1 = THREE.MathUtils.mapLinear(col, 0, numCols, -Math.PI, Math.PI);
for (let row = 0; row <= numRows; row++) {
const t2 = THREE.MathUtils.mapLinear(row, 0, numRows, -halfPi, halfPi);
const x = 0.5 * r * Math.cos(t1) * Math.cos(t2);
const z = r * Math.sin(t1) * Math.cos(t2);
const y = 0.5 * r * Math.sin(t2);
vertices.push(x, y, z);
const u = 1 - col / numCols;
const v = row / numRows;
uvs.push(u, v);
}
}
for (let col = 0; col < numCols; col++) {
for (let row = 0; row < numRows; row++) {
const a = col * (numRows + 1) + row;
const b = (col + 1) * (numRows + 1) + row;
const c = col * (numRows + 1) + row + 1;
const d = (col + 1) * (numRows + 1) + row + 1;
indices.push(a, c, b);
indices.push(b, c, d);
}
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(vertices, 3)
);
geometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2));
geometry.setIndex(indices);
geometry.computeVertexNormals();
return geometry;
}