/** * @author mrdoob / http://mrdoob.com/ * @author alteredq / http://alteredqualia.com/ */ THREE.GeometryUtils = { // Merge two geometries or geometry and geometry from object (using object's transform) merge: function ( geometry1, object2 /* mesh | geometry */, materialIndexOffset ) { var matrix, normalMatrix, 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 ]; if ( materialIndexOffset === undefined ) materialIndexOffset = 0; if ( object2 instanceof THREE.Mesh ) { object2.matrixAutoUpdate && object2.updateMatrix(); matrix = object2.matrix; normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); } // vertices for ( var i = 0, il = vertices2.length; i < il; i ++ ) { var vertex = vertices2[ i ]; var vertexCopy = vertex.clone(); if ( matrix ) vertexCopy.applyMatrix4( matrix ); 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; faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); faceCopy.normal.copy( face.normal ); if ( normalMatrix ) { faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); } for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { normal = faceVertexNormals[ j ].clone(); if ( normalMatrix ) { normal.applyMatrix3( normalMatrix ).normalize(); } 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() ); } faceCopy.materialIndex = face.materialIndex + materialIndexOffset; faceCopy.centroid.copy( face.centroid ); if ( matrix ) { faceCopy.centroid.applyMatrix4( matrix ); } faces1.push( faceCopy ); } // uvs for ( i = 0, il = uvs2.length; i < il; i ++ ) { var uv = uvs2[ i ], uvCopy = []; if ( uv === undefined ) { continue; } for ( var j = 0, jl = uv.length; j < jl; j ++ ) { uvCopy.push( new THREE.Vector2( uv[ j ].x, uv[ j ].y ) ); } uvs1.push( uvCopy ); } }, // Get random point in triangle (via barycentric coordinates) // (uniform distribution) // http://www.cgafaq.info/wiki/Random_Point_In_Triangle randomPointInTriangle: function () { var vector = new THREE.Vector3(); return function ( vectorA, vectorB, vectorC ) { var point = new THREE.Vector3(); var a = THREE.Math.random16(); var b = THREE.Math.random16(); if ( ( a + b ) > 1 ) { a = 1 - a; b = 1 - b; } var c = 1 - a - b; point.copy( vectorA ); point.multiplyScalar( a ); vector.copy( vectorB ); vector.multiplyScalar( b ); point.add( vector ); vector.copy( vectorC ); vector.multiplyScalar( c ); point.add( vector ); return point; }; }(), // Get random point in face (triangle / quad) // (uniform distribution) randomPointInFace: function ( face, geometry, useCachedAreas ) { var vA, vB, vC, vD; vA = geometry.vertices[ face.a ]; vB = geometry.vertices[ face.b ]; vC = geometry.vertices[ face.c ]; return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); }, // 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 ]; vA = vertices[ face.a ]; vB = vertices[ face.b ]; vC = vertices[ face.c ]; face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); 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.Math.random16() * 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 (half of parallelogram) // http://mathworld.wolfram.com/TriangleArea.html triangleArea: function () { var vector1 = new THREE.Vector3(); var vector2 = new THREE.Vector3(); return function ( vectorA, vectorB, vectorC ) { vector1.subVectors( vectorB, vectorA ); vector2.subVectors( vectorC, vectorA ); vector1.cross( vector2 ); return 0.5 * vector1.length(); }; }(), // Center geometry so that 0,0,0 is in center of bounding box center: function ( geometry ) { geometry.computeBoundingBox(); var bb = geometry.boundingBox; var offset = new THREE.Vector3(); offset.addVectors( bb.min, bb.max ); offset.multiplyScalar( -0.5 ); geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); geometry.computeBoundingBox(); return offset; }, triangulateQuads: function ( geometry ) { var i, il, j, jl; var faces = []; var faceVertexUvs = []; for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { faceVertexUvs[ i ] = []; } for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { var face = geometry.faces[ i ]; faces.push( face ); for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); } } geometry.faces = faces; geometry.faceVertexUvs = faceVertexUvs; geometry.computeCentroids(); geometry.computeFaceNormals(); geometry.computeVertexNormals(); if ( geometry.hasTangents ) geometry.computeTangents(); } };