Grid Class
class GRIDCELL {
constructor(f, origin, scale) {
this.pos = new Vector3(); // "position" of the cube
this.f = f;
this.origin = origin;
this.scale = scale;
this.cubeIndex = 0;
this.val = new Array(8);
this.p = [
new Vector3(0,0,0).multiply(this.scale),
new Vector3(1,0,0).multiply(this.scale),
new Vector3(1,0,1).multiply(this.scale),
new Vector3(0,0,1).multiply(this.scale),
new Vector3(0,1,0).multiply(this.scale),
new Vector3(1,1,0).multiply(this.scale),
new Vector3(1,1,1).multiply(this.scale),
new Vector3(0,1,1).multiply(this.scale)
]
}
march(i, j, k, isoLevel = 0) {
let origin = this.origin, scale = this.scale;
this.cubeIndex = 0; // reset cube index to zero
for (let v = 0; v<8; v++) {
// calculate the scalar value at the corner
let p = this.p[v]; // offset to get the relevant vertex
let s = this.f(
scale.x * i + p.x + origin.x,
scale.y * j + p.y + origin.y,
scale.z * k + p.z + origin.z
);
this.val[v] = s;
// calculate the index into the triangulation table
this.cubeIndex |= (s < isoLevel) ? (1 << v) : 0;
}
this.pos.set(scale.x * i + origin.x, scale.y * j + origin.y, scale.z * k + origin.z); // *position* of cube
}
}
Polygonize!
function polygonise(f, isoLevel, _size, _bounds) {
let size = _size; // this is the resolution (ie how many points to sample)
let bounds = _bounds; // define the bounds of the shape
let scale = bounds[1].clone().subtract(bounds[0]).divide(size);
let origin = bounds[0]; // when calculated scalar values, "shift" the "grid coordinate" by this to get to x,y,z for calculation
let grid = new GRIDCELL(f, origin, scale); // create cube to march!
let vertices = [];
let indices = [];
let edges = new Array(12); // working array to temporarily hold the intersecting edges
let vertList = Array.from(Array(12), ()=>new Vector3());
for (let i =0; i < size.x-1; i++) {
for (let j = 0; j < size.y-1; j++) {
for (let k = 0; k < size.z-1; k++) {
// for the indiviual marching cube, get the index into the triangulation table
grid.march(i, j, k, isoLevel);
let cubeIndex = grid.cubeIndex;
//Compute vertices
if(edgeTable[cubeIndex] === 0) continue;
let pos = grid.pos;
for (let i = 0; i < 12; i++) {
if((edgeTable[cubeIndex] & (1<<i)) === 0) continue; // if the edge is not intersecting, no need to compute the intersecting point
edges[i] = vertices.length/3; // 3 vertices for a triangle, so length of the vertices array / 3 gives the index into the face
let e = edgeIndex[i]; // e holds an array which holds the start and ending vertex indices of edge i
let eStart = e[0], eEnd = e[1]; // the ending and starting vertex index of an edge
let ix = vertexInterp(isoLevel, grid.p[eStart], grid.p[eEnd], grid.val[eStart], grid.val[eEnd]).add(pos)
vertices.push(ix.x, ix.y, ix.z)
}
//Add vertex indices for intersecting faces
let f = triTable[cubeIndex]; // f holds the sequence of intersecting edges to be joined together to form the intersecting faces
for (let i = 0; f[i] !== -1; i+=3) {
indices.push(edges[f[i]], edges[f[i+1]], edges[f[i+2]]);
}
}
}
}
return {verts: vertices, indices: indices}
}
CodePen
Example using
// Goursat Surface
let f3 = function(x,y,z) {
return Math.pow(x,4) + Math.pow(y,4) + Math.pow(z,4) - 1.5 * (x*x + y*y + z*z) + 1;
}
Useful References
Comments