Skip to content

Instantly share code, notes, and snippets.

@timothypratley
Created January 1, 2012 05:33
Show Gist options
  • Save timothypratley/1546370 to your computer and use it in GitHub Desktop.
Save timothypratley/1546370 to your computer and use it in GitHub Desktop.
polyhedrons for three.js
/**
* Polyhedrons which support subdivision.
*
* Vertices have 'smooth' normals,
* to make a sharp edge choose a material that uses face normals instead.
*
* @author timothypratley@gmail.com
* @author daniel.deady@knectar.com
* @param radius
* @param detail Final number of triangles = 4^detail * X
*/
THREE.TetrahedronGeometry = function (radius, detail) {
var vs = [[+1, +1, +1],
[-1, -1, +1],
[-1, +1, -1],
[+1, -1, -1]],
fs = [[2, 1, 0],
[0, 3, 2],
[1, 3, 0],
[2, 3, 1]];
THREE.PolyhedronGeometry.call(this, vs, fs, radius, detail);
};
THREE.TetrahedronGeometry.prototype = new THREE.Geometry();
THREE.TetrahedronGeometry.prototype.constructor = THREE.TetrahedronGeometry;
THREE.OctahedronGeometry = function (radius, detail) {
var vs = [[+1, 0, 0],
[-1, 0, 0], // left
[0, +1, 0], // up
[0, -1, 0], // down
[0, 0, +1], // front
[0, 0, -1]], // back
fs = [[0, 2, 4],
[0, 4, 3],
[0, 3, 5],
[0, 5, 2],
[1, 2, 5],
[1, 5, 3],
[1, 3, 4],
[1, 4, 2]];
THREE.PolyhedronGeometry.call(this, vs, fs, radius, detail);
};
THREE.OctahedronGeometry.prototype = new THREE.Geometry();
THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry;
THREE.IcosahedronGeometry = function (radius, detail) {
var t = (1 + Math.sqrt(5)) / 2,
// create 12 vertices of a Icosahedron
vs = [[-1, t, 0],
[1, t, 0],
[-1, -t, 0],
[1, -t, 0],
[0, -1, t],
[0, 1, t],
[0, -1, -t],
[0, 1, -t],
[t, 0, -1],
[t, 0, 1],
[-t, 0, -1],
[-t, 0, 1]],
fs = [
// 5 faces around point 0
[0, 11, 5],
[0, 5, 1],
[0, 1, 7],
[0, 7, 10],
[0, 10, 11],
// 5 adjacent faces
[1, 5, 9],
[5, 11, 4],
[11, 10, 2],
[10, 7, 6],
[7, 1, 8],
// 5 faces around point 3
[3, 9, 4],
[3, 4, 2],
[3, 2, 6],
[3, 6, 8],
[3, 8, 9],
// 5 adjacent faces
[4, 9, 5],
[2, 4, 11],
[6, 2, 10],
[8, 6, 7],
[9, 8, 1]];
THREE.PolyhedronGeometry.call(this, vs, fs, radius, detail);
};
THREE.IcosahedronGeometry.prototype = new THREE.Geometry();
THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry;
THREE.PolyhedronGeometry = function (vs, fs, radius, detail) {
THREE.Geometry.call(this);
radius = radius || 1;
detail = detail || 0;
var that = this,
ii;
for (ii = 0; ii < vs.length; ii++) {
prepare(new THREE.Vector3(vs[ii][0], vs[ii][1], vs[ii][2]));
}
var midpoints = [], p = this.vertices;
// careful to output faces counter-clockwise, that is required for meshes
for (ii = 0; ii < fs.length; ii++) {
make(p[fs[ii][0]], p[fs[ii][1]], p[fs[ii][2]], detail);
}
this.mergeVertices();
/**
* Project vector onto sphere's surface
*/
function prepare(vector) {
var vertex = new THREE.Vertex(vector.clone().normalize().multiplyScalar(radius));
vertex.index = that.vertices.push(vertex) - 1;
// Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.
var u = azimuth(vector) / 2 / Math.PI + 0.5;
var v = inclination(vector) / Math.PI + 0.5;
vertex.uv = new THREE.UV(u, v);
return vertex;
}
/**
* Approximate a curved face with recursively sub-divided triangles.
*/
function make(v1, v2, v3, detail) {
if (detail < 1) {
var face = new THREE.Face3(v1.index, v2.index, v3.index, [v1.position, v2.position, v3.position]);
face.centroid.addSelf(v1.position).addSelf(v2.position).addSelf(v3.position).divideScalar(3);
face.normal = face.centroid.clone().normalize();
that.faces.push(face);
var azi = azimuth(face.centroid);
that.faceVertexUvs[0].push([
correctUV(v1.uv, v1.position, azi),
correctUV(v2.uv, v2.position, azi),
correctUV(v3.uv, v3.position, azi)
]);
}
else {
detail -= 1;
// split triangle into 4 smaller triangles
make(v1, midpoint(v1, v2), midpoint(v1, v3), detail); // top quadrant
make(midpoint(v1, v2), v2, midpoint(v2, v3), detail); // left quadrant
make(midpoint(v1, v3), midpoint(v2, v3), v3, detail); // right quadrant
make(midpoint(v1, v2), midpoint(v2, v3), midpoint(v1, v3), detail); // center quadrant
}
}
function midpoint(v1, v2) {
if (!midpoints[v1.index]) midpoints[v1.index] = [];
if (!midpoints[v2.index]) midpoints[v2.index] = [];
var mid = midpoints[v1.index][v2.index];
if (mid === undefined) {
// generate mean point and project to surface with prepare()
midpoints[v1.index][v2.index] = midpoints[v2.index][v1.index] = mid = prepare(
new THREE.Vector3().add(v1.position, v2.position).divideScalar(2)
);
}
return mid;
}
/**
* Angle around the Y axis, counter-clockwise when looking from above.
*/
function azimuth(vector) {
return Math.atan2(vector.z, -vector.x);
}
/**
* Angle above the XZ plane.
*/
function inclination(vector) {
return Math.atan2(-vector.y, Math.sqrt((vector.x * vector.x) + (vector.z * vector.z)));
}
/**
* Texture fixing helper. Spheres have some odd behaviours.
*/
function correctUV(uv, vector, azimuth) {
if ((azimuth < 0) && (uv.u === 1)) uv = new THREE.UV(uv.u - 1, uv.v);
if ((vector.x === 0) && (vector.z === 0)) uv = new THREE.UV(azimuth / 2 / Math.PI + 0.5, uv.v);
return uv;
}
this.boundingSphere = { radius: radius };
};
THREE.PolyhedronGeometry.prototype = new THREE.Geometry();
THREE.PolyhedronGeometry.prototype.constructor = THREE.PolyhedronGeometry;
testGeometry = function (world) {
var green = new THREE.MeshLambertMaterial({ color: 0x00FF00 }),
tetrahedron = new THREE.Mesh(new THREE.TetrahedronGeometry(50), green),
tetrahedron2 = new THREE.Mesh(new THREE.TetrahedronGeometry(50, 2), green),
grey = new THREE.MeshLambertMaterial({ color: 0x444444 }),
octahedron = new THREE.Mesh(new THREE.OctahedronGeometry(50), grey),
octahedron2 = new THREE.Mesh(new THREE.OctahedronGeometry(50, 2), grey),
blue = new THREE.MeshLambertMaterial({ color: 0x0000FF }),
icosahedron = new THREE.Mesh(new THREE.IcosahedronGeometry(50), blue),
icosahedron2 = new THREE.Mesh(new THREE.IcosahedronGeometry(50, 2), blue);
tetrahedron.position.set(100, 0, 0);
world.add(tetrahedron);
octahedron.position.set(0, 100, 0);
world.add(octahedron);
icosahedron.position.set(100, 100, 0);
world.add(icosahedron);
tetrahedron2.position.set(-100, 0, 0);
world.add(tetrahedron2);
octahedron2.position.set(0, -100, 0);
world.add(octahedron2);
icosahedron2.position.set(-100, -100, 0);
world.add(icosahedron2);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment