123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- /**
- * @author zz85 / http://www.lab4games.net/zz85/blog
- *
- * Creates extruded geometry from a path shape.
- *
- * parameters = {
- *
- * size: <float>, // size of the text
- * height: <float>, // thickness to extrude text
- * curveSegments: <int>, // number of points on the curves
- * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
- amount: <int>, // Amount
- *
- * bevelEnabled: <bool>, // turn on bevel
- * bevelThickness: <float>, // how deep into text bevel goes
- * bevelSize: <float>, // how far from text outline is bevel
- * bevelSegments: <int>, // number of bevel layers
- *
- * extrudePath: <THREE.CurvePath> // 2d/3d spline path to extrude shape orthogonality to
- * bendPath: <THREE.CurvePath> // 2d path for bend the shape around x/y plane
- *
- * material: <int> // material index for front and back faces
- * extrudeMaterial: <int> // material index for extrusion and beveled faces
- *
- * }
- **/
- THREE.ExtrudeGeometry = function( shapes, options ) {
- if ( typeof( shapes ) === "undefined" ) {
- shapes = [];
- return;
- }
- THREE.Geometry.call( this );
- shapes = shapes instanceof Array ? shapes : [ shapes ];
- this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
- this.addShapeList( shapes, options );
- this.computeCentroids();
- this.computeFaceNormals();
- // can't really use automatic vertex normals
- // as then front and back sides get smoothed too
- // should do separate smoothing just for sides
- //this.computeVertexNormals();
- //console.log( "took", ( Date.now() - startTime ) );
- };
- THREE.ExtrudeGeometry.prototype = new THREE.Geometry();
- THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
- THREE.ExtrudeGeometry.prototype.addShapeList = function(shapes, options) {
- var sl = shapes.length;
-
- for ( var s = 0; s < sl; s ++ ) {
- var shape = shapes[ s ];
- this.addShape( shape, options );
- }
- };
- THREE.ExtrudeGeometry.prototype.addShape = function( shape, options ) {
- var amount = options.amount !== undefined ? options.amount : 100;
- var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
- var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
- var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
- var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
- var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
- var steps = options.steps !== undefined ? options.steps : 1;
- var bendPath = options.bendPath;
- var extrudePath = options.extrudePath;
- var extrudePts, extrudeByPath = false;
- var material = options.material;
- var extrudeMaterial = options.extrudeMaterial;
- var shapebb = this.shapebb;
- //shapebb = shape.getBoundingBox();
- var splineTube, binormal, normal, position2;
- if ( extrudePath ) {
- extrudePts = extrudePath.getSpacedPoints( steps );
- extrudeByPath = true;
- bevelEnabled = false; // bevels not supported for path extrusion
- // SETUP TNB variables
- // Reuse TNB from TubeGeomtry for now.
- // TODO1 - have a .isClosed in spline?
- splineTube = new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
- // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
- binormal = new THREE.Vector3();
- normal = new THREE.Vector3();
- position2 = new THREE.Vector3();
- }
- // Safeguards if bevels are not enabled
- if ( ! bevelEnabled ) {
- bevelSegments = 0;
- bevelThickness = 0;
- bevelSize = 0;
- }
- // Variables initalization
- var ahole, h, hl; // looping of holes
- var scope = this;
- var bevelPoints = [];
- var shapesOffset = this.vertices.length;
- if ( bendPath ) {
- shape.addWrapPath( bendPath );
- }
- var shapePoints = shape.extractPoints();
- var vertices = shapePoints.shape;
- var holes = shapePoints.holes;
- var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
- if ( reverse ) {
- vertices = vertices.reverse();
- // Maybe we should also check if holes are in the opposite direction, just to be safe ...
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
- holes[ h ] = ahole.reverse();
- }
- }
- reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
- }
- var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
- //var faces = THREE.Shape.Utils.triangulate2( vertices, holes );
- // Would it be better to move points after triangulation?
- // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath );
- // vertices = shapePoints.shape;
- // holes = shapePoints.holes;
- //console.log(faces);
- ////
- /// Handle Vertices
- ////
- var contour = vertices; // vertices has all points but contour has only points of circumference
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- vertices = vertices.concat( ahole );
- }
- function scalePt2 ( pt, vec, size ) {
- if ( !vec ) console.log( "die" );
- return vec.clone().multiplyScalar( size ).addSelf( pt );
- }
- var b, bs, t, z,
- vert, vlen = vertices.length,
- face, flen = faces.length,
- cont, clen = contour.length;
- //------
- // Find directions for point movement
- //
- var RAD_TO_DEGREES = 180 / Math.PI;
- function getBevelVec( pt_i, pt_j, pt_k ) {
- // Algorithm 2
- return getBevelVec2( pt_i, pt_j, pt_k );
- }
- function getBevelVec1( pt_i, pt_j, pt_k ) {
- var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
- var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
- if ( anglea > angleb ) {
- angleb += Math.PI * 2;
- }
- var anglec = ( anglea + angleb ) / 2;
- //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
- var x = - Math.cos( anglec );
- var y = - Math.sin( anglec );
- var vec = new THREE.Vector2( x, y ); //.normalize();
- return vec;
- }
- function getBevelVec2( pt_i, pt_j, pt_k ) {
- var a = THREE.ExtrudeGeometry.__v1,
- b = THREE.ExtrudeGeometry.__v2,
- v_hat = THREE.ExtrudeGeometry.__v3,
- w_hat = THREE.ExtrudeGeometry.__v4,
- p = THREE.ExtrudeGeometry.__v5,
- q = THREE.ExtrudeGeometry.__v6,
- v, w,
- v_dot_w_hat, q_sub_p_dot_w_hat,
- s, intersection;
- // good reading for line-line intersection
- // http://sputsoft.com/blog/2010/03/line-line-intersection.html
- // define a as vector j->i
- // define b as vectot k->i
- a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
- b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
- // get unit vectors
- v = a.normalize();
- w = b.normalize();
- // normals from pt i
- v_hat.set( -v.y, v.x );
- w_hat.set( w.y, -w.x );
- // pts from i
- p.copy( pt_i ).addSelf( v_hat );
- q.copy( pt_i ).addSelf( w_hat );
- if ( p.equals( q ) ) {
- //console.log("Warning: lines are straight");
- return w_hat.clone();
- }
- // Points from j, k. helps prevents points cross overover most of the time
- p.copy( pt_j ).addSelf( v_hat );
- q.copy( pt_k ).addSelf( w_hat );
- v_dot_w_hat = v.dot( w_hat );
- q_sub_p_dot_w_hat = q.subSelf( p ).dot( w_hat );
- // We should not reach these conditions
- if ( v_dot_w_hat === 0 ) {
- console.log( "Either infinite or no solutions!" );
- if ( q_sub_p_dot_w_hat === 0 ) {
- console.log( "Its finite solutions." );
- } else {
- console.log( "Too bad, no solutions." );
- }
- }
- s = q_sub_p_dot_w_hat / v_dot_w_hat;
- if ( s < 0 ) {
- // in case of emergecy, revert to algorithm 1.
- return getBevelVec1( pt_i, pt_j, pt_k );
- }
- intersection = v.multiplyScalar( s ).addSelf( p );
- return intersection.subSelf( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
- }
- var contourMovements = [];
- for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
- if ( j === il ) j = 0;
- if ( k === il ) k = 0;
- // (j)---(i)---(k)
- // console.log('i,j,k', i, j , k)
- var pt_i = contour[ i ];
- var pt_j = contour[ j ];
- var pt_k = contour[ k ];
- contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
- }
- var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- oneHoleMovements = [];
- for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
- if ( j === il ) j = 0;
- if ( k === il ) k = 0;
- // (j)---(i)---(k)
- oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
- }
- holesMovements.push( oneHoleMovements );
- verticesMovements = verticesMovements.concat( oneHoleMovements );
- }
- // Loop bevelSegments, 1 for the front, 1 for the back
- for ( b = 0; b < bevelSegments; b ++ ) {
- //for ( b = bevelSegments; b > 0; b -- ) {
- t = b / bevelSegments;
- z = bevelThickness * ( 1 - t );
- //z = bevelThickness * t;
- bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
- //bs = bevelSize * t ; // linear
- // contract shape
- for ( i = 0, il = contour.length; i < il; i ++ ) {
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- //vert = scalePt( contour[ i ], contourCentroid, bs, false );
- v( vert.x, vert.y, - z );
- }
- // expand holes
- for ( h = 0, hl = holes.length; h < hl; h++ ) {
- ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( i = 0, il = ahole.length; i < il; i++ ) {
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
- v( vert.x, vert.y, -z );
- }
- }
- }
- bs = bevelSize;
- // Back facing vertices
- for ( i = 0; i < vlen; i ++ ) {
- vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
- if ( !extrudeByPath ) {
- v( vert.x, vert.y, 0 );
- } else {
- // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
- normal.copy(splineTube.normals[0]).multiplyScalar(vert.x);
- binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y);
- position2.copy(extrudePts[0]).addSelf(normal).addSelf(binormal);
-
- v(position2.x, position2.y, position2.z);
- }
- }
- // Add stepped vertices...
- // Including front facing vertices
- var s;
- for ( s = 1; s <= steps; s ++ ) {
- for ( i = 0; i < vlen; i ++ ) {
- vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
- if ( !extrudeByPath ) {
- v( vert.x, vert.y, amount / steps * s );
- } else {
- // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
- normal.copy(splineTube.normals[s]).multiplyScalar(vert.x);
- binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y);
- position2.copy(extrudePts[s]).addSelf(normal).addSelf(binormal);
- v(position2.x, position2.y, position2.z );
- }
- }
- }
- // Add bevel segments planes
- //for ( b = 1; b <= bevelSegments; b ++ ) {
- for ( b = bevelSegments - 1; b >= 0; b -- ) {
- t = b / bevelSegments;
- z = bevelThickness * ( 1 - t );
- //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
- bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
- // contract shape
- for ( i = 0, il = contour.length; i < il; i ++ ) {
- vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
- v( vert.x, vert.y, amount + z );
- }
- // expand holes
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- oneHoleMovements = holesMovements[ h ];
- for ( i = 0, il = ahole.length; i < il; i++ ) {
- vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
- if ( !extrudeByPath ) {
- v( vert.x, vert.y, amount + z );
- } else {
- v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
- }
- }
- }
- }
- // set UV generator
- var uvgen = THREE.ExtrudeGeometry.WorldUVGenerator;
- ////
- /// Handle Faces
- ////
- // Top and bottom faces
- buildLidFaces();
- // Sides faces
- buildSideFaces();
- ///// Internal functions
- function buildLidFaces() {
- if ( bevelEnabled ) {
- var layer = 0 ; // steps + 1
- var offset = vlen * layer;
- // Bottom faces
-
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
- }
- layer = steps + bevelSegments * 2;
- offset = vlen * layer;
- // Top faces
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
- }
- } else {
-
- // Bottom faces
- for ( i = 0; i < flen; i++ ) {
- face = faces[ i ];
- f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
- }
- // Top faces
- for ( i = 0; i < flen; i ++ ) {
- face = faces[ i ];
- f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
- }
- }
- }
- // Create faces for the z-sides of the shape
- function buildSideFaces() {
- var layeroffset = 0;
- sidewalls( contour, layeroffset );
- layeroffset += contour.length;
- for ( h = 0, hl = holes.length; h < hl; h ++ ) {
- ahole = holes[ h ];
- sidewalls( ahole, layeroffset );
- //, true
- layeroffset += ahole.length;
- }
- }
- function sidewalls( contour, layeroffset ) {
- var j, k;
- i = contour.length;
- while ( --i >= 0 ) {
- j = i;
- k = i - 1;
- if ( k < 0 ) k = contour.length - 1;
- //console.log('b', i,j, i-1, k,vertices.length);
- var s = 0, sl = steps + bevelSegments * 2;
- for ( s = 0; s < sl; s ++ ) {
- var slen1 = vlen * s;
- var slen2 = vlen * ( s + 1 );
- var a = layeroffset + j + slen1,
- b = layeroffset + k + slen1,
- c = layeroffset + k + slen2,
- d = layeroffset + j + slen2;
- f4( a, b, c, d, contour, s, sl );
- }
- }
- }
- function v( x, y, z ) {
- scope.vertices.push( new THREE.Vector3( x, y, z ) );
- }
- function f3( a, b, c, isBottom ) {
- a += shapesOffset;
- b += shapesOffset;
- c += shapesOffset;
- // normal, color, material
- scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
- var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c)
- : uvgen.generateTopUV( scope, shape, options, a, b, c);
- scope.faceVertexUvs[ 0 ].push(uvs);
- }
- function f4( a, b, c, d, wallContour, stepIndex, stepsLength ) {
- a += shapesOffset;
- b += shapesOffset;
- c += shapesOffset;
- d += shapesOffset;
- scope.faces.push( new THREE.Face4( a, b, c, d, null, null, extrudeMaterial ) );
-
- var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, stepIndex, stepsLength);
- scope.faceVertexUvs[ 0 ].push(uvs);
- }
- };
- THREE.ExtrudeGeometry.WorldUVGenerator = {
- generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
- var ax = geometry.vertices[ indexA ].x,
- ay = geometry.vertices[ indexA ].y,
- bx = geometry.vertices[ indexB ].x,
- by = geometry.vertices[ indexB ].y,
- cx = geometry.vertices[ indexC ].x,
- cy = geometry.vertices[ indexC ].y;
-
- return [
- new THREE.UV( ax, 1 - ay ),
- new THREE.UV( bx, 1 - by ),
- new THREE.UV( cx, 1 - cy )
- ];
- },
- generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
- return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
- },
- generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
- indexA, indexB, indexC, indexD, stepIndex, stepsLength) {
- var ax = geometry.vertices[ indexA ].x,
- ay = geometry.vertices[ indexA ].y,
- az = geometry.vertices[ indexA ].z,
- bx = geometry.vertices[ indexB ].x,
- by = geometry.vertices[ indexB ].y,
- bz = geometry.vertices[ indexB ].z,
- cx = geometry.vertices[ indexC ].x,
- cy = geometry.vertices[ indexC ].y,
- cz = geometry.vertices[ indexC ].z,
- dx = geometry.vertices[ indexD ].x,
- dy = geometry.vertices[ indexD ].y,
- dz = geometry.vertices[ indexD ].z;
-
- if ( Math.abs( ay - by ) < 0.01 ) {
- return [
- new THREE.UV( ax, az ),
- new THREE.UV( bx, bz ),
- new THREE.UV( cx, cz ),
- new THREE.UV( dx, dz )
- ];
- } else {
- return [
- new THREE.UV( ay, az ),
- new THREE.UV( by, bz ),
- new THREE.UV( cy, cz ),
- new THREE.UV( dy, dz )
- ];
- }
- }
- };
- THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
- THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();
|