Skip to content

Instantly share code, notes, and snippets.

@warmwaffles
Forked from Inscrutabilis/GeodesicSphere.js
Created April 18, 2018 04:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save warmwaffles/ec7fa684623ead8fa0ca250390a2f289 to your computer and use it in GitHub Desktop.
Save warmwaffles/ec7fa684623ead8fa0ca250390a2f289 to your computer and use it in GitHub Desktop.
Three.js geodesic sphere primitive class
/**
* @author Inscrutabilis / mailto:iinscrutabilis@gmail.com
*/
/*
* Generates a geodesic sphere geometry
* By default, it is 1x1x1 octahedron, as a first approximation to sphere
* It will return more sphere-like object if you specify iterations > 0
* But please keep in mind that it generates (4 ^ iterations) * 8 faces
* Radius argument overrides default sphere radius (1)
*/
var GeodesicSphere = function(iterations, radius) {
iterations = iterations || 0;
radius = radius || 1;
THREE.Geometry.call(this);
// initial 6 vertices of octahedron
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 1, 0)).normalize().multiplyScalar(radius))); // #0
this.vertices.push(new THREE.Vertex((new THREE.Vector3(-1, 0, 1)).normalize().multiplyScalar(radius))); // #1
this.vertices.push(new THREE.Vertex((new THREE.Vector3(1, 0, 1)).normalize().multiplyScalar(radius))); // #2
this.vertices.push(new THREE.Vertex((new THREE.Vector3(1, 0, -1)).normalize().multiplyScalar(radius))); // #3
this.vertices.push(new THREE.Vertex((new THREE.Vector3(-1, 0, -1)).normalize().multiplyScalar(radius))); // #4
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, -1, 0)).normalize().multiplyScalar(radius))); // #5
// and these duplicates we need to correctly map texture
/*this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, 1)).normalize().multiplyScalar(radius))); // #6 (#0 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, 1)).normalize().multiplyScalar(radius))); // #7 (#0 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, 1)).normalize().multiplyScalar(radius))); // #8 (#0 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(-1, 1, 0)).normalize().multiplyScalar(radius))); // #9 (#1 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, -1)).normalize().multiplyScalar(radius))); // #10 (#5 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, -1)).normalize().multiplyScalar(radius))); // #11 (#5 duplicate)
this.vertices.push(new THREE.Vertex((new THREE.Vector3(0, 0, -1)).normalize().multiplyScalar(radius))); // #12 (#5 duplicate)*/
// now init UVs for the above; we can't store them yet
var tuvs = [];
tuvs.push(new THREE.UV(0.125, 0)); // #0
tuvs.push(new THREE.UV(0, 0.5)); // #1
tuvs.push(new THREE.UV(0.25, 0.5)); // #2
tuvs.push(new THREE.UV(0.5, 0.5)); // #3
tuvs.push(new THREE.UV(0.75, 0.5)); // #4
tuvs.push(new THREE.UV(0.125, 1)); // #5
tuvs.push(new THREE.UV(0.375, 0)); // #6
tuvs.push(new THREE.UV(0.625, 0)); // #7
tuvs.push(new THREE.UV(0.875, 0)); // #8
tuvs.push(new THREE.UV(1, 0.5)); // #9
tuvs.push(new THREE.UV(0.375, 1)); // #10
tuvs.push(new THREE.UV(0.625, 1)); // #11
tuvs.push(new THREE.UV(0.875, 1)); // #12
// FIXME: if someone comes up with idea on how to reuse all of the generated vertices, implement it ASAP
// FIXME: due to nature of faces, we need to pass texture UVs down to the last step. Absolutely logical, but looks non-elegant
// FIXME: it is imperfect universe
var faceGen = function faceGenerator(g, ia, ib, ic, ta, tb, tc, iterations) {
if (iterations <= 0) {
// this is the last iteration -- just save face and its UVs
g.faces.push(new THREE.Face3(ia, ib, ic));
g.uvs.push([ta, tb, tc]);
} else {
var va = g.vertices[ia];
var vb = g.vertices[ib];
var vc = g.vertices[ic];
// calculate midpoints
var pab = new THREE.Vector3((va.position.x + vb.position.x) / 2, (va.position.y + vb.position.y) / 2, (va.position.z + vb.position.z) / 2);
var pbc = new THREE.Vector3((vb.position.x + vc.position.x) / 2, (vb.position.y + vc.position.y) / 2, (vb.position.z + vc.position.z) / 2);
var pca = new THREE.Vector3((vc.position.x + va.position.x) / 2, (vc.position.y + va.position.y) / 2, (vc.position.z + va.position.z) / 2);
// normalize them
pab = pab.normalize().multiplyScalar(radius);
pbc = pbc.normalize().multiplyScalar(radius);
pca = pca.normalize().multiplyScalar(radius);
// and their texture coordinates
var tab = new THREE.UV((ta.u + tb.u) / 2, (ta.v + tb.v) / 2);
var tbc = new THREE.UV((tb.u + tc.u) / 2, (tb.v + tc.v) / 2);
var tca = new THREE.UV((tc.u + ta.u) / 2, (tc.v + ta.v) / 2);
// save vertices array length for later use
var vertArrayLength = g.vertices.length;
// now create vertices and store them
g.vertices.push(new THREE.Vertex(pab));
g.vertices.push(new THREE.Vertex(pbc));
g.vertices.push(new THREE.Vertex(pca));
// and we know their indices
var iab = vertArrayLength;
var ibc = vertArrayLength + 1;
var ica = vertArrayLength + 2;
faceGenerator(g, ica, iab, ia, tca, tab, ta, iterations - 1);
faceGenerator(g, ibc, ib, iab, tbc, tb, tab, iterations - 1);
faceGenerator(g, ic, ibc, ica, tc, tbc, tca, iterations - 1);
faceGenerator(g, ica, ibc, iab, tca, tbc, tab, iterations - 1);
}
}
// finally! Do face generation for octahedron planes
faceGen(this, 2, 1, 0, tuvs[2], tuvs[1], tuvs[0], iterations);
faceGen(this, 3, 2, 0, tuvs[3], tuvs[2], tuvs[6], iterations);
faceGen(this, 4, 3, 0, tuvs[4], tuvs[3], tuvs[7], iterations);
faceGen(this, 1, 4, 0, tuvs[9], tuvs[4], tuvs[8], iterations);
faceGen(this, 1, 2, 5, tuvs[1], tuvs[2], tuvs[5], iterations);
faceGen(this, 2, 3, 5, tuvs[2], tuvs[3], tuvs[10], iterations);
faceGen(this, 3, 4, 5, tuvs[3], tuvs[4], tuvs[11], iterations);
faceGen(this, 4, 1, 5, tuvs[4], tuvs[9], tuvs[12], iterations);
this.computeCentroids();
this.computeFaceNormals();
this.sortFacesByMaterial();
}
GeodesicSphere.prototype = new THREE.Geometry();
GeodesicSphere.prototype.constructor = GeodesicSphere;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment