From 2d4ea9ceddaefe09d1f9be6f6b8e59d90548d32f Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 2 Oct 2011 22:28:36 +0100 Subject: [PATCH 1/3] New Sphere class that uses a subdivided octahedron geometry. --- src/extras/geometries/OctahedronGeometry.js | 137 ++++++++++++++++++++ src/extras/objects/Sphere.js | 30 +++++ utils/build.py | 1 + 3 files changed, 168 insertions(+) create mode 100644 src/extras/geometries/OctahedronGeometry.js create mode 100644 src/extras/objects/Sphere.js diff --git a/src/extras/geometries/OctahedronGeometry.js b/src/extras/geometries/OctahedronGeometry.js new file mode 100644 index 00000000..8ed2f9b4 --- /dev/null +++ b/src/extras/geometries/OctahedronGeometry.js @@ -0,0 +1,137 @@ +/** + * Octahedrons have 8 sides. This octahedron supports subdivision. + * + * Vertices have 'smooth' normals, + * to make a sharp edge choose a material that uses face normals instead. + * + * @author daniel.deady@knectar.com + * @param detail Final number of triangles = 4^detail * 8 + */ + +THREE.OctahedronGeometry = function ( detail ) { + + THREE.Geometry.call( this ); + + detail = isFinite(detail) ? detail : 3; // allow a zero value + + var that = this; // ugly scope hack + + prepare( new THREE.Vector3( +1, 0, 0 ) ); // right + prepare( new THREE.Vector3( -1, 0, 0 ) ); // left + prepare( new THREE.Vector3( 0, +1, 0 ) ); // up + prepare( new THREE.Vector3( 0, -1, 0 ) ); // down + prepare( new THREE.Vector3( 0, 0, +1 ) ); // front + prepare( new THREE.Vector3( 0, 0, -1 ) ); // back + var midpoints = [], p = this.vertices; + + // careful to output faces counter-clockwise, that is required for meshes + make( p[0], p[2], p[4], detail ); + make( p[0], p[4], p[3], detail ); + make( p[0], p[3], p[5], detail ); + make( p[0], p[5], p[2], detail ); + make( p[1], p[2], p[5], detail ); + make( p[1], p[5], p[3], detail ); + make( p[1], p[3], p[4], detail ); + make( p[1], p[4], p[2], detail ); + + /** + * Project vector onto sphere's surface + */ + function prepare( vector ) { + + var normal = vector.clone().normalize(); + var vertex = new THREE.Vertex( normal ); + 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: 1 }; + +}; + +THREE.OctahedronGeometry.prototype = new THREE.Geometry(); +THREE.OctahedronGeometry.prototype.constructor = THREE.OctahedronGeometry; diff --git a/src/extras/objects/Sphere.js b/src/extras/objects/Sphere.js new file mode 100644 index 00000000..702b8ccf --- /dev/null +++ b/src/extras/objects/Sphere.js @@ -0,0 +1,30 @@ +/** + * This sphere is smooth and faces are evenly spaced in all directions. + * + * To create there does not need to be a geometry, just a radius. eg. + * var sphere = new THREE.Sphere(100, new THREE.MeshBasicMaterial()); + * + * The number of faces multiplies by 4 for every 1 increase in `detail`. + * 1 detail = 32 faces + * 2 detail = 128 faces + * 3 detail = 512 faces + * + * @author daniel.deady@knectar.com + * @param radius + * @param materials + * @param detail A logarithmic value of fidelity. Defaults to 3. + */ + +THREE.Sphere = function ( radius, materials, detail ) { + + var geometry = new THREE.OctahedronGeometry( detail ); + + THREE.Mesh.call( this, geometry, materials ); + + this.scale.multiplyScalar(radius); + +} + +THREE.Sphere.prototype = new THREE.Mesh(); +THREE.Sphere.prototype.constructor = THREE.Sphere; +THREE.Sphere.prototype.supr = THREE.Mesh.prototype; diff --git a/utils/build.py b/utils/build.py index 5a9ec1cb..cd077019 100644 --- a/utils/build.py +++ b/utils/build.py @@ -104,6 +104,7 @@ EXTRAS_FILES = [ 'extras/geometries/ExtrudeGeometry.js', 'extras/geometries/IcosahedronGeometry.js', 'extras/geometries/LatheGeometry.js', +'extras/geometries/OctahedronGeometry.js', 'extras/geometries/PlaneGeometry.js', 'extras/geometries/SphereGeometry.js', 'extras/geometries/TextGeometry.js', From dad2146d7923315c8d8baf7f52b226565815f1a0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 2 Oct 2011 22:33:07 +0100 Subject: [PATCH 2/3] Include new Sphere class in build. --- utils/build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/build.py b/utils/build.py index cd077019..2e57cd94 100644 --- a/utils/build.py +++ b/utils/build.py @@ -117,6 +117,7 @@ EXTRAS_FILES = [ 'extras/loaders/SceneLoader.js', 'extras/loaders/UTF8Loader.js', 'extras/objects/MarchingCubes.js', +'extras/objects/Sphere.js', 'extras/objects/Trident.js', 'extras/physics/Collisions.js', 'extras/physics/CollisionUtils.js', From 5ef31d7fe8f35abdfa8d2687eaa420f7356288bb Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 3 Oct 2011 11:12:37 +0100 Subject: [PATCH 3/3] Remove Sphere class. Give radius to octahedron. --- src/extras/geometries/OctahedronGeometry.js | 7 ++--- src/extras/objects/Sphere.js | 30 --------------------- utils/build.py | 1 - 3 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 src/extras/objects/Sphere.js diff --git a/src/extras/geometries/OctahedronGeometry.js b/src/extras/geometries/OctahedronGeometry.js index 8ed2f9b4..7a856bd0 100644 --- a/src/extras/geometries/OctahedronGeometry.js +++ b/src/extras/geometries/OctahedronGeometry.js @@ -5,10 +5,11 @@ * to make a sharp edge choose a material that uses face normals instead. * * @author daniel.deady@knectar.com + * @param radius * @param detail Final number of triangles = 4^detail * 8 */ -THREE.OctahedronGeometry = function ( detail ) { +THREE.OctahedronGeometry = function ( radius, detail ) { THREE.Geometry.call( this ); @@ -40,7 +41,7 @@ THREE.OctahedronGeometry = function ( detail ) { function prepare( vector ) { var normal = vector.clone().normalize(); - var vertex = new THREE.Vertex( normal ); + var vertex = new THREE.Vertex( normal.clone().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. @@ -129,7 +130,7 @@ THREE.OctahedronGeometry = function ( detail ) { } - this.boundingSphere = { radius: 1 }; + this.boundingSphere = { radius: radius }; }; diff --git a/src/extras/objects/Sphere.js b/src/extras/objects/Sphere.js deleted file mode 100644 index 702b8ccf..00000000 --- a/src/extras/objects/Sphere.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * This sphere is smooth and faces are evenly spaced in all directions. - * - * To create there does not need to be a geometry, just a radius. eg. - * var sphere = new THREE.Sphere(100, new THREE.MeshBasicMaterial()); - * - * The number of faces multiplies by 4 for every 1 increase in `detail`. - * 1 detail = 32 faces - * 2 detail = 128 faces - * 3 detail = 512 faces - * - * @author daniel.deady@knectar.com - * @param radius - * @param materials - * @param detail A logarithmic value of fidelity. Defaults to 3. - */ - -THREE.Sphere = function ( radius, materials, detail ) { - - var geometry = new THREE.OctahedronGeometry( detail ); - - THREE.Mesh.call( this, geometry, materials ); - - this.scale.multiplyScalar(radius); - -} - -THREE.Sphere.prototype = new THREE.Mesh(); -THREE.Sphere.prototype.constructor = THREE.Sphere; -THREE.Sphere.prototype.supr = THREE.Mesh.prototype; diff --git a/utils/build.py b/utils/build.py index 2e57cd94..cd077019 100644 --- a/utils/build.py +++ b/utils/build.py @@ -117,7 +117,6 @@ EXTRAS_FILES = [ 'extras/loaders/SceneLoader.js', 'extras/loaders/UTF8Loader.js', 'extras/objects/MarchingCubes.js', -'extras/objects/Sphere.js', 'extras/objects/Trident.js', 'extras/physics/Collisions.js', 'extras/physics/CollisionUtils.js',