|
| 1 | +/** |
| 2 | + * @author Mugen87 / https://github.com/Mugen87 |
| 3 | + * |
| 4 | + * see: http://www.blackpawn.com/texts/pqtorus/ |
| 5 | + */ |
| 6 | +THREE.TorusKnotBufferGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale, arc ) { |
| 7 | + |
| 8 | + THREE.BufferGeometry.call( this ); |
| 9 | + |
| 10 | + this.type = 'TorusKnotBufferGeometry'; |
| 11 | + |
| 12 | + this.parameters = { |
| 13 | + radius: radius, |
| 14 | + tube: tube, |
| 15 | + radialSegments: radialSegments, |
| 16 | + tubularSegments: tubularSegments, |
| 17 | + p: p, |
| 18 | + q: q, |
| 19 | + heightScale: heightScale, |
| 20 | + arc: arc |
| 21 | + }; |
| 22 | + |
| 23 | + radius = radius || 100; |
| 24 | + tube = tube || 40; |
| 25 | + radialSegments = Math.floor( radialSegments ) || 64; |
| 26 | + tubularSegments = Math.floor( tubularSegments ) || 6; |
| 27 | + p = p || 2; |
| 28 | + q = q || 3; |
| 29 | + heightScale = heightScale || 1; |
| 30 | + arc = arc || Math.PI * 2; |
| 31 | + |
| 32 | + // used to calculate buffer length |
| 33 | + var vertexCount = ( ( radialSegments + 1 ) * ( tubularSegments + 1 ) ); |
| 34 | + var indexCount = radialSegments * tubularSegments * 2 * 3; |
| 35 | + |
| 36 | + // buffers |
| 37 | + var indices = new THREE.BufferAttribute( new ( indexCount > 65535 ? Uint32Array : Uint16Array )( indexCount ) , 1 ); |
| 38 | + var vertices = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); |
| 39 | + var normals = new THREE.BufferAttribute( new Float32Array( vertexCount * 3 ), 3 ); |
| 40 | + var uvs = new THREE.BufferAttribute( new Float32Array( vertexCount * 2 ), 2 ); |
| 41 | + |
| 42 | + // helper variables |
| 43 | + var i, j, index = 0, indexOffset = 0; |
| 44 | + |
| 45 | + var vertex = new THREE.Vector3(); |
| 46 | + var normal = new THREE.Vector3(); |
| 47 | + var uv = new THREE.Vector2(); |
| 48 | + |
| 49 | + var P1 = new THREE.Vector3(); |
| 50 | + var P2 = new THREE.Vector3(); |
| 51 | + |
| 52 | + var B = new THREE.Vector3(); |
| 53 | + var T = new THREE.Vector3(); |
| 54 | + var N = new THREE.Vector3(); |
| 55 | + |
| 56 | + // generate vertices, normals and uvs |
| 57 | + |
| 58 | + for ( i = 0; i <= radialSegments; ++ i ) { |
| 59 | + |
| 60 | + // the radian "u" is used to calculate the position on the torus curve of the current radial segement |
| 61 | + |
| 62 | + var u = i / radialSegments * p * arc; |
| 63 | + |
| 64 | + // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead. |
| 65 | + // these points are used to create a special "coordinate space", which is necessary to calculate the correct vertex positions |
| 66 | + |
| 67 | + calculatePositionOnCurve( u, p, q, radius, heightScale, P1 ); |
| 68 | + calculatePositionOnCurve( u + 0.01, p, q, radius, heightScale, P2 ); |
| 69 | + |
| 70 | + // calculate orthonormal basis |
| 71 | + |
| 72 | + T.subVectors( P2, P1 ); |
| 73 | + N.addVectors( P2, P1 ); |
| 74 | + B.crossVectors( T, N ); |
| 75 | + N.crossVectors( B, T ); |
| 76 | + |
| 77 | + // normalize B, N. T can be ignored, we don't use it |
| 78 | + |
| 79 | + B.normalize(); |
| 80 | + N.normalize(); |
| 81 | + |
| 82 | + for ( j = 0; j <= tubularSegments; ++ j ) { |
| 83 | + |
| 84 | + // now calculate the vertices. they are nothing more than an extrusion of the torus curve. |
| 85 | + // because we extrude a shape in the xy-plane, there is no need to calculate a z-value. |
| 86 | + |
| 87 | + var v = j / tubularSegments * Math.PI * 2; |
| 88 | + var cx = - tube * Math.cos( v ); |
| 89 | + var cy = tube * Math.sin( v ); |
| 90 | + |
| 91 | + // now calculate the final vertex position. |
| 92 | + // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve |
| 93 | + |
| 94 | + vertex.x = P1.x + ( cx * N.x + cy * B.x ); |
| 95 | + vertex.y = P1.y + ( cx * N.y + cy * B.y ); |
| 96 | + vertex.z = P1.z + ( cx * N.z + cy * B.z ); |
| 97 | + |
| 98 | + // vertex |
| 99 | + vertices.setXYZ( index, vertex.x, vertex.y, vertex.z ); |
| 100 | + |
| 101 | + // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal) |
| 102 | + normal.subVectors( vertex, P1 ).normalize(); |
| 103 | + normals.setXYZ( index, normal.x, normal.y, normal.z ); |
| 104 | + |
| 105 | + // uv |
| 106 | + uv.x = i / radialSegments; |
| 107 | + uv.y = j / tubularSegments; |
| 108 | + uvs.setXY( index, uv.x, uv.y ); |
| 109 | + |
| 110 | + // increase index |
| 111 | + index ++; |
| 112 | + |
| 113 | + } |
| 114 | + |
| 115 | + } |
| 116 | + |
| 117 | + // generate indices |
| 118 | + |
| 119 | + for ( j = 1; j <= radialSegments; j ++ ) { |
| 120 | + |
| 121 | + for ( i = 1; i <= tubularSegments; i ++ ) { |
| 122 | + |
| 123 | + // indices |
| 124 | + var a = ( tubularSegments + 1 ) * ( j - 1 ) + ( i - 1 ); |
| 125 | + var b = ( tubularSegments + 1 ) * j + ( i - 1 ); |
| 126 | + var c = ( tubularSegments + 1 ) * j + i; |
| 127 | + var d = ( tubularSegments + 1 ) * ( j - 1 ) + i; |
| 128 | + |
| 129 | + // face one |
| 130 | + indices.setX( indexOffset, a ); indexOffset++; |
| 131 | + indices.setX( indexOffset, b ); indexOffset++; |
| 132 | + indices.setX( indexOffset, d ); indexOffset++; |
| 133 | + |
| 134 | + // face two |
| 135 | + indices.setX( indexOffset, b ); indexOffset++; |
| 136 | + indices.setX( indexOffset, c ); indexOffset++; |
| 137 | + indices.setX( indexOffset, d ); indexOffset++; |
| 138 | + |
| 139 | + } |
| 140 | + |
| 141 | + } |
| 142 | + |
| 143 | + // build geometry |
| 144 | + |
| 145 | + this.setIndex( indices ); |
| 146 | + this.addAttribute( 'position', vertices ); |
| 147 | + this.addAttribute( 'normal', normals ); |
| 148 | + this.addAttribute( 'uv', uvs ); |
| 149 | + |
| 150 | + // this function calculates the current position on the torus curve |
| 151 | + |
| 152 | + function calculatePositionOnCurve( u, p, q, radius, heightScale, position ) { |
| 153 | + |
| 154 | + var cu = Math.cos( u ); |
| 155 | + var su = Math.sin( u ); |
| 156 | + var quOverP = q / p * u; |
| 157 | + var cs = Math.cos( quOverP ); |
| 158 | + |
| 159 | + position.x = radius * ( 2 + cs ) * 0.5 * cu; |
| 160 | + position.y = radius * ( 2 + cs ) * su * 0.5; |
| 161 | + position.z = heightScale * radius * Math.sin( quOverP ) * 0.5; |
| 162 | + |
| 163 | + } |
| 164 | + |
| 165 | +}; |
| 166 | + |
| 167 | +THREE.TorusKnotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); |
| 168 | +THREE.TorusKnotBufferGeometry.prototype.constructor = THREE.TorusKnotBufferGeometry; |
0 commit comments