ExtrudeGeometry.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. /**
  2. * @author zz85 / http://www.lab4games.net/zz85/blog
  3. *
  4. * Creates extruded geometry from a path shape.
  5. *
  6. * parameters = {
  7. *
  8. * curveSegments: <int>, // number of points on the curves
  9. * steps: <int>, // number of points for z-side extrusions / used for subdividing segements of extrude spline too
  10. * amount: <int>, // Depth to extrude the shape
  11. *
  12. * bevelEnabled: <bool>, // turn on bevel
  13. * bevelThickness: <float>, // how deep into the original shape bevel goes
  14. * bevelSize: <float>, // how far from shape outline is bevel
  15. * bevelSegments: <int>, // number of bevel layers
  16. *
  17. * extrudePath: <THREE.CurvePath> // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined)
  18. * frames: <THREE.TubeGeometry.FrenetFrames> // containing arrays of tangents, normals, binormals
  19. *
  20. * material: <int> // material index for front and back faces
  21. * extrudeMaterial: <int> // material index for extrusion and beveled faces
  22. * uvGenerator: <Object> // object that provides UV generator functions
  23. *
  24. * }
  25. **/
  26. THREE.ExtrudeGeometry = function ( shapes, options ) {
  27. if ( typeof( shapes ) === "undefined" ) {
  28. shapes = [];
  29. return;
  30. }
  31. THREE.Geometry.call( this );
  32. this.type = 'ExtrudeGeometry';
  33. shapes = shapes instanceof Array ? shapes : [ shapes ];
  34. this.addShapeList( shapes, options );
  35. this.computeFaceNormals();
  36. // can't really use automatic vertex normals
  37. // as then front and back sides get smoothed too
  38. // should do separate smoothing just for sides
  39. //this.computeVertexNormals();
  40. //THREE.log( "took", ( Date.now() - startTime ) );
  41. };
  42. THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
  43. THREE.ExtrudeGeometry.prototype.constructor = THREE.ExtrudeGeometry;
  44. THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
  45. var sl = shapes.length;
  46. for ( var s = 0; s < sl; s ++ ) {
  47. var shape = shapes[ s ];
  48. this.addShape( shape, options );
  49. }
  50. };
  51. THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
  52. var amount = options.amount !== undefined ? options.amount : 100;
  53. var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
  54. var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
  55. var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
  56. var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
  57. var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
  58. var steps = options.steps !== undefined ? options.steps : 1;
  59. var extrudePath = options.extrudePath;
  60. var extrudePts, extrudeByPath = false;
  61. var material = options.material;
  62. var extrudeMaterial = options.extrudeMaterial;
  63. // Use default WorldUVGenerator if no UV generators are specified.
  64. var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
  65. var splineTube, binormal, normal, position2;
  66. if ( extrudePath ) {
  67. extrudePts = extrudePath.getSpacedPoints( steps );
  68. extrudeByPath = true;
  69. bevelEnabled = false; // bevels not supported for path extrusion
  70. // SETUP TNB variables
  71. // Reuse TNB from TubeGeomtry for now.
  72. // TODO1 - have a .isClosed in spline?
  73. splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
  74. // THREE.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
  75. binormal = new THREE.Vector3();
  76. normal = new THREE.Vector3();
  77. position2 = new THREE.Vector3();
  78. }
  79. // Safeguards if bevels are not enabled
  80. if ( ! bevelEnabled ) {
  81. bevelSegments = 0;
  82. bevelThickness = 0;
  83. bevelSize = 0;
  84. }
  85. // Variables initalization
  86. var ahole, h, hl; // looping of holes
  87. var scope = this;
  88. var shapesOffset = this.vertices.length;
  89. var shapePoints = shape.extractPoints( curveSegments );
  90. var vertices = shapePoints.shape;
  91. var holes = shapePoints.holes;
  92. var reverse = ! THREE.Shape.Utils.isClockWise( vertices ) ;
  93. if ( reverse ) {
  94. vertices = vertices.reverse();
  95. // Maybe we should also check if holes are in the opposite direction, just to be safe ...
  96. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  97. ahole = holes[ h ];
  98. if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
  99. holes[ h ] = ahole.reverse();
  100. }
  101. }
  102. reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
  103. }
  104. var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
  105. /* Vertices */
  106. var contour = vertices; // vertices has all points but contour has only points of circumference
  107. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  108. ahole = holes[ h ];
  109. vertices = vertices.concat( ahole );
  110. }
  111. function scalePt2 ( pt, vec, size ) {
  112. if ( ! vec ) THREE.error( "THREE.ExtrudeGeometry: vec does not exist" );
  113. return vec.clone().multiplyScalar( size ).add( pt );
  114. }
  115. var b, bs, t, z,
  116. vert, vlen = vertices.length,
  117. face, flen = faces.length;
  118. // Find directions for point movement
  119. function getBevelVec( inPt, inPrev, inNext ) {
  120. var EPSILON = 0.0000000001;
  121. // computes for inPt the corresponding point inPt' on a new contour
  122. // shiftet by 1 unit (length of normalized vector) to the left
  123. // if we walk along contour clockwise, this new contour is outside the old one
  124. //
  125. // inPt' is the intersection of the two lines parallel to the two
  126. // adjacent edges of inPt at a distance of 1 unit on the left side.
  127. var v_trans_x, v_trans_y, shrink_by = 1; // resulting translation vector for inPt
  128. // good reading for geometry algorithms (here: line-line intersection)
  129. // http://geomalgorithms.com/a05-_intersect-1.html
  130. var v_prev_x = inPt.x - inPrev.x, v_prev_y = inPt.y - inPrev.y;
  131. var v_next_x = inNext.x - inPt.x, v_next_y = inNext.y - inPt.y;
  132. var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );
  133. // check for colinear edges
  134. var colinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );
  135. if ( Math.abs( colinear0 ) > EPSILON ) { // not colinear
  136. // length of vectors for normalizing
  137. var v_prev_len = Math.sqrt( v_prev_lensq );
  138. var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );
  139. // shift adjacent points by unit vectors to the left
  140. var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );
  141. var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );
  142. var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );
  143. var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );
  144. // scaling factor for v_prev to intersection point
  145. var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -
  146. ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /
  147. ( v_prev_x * v_next_y - v_prev_y * v_next_x );
  148. // vector from inPt to intersection point
  149. v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );
  150. v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );
  151. // Don't normalize!, otherwise sharp corners become ugly
  152. // but prevent crazy spikes
  153. var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y )
  154. if ( v_trans_lensq <= 2 ) {
  155. return new THREE.Vector2( v_trans_x, v_trans_y );
  156. } else {
  157. shrink_by = Math.sqrt( v_trans_lensq / 2 );
  158. }
  159. } else { // handle special case of colinear edges
  160. var direction_eq = false; // assumes: opposite
  161. if ( v_prev_x > EPSILON ) {
  162. if ( v_next_x > EPSILON ) { direction_eq = true; }
  163. } else {
  164. if ( v_prev_x < - EPSILON ) {
  165. if ( v_next_x < - EPSILON ) { direction_eq = true; }
  166. } else {
  167. if ( Math.sign(v_prev_y) == Math.sign(v_next_y) ) { direction_eq = true; }
  168. }
  169. }
  170. if ( direction_eq ) {
  171. // THREE.log("Warning: lines are a straight sequence");
  172. v_trans_x = - v_prev_y;
  173. v_trans_y = v_prev_x;
  174. shrink_by = Math.sqrt( v_prev_lensq );
  175. } else {
  176. // THREE.log("Warning: lines are a straight spike");
  177. v_trans_x = v_prev_x;
  178. v_trans_y = v_prev_y;
  179. shrink_by = Math.sqrt( v_prev_lensq / 2 );
  180. }
  181. }
  182. return new THREE.Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );
  183. }
  184. var contourMovements = [];
  185. for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  186. if ( j === il ) j = 0;
  187. if ( k === il ) k = 0;
  188. // (j)---(i)---(k)
  189. // THREE.log('i,j,k', i, j , k)
  190. contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
  191. }
  192. var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
  193. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  194. ahole = holes[ h ];
  195. oneHoleMovements = [];
  196. for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
  197. if ( j === il ) j = 0;
  198. if ( k === il ) k = 0;
  199. // (j)---(i)---(k)
  200. oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
  201. }
  202. holesMovements.push( oneHoleMovements );
  203. verticesMovements = verticesMovements.concat( oneHoleMovements );
  204. }
  205. // Loop bevelSegments, 1 for the front, 1 for the back
  206. for ( b = 0; b < bevelSegments; b ++ ) {
  207. //for ( b = bevelSegments; b > 0; b -- ) {
  208. t = b / bevelSegments;
  209. z = bevelThickness * ( 1 - t );
  210. //z = bevelThickness * t;
  211. bs = bevelSize * ( Math.sin ( t * Math.PI / 2 ) ) ; // curved
  212. //bs = bevelSize * t ; // linear
  213. // contract shape
  214. for ( i = 0, il = contour.length; i < il; i ++ ) {
  215. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  216. v( vert.x, vert.y, - z );
  217. }
  218. // expand holes
  219. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  220. ahole = holes[ h ];
  221. oneHoleMovements = holesMovements[ h ];
  222. for ( i = 0, il = ahole.length; i < il; i ++ ) {
  223. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  224. v( vert.x, vert.y, - z );
  225. }
  226. }
  227. }
  228. bs = bevelSize;
  229. // Back facing vertices
  230. for ( i = 0; i < vlen; i ++ ) {
  231. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  232. if ( ! extrudeByPath ) {
  233. v( vert.x, vert.y, 0 );
  234. } else {
  235. // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
  236. normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
  237. binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
  238. position2.copy( extrudePts[0] ).add(normal).add(binormal);
  239. v( position2.x, position2.y, position2.z );
  240. }
  241. }
  242. // Add stepped vertices...
  243. // Including front facing vertices
  244. var s;
  245. for ( s = 1; s <= steps; s ++ ) {
  246. for ( i = 0; i < vlen; i ++ ) {
  247. vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
  248. if ( ! extrudeByPath ) {
  249. v( vert.x, vert.y, amount / steps * s );
  250. } else {
  251. // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
  252. normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
  253. binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
  254. position2.copy( extrudePts[s] ).add( normal ).add( binormal );
  255. v( position2.x, position2.y, position2.z );
  256. }
  257. }
  258. }
  259. // Add bevel segments planes
  260. //for ( b = 1; b <= bevelSegments; b ++ ) {
  261. for ( b = bevelSegments - 1; b >= 0; b -- ) {
  262. t = b / bevelSegments;
  263. z = bevelThickness * ( 1 - t );
  264. //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
  265. bs = bevelSize * Math.sin ( t * Math.PI / 2 ) ;
  266. // contract shape
  267. for ( i = 0, il = contour.length; i < il; i ++ ) {
  268. vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
  269. v( vert.x, vert.y, amount + z );
  270. }
  271. // expand holes
  272. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  273. ahole = holes[ h ];
  274. oneHoleMovements = holesMovements[ h ];
  275. for ( i = 0, il = ahole.length; i < il; i ++ ) {
  276. vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
  277. if ( ! extrudeByPath ) {
  278. v( vert.x, vert.y, amount + z );
  279. } else {
  280. v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
  281. }
  282. }
  283. }
  284. }
  285. /* Faces */
  286. // Top and bottom faces
  287. buildLidFaces();
  288. // Sides faces
  289. buildSideFaces();
  290. ///// Internal functions
  291. function buildLidFaces() {
  292. if ( bevelEnabled ) {
  293. var layer = 0 ; // steps + 1
  294. var offset = vlen * layer;
  295. // Bottom faces
  296. for ( i = 0; i < flen; i ++ ) {
  297. face = faces[ i ];
  298. f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );
  299. }
  300. layer = steps + bevelSegments * 2;
  301. offset = vlen * layer;
  302. // Top faces
  303. for ( i = 0; i < flen; i ++ ) {
  304. face = faces[ i ];
  305. f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );
  306. }
  307. } else {
  308. // Bottom faces
  309. for ( i = 0; i < flen; i ++ ) {
  310. face = faces[ i ];
  311. f3( face[ 2 ], face[ 1 ], face[ 0 ] );
  312. }
  313. // Top faces
  314. for ( i = 0; i < flen; i ++ ) {
  315. face = faces[ i ];
  316. f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );
  317. }
  318. }
  319. }
  320. // Create faces for the z-sides of the shape
  321. function buildSideFaces() {
  322. var layeroffset = 0;
  323. sidewalls( contour, layeroffset );
  324. layeroffset += contour.length;
  325. for ( h = 0, hl = holes.length; h < hl; h ++ ) {
  326. ahole = holes[ h ];
  327. sidewalls( ahole, layeroffset );
  328. //, true
  329. layeroffset += ahole.length;
  330. }
  331. }
  332. function sidewalls( contour, layeroffset ) {
  333. var j, k;
  334. i = contour.length;
  335. while ( -- i >= 0 ) {
  336. j = i;
  337. k = i - 1;
  338. if ( k < 0 ) k = contour.length - 1;
  339. //THREE.log('b', i,j, i-1, k,vertices.length);
  340. var s = 0, sl = steps + bevelSegments * 2;
  341. for ( s = 0; s < sl; s ++ ) {
  342. var slen1 = vlen * s;
  343. var slen2 = vlen * ( s + 1 );
  344. var a = layeroffset + j + slen1,
  345. b = layeroffset + k + slen1,
  346. c = layeroffset + k + slen2,
  347. d = layeroffset + j + slen2;
  348. f4( a, b, c, d, contour, s, sl, j, k );
  349. }
  350. }
  351. }
  352. function v( x, y, z ) {
  353. scope.vertices.push( new THREE.Vector3( x, y, z ) );
  354. }
  355. function f3( a, b, c ) {
  356. a += shapesOffset;
  357. b += shapesOffset;
  358. c += shapesOffset;
  359. // normal, color, material
  360. scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
  361. var uvs = uvgen.generateTopUV( scope, a, b, c );
  362. scope.faceVertexUvs[ 0 ].push( uvs );
  363. }
  364. function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
  365. a += shapesOffset;
  366. b += shapesOffset;
  367. c += shapesOffset;
  368. d += shapesOffset;
  369. scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
  370. scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
  371. var uvs = uvgen.generateSideWallUV( scope, a, b, c, d );
  372. scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
  373. scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
  374. }
  375. };
  376. THREE.ExtrudeGeometry.WorldUVGenerator = {
  377. generateTopUV: function ( geometry, indexA, indexB, indexC ) {
  378. var vertices = geometry.vertices;
  379. var a = vertices[ indexA ];
  380. var b = vertices[ indexB ];
  381. var c = vertices[ indexC ];
  382. return [
  383. new THREE.Vector2( a.x, a.y ),
  384. new THREE.Vector2( b.x, b.y ),
  385. new THREE.Vector2( c.x, c.y )
  386. ];
  387. },
  388. generateSideWallUV: function ( geometry, indexA, indexB, indexC, indexD ) {
  389. var vertices = geometry.vertices;
  390. var a = vertices[ indexA ];
  391. var b = vertices[ indexB ];
  392. var c = vertices[ indexC ];
  393. var d = vertices[ indexD ];
  394. if ( Math.abs( a.y - b.y ) < 0.01 ) {
  395. return [
  396. new THREE.Vector2( a.x, 1 - a.z ),
  397. new THREE.Vector2( b.x, 1 - b.z ),
  398. new THREE.Vector2( c.x, 1 - c.z ),
  399. new THREE.Vector2( d.x, 1 - d.z )
  400. ];
  401. } else {
  402. return [
  403. new THREE.Vector2( a.y, 1 - a.z ),
  404. new THREE.Vector2( b.y, 1 - b.z ),
  405. new THREE.Vector2( c.y, 1 - c.z ),
  406. new THREE.Vector2( d.y, 1 - d.z )
  407. ];
  408. }
  409. }
  410. };
粤ICP备19079148号