00001 'use strict';
00002 window.ThreeBSP = (function() {
00003
00004 var ThreeBSP,
00005 EPSILON = 1e-5,
00006 COPLANAR = 0,
00007 FRONT = 1,
00008 BACK = 2,
00009 SPANNING = 3;
00010
00011 ThreeBSP = function( geometry ) {
00012
00013 var i, _length_i,
00014 face, vertex, faceVertexUvs, uvs,
00015 polygon,
00016 polygons = [],
00017 tree;
00018
00019 if ( geometry instanceof THREE.Geometry ) {
00020 this.matrix = new THREE.Matrix4;
00021 } else if ( geometry instanceof THREE.Mesh ) {
00022
00023 geometry.updateMatrix();
00024 this.matrix = geometry.matrix.clone();
00025 geometry = geometry.geometry;
00026 } else if ( geometry instanceof ThreeBSP.Node ) {
00027 this.tree = geometry;
00028 this.matrix = new THREE.Matrix4;
00029 return this;
00030 } else {
00031 throw 'ThreeBSP: Given geometry is unsupported';
00032 }
00033
00034 for ( i = 0, _length_i = geometry.faces.length; i < _length_i; i++ ) {
00035 face = geometry.faces[i];
00036 faceVertexUvs = null;
00037 polygon = new ThreeBSP.Polygon;
00038
00039 if ( face instanceof THREE.Face3 ) {
00040 vertex = geometry.vertices[ face.a ];
00041 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
00042 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );
00043 vertex.applyMatrix4(this.matrix);
00044 polygon.vertices.push( vertex );
00045
00046 vertex = geometry.vertices[ face.b ];
00047 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
00048 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs );
00049 vertex.applyMatrix4(this.matrix);
00050 polygon.vertices.push( vertex );
00051
00052 vertex = geometry.vertices[ face.c ];
00053 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
00054 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
00055 vertex.applyMatrix4(this.matrix);
00056 polygon.vertices.push( vertex );
00057 } else if ( typeof THREE.Face4 ) {
00058 vertex = geometry.vertices[ face.a ];
00059 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[0].x, faceVertexUvs[0].y ) : null;
00060 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[0], uvs );
00061 vertex.applyMatrix4(this.matrix);
00062 polygon.vertices.push( vertex );
00063
00064 vertex = geometry.vertices[ face.b ];
00065 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[1].x, faceVertexUvs[1].y ) : null;
00066 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[1], uvs );
00067 vertex.applyMatrix4(this.matrix);
00068 polygon.vertices.push( vertex );
00069
00070 vertex = geometry.vertices[ face.c ];
00071 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[2].x, faceVertexUvs[2].y ) : null;
00072 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[2], uvs );
00073 vertex.applyMatrix4(this.matrix);
00074 polygon.vertices.push( vertex );
00075
00076 vertex = geometry.vertices[ face.d ];
00077 uvs = faceVertexUvs ? new THREE.Vector2( faceVertexUvs[3].x, faceVertexUvs[3].y ) : null;
00078 vertex = new ThreeBSP.Vertex( vertex.x, vertex.y, vertex.z, face.vertexNormals[3], uvs );
00079 vertex.applyMatrix4(this.matrix);
00080 polygon.vertices.push( vertex );
00081 } else {
00082 throw 'Invalid face type at index ' + i;
00083 }
00084
00085 polygon.calculateProperties();
00086 polygons.push( polygon );
00087 };
00088
00089 this.tree = new ThreeBSP.Node( polygons );
00090
00091 };
00092 ThreeBSP.prototype.subtract = function( other_tree ) {
00093 var a = this.tree.clone(),
00094 b = other_tree.tree.clone();
00095
00096 a.invert();
00097 a.clipTo( b );
00098 b.clipTo( a );
00099 b.invert();
00100 b.clipTo( a );
00101 b.invert();
00102 a.build( b.allPolygons() );
00103 a.invert();
00104 a = new ThreeBSP( a );
00105 a.matrix = this.matrix;
00106 return a;
00107 };
00108 ThreeBSP.prototype.union = function( other_tree ) {
00109 var a = this.tree.clone(),
00110 b = other_tree.tree.clone();
00111
00112 a.clipTo( b );
00113 b.clipTo( a );
00114 b.invert();
00115 b.clipTo( a );
00116 b.invert();
00117 a.build( b.allPolygons() );
00118 a = new ThreeBSP( a );
00119 a.matrix = this.matrix;
00120 return a;
00121 };
00122 ThreeBSP.prototype.intersect = function( other_tree ) {
00123 var a = this.tree.clone(),
00124 b = other_tree.tree.clone();
00125
00126 a.invert();
00127 b.clipTo( a );
00128 b.invert();
00129 a.clipTo( b );
00130 b.clipTo( a );
00131 a.build( b.allPolygons() );
00132 a.invert();
00133 a = new ThreeBSP( a );
00134 a.matrix = this.matrix;
00135 return a;
00136 };
00137 ThreeBSP.prototype.toGeometry = function() {
00138 var i, j,
00139 matrix = new THREE.Matrix4().getInverse( this.matrix ),
00140 geometry = new THREE.Geometry(),
00141 polygons = this.tree.allPolygons(),
00142 polygon_count = polygons.length,
00143 polygon, polygon_vertice_count,
00144 vertice_dict = {},
00145 vertex_idx_a, vertex_idx_b, vertex_idx_c,
00146 vertex, face,
00147 verticeUvs;
00148
00149 for ( i = 0; i < polygon_count; i++ ) {
00150 polygon = polygons[i];
00151 polygon_vertice_count = polygon.vertices.length;
00152
00153 for ( j = 2; j < polygon_vertice_count; j++ ) {
00154 verticeUvs = [];
00155
00156 vertex = polygon.vertices[0];
00157 verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
00158 vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
00159 vertex.applyMatrix4(matrix);
00160
00161 if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
00162 vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
00163 } else {
00164 geometry.vertices.push( vertex );
00165 vertex_idx_a = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
00166 }
00167
00168 vertex = polygon.vertices[j-1];
00169 verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
00170 vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
00171 vertex.applyMatrix4(matrix);
00172 if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
00173 vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
00174 } else {
00175 geometry.vertices.push( vertex );
00176 vertex_idx_b = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
00177 }
00178
00179 vertex = polygon.vertices[j];
00180 verticeUvs.push( new THREE.Vector2( vertex.uv.x, vertex.uv.y ) );
00181 vertex = new THREE.Vector3( vertex.x, vertex.y, vertex.z );
00182 vertex.applyMatrix4(matrix);
00183 if ( typeof vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] !== 'undefined' ) {
00184 vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ];
00185 } else {
00186 geometry.vertices.push( vertex );
00187 vertex_idx_c = vertice_dict[ vertex.x + ',' + vertex.y + ',' + vertex.z ] = geometry.vertices.length - 1;
00188 }
00189
00190 face = new THREE.Face3(
00191 vertex_idx_a,
00192 vertex_idx_b,
00193 vertex_idx_c,
00194 new THREE.Vector3( polygon.normal.x, polygon.normal.y, polygon.normal.z )
00195 );
00196
00197 geometry.faces.push( face );
00198 geometry.faceVertexUvs[0].push( verticeUvs );
00199 }
00200
00201 }
00202 return geometry;
00203 };
00204 ThreeBSP.prototype.toMesh = function( material ) {
00205 var geometry = this.toGeometry(),
00206 mesh = new THREE.Mesh( geometry, material );
00207
00208 mesh.position.setFromMatrixPosition( this.matrix );
00209 mesh.rotation.setFromRotationMatrix( this.matrix );
00210
00211 return mesh;
00212 };
00213
00214
00215 ThreeBSP.Polygon = function( vertices, normal, w ) {
00216 if ( !( vertices instanceof Array ) ) {
00217 vertices = [];
00218 }
00219
00220 this.vertices = vertices;
00221 if ( vertices.length > 0 ) {
00222 this.calculateProperties();
00223 } else {
00224 this.normal = this.w = undefined;
00225 }
00226 };
00227 ThreeBSP.Polygon.prototype.calculateProperties = function() {
00228 var a = this.vertices[0],
00229 b = this.vertices[1],
00230 c = this.vertices[2];
00231
00232 this.normal = b.clone().subtract( a ).cross(
00233 c.clone().subtract( a )
00234 ).normalize();
00235
00236 this.w = this.normal.clone().dot( a );
00237
00238 return this;
00239 };
00240 ThreeBSP.Polygon.prototype.clone = function() {
00241 var i, vertice_count,
00242 polygon = new ThreeBSP.Polygon;
00243
00244 for ( i = 0, vertice_count = this.vertices.length; i < vertice_count; i++ ) {
00245 polygon.vertices.push( this.vertices[i].clone() );
00246 };
00247 polygon.calculateProperties();
00248
00249 return polygon;
00250 };
00251
00252 ThreeBSP.Polygon.prototype.flip = function() {
00253 var i, vertices = [];
00254
00255 this.normal.multiplyScalar( -1 );
00256 this.w *= -1;
00257
00258 for ( i = this.vertices.length - 1; i >= 0; i-- ) {
00259 vertices.push( this.vertices[i] );
00260 };
00261 this.vertices = vertices;
00262
00263 return this;
00264 };
00265 ThreeBSP.Polygon.prototype.classifyVertex = function( vertex ) {
00266 var side_value = this.normal.dot( vertex ) - this.w;
00267
00268 if ( side_value < -EPSILON ) {
00269 return BACK;
00270 } else if ( side_value > EPSILON ) {
00271 return FRONT;
00272 } else {
00273 return COPLANAR;
00274 }
00275 };
00276 ThreeBSP.Polygon.prototype.classifySide = function( polygon ) {
00277 var i, vertex, classification,
00278 num_positive = 0,
00279 num_negative = 0,
00280 vertice_count = polygon.vertices.length;
00281
00282 for ( i = 0; i < vertice_count; i++ ) {
00283 vertex = polygon.vertices[i];
00284 classification = this.classifyVertex( vertex );
00285 if ( classification === FRONT ) {
00286 num_positive++;
00287 } else if ( classification === BACK ) {
00288 num_negative++;
00289 }
00290 }
00291
00292 if ( num_positive > 0 && num_negative === 0 ) {
00293 return FRONT;
00294 } else if ( num_positive === 0 && num_negative > 0 ) {
00295 return BACK;
00296 } else if ( num_positive === 0 && num_negative === 0 ) {
00297 return COPLANAR;
00298 } else {
00299 return SPANNING;
00300 }
00301 };
00302 ThreeBSP.Polygon.prototype.splitPolygon = function( polygon, coplanar_front, coplanar_back, front, back ) {
00303 var classification = this.classifySide( polygon );
00304
00305 if ( classification === COPLANAR ) {
00306
00307 ( this.normal.dot( polygon.normal ) > 0 ? coplanar_front : coplanar_back ).push( polygon );
00308
00309 } else if ( classification === FRONT ) {
00310
00311 front.push( polygon );
00312
00313 } else if ( classification === BACK ) {
00314
00315 back.push( polygon );
00316
00317 } else {
00318
00319 var vertice_count,
00320 i, j, ti, tj, vi, vj,
00321 t, v,
00322 f = [],
00323 b = [];
00324
00325 for ( i = 0, vertice_count = polygon.vertices.length; i < vertice_count; i++ ) {
00326
00327 j = (i + 1) % vertice_count;
00328 vi = polygon.vertices[i];
00329 vj = polygon.vertices[j];
00330 ti = this.classifyVertex( vi );
00331 tj = this.classifyVertex( vj );
00332
00333 if ( ti != BACK ) f.push( vi );
00334 if ( ti != FRONT ) b.push( vi );
00335 if ( (ti | tj) === SPANNING ) {
00336 t = ( this.w - this.normal.dot( vi ) ) / this.normal.dot( vj.clone().subtract( vi ) );
00337 v = vi.interpolate( vj, t );
00338 f.push( v );
00339 b.push( v );
00340 }
00341 }
00342
00343
00344 if ( f.length >= 3 ) front.push( new ThreeBSP.Polygon( f ).calculateProperties() );
00345 if ( b.length >= 3 ) back.push( new ThreeBSP.Polygon( b ).calculateProperties() );
00346 }
00347 };
00348
00349 ThreeBSP.Vertex = function( x, y, z, normal, uv ) {
00350 this.x = x;
00351 this.y = y;
00352 this.z = z;
00353 this.normal = normal || new THREE.Vector3;
00354 this.uv = uv || new THREE.Vector2;
00355 };
00356 ThreeBSP.Vertex.prototype.clone = function() {
00357 return new ThreeBSP.Vertex( this.x, this.y, this.z, this.normal.clone(), this.uv.clone() );
00358 };
00359 ThreeBSP.Vertex.prototype.add = function( vertex ) {
00360 this.x += vertex.x;
00361 this.y += vertex.y;
00362 this.z += vertex.z;
00363 return this;
00364 };
00365 ThreeBSP.Vertex.prototype.subtract = function( vertex ) {
00366 this.x -= vertex.x;
00367 this.y -= vertex.y;
00368 this.z -= vertex.z;
00369 return this;
00370 };
00371 ThreeBSP.Vertex.prototype.multiplyScalar = function( scalar ) {
00372 this.x *= scalar;
00373 this.y *= scalar;
00374 this.z *= scalar;
00375 return this;
00376 };
00377 ThreeBSP.Vertex.prototype.cross = function( vertex ) {
00378 var x = this.x,
00379 y = this.y,
00380 z = this.z;
00381
00382 this.x = y * vertex.z - z * vertex.y;
00383 this.y = z * vertex.x - x * vertex.z;
00384 this.z = x * vertex.y - y * vertex.x;
00385
00386 return this;
00387 };
00388 ThreeBSP.Vertex.prototype.normalize = function() {
00389 var length = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
00390
00391 this.x /= length;
00392 this.y /= length;
00393 this.z /= length;
00394
00395 return this;
00396 };
00397 ThreeBSP.Vertex.prototype.dot = function( vertex ) {
00398 return this.x * vertex.x + this.y * vertex.y + this.z * vertex.z;
00399 };
00400 ThreeBSP.Vertex.prototype.lerp = function( a, t ) {
00401 this.add(
00402 a.clone().subtract( this ).multiplyScalar( t )
00403 );
00404
00405 this.normal.add(
00406 a.normal.clone().sub( this.normal ).multiplyScalar( t )
00407 );
00408
00409 this.uv.add(
00410 a.uv.clone().sub( this.uv ).multiplyScalar( t )
00411 );
00412
00413 return this;
00414 };
00415 ThreeBSP.Vertex.prototype.interpolate = function( other, t ) {
00416 return this.clone().lerp( other, t );
00417 };
00418 ThreeBSP.Vertex.prototype.applyMatrix4 = function ( m ) {
00419
00420
00421
00422 var x = this.x, y = this.y, z = this.z;
00423
00424 var e = m.elements;
00425
00426 this.x = e[0] * x + e[4] * y + e[8] * z + e[12];
00427 this.y = e[1] * x + e[5] * y + e[9] * z + e[13];
00428 this.z = e[2] * x + e[6] * y + e[10] * z + e[14];
00429
00430 return this;
00431
00432 }
00433
00434
00435 ThreeBSP.Node = function( polygons ) {
00436 var i, polygon_count,
00437 front = [],
00438 back = [];
00439
00440 this.polygons = [];
00441 this.front = this.back = undefined;
00442
00443 if ( !(polygons instanceof Array) || polygons.length === 0 ) return;
00444
00445 this.divider = polygons[0].clone();
00446
00447
00448
00449 for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
00450 this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
00451 }
00452
00453
00454
00455 if ( front.length > 0 ) {
00456 this.front = new ThreeBSP.Node( front );
00457 }
00458
00459 if ( back.length > 0 ) {
00460 this.back = new ThreeBSP.Node( back );
00461 }
00462 };
00463 ThreeBSP.Node.isConvex = function( polygons ) {
00464 var i, j;
00465 for ( i = 0; i < polygons.length; i++ ) {
00466 for ( j = 0; j < polygons.length; j++ ) {
00467 if ( i !== j && polygons[i].classifySide( polygons[j] ) !== BACK ) {
00468 return false;
00469 }
00470 }
00471 }
00472 return true;
00473 };
00474 ThreeBSP.Node.prototype.build = function( polygons ) {
00475 var i, polygon_count,
00476 front = [],
00477 back = [];
00478
00479 if ( !this.divider ) {
00480 this.divider = polygons[0].clone();
00481 }
00482
00483 for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
00484 this.divider.splitPolygon( polygons[i], this.polygons, this.polygons, front, back );
00485 }
00486
00487 if ( front.length > 0 ) {
00488 if ( !this.front ) this.front = new ThreeBSP.Node();
00489 this.front.build( front );
00490 }
00491
00492 if ( back.length > 0 ) {
00493 if ( !this.back ) this.back = new ThreeBSP.Node();
00494 this.back.build( back );
00495 }
00496 };
00497 ThreeBSP.Node.prototype.allPolygons = function() {
00498 var polygons = this.polygons.slice();
00499 if ( this.front ) polygons = polygons.concat( this.front.allPolygons() );
00500 if ( this.back ) polygons = polygons.concat( this.back.allPolygons() );
00501 return polygons;
00502 };
00503 ThreeBSP.Node.prototype.clone = function() {
00504 var node = new ThreeBSP.Node();
00505
00506 node.divider = this.divider.clone();
00507 node.polygons = this.polygons.map( function( polygon ) { return polygon.clone(); } );
00508 node.front = this.front && this.front.clone();
00509 node.back = this.back && this.back.clone();
00510
00511 return node;
00512 };
00513 ThreeBSP.Node.prototype.invert = function() {
00514 var i, polygon_count, temp;
00515
00516 for ( i = 0, polygon_count = this.polygons.length; i < polygon_count; i++ ) {
00517 this.polygons[i].flip();
00518 }
00519
00520 this.divider.flip();
00521 if ( this.front ) this.front.invert();
00522 if ( this.back ) this.back.invert();
00523
00524 temp = this.front;
00525 this.front = this.back;
00526 this.back = temp;
00527
00528 return this;
00529 };
00530 ThreeBSP.Node.prototype.clipPolygons = function( polygons ) {
00531 var i, polygon_count,
00532 front, back;
00533
00534 if ( !this.divider ) return polygons.slice();
00535
00536 front = [], back = [];
00537
00538 for ( i = 0, polygon_count = polygons.length; i < polygon_count; i++ ) {
00539 this.divider.splitPolygon( polygons[i], front, back, front, back );
00540 }
00541
00542 if ( this.front ) front = this.front.clipPolygons( front );
00543 if ( this.back ) back = this.back.clipPolygons( back );
00544 else back = [];
00545
00546 return front.concat( back );
00547 };
00548
00549 ThreeBSP.Node.prototype.clipTo = function( node ) {
00550 this.polygons = node.clipPolygons( this.polygons );
00551 if ( this.front ) this.front.clipTo( node );
00552 if ( this.back ) this.back.clipTo( node );
00553 };
00554
00555
00556 return ThreeBSP;
00557 })();