diff --git a/src/extras/modifiers/SubdivisionModifier.js b/src/extras/modifiers/SubdivisionModifier.js index 34530c36633d7ddb320e247c655b0b076644aa4c..ecac003d6c42eb55fc2a74a9dc376b940e589971 100644 --- a/src/extras/modifiers/SubdivisionModifier.js +++ b/src/extras/modifiers/SubdivisionModifier.js @@ -1,614 +1,671 @@ -/* - * @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 THREE.GeometryUtils.clone() if orignal geoemtry 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" - * - * 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 ); - } - -}; - -// 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; - - 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 face map - - for( i = 0, il = geometry.faces.length; i < il; i ++ ) { - - face = geometry.faces[ i ]; - - if ( face instanceof THREE.Face3 ) { - - hash = edge_hash( face.a, face.b ); - mapEdgeHash( hash, i ); - - hash = edge_hash( face.b, face.c ); - mapEdgeHash( hash, i ); - - hash = edge_hash( face.c, face.a ); - mapEdgeHash( hash, i ); - - } else if ( face instanceof THREE.Face4 ) { - - hash = edge_hash( face.a, face.b ); - mapEdgeHash( hash, i ); - - hash = edge_hash( face.b, face.c ); - mapEdgeHash( hash, i ); - - hash = edge_hash( face.c, face.d ); - mapEdgeHash( hash, i ); - - hash = edge_hash( 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; - - } - - var originalPoints = oldGeometry.vertices; - var originalFaces = oldGeometry.faces; - - var newPoints = originalPoints.concat(); // Vertices - - var facePoints = [], edgePoints = {}; - - var sharpEdges = {}, sharpVertices = [], sharpFaces = []; - - var uvForVertices = {}; // Stored in {vertex}:{old face} format - - var originalVerticesLength = originalPoints.length; - - function getUV(vertexNo, oldFaceNo) { - var j,jl; - - var key = vertexNo+':'+oldFaceNo; - var theUV = uvForVertices[key]; - - if (!theUV) { - if (vertexNo>=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 - 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;j2) { - // TODO - } - */ - - F.divideScalar(f); - - - - for (j=0; j 0 ) { + this.smooth( geometry ); + } + +}; + +/// REFACTORING THIS OUT + +function orderedKey( a, b ) { + + return Math.min( a, b ) + "_" + Math.max( a, b ); + +} + +var G = { + + + // Returns a hashmap - of { edge_key: face_index } + computeEdgeFaces: function ( geometry ) { + + var i, il, v1, v2, j, k, + face, faceIndices, faceIndex, + edge, + hash, + edgeFaceMap = {}; + + 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; + + 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.addSelf(originalPoints[edgeVertexA]); + avg.addSelf(originalPoints[edgeVertexB]); + avg.multiplyScalar(0.5); + + sharpVertices[newPoints.length] = true; + + } else { + + avg.addSelf(facePoints[faceIndexA]); + avg.addSelf(facePoints[faceIndexB]); + + avg.addSelf(originalPoints[edgeVertexA]); + avg.addSelf(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.UV(); + + avgUv.u = getUV(edgeVertexA, faceIndexA).u + getUV(edgeVertexB, faceIndexA).u; + avgUv.v = getUV(edgeVertexA, faceIndexA).v + getUV(edgeVertexB, faceIndexA).v; + avgUv.u /= 2; + avgUv.v /= 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.UV(); + + avgUv.u = getUV(edgeVertexA, faceIndexB).u + getUV(edgeVertexB, faceIndexB).u; + avgUv.v = getUV(edgeVertexA, faceIndexB).v + getUV(edgeVertexB, faceIndexB).v; + avgUv.u /= 2; + avgUv.v /= 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. + debugCoreStuff(); + + 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