UNPKG

@wgl2/geometries

Version:
132 lines (131 loc) 5.53 kB
import { normalize3 } from "@thi.ng/vectors"; const TMP = [0, 0, 0]; export const cylinder = ({ height = 1, radius = 0.25, nx = 16, ny = 1, radiusApex = radius, capSegments = 1, capApex = true, capBase = true, capBaseSegments = capSegments, phi = Math.PI * 2, } = {}) => { let capCount = 0; if (capApex) capCount += capSegments; if (capBase) capCount += capBaseSegments; const segments = nx + 1; const slices = ny + 1; const size = segments * slices + segments * 2 * capCount; const positions = new Float32Array(size * 3); const normals = new Float32Array(size * 3); const texcoords = new Float32Array(size * 2); const indices = new Uint16Array((nx * ny + nx * capCount) * 6); let vertexIndex = 0; let cellIndex = 0; const halfHeight = height / 2; const segmentIncrement = 1 / (segments - 1); const ringIncrement = 1 / (slices - 1); for (let i = 0; i < segments; i++) { const u = i * segmentIncrement; for (let j = 0; j < slices; j++) { const v = j * ringIncrement; const p = u * phi, cosPhi = -Math.cos(p), sinPhi = Math.sin(p); const r = radius * (1 - v) + radiusApex * v; positions[vertexIndex * 3] = r * cosPhi; positions[vertexIndex * 3 + 1] = height * v - halfHeight; positions[vertexIndex * 3 + 2] = r * sinPhi; TMP[0] = height * cosPhi; TMP[1] = radius - radiusApex; TMP[2] = height * sinPhi; normalize3(null, TMP); normals[vertexIndex * 3] = TMP[0]; normals[vertexIndex * 3 + 1] = TMP[1]; normals[vertexIndex * 3 + 2] = TMP[2]; texcoords[vertexIndex * 2] = u; texcoords[vertexIndex * 2 + 1] = v; vertexIndex++; } } for (let j = 0; j < slices - 1; j++) { for (let i = 0; i < segments - 1; i++) { indices[cellIndex + 0] = (i + 0) * slices + (j + 0); indices[cellIndex + 1] = (i + 1) * slices + (j + 0); indices[cellIndex + 2] = (i + 1) * slices + (j + 1); indices[cellIndex + 3] = (i + 0) * slices + (j + 0); indices[cellIndex + 4] = (i + 1) * slices + (j + 1); indices[cellIndex + 5] = (i + 0) * slices + (j + 1); cellIndex += 6; } } function computeCap(flip, height, radius, capSegments) { const index = vertexIndex; const segmentIncrement = 1 / (segments - 1); for (let r = 0; r < capSegments; r++) { for (let i = 0; i < segments; i++) { const p = i * segmentIncrement * phi; const cosPhi = -Math.cos(p), sinPhi = Math.sin(p); // inner point positions[vertexIndex * 3] = (radius * cosPhi * r) / capSegments; positions[vertexIndex * 3 + 1] = height; positions[vertexIndex * 3 + 2] = (radius * sinPhi * r) / capSegments; normals[vertexIndex * 3 + 1] = -flip; texcoords[vertexIndex * 2] = (0.5 * cosPhi * r) / capSegments + 0.5; texcoords[vertexIndex * 2 + 1] = (0.5 * sinPhi * r) / capSegments + 0.5; vertexIndex++; // outer point positions[vertexIndex * 3] = (radius * cosPhi * (r + 1)) / capSegments; positions[vertexIndex * 3 + 1] = height; positions[vertexIndex * 3 + 2] = (radius * sinPhi * (r + 1)) / capSegments; normals[vertexIndex * 3 + 1] = -flip; texcoords[vertexIndex * 2] = (0.5 * (cosPhi * (r + 1))) / capSegments + 0.5; texcoords[vertexIndex * 2 + 1] = (0.5 * (sinPhi * (r + 1))) / capSegments + 0.5; vertexIndex++; } } for (let r = 0; r < capSegments; r++) { for (let i = 0; i < segments - 1; i++) { const n = index + r * segments * 2 + i * 2; const a = n + 0, b = n + 1, c = n + 2, d = n + 3; if (flip === 1) { indices[cellIndex] = a; indices[cellIndex + 1] = c; indices[cellIndex + 2] = d; indices[cellIndex + 3] = a; indices[cellIndex + 4] = d; indices[cellIndex + 5] = b; } else { indices[cellIndex + 0] = a; indices[cellIndex + 1] = d; indices[cellIndex + 2] = c; indices[cellIndex + 3] = a; indices[cellIndex + 4] = b; indices[cellIndex + 5] = d; } cellIndex += 6; } } } if (capBase) computeCap(1, -halfHeight, radius, capBaseSegments); if (capApex) computeCap(-1, halfHeight, radiusApex, capSegments); return { attribs: { position: { data: positions, size: 3 }, texcoords: { data: texcoords, size: 2 }, normal: { data: normals, size: 3 }, }, indices: { data: indices }, count: indices.length, mode: 4 // gl.TRIANGLES }; }; export const cone = (opts = {}) => cylinder({ ...opts, radiusApex: 0, capApex: false, }); export const tetrahedron = ({ radius = .5 } = {}) => cylinder({ height: radius * 1.5, radius, nx: 3, ny: 1, radiusApex: 0, capSegments: 0, capApex: false, capBaseSegments: 1, });