/** * @author clockworkgeek / https://github.com/clockworkgeek * @author timothypratley / https://github.com/timothypratley */ THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) { THREE.Geometry.call( this ); radius = radius || 1; detail = detail || 0; var that = this; for ( var i = 0, l = vertices.length; i < l; i ++ ) { prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) ); } var midpoints = [], p = this.vertices; for ( var i = 0, l = faces.length; i < l; i ++ ) { make( p[ faces[ i ][ 0 ] ], p[ faces[ i ][ 1 ] ], p[ faces[ i ][ 2 ] ], detail ); } this.mergeVertices(); // Apply radius for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { this.vertices[ i ].position.multiplyScalar( radius ); } /** * Project vector onto sphere's surface */ function prepare( vector ) { var vertex = new THREE.Vertex( vector.normalize() ); 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.clone(), v2.position.clone(), v3.position.clone() ] ); 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;