/** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ THREE.GeometryUtils = { merge: function ( geometry1, object2 /* mesh | geometry */ ) { var matrix, matrixRotation, vertexOffset = geometry1.vertices.length, uvPosition = geometry1.faceVertexUvs[ 0 ].length, geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2, vertices1 = geometry1.vertices, vertices2 = geometry2.vertices, faces1 = geometry1.faces, faces2 = geometry2.faces, uvs1 = geometry1.faceVertexUvs[ 0 ], uvs2 = geometry2.faceVertexUvs[ 0 ]; var geo1MaterialsMap = {}; for ( var i = 0; i < geometry1.materials.length; i ++ ) { var id = geometry1.materials[ i ].id; geo1MaterialsMap[ id ] = i; } if ( object2 instanceof THREE.Mesh ) { object2.matrixAutoUpdate && object2.updateMatrix(); matrix = object2.matrix; matrixRotation = new THREE.Matrix4(); matrixRotation.extractRotation( matrix, object2.scale ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = new THREE.Vertex( vertex.position.clone() ); if ( matrix ) matrix.multiplyVector3( vertexCopy.position ); vertices1.push( vertexCopy ); } // faces for ( i = 0, il = faces2.length; i < il; i ++ ) { var face = faces2[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; if ( face instanceof THREE.Face3 ) { faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); } else if ( face instanceof THREE.Face4 ) { faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset ); } faceCopy.normal.copy( face.normal ); if ( matrixRotation ) matrixRotation.multiplyVector3( faceCopy.normal ); for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( matrixRotation ) matrixRotation.multiplyVector3( normal ); faceCopy.vertexNormals.push( normal ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } if ( face.materialIndex !== undefined ) { var material2 = geometry2.materials[ face.materialIndex ]; var materialId2 = material2.id; var materialIndex = geo1MaterialsMap[ materialId2 ]; if ( materialIndex === undefined ) { materialIndex = geometry1.materials.length; geometry1.materials.push( material2 ); } faceCopy.materialIndex = materialIndex; } faceCopy.centroid.copy( face.centroid ); if ( matrix ) matrix.multiplyVector3( faceCopy.centroid ); faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); } uvs1.push( uvCopy ); } }, clone: function ( geometry ) { var cloneGeo = new THREE.Geometry(); var i, il; var vertices = geometry.vertices, faces = geometry.faces, uvs = geometry.faceVertexUvs[ 0 ]; // materials if ( geometry.materials ) { cloneGeo.materials = geometry.materials.slice(); } // vertices for ( i = 0, il = vertices.length; i < il; i ++ ) { var vertex = vertices[ i ]; var vertexCopy = new THREE.Vertex( vertex.position.clone() ); cloneGeo.vertices.push( vertexCopy ); } // faces for ( i = 0, il = faces.length; i < il; i ++ ) { var face = faces[ i ], faceCopy, normal, color, faceVertexNormals = face.vertexNormals, faceVertexColors = face.vertexColors; if ( face instanceof THREE.Face3 ) { faceCopy = new THREE.Face3( face.a, face.b, face.c ); } else if ( face instanceof THREE.Face4 ) { faceCopy = new THREE.Face4( face.a, face.b, face.c, face.d ); } faceCopy.normal.copy( face.normal ); for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ]; faceCopy.vertexNormals.push( normal.clone() ); } faceCopy.color.copy( face.color ); for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { color = faceVertexColors[ j ]; faceCopy.vertexColors.push( color.clone() ); } faceCopy.materialIndex = face.materialIndex; faceCopy.centroid.copy( face.centroid ); cloneGeo.faces.push( faceCopy ); } // uvs for ( i = 0, il = uvs.length; i < il; i ++ ) { var uv = uvs[ i ], uvCopy = []; for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); } cloneGeo.faceVertexUvs[ 0 ].push( uvCopy ); } return cloneGeo; }, // Get random point in triangle (via barycentric coordinates) // (uniform distribution) // http://www.cgafaq.info/wiki/Random_Point_In_Triangle randomPointInTriangle: function( vectorA, vectorB, vectorC ) { var a, b, c, point = new THREE.Vector3(), tmp = THREE.GeometryUtils.__v1; a = THREE.GeometryUtils.random(); b = THREE.GeometryUtils.random(); if ( ( a + b ) > 1 ) { a = 1 - a; b = 1 - b; } c = 1 - a - b; point.copy( vectorA ); point.multiplyScalar( a ); tmp.copy( vectorB ); tmp.multiplyScalar( b ); point.addSelf( tmp ); tmp.copy( vectorC ); tmp.multiplyScalar( c ); point.addSelf( tmp ); return point; }, // Get random point in face (triangle / quad) // (uniform distribution) randomPointInFace: function( face, geometry, useCachedAreas ) { var vA, vB, vC, vD; if ( face instanceof THREE.Face3 ) { vA = geometry.vertices[ face.a ].position; vB = geometry.vertices[ face.b ].position; vC = geometry.vertices[ face.c ].position; return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); } else if ( face instanceof THREE.Face4 ) { vA = geometry.vertices[ face.a ].position; vB = geometry.vertices[ face.b ].position; vC = geometry.vertices[ face.c ].position; vD = geometry.vertices[ face.d ].position; var area1, area2; if ( useCachedAreas ) { if ( face._area1 && face._area2 ) { area1 = face._area1; area2 = face._area2; } else { area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); face._area1 = area1; face._area2 = area2; } } else { area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ), area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); } var r = THREE.GeometryUtils.random() * ( area1 + area2 ); if ( r < area1 ) { return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vD ); } else { return THREE.GeometryUtils.randomPointInTriangle( vB, vC, vD ); } } }, // Get uniformly distributed random points in mesh // - create array with cumulative sums of face areas // - pick random number from 0 to total area // - find corresponding place in area array by binary search // - get random point in face randomPointsInGeometry: function( geometry, n ) { var face, i, faces = geometry.faces, vertices = geometry.vertices, il = faces.length, totalArea = 0, cumulativeAreas = [], vA, vB, vC, vD; // precompute face areas for ( i = 0; i < il; i ++ ) { face = faces[ i ]; if ( face instanceof THREE.Face3 ) { vA = vertices[ face.a ].position; vB = vertices[ face.b ].position; vC = vertices[ face.c ].position; face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); } else if ( face instanceof THREE.Face4 ) { vA = vertices[ face.a ].position; vB = vertices[ face.b ].position; vC = vertices[ face.c ].position; vD = vertices[ face.d ].position; face._area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); face._area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); face._area = face._area1 + face._area2; } totalArea += face._area; cumulativeAreas[ i ] = totalArea; } // binary search cumulative areas array function binarySearchIndices( value ) { function binarySearch( start, end ) { // return closest larger index // if exact number is not found if ( end < start ) return start; var mid = start + Math.floor( ( end - start ) / 2 ); if ( cumulativeAreas[ mid ] > value ) { return binarySearch( start, mid - 1 ); } else if ( cumulativeAreas[ mid ] < value ) { return binarySearch( mid + 1, end ); } else { return mid; } } var result = binarySearch( 0, cumulativeAreas.length - 1 ) return result; } // pick random face weighted by face area var r, index, result = []; var stats = {}; for ( i = 0; i < n; i ++ ) { r = THREE.GeometryUtils.random() * totalArea; index = binarySearchIndices( r ); result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); if ( ! stats[ index ] ) { stats[ index ] = 1; } else { stats[ index ] += 1; } } return result; }, // Get triangle area (by Heron's formula) // http://en.wikipedia.org/wiki/Heron%27s_formula triangleArea: function( vectorA, vectorB, vectorC ) { var s, a, b, c, tmp = THREE.GeometryUtils.__v1; tmp.sub( vectorA, vectorB ); a = tmp.length(); tmp.sub( vectorA, vectorC ); b = tmp.length(); tmp.sub( vectorB, vectorC ); c = tmp.length(); s = 0.5 * ( a + b + c ); return Math.sqrt( s * ( s - a ) * ( s - b ) * ( s - c ) ); }, center: function( geometry ) { geometry.computeBoundingBox(); var matrix = new THREE.Matrix4(); var dx = -0.5 * ( geometry.boundingBox.x[ 1 ] + geometry.boundingBox.x[ 0 ] ); var dy = -0.5 * ( geometry.boundingBox.y[ 1 ] + geometry.boundingBox.y[ 0 ] ); var dz = -0.5 * ( geometry.boundingBox.z[ 1 ] + geometry.boundingBox.z[ 0 ] ); matrix.setTranslation( dx, dy, dz ); geometry.applyMatrix( matrix ); geometry.computeBoundingBox(); } }; THREE.GeometryUtils.random = THREE.Math.random16; THREE.GeometryUtils.__v1 = new THREE.Vector3();