/* * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog * * Subdivision Geometry Modifier * using Catmull-Clark Subdivision Surfaces * for creating smooth geometry meshes * * Note: a modifier modifies vertices and faces of geometry, * so use geometry.clone() if original geometry needs to be retained * * Readings: * http://en.wikipedia.org/wiki/Catmull%E2%80%93Clark_subdivision_surface * http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/ * http://xrt.wikidot.com/blog:31 * "Subdivision Surfaces in Character Animation" * * (on boundary edges) * http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface * https://graphics.stanford.edu/wikis/cs148-09-summer/Assignment3Description * * Supports: * Closed and Open geometries. * * TODO: * crease vertex and "semi-sharp" features * selective subdivision */ THREE.SubdivisionModifier = function ( subdivisions ) { this.subdivisions = (subdivisions === undefined ) ? 1 : subdivisions; // Settings this.useOldVertexColors = false; this.supportUVs = true; this.debug = false; }; // Applies the "modify" pattern THREE.SubdivisionModifier.prototype.modify = function ( geometry ) { var repeats = this.subdivisions; while ( repeats-- > 0 ) { this.smooth( geometry ); } }; /// REFACTORING THIS OUT THREE.GeometryUtils.orderedKey = function ( a, b ) { return Math.min( a, b ) + "_" + Math.max( a, b ); }; // Returns a hashmap - of { edge_key: face_index } THREE.GeometryUtils.computeEdgeFaces = function ( geometry ) { var i, il, v1, v2, j, k, face, faceIndices, faceIndex, edge, hash, edgeFaceMap = {}; var orderedKey = THREE.GeometryUtils.orderedKey; function mapEdgeHash( hash, i ) { if ( edgeFaceMap[ hash ] === undefined ) { edgeFaceMap[ hash ] = []; } edgeFaceMap[ hash ].push( i ); } // construct vertex -> face map for( i = 0, il = geometry.faces.length; i < il; i ++ ) { face = geometry.faces[ i ]; if ( face instanceof THREE.Face3 ) { hash = orderedKey( face.a, face.b ); mapEdgeHash( hash, i ); hash = orderedKey( face.b, face.c ); mapEdgeHash( hash, i ); hash = orderedKey( face.c, face.a ); mapEdgeHash( hash, i ); } else if ( face instanceof THREE.Face4 ) { hash = orderedKey( face.a, face.b ); mapEdgeHash( hash, i ); hash = orderedKey( face.b, face.c ); mapEdgeHash( hash, i ); hash = orderedKey( face.c, face.d ); mapEdgeHash( hash, i ); hash = orderedKey( face.d, face.a ); mapEdgeHash( hash, i ); } } // extract faces // var edges = []; // // var numOfEdges = 0; // for (i in edgeFaceMap) { // numOfEdges++; // // edge = edgeFaceMap[i]; // edges.push(edge); // // } //debug('edgeFaceMap', edgeFaceMap, 'geometry.edges',geometry.edges, 'numOfEdges', numOfEdges); return edgeFaceMap; } ///////////////////////////// // Performs an iteration of Catmull-Clark Subdivision THREE.SubdivisionModifier.prototype.smooth = function ( oldGeometry ) { //debug( 'running smooth' ); // New set of vertices, faces and uvs var newVertices = [], newFaces = [], newUVs = []; function v( x, y, z ) { newVertices.push( new THREE.Vector3( x, y, z ) ); } var scope = this; var orderedKey = THREE.GeometryUtils.orderedKey; var computeEdgeFaces = THREE.GeometryUtils.computeEdgeFaces; function assert() { if (scope.debug && console && console.assert) console.assert.apply(console, arguments); } function debug() { if (scope.debug) console.log.apply(console, arguments); } function warn() { if (console) console.log.apply(console, arguments); } function f4( a, b, c, d, oldFace, orders, facei ) { // TODO move vertex selection over here! var newFace = new THREE.Face4( a, b, c, d, null, oldFace.color, oldFace.materialIndex ); if (scope.useOldVertexColors) { newFace.vertexColors = []; var color, tmpColor, order; for (var i=0;i<4;i++) { order = orders[i]; color = new THREE.Color(), color.setRGB(0,0,0); for (var j=0, jl=0; j=originalVerticesLength && vertexNo < (originalVerticesLength + originalFaces.length)) { debug('face pt'); } else { debug('edge pt'); } warn('warning, UV not found for', key); return null; } return theUV; // Original faces -> Vertex Nos. // new Facepoint -> Vertex Nos. // edge Points } function addUV(vertexNo, oldFaceNo, value) { var key = vertexNo+':'+oldFaceNo; if (!(key in uvForVertices)) { uvForVertices[key] = value; } else { warn('dup vertexNo', vertexNo, 'oldFaceNo', oldFaceNo, 'value', value, 'key', key, uvForVertices[key]); } } // Step 1 // For each face, add a face point // Set each face point to be the centroid of all original points for the respective face. // debug(oldGeometry); var i, il, j, jl, face; // For Uvs var uvs = oldGeometry.faceVertexUvs[0]; var abcd = 'abcd', vertice; debug('originalFaces, uvs, originalVerticesLength', originalFaces.length, uvs.length, originalVerticesLength); if (scope.supportUVs) for (i=0, il = uvs.length; i Faces Index eg { edge_key: [face_index, face_index2 ]} var edge, faceIndexA, faceIndexB, avg; // debug('edgeFaceMap', edgeFaceMap); var edgeCount = 0; var edgeVertex, edgeVertexA, edgeVertexB; //// var vertexEdgeMap = {}; // Gives edges connecting from each vertex var vertexFaceMap = {}; // Gives faces connecting from each vertex function addVertexEdgeMap(vertex, edge) { if (vertexEdgeMap[vertex]===undefined) { vertexEdgeMap[vertex] = []; } vertexEdgeMap[vertex].push(edge); } function addVertexFaceMap(vertex, face, edge) { if (vertexFaceMap[vertex]===undefined) { vertexFaceMap[vertex] = {}; } vertexFaceMap[vertex][face] = edge; // vertexFaceMap[vertex][face] = null; } // Prepares vertexEdgeMap and vertexFaceMap for (i in edgeFaceMap) { // This is for every edge edge = edgeFaceMap[i]; edgeVertex = i.split('_'); edgeVertexA = edgeVertex[0]; edgeVertexB = edgeVertex[1]; // Maps an edgeVertex to connecting edges addVertexEdgeMap(edgeVertexA, [edgeVertexA, edgeVertexB] ); addVertexEdgeMap(edgeVertexB, [edgeVertexA, edgeVertexB] ); for (j=0,jl=edge.length;j 0, 'an edge without faces?!'); if (edge.length==1) { avg.add( originalPoints[ edgeVertexA ] ); avg.add( originalPoints[ edgeVertexB ] ); avg.multiplyScalar( 0.5 ); sharpVertices[newPoints.length] = true; } else { avg.add( facePoints[ faceIndexA ] ); avg.add( facePoints[ faceIndexB ] ); avg.add( originalPoints[ edgeVertexA ] ); avg.add( originalPoints[ edgeVertexB ] ); avg.multiplyScalar( 0.25 ); } edgePoints[i] = originalVerticesLength + originalFaces.length + edgeCount; newPoints.push( avg ); edgeCount ++; if (!scope.supportUVs) { continue; } // Prepare subdivided uv avgUv = new THREE.Vector2(); avgUv.x = getUV(edgeVertexA, faceIndexA).x + getUV(edgeVertexB, faceIndexA).x; avgUv.y = getUV(edgeVertexA, faceIndexA).y + getUV(edgeVertexB, faceIndexA).y; avgUv.x /= 2; avgUv.y /= 2; addUV(edgePoints[i], faceIndexA, avgUv); if (edge.length>=2) { assert(edge.length == 2, 'did we plan for more than 2 edges?'); avgUv = new THREE.Vector2(); avgUv.x = getUV(edgeVertexA, faceIndexB).x + getUV(edgeVertexB, faceIndexB).x; avgUv.y = getUV(edgeVertexA, faceIndexB).y + getUV(edgeVertexB, faceIndexB).y; avgUv.x /= 2; avgUv.y /= 2; addUV(edgePoints[i], faceIndexB, avgUv); } } debug('-- Step 2 done'); // Step 3 // For each face point, add an edge for every edge of the face, // connecting the face point to each edge point for the face. var facePt, currentVerticeIndex; var hashAB, hashBC, hashCD, hashDA, hashCA; var abc123 = ['123', '12', '2', '23']; var bca123 = ['123', '23', '3', '31']; var cab123 = ['123', '31', '1', '12']; var abc1234 = ['1234', '12', '2', '23']; var bcd1234 = ['1234', '23', '3', '34']; var cda1234 = ['1234', '34', '4', '41']; var dab1234 = ['1234', '41', '1', '12']; for (i=0, il = facePoints.length; i2) { // TODO } */ F.divideScalar(f); var boundary_edges = 0; if (boundary_case) { var bb_edge; for (j=0; j