294 lines
6.8 KiB
JavaScript
294 lines
6.8 KiB
JavaScript
/**
|
|
* @author WestLangley / https://github.com/WestLangley
|
|
* @author zz85 / https://github.com/zz85
|
|
* @author miningold / https://github.com/miningold
|
|
* @author jonobr1 / https://github.com/jonobr1
|
|
*
|
|
* Modified from the TorusKnotGeometry by @oosmoxiecode
|
|
*
|
|
* Creates a tube which extrudes along a 3d spline
|
|
*
|
|
* Uses parallel transport frames as described in
|
|
* http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
*/
|
|
|
|
THREE.TubeGeometry = function ( path, segments, radius, radialSegments, closed, taper ) {
|
|
|
|
THREE.Geometry.call( this );
|
|
|
|
this.type = 'TubeGeometry';
|
|
|
|
this.parameters = {
|
|
path: path,
|
|
segments: segments,
|
|
radius: radius,
|
|
radialSegments: radialSegments,
|
|
closed: closed
|
|
};
|
|
|
|
segments = segments || 64;
|
|
radius = radius || 1;
|
|
radialSegments = radialSegments || 8;
|
|
closed = closed || false;
|
|
taper = taper || THREE.TubeGeometry.NoTaper;
|
|
|
|
var grid = [];
|
|
|
|
var scope = this,
|
|
|
|
tangent,
|
|
normal,
|
|
binormal,
|
|
|
|
numpoints = segments + 1,
|
|
|
|
u, v, r,
|
|
|
|
cx, cy,
|
|
pos, pos2 = new THREE.Vector3(),
|
|
i, j,
|
|
ip, jp,
|
|
a, b, c, d,
|
|
uva, uvb, uvc, uvd;
|
|
|
|
var frames = new THREE.TubeGeometry.FrenetFrames( path, segments, closed ),
|
|
tangents = frames.tangents,
|
|
normals = frames.normals,
|
|
binormals = frames.binormals;
|
|
|
|
// proxy internals
|
|
this.tangents = tangents;
|
|
this.normals = normals;
|
|
this.binormals = binormals;
|
|
|
|
function vert( x, y, z ) {
|
|
|
|
return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1;
|
|
|
|
}
|
|
|
|
// consruct the grid
|
|
|
|
for ( i = 0; i < numpoints; i ++ ) {
|
|
|
|
grid[ i ] = [];
|
|
|
|
u = i / ( numpoints - 1 );
|
|
|
|
pos = path.getPointAt( u );
|
|
|
|
tangent = tangents[ i ];
|
|
normal = normals[ i ];
|
|
binormal = binormals[ i ];
|
|
|
|
r = radius * taper( u );
|
|
|
|
for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
|
v = j / radialSegments * 2 * Math.PI;
|
|
|
|
cx = - r * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
|
cy = r * Math.sin( v );
|
|
|
|
pos2.copy( pos );
|
|
pos2.x += cx * normal.x + cy * binormal.x;
|
|
pos2.y += cx * normal.y + cy * binormal.y;
|
|
pos2.z += cx * normal.z + cy * binormal.z;
|
|
|
|
grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// construct the mesh
|
|
|
|
for ( i = 0; i < segments; i ++ ) {
|
|
|
|
for ( j = 0; j < radialSegments; j ++ ) {
|
|
|
|
ip = ( closed ) ? (i + 1) % segments : i + 1;
|
|
jp = (j + 1) % radialSegments;
|
|
|
|
a = grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
|
|
b = grid[ ip ][ j ];
|
|
c = grid[ ip ][ jp ];
|
|
d = grid[ i ][ jp ];
|
|
|
|
uva = new THREE.Vector2( i / segments, j / radialSegments );
|
|
uvb = new THREE.Vector2( ( i + 1 ) / segments, j / radialSegments );
|
|
uvc = new THREE.Vector2( ( i + 1 ) / segments, ( j + 1 ) / radialSegments );
|
|
uvd = new THREE.Vector2( i / segments, ( j + 1 ) / radialSegments );
|
|
|
|
this.faces.push( new THREE.Face3( a, b, d ) );
|
|
this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvd ] );
|
|
|
|
this.faces.push( new THREE.Face3( b, c, d ) );
|
|
this.faceVertexUvs[ 0 ].push( [ uvb.clone(), uvc, uvd.clone() ] );
|
|
|
|
}
|
|
}
|
|
|
|
this.computeFaceNormals();
|
|
this.computeVertexNormals();
|
|
|
|
};
|
|
|
|
THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype );
|
|
THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
|
|
|
|
THREE.TubeGeometry.NoTaper = function ( u ) {
|
|
|
|
return 1;
|
|
|
|
};
|
|
|
|
THREE.TubeGeometry.SinusoidalTaper = function ( u ) {
|
|
|
|
return Math.sin( Math.PI * u );
|
|
|
|
};
|
|
|
|
// For computing of Frenet frames, exposing the tangents, normals and binormals the spline
|
|
THREE.TubeGeometry.FrenetFrames = function ( path, segments, closed ) {
|
|
|
|
var normal = new THREE.Vector3(),
|
|
|
|
tangents = [],
|
|
normals = [],
|
|
binormals = [],
|
|
|
|
vec = new THREE.Vector3(),
|
|
mat = new THREE.Matrix4(),
|
|
|
|
numpoints = segments + 1,
|
|
theta,
|
|
epsilon = 0.0001,
|
|
smallest,
|
|
|
|
tx, ty, tz,
|
|
i, u;
|
|
|
|
|
|
// expose internals
|
|
this.tangents = tangents;
|
|
this.normals = normals;
|
|
this.binormals = binormals;
|
|
|
|
// compute the tangent vectors for each segment on the path
|
|
|
|
for ( i = 0; i < numpoints; i ++ ) {
|
|
|
|
u = i / ( numpoints - 1 );
|
|
|
|
tangents[ i ] = path.getTangentAt( u );
|
|
tangents[ i ].normalize();
|
|
|
|
}
|
|
|
|
initialNormal3();
|
|
|
|
/*
|
|
function initialNormal1(lastBinormal) {
|
|
// fixed start binormal. Has dangers of 0 vectors
|
|
normals[ 0 ] = new THREE.Vector3();
|
|
binormals[ 0 ] = new THREE.Vector3();
|
|
if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 );
|
|
normals[ 0 ].crossVectors( lastBinormal, tangents[ 0 ] ).normalize();
|
|
binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
}
|
|
|
|
function initialNormal2() {
|
|
|
|
// This uses the Frenet-Serret formula for deriving binormal
|
|
var t2 = path.getTangentAt( epsilon );
|
|
|
|
normals[ 0 ] = new THREE.Vector3().subVectors( t2, tangents[ 0 ] ).normalize();
|
|
binormals[ 0 ] = new THREE.Vector3().crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
|
|
normals[ 0 ].crossVectors( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent
|
|
binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] ).normalize();
|
|
|
|
}
|
|
*/
|
|
|
|
function initialNormal3() {
|
|
// select an initial normal vector perpenicular to the first tangent vector,
|
|
// and in the direction of the smallest tangent xyz component
|
|
|
|
normals[ 0 ] = new THREE.Vector3();
|
|
binormals[ 0 ] = new THREE.Vector3();
|
|
smallest = Number.MAX_VALUE;
|
|
tx = Math.abs( tangents[ 0 ].x );
|
|
ty = Math.abs( tangents[ 0 ].y );
|
|
tz = Math.abs( tangents[ 0 ].z );
|
|
|
|
if ( tx <= smallest ) {
|
|
smallest = tx;
|
|
normal.set( 1, 0, 0 );
|
|
}
|
|
|
|
if ( ty <= smallest ) {
|
|
smallest = ty;
|
|
normal.set( 0, 1, 0 );
|
|
}
|
|
|
|
if ( tz <= smallest ) {
|
|
normal.set( 0, 0, 1 );
|
|
}
|
|
|
|
vec.crossVectors( tangents[ 0 ], normal ).normalize();
|
|
|
|
normals[ 0 ].crossVectors( tangents[ 0 ], vec );
|
|
binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );
|
|
}
|
|
|
|
|
|
// compute the slowly-varying normal and binormal vectors for each segment on the path
|
|
|
|
for ( i = 1; i < numpoints; i ++ ) {
|
|
|
|
normals[ i ] = normals[ i - 1 ].clone();
|
|
|
|
binormals[ i ] = binormals[ i - 1 ].clone();
|
|
|
|
vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );
|
|
|
|
if ( vec.length() > epsilon ) {
|
|
|
|
vec.normalize();
|
|
|
|
theta = Math.acos( THREE.Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors
|
|
|
|
normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );
|
|
|
|
}
|
|
|
|
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
|
}
|
|
|
|
|
|
// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
|
|
|
|
if ( closed ) {
|
|
|
|
theta = Math.acos( THREE.Math.clamp( normals[ 0 ].dot( normals[ numpoints - 1 ] ), - 1, 1 ) );
|
|
theta /= ( numpoints - 1 );
|
|
|
|
if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ numpoints - 1 ] ) ) > 0 ) {
|
|
|
|
theta = - theta;
|
|
|
|
}
|
|
|
|
for ( i = 1; i < numpoints; i ++ ) {
|
|
|
|
// twist a little...
|
|
normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );
|
|
binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );
|
|
|
|
}
|
|
|
|
}
|
|
};
|