ExtrudeGeometry.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. *
  4. * Creates extruded geometry from a path shape.
  5. *
  6. * parameters = {
  7. *
  8. * size: <float>, // size of the text
  9. * height: <float>, // thickness to extrude text
  10. * curveSegments: <int>, // number of points on the curves
  11. * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
  12. amount: <int>, // Amount
  13. *
  14. * bevelEnabled: <bool>, // turn on bevel
  15. * bevelThickness: <float>, // how deep into text bevel goes
  16. * bevelSize: <float>, // how far from text outline is bevel
  17. * bevelSegments: <int>, // number of bevel layers
  18. *
  19. * extrudePath: <THREE.CurvePath> // 2d/3d spline path to extrude shape orthogonality to
  20. * bendPath: <THREE.CurvePath> // 2d path for bend the shape around x/y plane
  21. *
  22. * material: <int> // material index for front and back faces
  23. * extrudeMaterial: <int> // material index for extrusion and beveled faces
  24. *
  25. * }
  26. **/
  27. THREE.ExtrudeGeometry = function( shapes, options ) {
  28. if ( typeof( shapes ) === "undefined" ) {
  29. shapes = [];
  30. return;
  31. }
  32. THREE.Geometry.call( this );
  33. shapes = shapes instanceof Array ? shapes : [ shapes ];
  34. this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
  35. this.addShapeList( shapes, options );
  36. this.computeCentroids();
  37. this.computeFaceNormals();
  38. // can't really use automatic vertex normals
  39. // as then front and back sides get smoothed too
  40. // should do separate smoothing just for sides
  41. //this.computeVertexNormals();
  42. //console.log( "took", ( Date.now() - startTime ) );
  43. };
  44. THREE.ExtrudeGeometry.prototype = new THREE.Geometry();
  45. THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
  46. THREE.ExtrudeGeometry.prototype.addShapeList = function(shapes, options) {
  47. var sl = shapes.length;
  48. for ( var s = 0; s < sl; s ++ ) {
  49. var shape = shapes[ s ];
  50. this.addShape( shape, options );
  51. }
  52. };
  53. THREE.ExtrudeGeometry.prototype.addShape = function( shape, options ) {
  54. var amount = options.amount !== undefined ? options.amount : 100;
  55. var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
  56. var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
  57. var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
  58. var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
  59. var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
  60. var steps = options.steps !== undefined ? options.steps : 1;
  61. var bendPath = options.bendPath;
  62. var extrudePath = options.extrudePath;
  63. var extrudePts, extrudeByPath = false;
  64. var material = options.material;
  65. var extrudeMaterial = options.extrudeMaterial;
  66. var shapebb = this.shapebb;
  67. //shapebb = shape.getBoundingBox();
  68. var splineTube, binormal, normal, position2;
  69. if ( extrudePath ) {
  70. extrudePts = extrudePath.getSpacedPoints( steps );
  71. extrudeByPath = true;
  72. bevelEnabled = false; // bevels not supported for path extrusion
  73. // SETUP TNB variables
  74. // Reuse TNB from TubeGeomtry for now.
  75. // TODO1 - have a .isClosed in spline?
  76. splineTube = new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
  77. // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
  78. binormal = new THREE.Vector3();
  79. normal = new THREE.Vector3();
  80. position2 = new THREE.Vector3();
  81. }
  82. // Safeguards if bevels are not enabled
  83. if ( ! bevelEnabled ) {
  84. bevelSegments = 0;
  85. bevelThickness = 0;
  86. bevelSize = 0;
  87. }
  88. // Variables initalization
  89. var ahole, h, hl; // looping of holes
  90. var scope = this;
  91. var bevelPoints = [];
  92. var shapesOffset = this.vertices.length;
  93. if ( bendPath ) {
  94. shape.addWrapPath( bendPath );
  95. }
  96. var shapePoints = shape.extractPoints();
  97. var vertices = shapePoints.shape;
  98. var holes = shapePoints.holes;
  99. var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
  100. if ( reverse ) {
  101. vertices = vertices.reverse();
  102. // Maybe we should also check if holes are in the opposite direction, just to be safe ...
  103. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  104. ahole = holes[ h ];
  105. if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
  106. holes[ h ] = ahole.reverse();
  107. }
  108. }
  109. reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
  110. }
  111. var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
  112. //var faces = THREE.Shape.Utils.triangulate2( vertices, holes );
  113. // Would it be better to move points after triangulation?
  114. // shapePoints = shape.extractAllPointsWithBend( curveSegments, bendPath );
  115. // vertices = shapePoints.shape;
  116. // holes = shapePoints.holes;
  117. //console.log(faces);
  118. ////
  119. /// Handle Vertices
  120. ////
  121. var contour = vertices; // vertices has all points but contour has only points of circumference
  122. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  123. ahole = holes[ h ];
  124. vertices = vertices.concat( ahole );
  125. }
  126. function scalePt2 ( pt, vec, size ) {
  127. if ( !vec ) console.log( "die" );
  128. return vec.clone().multiplyScalar( size ).addSelf( pt );
  129. }
  130. var b, bs, t, z,
  131. vert, vlen = vertices.length,
  132. face, flen = faces.length,
  133. cont, clen = contour.length;
  134. //------
  135. // Find directions for point movement
  136. //
  137. var RAD_TO_DEGREES = 180 / Math.PI;
  138. function getBevelVec( pt_i, pt_j, pt_k ) {
  139. // Algorithm 2
  140. return getBevelVec2( pt_i, pt_j, pt_k );
  141. }
  142. function getBevelVec1( pt_i, pt_j, pt_k ) {
  143. var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
  144. var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
  145. if ( anglea > angleb ) {
  146. angleb += Math.PI * 2;
  147. }
  148. var anglec = ( anglea + angleb ) / 2;
  149. //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
  150. var x = - Math.cos( anglec );
  151. var y = - Math.sin( anglec );
  152. var vec = new THREE.Vector2( x, y ); //.normalize();
  153. return vec;
  154. }
  155. function getBevelVec2( pt_i, pt_j, pt_k ) {
  156. var a = THREE.ExtrudeGeometry.__v1,
  157. b = THREE.ExtrudeGeometry.__v2,
  158. v_hat = THREE.ExtrudeGeometry.__v3,
  159. w_hat = THREE.ExtrudeGeometry.__v4,
  160. p = THREE.ExtrudeGeometry.__v5,
  161. q = THREE.ExtrudeGeometry.__v6,
  162. v, w,
  163. v_dot_w_hat, q_sub_p_dot_w_hat,
  164. s, intersection;
  165. // good reading for line-line intersection
  166. // http://sputsoft.com/blog/2010/03/line-line-intersection.html
  167. // define a as vector j->i
  168. // define b as vectot k->i
  169. a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
  170. b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
  171. // get unit vectors
  172. v = a.normalize();
  173. w = b.normalize();
  174. // normals from pt i
  175. v_hat.set( -v.y, v.x );
  176. w_hat.set( w.y, -w.x );
  177. // pts from i
  178. p.copy( pt_i ).addSelf( v_hat );
  179. q.copy( pt_i ).addSelf( w_hat );
  180. if ( p.equals( q ) ) {
  181. //console.log("Warning: lines are straight");
  182. return w_hat.clone();
  183. }
  184. // Points from j, k. helps prevents points cross overover most of the time
  185. p.copy( pt_j ).addSelf( v_hat );
  186. q.copy( pt_k ).addSelf( w_hat );
  187. v_dot_w_hat = v.dot( w_hat );
  188. q_sub_p_dot_w_hat = q.subSelf( p ).dot( w_hat );
  189. // We should not reach these conditions
  190. if ( v_dot_w_hat === 0 ) {
  191. console.log( "Either infinite or no solutions!" );
  192. if ( q_sub_p_dot_w_hat === 0 ) {
  193. console.log( "Its finite solutions." );
  194. } else {
  195. console.log( "Too bad, no solutions." );
  196. }
  197. }
  198. s = q_sub_p_dot_w_hat / v_dot_w_hat;
  199. if ( s < 0 ) {
  200. // in case of emergecy, revert to algorithm 1.
  201. return getBevelVec1( pt_i, pt_j, pt_k );
  202. }
  203. intersection = v.multiplyScalar( s ).addSelf( p );
  204. return intersection.subSelf( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
  205. }
  206. var contourMovements = [];
  207. for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  208. if ( j === il ) j = 0;
  209. if ( k === il ) k = 0;
  210. // (j)---(i)---(k)
  211. // console.log('i,j,k', i, j , k)
  212. var pt_i = contour[ i ];
  213. var pt_j = contour[ j ];
  214. var pt_k = contour[ k ];
  215. contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  216. }
  217. var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
  218. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  219. ahole = holes[ h ];
  220. oneHoleMovements = [];
  221. for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  222. if ( j === il ) j = 0;
  223. if ( k === il ) k = 0;
  224. // (j)---(i)---(k)
  225. oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  226. }
  227. holesMovements.push( oneHoleMovements );
  228. verticesMovements = verticesMovements.concat( oneHoleMovements );
  229. }
  230. // Loop bevelSegments, 1 for the front, 1 for the back
  231. for ( b = 0; b < bevelSegments; b ++ ) {
  232. //for ( b = bevelSegments; b > 0; b -- ) {
  233. t = b / bevelSegments;
  234. z = bevelThickness * ( 1 - t );
  235. //z = bevelThickness * t;
  236. bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
  237. //bs = bevelSize * t ; // linear
  238. // contract shape
  239. for ( i = 0, il = contour.length; i < il; i ++ ) {
  240. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  241. //vert = scalePt( contour[ i ], contourCentroid, bs, false );
  242. v( vert.x, vert.y, - z );
  243. }
  244. // expand holes
  245. for ( h = 0, hl = holes.length; h < hl; h++ ) {
  246. ahole = holes[ h ];
  247. oneHoleMovements = holesMovements[ h ];
  248. for ( i = 0, il = ahole.length; i < il; i++ ) {
  249. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  250. //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
  251. v( vert.x, vert.y, -z );
  252. }
  253. }
  254. }
  255. bs = bevelSize;
  256. // Back facing vertices
  257. for ( i = 0; i < vlen; i ++ ) {
  258. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  259. if ( !extrudeByPath ) {
  260. v( vert.x, vert.y, 0 );
  261. } else {
  262. // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  263. normal.copy(splineTube.normals[0]).multiplyScalar(vert.x);
  264. binormal.copy(splineTube.binormals[0]).multiplyScalar(vert.y);
  265. position2.copy(extrudePts[0]).addSelf(normal).addSelf(binormal);
  266. v(position2.x, position2.y, position2.z);
  267. }
  268. }
  269. // Add stepped vertices...
  270. // Including front facing vertices
  271. var s;
  272. for ( s = 1; s <= steps; s ++ ) {
  273. for ( i = 0; i < vlen; i ++ ) {
  274. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  275. if ( !extrudeByPath ) {
  276. v( vert.x, vert.y, amount / steps * s );
  277. } else {
  278. // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  279. normal.copy(splineTube.normals[s]).multiplyScalar(vert.x);
  280. binormal.copy(splineTube.binormals[s]).multiplyScalar(vert.y);
  281. position2.copy(extrudePts[s]).addSelf(normal).addSelf(binormal);
  282. v(position2.x, position2.y, position2.z );
  283. }
  284. }
  285. }
  286. // Add bevel segments planes
  287. //for ( b = 1; b <= bevelSegments; b ++ ) {
  288. for ( b = bevelSegments - 1; b >= 0; b -- ) {
  289. t = b / bevelSegments;
  290. z = bevelThickness * ( 1 - t );
  291. //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
  292. bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
  293. // contract shape
  294. for ( i = 0, il = contour.length; i < il; i ++ ) {
  295. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  296. v( vert.x, vert.y, amount + z );
  297. }
  298. // expand holes
  299. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  300. ahole = holes[ h ];
  301. oneHoleMovements = holesMovements[ h ];
  302. for ( i = 0, il = ahole.length; i < il; i++ ) {
  303. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  304. if ( !extrudeByPath ) {
  305. v( vert.x, vert.y, amount + z );
  306. } else {
  307. v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
  308. }
  309. }
  310. }
  311. }
  312. // set UV generator
  313. var uvgen = THREE.ExtrudeGeometry.WorldUVGenerator;
  314. ////
  315. /// Handle Faces
  316. ////
  317. // Top and bottom faces
  318. buildLidFaces();
  319. // Sides faces
  320. buildSideFaces();
  321. ///// Internal functions
  322. function buildLidFaces() {
  323. if ( bevelEnabled ) {
  324. var layer = 0 ; // steps + 1
  325. var offset = vlen * layer;
  326. // Bottom faces
  327. for ( i = 0; i < flen; i ++ ) {
  328. face = faces[ i ];
  329. f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
  330. }
  331. layer = steps + bevelSegments * 2;
  332. offset = vlen * layer;
  333. // Top faces
  334. for ( i = 0; i < flen; i ++ ) {
  335. face = faces[ i ];
  336. f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
  337. }
  338. } else {
  339. // Bottom faces
  340. for ( i = 0; i < flen; i++ ) {
  341. face = faces[ i ];
  342. f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
  343. }
  344. // Top faces
  345. for ( i = 0; i < flen; i ++ ) {
  346. face = faces[ i ];
  347. f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
  348. }
  349. }
  350. }
  351. // Create faces for the z-sides of the shape
  352. function buildSideFaces() {
  353. var layeroffset = 0;
  354. sidewalls( contour, layeroffset );
  355. layeroffset += contour.length;
  356. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  357. ahole = holes[ h ];
  358. sidewalls( ahole, layeroffset );
  359. //, true
  360. layeroffset += ahole.length;
  361. }
  362. }
  363. function sidewalls( contour, layeroffset ) {
  364. var j, k;
  365. i = contour.length;
  366. while ( --i >= 0 ) {
  367. j = i;
  368. k = i - 1;
  369. if ( k < 0 ) k = contour.length - 1;
  370. //console.log('b', i,j, i-1, k,vertices.length);
  371. var s = 0, sl = steps + bevelSegments * 2;
  372. for ( s = 0; s < sl; s ++ ) {
  373. var slen1 = vlen * s;
  374. var slen2 = vlen * ( s + 1 );
  375. var a = layeroffset + j + slen1,
  376. b = layeroffset + k + slen1,
  377. c = layeroffset + k + slen2,
  378. d = layeroffset + j + slen2;
  379. f4( a, b, c, d, contour, s, sl );
  380. }
  381. }
  382. }
  383. function v( x, y, z ) {
  384. scope.vertices.push( new THREE.Vector3( x, y, z ) );
  385. }
  386. function f3( a, b, c, isBottom ) {
  387. a += shapesOffset;
  388. b += shapesOffset;
  389. c += shapesOffset;
  390. // normal, color, material
  391. scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
  392. var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c)
  393. : uvgen.generateTopUV( scope, shape, options, a, b, c);
  394. scope.faceVertexUvs[ 0 ].push(uvs);
  395. }
  396. function f4( a, b, c, d, wallContour, stepIndex, stepsLength ) {
  397. a += shapesOffset;
  398. b += shapesOffset;
  399. c += shapesOffset;
  400. d += shapesOffset;
  401. scope.faces.push( new THREE.Face4( a, b, c, d, null, null, extrudeMaterial ) );
  402. var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, stepIndex, stepsLength);
  403. scope.faceVertexUvs[ 0 ].push(uvs);
  404. }
  405. };
  406. THREE.ExtrudeGeometry.WorldUVGenerator = {
  407. generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
  408. var ax = geometry.vertices[ indexA ].x,
  409. ay = geometry.vertices[ indexA ].y,
  410. bx = geometry.vertices[ indexB ].x,
  411. by = geometry.vertices[ indexB ].y,
  412. cx = geometry.vertices[ indexC ].x,
  413. cy = geometry.vertices[ indexC ].y;
  414. return [
  415. new THREE.UV( ax, 1 - ay ),
  416. new THREE.UV( bx, 1 - by ),
  417. new THREE.UV( cx, 1 - cy )
  418. ];
  419. },
  420. generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC) {
  421. return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
  422. },
  423. generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
  424. indexA, indexB, indexC, indexD, stepIndex, stepsLength) {
  425. var ax = geometry.vertices[ indexA ].x,
  426. ay = geometry.vertices[ indexA ].y,
  427. az = geometry.vertices[ indexA ].z,
  428. bx = geometry.vertices[ indexB ].x,
  429. by = geometry.vertices[ indexB ].y,
  430. bz = geometry.vertices[ indexB ].z,
  431. cx = geometry.vertices[ indexC ].x,
  432. cy = geometry.vertices[ indexC ].y,
  433. cz = geometry.vertices[ indexC ].z,
  434. dx = geometry.vertices[ indexD ].x,
  435. dy = geometry.vertices[ indexD ].y,
  436. dz = geometry.vertices[ indexD ].z;
  437. if ( Math.abs( ay - by ) < 0.01 ) {
  438. return [
  439. new THREE.UV( ax, az ),
  440. new THREE.UV( bx, bz ),
  441. new THREE.UV( cx, cz ),
  442. new THREE.UV( dx, dz )
  443. ];
  444. } else {
  445. return [
  446. new THREE.UV( ay, az ),
  447. new THREE.UV( by, bz ),
  448. new THREE.UV( cy, cz ),
  449. new THREE.UV( dy, dz )
  450. ];
  451. }
  452. }
  453. };
  454. THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
  455. THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
  456. THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
  457. THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
  458. THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
  459. THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();