提交 a44bf80c 编写于 作者: W WestLangley

TubeGeometry: Added support for closed paths using parallel transport frames

上级 70ee0cf5
......@@ -396,7 +396,8 @@
// 3d shape
var mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, [
new THREE.MeshLambertMaterial({
color: color
color: color,
opacity: 0.2
}),
new THREE.MeshBasicMaterial({
color: 0x000000,
......@@ -433,7 +434,7 @@
// new THREE.Vector3(0, -40, 40),
// ]);
// extrudePath = new THREE.TrefoilKnot();
extrudePath = new THREE.TrefoilKnot();
// extrudePath = new THREE.TorusKnot(20);
// extrudePath = new THREE.CinquefoilKnot(20);
// extrudePath = new THREE.TrefoilPolynomialKnot(14);
......@@ -441,13 +442,13 @@
// extrudePath = new THREE.DecoratedTorusKnot4a();
// extrudePath = new THREE.DecoratedTorusKnot4b();
// extrudePath = new THREE.DecoratedTorusKnot5a();
extrudePath = new THREE.DecoratedTorusKnot5c();
// extrudePath = new THREE.DecoratedTorusKnot5c();
//var tube = new THREE.TubeGeometry(5, 100, 20, extrudePath, true);
var tube = new THREE.TubeGeometry(2, 400, 2, extrudePath, true);
var closed = true;
var debug = true;
var tube = new THREE.TubeGeometry(extrudePath, 100, 2, 3, closed, debug);
addGeometry(tube, 0xff00ff, 0, -80, 0, 0, 0, 0, 3);
addGeometry(tube, 0xff00ff, 0, -80, 0, 0, 0, 0, 6);
//
......
......@@ -2,138 +2,182 @@
* @author WestLangley / https://github.com/WestLangley
* @author zz85 / https://github.com/zz85
* @author miningold / https://github.com/miningold
*
* Modified from the TorusKnotGeometry by @oosmoxiecode
* 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( radius, segments, segmentsRadius, path, debug ) {
THREE.TubeGeometry = function( path, segments, radius, segmentsRadius, closed, debug ) {
THREE.Geometry.call( this );
var scope = this;
this.radius = radius || 40;
this.path = path;
this.segments = segments || 64;
this.radius = radius || 1;
this.segmentsRadius = segmentsRadius || 8;
this.grid = new Array( this.segments );
this.path = path;
this.closed = closed || false;
if ( debug ) this.debug = new THREE.Object3D();
var tang = new THREE.Vector3();
var binormal = new THREE.Vector3();
var normal = new THREE.Vector3();
var pos = new THREE.Vector3();
this.grid = [];
var epsilon = 0.001;
var scope = this,
tangent = new THREE.Vector3(),
normal = new THREE.Vector3(),
binormal = new THREE.Vector3(),
var u, v;
vec = new THREE.Vector3(),
mat = new THREE.Matrix4(),
var p1, p2;
var cx, cy;
tangents = [],
normals = [],
binormals = [],
var oldB;
numpoints = this.segments + 1,
theta,
epsilon = 0.0001,
smallest,
x, y, z,
tx, ty, tz,
u, v,
p1, p2,
cx, cy,
pos, pos2,
i, j,
ip, jp,
a, b, c, d,
uva, uvb, uvc, uvd;
for ( var i = 0; i < this.segments; ++ i ) {
this.grid[ i ] = new Array( this.segmentsRadius );
function vert( x, y, z ) {
u = i / ( this.segments - 1 );
return scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ) - 1;
pos = this.path.getPointAt( u );
tang = this.path.getTangentAt( u );
}
// compute the tangent vectors for each segment on the path
for ( i = 0; i < numpoints; i++ ) {
if ( oldB === undefined ) {
u = i / ( numpoints - 1 );
// Method 1, random arbitrary vector
// oldB = new THREE.Vector3(Math.random(), Math.random(), Math.random()).normalize();
tangents[ i ] = this.path.getTangentAt( u );
tangents[ i ].normalize();
// Method 2, use a fixed start binormal. Has dangers of 0 vectors too.
// oldB = new THREE.Vector3( 0, -1, 0 );
}
// 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 );
}
// Method 3 - This uses the Frenet-Serret formula for deriving binormal
if ( ty <= smallest ) {
smallest = ty;
normal.set( 0, 1, 0 );
}
if ( tz <= smallest ) {
normal.set( 0, 0, 1 );
}
var t1, t2;
vec.cross( tangents[ 0 ], normal ).normalize();
t1 = u - epsilon;
if (t1 < 0) t1 = 0;
t1 = this.path.getTangentAt( t1 );
normals[ 0 ].cross( tangents[ 0 ], vec );
binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] );
t2 = u + epsilon;
if (t2 > 1) t2 = 1;
t2 = this.path.getTangentAt( t2 );
normal.sub( t2, t1 ).normalize();
// compute the slowly-varying normal and binormal vectors for each segment on the path
binormal.cross( tang, normal );
oldB = binormal;
for ( i = 1; i < numpoints; i++ ) {
if ( oldB.length() == 0 ) {
normals[ i ] = normals[ i-1 ].clone();
// When binormal is a zero vector, we could brute force another vector ?
// oldB.set( 1, 0, 0 );
// if (normal.cross(oldB, tang).normalize().length()==0) {
// oldB.set( 0, 1, 0 );
// if (normal.cross(oldB, tang).normalize().length()==0) {
// oldB.set( 0, 0, 1 );
// }
// }
binormals[ i ] = binormals[ i-1 ].clone();
// Method 4 - Sets binormal direction in the smallest tangent xyz component
vec.cross( tangents[ i-1 ], tangents[ i ] );
var smallest = Number.MAX_VALUE;
var x, y, z;
if ( vec.length() > epsilon ) {
var tx = Math.abs( tang.x );
var ty = Math.abs( tang.y );
var tz = Math.abs( tang.z );
vec.normalize();
if ( tx <= smallest ) {
theta = Math.acos( tangents[ i-1 ].dot( tangents[ i ] ) );
smallest = tx;
oldB.set( 1, 0, 0 );
mat.setRotationAxis( vec, theta ).multiplyVector3( normals[ i ] );
}
}
binormals[ i ].cross( tangents[ i ], normals[ i ] );
if ( ty <= smallest ) {
}
smallest = ty;
oldB.set( 0, 1, 0 );
}
// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
if ( tz <= smallest ) {
if ( this.closed ) {
oldB.set( 0, 0, 1 );
theta = Math.acos( normals[ 0 ].dot( normals[ numpoints-1 ] ) );
theta /= ( numpoints - 1 );
}
if ( tangents[ 0 ].dot( vec.cross( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) {
}
theta = -theta;
}
normal.cross( oldB, tang ).normalize();
binormal.cross( tang, normal ).normalize();
oldB = binormal;
for ( i = 1; i < numpoints; i++ ) {
// twist a little...
mat.setRotationAxis( tangents[ i ], theta * i ).multiplyVector3( normals[ i ] );
binormals[ i ].cross( tangents[ i ], normals[ i ] );
}
}
// consruct the grid
for ( i = 0; i < numpoints; i++ ) {
this.grid[ i ] = [];
u = i / ( numpoints - 1 );
pos = this.path.getPointAt( u );
tangent = tangents[ i ];
normal = normals[ i ];
binormal = binormals[ i ];
if ( this.debug ) {
this.debug.add( new THREE.ArrowHelper( normal, pos, radius * 2, 0xff0000 ) );
// this.debug.add(new THREE.ArrowHelper(binormal, pos, radius * 2, 0x00ff00));
// this.debug.add(new THREE.ArrowHelper(tang, pos, radius * 2, 0x0000ff));
this.debug.add(new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff));
this.debug.add(new THREE.ArrowHelper(normal, pos, radius, 0xff0000));
this.debug.add(new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00));
}
for ( var j = 0; j < this.segmentsRadius; ++ j ) {
for ( j = 0; j < this.segmentsRadius; j++ ) {
v = j / this.segmentsRadius * 2 * Math.PI;
cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
cy = this.radius * Math.sin( v );
var pos2 = pos.clone();
pos2 = new THREE.Vector3().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;
......@@ -141,44 +185,39 @@ THREE.TubeGeometry = function( radius, segments, segmentsRadius, path, debug ) {
this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z );
}
}
for ( var i = 0; i < this.segments -1; ++ i ) {
for ( var j = 0; j < this.segmentsRadius; ++ j ) {
// construct the mesh
for ( i = 0; i < this.segments; i++ ) {
var ip = ( i + 1 ) % this.segments;
var jp = ( j + 1 ) % this.segmentsRadius;
for ( j = 0; j < this.segmentsRadius; j++ ) {
var a = this.grid[ i ][ j ];
var b = this.grid[ ip ][ j ];
var c = this.grid[ ip ][ jp ];
var d = this.grid[ i ][ jp ];
ip = ( closed ) ? (i + 1) % this.segments : i + 1;
jp = (j + 1) % this.segmentsRadius;
var uva = new THREE.UV( i / this.segments, j / this.segmentsRadius );
var uvb = new THREE.UV( ( i + 1 ) / this.segments, j / this.segmentsRadius );
var uvc = new THREE.UV( ( i + 1 ) / this.segments, ( j + 1 ) / this.segmentsRadius );
var uvd = new THREE.UV( i / this.segments, ( j + 1 ) / this.segmentsRadius );
a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! ***
b = this.grid[ ip ][ j ];
c = this.grid[ ip ][ jp ];
d = this.grid[ i ][ jp ];
uva = new THREE.UV( i / this.segments, j / this.segmentsRadius );
uvb = new THREE.UV( ( i + 1 ) / this.segments, j / this.segmentsRadius );
uvc = new THREE.UV( ( i + 1 ) / this.segments, ( j + 1 ) / this.segmentsRadius );
uvd = new THREE.UV( i / this.segments, ( j + 1 ) / this.segmentsRadius );
this.faces.push( new THREE.Face4( a, b, c, d ) );
this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc, uvd ] );
}
}
this.computeCentroids();
this.computeFaceNormals();
this.computeVertexNormals();
function vert( x, y, z ) {
return scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ) - 1;
}
};
THREE.TubeGeometry.prototype = new THREE.Geometry();
THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
\ No newline at end of file
THREE.TubeGeometry.prototype.constructor = THREE.TubeGeometry;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册