Files
three.js/src/core/Geometry.js
T
2011-09-30 01:52:20 +08:00

595 lines
12 KiB
JavaScript

/**
* @author mr.doob / http://mrdoob.com/
* @author kile / http://kile.stravaganza.org/
* @author alteredq / http://alteredqualia.com/
* @author mikael emtinger / http://gomo.se/
*/
THREE.Geometry = function () {
this.id = THREE.GeometryCount ++;
this.vertices = [];
this.colors = []; // one-to-one vertex colors, used in ParticleSystem, Line and Ribbon
this.faces = [];
//this.edges = [];
this.faceUvs = [[]];
this.faceVertexUvs = [[]];
this.morphTargets = [];
this.morphColors = [];
this.skinWeights = [];
this.skinIndices = [];
this.boundingBox = null;
this.boundingSphere = null;
this.hasTangents = false;
this.dynamic = false; // unless set to true the *Arrays will be deleted once sent to a buffer.
};
THREE.Geometry.prototype = {
constructor : THREE.Geometry,
applyMatrix: function ( matrix ) {
var matrixRotation = new THREE.Matrix4();
matrixRotation.extractRotation( matrix, new THREE.Vector3( 1, 1, 1 ) );
for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {
var vertex = this.vertices[ i ];
matrix.multiplyVector3( vertex.position );
}
for ( var i = 0, il = this.faces.length; i < il; i ++ ) {
var face = this.faces[ i ];
matrixRotation.multiplyVector3( face.normal );
for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {
matrixRotation.multiplyVector3( face.vertexNormals[ j ] );
}
matrix.multiplyVector3( face.centroid );
}
},
computeCentroids: function () {
var f, fl, face;
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
face.centroid.set( 0, 0, 0 );
if ( face instanceof THREE.Face3 ) {
face.centroid.addSelf( this.vertices[ face.a ].position );
face.centroid.addSelf( this.vertices[ face.b ].position );
face.centroid.addSelf( this.vertices[ face.c ].position );
face.centroid.divideScalar( 3 );
} else if ( face instanceof THREE.Face4 ) {
face.centroid.addSelf( this.vertices[ face.a ].position );
face.centroid.addSelf( this.vertices[ face.b ].position );
face.centroid.addSelf( this.vertices[ face.c ].position );
face.centroid.addSelf( this.vertices[ face.d ].position );
face.centroid.divideScalar( 4 );
}
}
},
computeFaceNormals: function ( useVertexNormals ) {
var n, nl, v, vl, vertex, f, fl, face, vA, vB, vC,
cb = new THREE.Vector3(), ab = new THREE.Vector3();
/*
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertex = this.vertices[ v ];
vertex.normal.set( 0, 0, 0 );
}
*/
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( useVertexNormals && face.vertexNormals.length ) {
cb.set( 0, 0, 0 );
for ( n = 0, nl = face.vertexNormals.length; n < nl; n++ ) {
cb.addSelf( face.vertexNormals[n] );
}
cb.divideScalar( 3 );
if ( ! cb.isZero() ) {
cb.normalize();
}
face.normal.copy( cb );
} else {
vA = this.vertices[ face.a ];
vB = this.vertices[ face.b ];
vC = this.vertices[ face.c ];
cb.sub( vC.position, vB.position );
ab.sub( vA.position, vB.position );
cb.crossSelf( ab );
if ( !cb.isZero() ) {
cb.normalize();
}
face.normal.copy( cb );
}
}
},
computeVertexNormals: function () {
var v, vl, f, fl, face, vertices;
// create internal buffers for reuse when calling this method repeatedly
// (otherwise memory allocation / deallocation every frame is big resource hog)
if ( this.__tmpVertices == undefined ) {
this.__tmpVertices = new Array( this.vertices.length );
vertices = this.__tmpVertices;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ] = new THREE.Vector3();
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
} else if ( face instanceof THREE.Face4 ) {
face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ];
}
}
} else {
vertices = this.__tmpVertices;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ].set( 0, 0, 0 );
}
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
vertices[ face.a ].addSelf( face.normal );
vertices[ face.b ].addSelf( face.normal );
vertices[ face.c ].addSelf( face.normal );
} else if ( face instanceof THREE.Face4 ) {
vertices[ face.a ].addSelf( face.normal );
vertices[ face.b ].addSelf( face.normal );
vertices[ face.c ].addSelf( face.normal );
vertices[ face.d ].addSelf( face.normal );
}
}
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
vertices[ v ].normalize();
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
if ( face instanceof THREE.Face3 ) {
face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
} else if ( face instanceof THREE.Face4 ) {
face.vertexNormals[ 0 ].copy( vertices[ face.a ] );
face.vertexNormals[ 1 ].copy( vertices[ face.b ] );
face.vertexNormals[ 2 ].copy( vertices[ face.c ] );
face.vertexNormals[ 3 ].copy( vertices[ face.d ] );
}
}
},
computeTangents: function () {
// based on http://www.terathon.com/code/tangent.html
// tangents go to vertices
var f, fl, v, vl, i, il, vertexIndex,
face, uv, vA, vB, vC, uvA, uvB, uvC,
x1, x2, y1, y2, z1, z2,
s1, s2, t1, t2, r, t, test,
tan1 = [], tan2 = [],
sdir = new THREE.Vector3(), tdir = new THREE.Vector3(),
tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(),
n = new THREE.Vector3(), w;
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {
tan1[ v ] = new THREE.Vector3();
tan2[ v ] = new THREE.Vector3();
}
function handleTriangle( context, a, b, c, ua, ub, uc ) {
vA = context.vertices[ a ].position;
vB = context.vertices[ b ].position;
vC = context.vertices[ c ].position;
uvA = uv[ ua ];
uvB = uv[ ub ];
uvC = uv[ uc ];
x1 = vB.x - vA.x;
x2 = vC.x - vA.x;
y1 = vB.y - vA.y;
y2 = vC.y - vA.y;
z1 = vB.z - vA.z;
z2 = vC.z - vA.z;
s1 = uvB.u - uvA.u;
s2 = uvC.u - uvA.u;
t1 = uvB.v - uvA.v;
t2 = uvC.v - uvA.v;
r = 1.0 / ( s1 * t2 - s2 * t1 );
sdir.set( ( t2 * x1 - t1 * x2 ) * r,
( t2 * y1 - t1 * y2 ) * r,
( t2 * z1 - t1 * z2 ) * r );
tdir.set( ( s1 * x2 - s2 * x1 ) * r,
( s1 * y2 - s2 * y1 ) * r,
( s1 * z2 - s2 * z1 ) * r );
tan1[ a ].addSelf( sdir );
tan1[ b ].addSelf( sdir );
tan1[ c ].addSelf( sdir );
tan2[ a ].addSelf( tdir );
tan2[ b ].addSelf( tdir );
tan2[ c ].addSelf( tdir );
}
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents
if ( face instanceof THREE.Face3 ) {
handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
} else if ( face instanceof THREE.Face4 ) {
handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 );
handleTriangle( this, face.a, face.b, face.d, 0, 1, 3 );
}
}
var faceIndex = [ 'a', 'b', 'c', 'd' ];
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {
face = this.faces[ f ];
for ( i = 0; i < face.vertexNormals.length; i++ ) {
n.copy( face.vertexNormals[ i ] );
vertexIndex = face[ faceIndex[ i ] ];
t = tan1[ vertexIndex ];
// Gram-Schmidt orthogonalize
tmp.copy( t );
tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize();
// Calculate handedness
tmp2.cross( face.vertexNormals[ i ], t );
test = tmp2.dot( tan2[ vertexIndex ] );
w = (test < 0.0) ? -1.0 : 1.0;
face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w );
}
}
this.hasTangents = true;
},
computeBoundingBox: function () {
var vertex;
if ( this.vertices.length > 0 ) {
this.boundingBox = { 'x': [ this.vertices[ 0 ].position.x, this.vertices[ 0 ].position.x ],
'y': [ this.vertices[ 0 ].position.y, this.vertices[ 0 ].position.y ],
'z': [ this.vertices[ 0 ].position.z, this.vertices[ 0 ].position.z ] };
for ( var v = 1, vl = this.vertices.length; v < vl; v ++ ) {
vertex = this.vertices[ v ];
if ( vertex.position.x < this.boundingBox.x[ 0 ] ) {
this.boundingBox.x[ 0 ] = vertex.position.x;
} else if ( vertex.position.x > this.boundingBox.x[ 1 ] ) {
this.boundingBox.x[ 1 ] = vertex.position.x;
}
if ( vertex.position.y < this.boundingBox.y[ 0 ] ) {
this.boundingBox.y[ 0 ] = vertex.position.y;
} else if ( vertex.position.y > this.boundingBox.y[ 1 ] ) {
this.boundingBox.y[ 1 ] = vertex.position.y;
}
if ( vertex.position.z < this.boundingBox.z[ 0 ] ) {
this.boundingBox.z[ 0 ] = vertex.position.z;
} else if ( vertex.position.z > this.boundingBox.z[ 1 ] ) {
this.boundingBox.z[ 1 ] = vertex.position.z;
}
}
}
},
computeBoundingSphere: function () {
// var radius = this.boundingSphere === null ? 0 : this.boundingSphere.radius;
var radius = 0;
for ( var v = 0, vl = this.vertices.length; v < vl; v ++ ) {
radius = Math.max( radius, this.vertices[ v ].position.length() );
}
this.boundingSphere = { radius: radius };
},
computeEdgeFaces: function () {
function edge_hash( a, b ) {
return Math.min( a, b ) + "_" + Math.max( a, b );
};
function addToMap( map, hash, i ) {
if ( map[ hash ] === undefined ) {
map[ hash ] = { "set": {}, "array": [] };
}
map[ hash ].set[ i ] = 1;
map[ hash ].array.push( i );
};
var i, il, v1, v2, j, k,
face, faceIndices, faceIndex,
edge,
hash,
vfMap = {};
// construct vertex -> face map
for( i = 0, il = this.faces.length; i < il; i ++ ) {
face = this.faces[ i ];
if ( face instanceof THREE.Face3 ) {
hash = edge_hash( face.a, face.b );
addToMap( vfMap, hash, i );
hash = edge_hash( face.b, face.c );
addToMap( vfMap, hash, i );
hash = edge_hash( face.c, face.a );
addToMap( vfMap, hash, i );
} else if ( face instanceof THREE.Face4 ) {
// in WebGLRenderer quad is tesselated
// to triangles: a,b,d / b,c,d
// shared edge is: b,d
// add edge B-D only if you wish to slice a face4
// hash = edge_hash( face.b, face.d );
// addToMap( vfMap, hash, i );
hash = edge_hash( face.a, face.b );
addToMap( vfMap, hash, i );
hash = edge_hash( face.b, face.c );
addToMap( vfMap, hash, i );
hash = edge_hash( face.c, face.d );
addToMap( vfMap, hash, i );
hash = edge_hash( face.d, face.a );
addToMap( vfMap, hash, i );
}
}
// extract faces
this.vfMap = vfMap;
this.edges = [];
var numOfEdges = 0;
for (i in vfMap) {
numOfEdges++;
edge = vfMap[i];
this.edges.push(edge.array);
}
//console.log('vfMap', vfMap, 'this.edges',this.edges, 'numOfEdges', numOfEdges);
return vfMap;
},
checkDupVertices: function(toPatch) {
var uniqueVertices = {}, patch = {};
var v, key;
var precision = 1000;
var i,il, face;
for (i=0,il=this.vertices.length;i<il;i++) {
v = this.vertices[i].position;
key = [Math.round(v.x * precision), Math.round(v.y* precision), Math.round(v.z* precision)].join('_');
if (uniqueVertices[key]===undefined) {
uniqueVertices[key] = i;
} else {
console.log('Duplicate vertex found. ', i, ' could be using ', uniqueVertices[key]);
patch[i] = uniqueVertices[key];
}
};
if (!toPatch) return;
// Start to patch.
console.log('Start' , patch, toPatch);
//TODO: Clear delete vertices?
var runPatch = function(i) {
if (patch[i] !== undefined) {
//console.log('fixing',i, 'to', patch[i]);
return patch[i];
}
return i;
};
for( i = 0, il = this.faces.length; i < il; i ++ ) {
face = this.faces[ i ];
if ( face instanceof THREE.Face3 ) {
face.a = runPatch(face.a);
face.b = runPatch(face.b);
face.c = runPatch(face.c);
} if ( face instanceof THREE.Face4 ) {
face.a = runPatch(face.a);
face.b = runPatch(face.b);
face.c = runPatch(face.c);
face.d = runPatch(face.d);
}
}
//console.log(this);
}
};
THREE.GeometryCount = 0;