@wgl2/geometries
Version:
Geometry Primitives
132 lines (131 loc) • 5.53 kB
JavaScript
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,
});