|
@@ -0,0 +1,168 @@
|
|
|
+/**
|
|
|
+ * @author Mugen87 / https://github.com/Mugen87
|
|
|
+ *
|
|
|
+ * see: http://www.blackpawn.com/texts/pqtorus/
|
|
|
+ */
|
|
|
+THREE.TorusKnotBufferGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale, arc ) {
|
|
|
+
|
|
|
+ THREE.BufferGeometry.call( this );
|
|
|
+
|
|
|
+ this.type = 'TorusKnotBufferGeometry';
|
|
|
+
|
|
|
+ this.parameters = {
|
|
|
+ radius: radius,
|
|
|
+ tube: tube,
|
|
|
+ radialSegments: radialSegments,
|
|
|
+ tubularSegments: tubularSegments,
|
|
|
+ p: p,
|
|
|
+ q: q,
|
|
|
+ heightScale: heightScale,
|
|
|
+ arc: arc
|
|
|
+ };
|
|
|
+
|
|
|
+ radius = radius || 100;
|
|
|
+ tube = tube || 40;
|
|
|
+ radialSegments = Math.floor( radialSegments ) || 64;
|
|
|
+ tubularSegments = Math.floor( tubularSegments ) || 6;
|
|
|
+ p = p || 2;
|
|
|
+ q = q || 3;
|
|
|
+ heightScale = heightScale || 1;
|
|
|
+ arc = arc || Math.PI * 2;
|
|
|
+
|
|
|
+ // used to calculate buffer length
|
|
|
+ var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) );
|
|
|
+ var indexCount = radialSegments * tubularSegments * 2 * 3;
|
|
|
+
|
|
|
+ // buffers
|
|
|
+ var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 );
|
|
|
+ var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
|
|
|
+ var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 );
|
|
|
+ var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 );
|
|
|
+
|
|
|
+ // helper variables
|
|
|
+ var i, j, index = 0, indexOffset = 0;
|
|
|
+
|
|
|
+ var vertex = new THREE.Vector3();
|
|
|
+ var normal = new THREE.Vector3();
|
|
|
+ var uv = new THREE.Vector2();
|
|
|
+
|
|
|
+ var P1 = new THREE.Vector3();
|
|
|
+ var P2 = new THREE.Vector3();
|
|
|
+
|
|
|
+ var B = new THREE.Vector3();
|
|
|
+ var T = new THREE.Vector3();
|
|
|
+ var N = new THREE.Vector3();
|
|
|
+
|
|
|
+ // generate vertices, normals and uvs
|
|
|
+
|
|
|
+ for ( i = 0; i <= radialSegments; ++ i ) {
|
|
|
+
|
|
|
+ // the radian "u" is used to calculate the position on the torus curve of the current radial segement
|
|
|
+
|
|
|
+ var u = i / radialSegments * p * arc;
|
|
|
+
|
|
|
+ // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.
|
|
|
+ // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions
|
|
|
+
|
|
|
+ calculatePositionOnCurve( u, p, q, radius, heightScale, P1 );
|
|
|
+ calculatePositionOnCurve( u + 0.01, p, q, radius, heightScale, P2 );
|
|
|
+
|
|
|
+ // calculate orthonormal basis
|
|
|
+
|
|
|
+ T.subVectors( P2, P1 );
|
|
|
+ N.addVectors( P2, P1 );
|
|
|
+ B.crossVectors( T, N );
|
|
|
+ N.crossVectors( B, T );
|
|
|
+
|
|
|
+ // normalize B, N. T can be ignored, we don't use it
|
|
|
+
|
|
|
+ B.normalize();
|
|
|
+ N.normalize();
|
|
|
+
|
|
|
+ for ( j = 0; j <= tubularSegments; ++ j ) {
|
|
|
+
|
|
|
+ // now calculate the vertices. they are nothing more than an extrusion of the torus curve.
|
|
|
+ // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.
|
|
|
+
|
|
|
+ var v = j / tubularSegments * Math.PI * 2;
|
|
|
+ var cx = - tube * Math.cos( v );
|
|
|
+ var cy = tube * Math.sin( v );
|
|
|
+
|
|
|
+ // now calculate the final vertex position.
|
|
|
+ // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve
|
|
|
+
|
|
|
+ vertex.x = P1.x + ( cx * N.x + cy * B.x );
|
|
|
+ vertex.y = P1.y + ( cx * N.y + cy * B.y );
|
|
|
+ vertex.z = P1.z + ( cx * N.z + cy * B.z );
|
|
|
+
|
|
|
+ // vertex
|
|
|
+ vertices.setXYZ( index, vertex.x, vertex.y, vertex.z );
|
|
|
+
|
|
|
+ // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)
|
|
|
+ normal.subVectors( vertex, P1 ).normalize();
|
|
|
+ normals.setXYZ( index, normal.x, normal.y, normal.z );
|
|
|
+
|
|
|
+ // uv
|
|
|
+ uv.x = i / radialSegments;
|
|
|
+ uv.y = j / tubularSegments;
|
|
|
+ uvs.setXY( index, uv.x, uv.y );
|
|
|
+
|
|
|
+ // increase index
|
|
|
+ index ++;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // generate indices
|
|
|
+
|
|
|
+ for ( j = 1; j <= radialSegments; j ++ ) {
|
|
|
+
|
|
|
+ for ( i = 1; i <= tubularSegments; i ++ ) {
|
|
|
+
|
|
|
+ // indices
|
|
|
+ var a = ( tubularSegments + 1 ) * ( j - 1 ) + ( i - 1 );
|
|
|
+ var b = ( tubularSegments + 1 ) * j + ( i - 1 );
|
|
|
+ var c = ( tubularSegments + 1 ) * j + i;
|
|
|
+ var d = ( tubularSegments + 1 ) * ( j - 1 ) + i;
|
|
|
+
|
|
|
+ // face one
|
|
|
+ indices.setX( indexOffset, a ); indexOffset++;
|
|
|
+ indices.setX( indexOffset, b ); indexOffset++;
|
|
|
+ indices.setX( indexOffset, d ); indexOffset++;
|
|
|
+
|
|
|
+ // face two
|
|
|
+ indices.setX( indexOffset, b ); indexOffset++;
|
|
|
+ indices.setX( indexOffset, c ); indexOffset++;
|
|
|
+ indices.setX( indexOffset, d ); indexOffset++;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // build geometry
|
|
|
+
|
|
|
+ this.setIndex( indices );
|
|
|
+ this.addAttribute( 'position', vertices );
|
|
|
+ this.addAttribute( 'normal', normals );
|
|
|
+ this.addAttribute( 'uv', uvs );
|
|
|
+
|
|
|
+ // this function calculates the current position on the torus curve
|
|
|
+
|
|
|
+ function calculatePositionOnCurve( u, p, q, radius, heightScale, position ) {
|
|
|
+
|
|
|
+ var cu = Math.cos( u );
|
|
|
+ var su = Math.sin( u );
|
|
|
+ var quOverP = q / p * u;
|
|
|
+ var cs = Math.cos( quOverP );
|
|
|
+
|
|
|
+ position.x = radius * ( 2 + cs ) * 0.5 * cu;
|
|
|
+ position.y = radius * ( 2 + cs ) * su * 0.5;
|
|
|
+ position.z = heightScale * radius * Math.sin( quOverP ) * 0.5;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+THREE.TorusKnotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
|
|
|
+THREE.TorusKnotBufferGeometry.prototype.constructor = THREE.TorusKnotBufferGeometry;
|