source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/src/extras/geometries/ExtrudeGeometry.js@ 28897

Last change on this file since 28897 was 28897, checked in by davidb, 10 years ago

GUI front-end to server base plus web page content

File size: 16.4 KB
Line 
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
27THREE.ExtrudeGeometry = function ( shapes, options ) {
28
29 if ( typeof( shapes ) === "undefined" ) {
30 shapes = [];
31 return;
32 }
33
34 THREE.Geometry.call( this );
35
36 shapes = shapes instanceof Array ? shapes : [ shapes ];
37
38 this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox();
39
40 this.addShapeList( shapes, options );
41
42 this.computeCentroids();
43 this.computeFaceNormals();
44
45 // can't really use automatic vertex normals
46 // as then front and back sides get smoothed too
47 // should do separate smoothing just for sides
48
49 //this.computeVertexNormals();
50
51 //console.log( "took", ( Date.now() - startTime ) );
52
53};
54
55THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype );
56
57THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) {
58 var sl = shapes.length;
59
60 for ( var s = 0; s < sl; s ++ ) {
61 var shape = shapes[ s ];
62 this.addShape( shape, options );
63 }
64};
65
66THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) {
67
68 var amount = options.amount !== undefined ? options.amount : 100;
69
70 var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10
71 var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8
72 var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;
73
74 var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false
75
76 var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;
77
78 var steps = options.steps !== undefined ? options.steps : 1;
79
80 var extrudePath = options.extrudePath;
81 var extrudePts, extrudeByPath = false;
82
83 var material = options.material;
84 var extrudeMaterial = options.extrudeMaterial;
85
86 // Use default WorldUVGenerator if no UV generators are specified.
87 var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator;
88
89 var shapebb = this.shapebb;
90 //shapebb = shape.getBoundingBox();
91
92
93
94 var splineTube, binormal, normal, position2;
95 if ( extrudePath ) {
96
97 extrudePts = extrudePath.getSpacedPoints( steps );
98
99 extrudeByPath = true;
100 bevelEnabled = false; // bevels not supported for path extrusion
101
102 // SETUP TNB variables
103
104 // Reuse TNB from TubeGeomtry for now.
105 // TODO1 - have a .isClosed in spline?
106
107 splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false);
108
109 // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);
110
111 binormal = new THREE.Vector3();
112 normal = new THREE.Vector3();
113 position2 = new THREE.Vector3();
114
115 }
116
117 // Safeguards if bevels are not enabled
118
119 if ( ! bevelEnabled ) {
120
121 bevelSegments = 0;
122 bevelThickness = 0;
123 bevelSize = 0;
124
125 }
126
127 // Variables initalization
128
129 var ahole, h, hl; // looping of holes
130 var scope = this;
131 var bevelPoints = [];
132
133 var shapesOffset = this.vertices.length;
134
135 var shapePoints = shape.extractPoints( curveSegments );
136
137 var vertices = shapePoints.shape;
138 var holes = shapePoints.holes;
139
140 var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ;
141
142 if ( reverse ) {
143
144 vertices = vertices.reverse();
145
146 // Maybe we should also check if holes are in the opposite direction, just to be safe ...
147
148 for ( h = 0, hl = holes.length; h < hl; h ++ ) {
149
150 ahole = holes[ h ];
151
152 if ( THREE.Shape.Utils.isClockWise( ahole ) ) {
153
154 holes[ h ] = ahole.reverse();
155
156 }
157
158 }
159
160 reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)!
161
162 }
163
164
165 var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes );
166
167 /* Vertices */
168
169 var contour = vertices; // vertices has all points but contour has only points of circumference
170
171 for ( h = 0, hl = holes.length; h < hl; h ++ ) {
172
173 ahole = holes[ h ];
174
175 vertices = vertices.concat( ahole );
176
177 }
178
179
180 function scalePt2 ( pt, vec, size ) {
181
182 if ( !vec ) console.log( "die" );
183
184 return vec.clone().multiplyScalar( size ).add( pt );
185
186 }
187
188 var b, bs, t, z,
189 vert, vlen = vertices.length,
190 face, flen = faces.length,
191 cont, clen = contour.length;
192
193
194 // Find directions for point movement
195
196 var RAD_TO_DEGREES = 180 / Math.PI;
197
198
199 function getBevelVec( pt_i, pt_j, pt_k ) {
200
201 // Algorithm 2
202
203 return getBevelVec2( pt_i, pt_j, pt_k );
204
205 }
206
207 function getBevelVec1( pt_i, pt_j, pt_k ) {
208
209 var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x );
210 var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x );
211
212 if ( anglea > angleb ) {
213
214 angleb += Math.PI * 2;
215
216 }
217
218 var anglec = ( anglea + angleb ) / 2;
219
220
221 //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES);
222
223 var x = - Math.cos( anglec );
224 var y = - Math.sin( anglec );
225
226 var vec = new THREE.Vector2( x, y ); //.normalize();
227
228 return vec;
229
230 }
231
232 function getBevelVec2( pt_i, pt_j, pt_k ) {
233
234 var a = THREE.ExtrudeGeometry.__v1,
235 b = THREE.ExtrudeGeometry.__v2,
236 v_hat = THREE.ExtrudeGeometry.__v3,
237 w_hat = THREE.ExtrudeGeometry.__v4,
238 p = THREE.ExtrudeGeometry.__v5,
239 q = THREE.ExtrudeGeometry.__v6,
240 v, w,
241 v_dot_w_hat, q_sub_p_dot_w_hat,
242 s, intersection;
243
244 // good reading for line-line intersection
245 // http://sputsoft.com/blog/2010/03/line-line-intersection.html
246
247 // define a as vector j->i
248 // define b as vectot k->i
249
250 a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y );
251 b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y );
252
253 // get unit vectors
254
255 v = a.normalize();
256 w = b.normalize();
257
258 // normals from pt i
259
260 v_hat.set( -v.y, v.x );
261 w_hat.set( w.y, -w.x );
262
263 // pts from i
264
265 p.copy( pt_i ).add( v_hat );
266 q.copy( pt_i ).add( w_hat );
267
268 if ( p.equals( q ) ) {
269
270 //console.log("Warning: lines are straight");
271 return w_hat.clone();
272
273 }
274
275 // Points from j, k. helps prevents points cross overover most of the time
276
277 p.copy( pt_j ).add( v_hat );
278 q.copy( pt_k ).add( w_hat );
279
280 v_dot_w_hat = v.dot( w_hat );
281 q_sub_p_dot_w_hat = q.sub( p ).dot( w_hat );
282
283 // We should not reach these conditions
284
285 if ( v_dot_w_hat === 0 ) {
286
287 console.log( "Either infinite or no solutions!" );
288
289 if ( q_sub_p_dot_w_hat === 0 ) {
290
291 console.log( "Its finite solutions." );
292
293 } else {
294
295 console.log( "Too bad, no solutions." );
296
297 }
298
299 }
300
301 s = q_sub_p_dot_w_hat / v_dot_w_hat;
302
303 if ( s < 0 ) {
304
305 // in case of emergecy, revert to algorithm 1.
306
307 return getBevelVec1( pt_i, pt_j, pt_k );
308
309 }
310
311 intersection = v.multiplyScalar( s ).add( p );
312
313 return intersection.sub( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly
314
315 }
316
317 var contourMovements = [];
318
319 for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
320
321 if ( j === il ) j = 0;
322 if ( k === il ) k = 0;
323
324 // (j)---(i)---(k)
325 // console.log('i,j,k', i, j , k)
326
327 var pt_i = contour[ i ];
328 var pt_j = contour[ j ];
329 var pt_k = contour[ k ];
330
331 contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] );
332
333 }
334
335 var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat();
336
337 for ( h = 0, hl = holes.length; h < hl; h ++ ) {
338
339 ahole = holes[ h ];
340
341 oneHoleMovements = [];
342
343 for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {
344
345 if ( j === il ) j = 0;
346 if ( k === il ) k = 0;
347
348 // (j)---(i)---(k)
349 oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );
350
351 }
352
353 holesMovements.push( oneHoleMovements );
354 verticesMovements = verticesMovements.concat( oneHoleMovements );
355
356 }
357
358
359 // Loop bevelSegments, 1 for the front, 1 for the back
360
361 for ( b = 0; b < bevelSegments; b ++ ) {
362 //for ( b = bevelSegments; b > 0; b -- ) {
363
364 t = b / bevelSegments;
365 z = bevelThickness * ( 1 - t );
366
367 //z = bevelThickness * t;
368 bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved
369 //bs = bevelSize * t ; // linear
370
371 // contract shape
372
373 for ( i = 0, il = contour.length; i < il; i ++ ) {
374
375 vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
376 //vert = scalePt( contour[ i ], contourCentroid, bs, false );
377 v( vert.x, vert.y, - z );
378
379 }
380
381 // expand holes
382
383 for ( h = 0, hl = holes.length; h < hl; h++ ) {
384
385 ahole = holes[ h ];
386 oneHoleMovements = holesMovements[ h ];
387
388 for ( i = 0, il = ahole.length; i < il; i++ ) {
389
390 vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
391 //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true );
392
393 v( vert.x, vert.y, -z );
394
395 }
396
397 }
398
399 }
400
401 bs = bevelSize;
402
403 // Back facing vertices
404
405 for ( i = 0; i < vlen; i ++ ) {
406
407 vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
408
409 if ( !extrudeByPath ) {
410
411 v( vert.x, vert.y, 0 );
412
413 } else {
414
415 // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );
416
417 normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x);
418 binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y);
419
420 position2.copy( extrudePts[0] ).add(normal).add(binormal);
421
422 v( position2.x, position2.y, position2.z );
423
424 }
425
426 }
427
428 // Add stepped vertices...
429 // Including front facing vertices
430
431 var s;
432
433 for ( s = 1; s <= steps; s ++ ) {
434
435 for ( i = 0; i < vlen; i ++ ) {
436
437 vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];
438
439 if ( !extrudeByPath ) {
440
441 v( vert.x, vert.y, amount / steps * s );
442
443 } else {
444
445 // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );
446
447 normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x );
448 binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y );
449
450 position2.copy( extrudePts[s] ).add( normal ).add( binormal );
451
452 v( position2.x, position2.y, position2.z );
453
454 }
455
456 }
457
458 }
459
460
461 // Add bevel segments planes
462
463 //for ( b = 1; b <= bevelSegments; b ++ ) {
464 for ( b = bevelSegments - 1; b >= 0; b -- ) {
465
466 t = b / bevelSegments;
467 z = bevelThickness * ( 1 - t );
468 //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) );
469 bs = bevelSize * Math.sin ( t * Math.PI/2 ) ;
470
471 // contract shape
472
473 for ( i = 0, il = contour.length; i < il; i ++ ) {
474
475 vert = scalePt2( contour[ i ], contourMovements[ i ], bs );
476 v( vert.x, vert.y, amount + z );
477
478 }
479
480 // expand holes
481
482 for ( h = 0, hl = holes.length; h < hl; h ++ ) {
483
484 ahole = holes[ h ];
485 oneHoleMovements = holesMovements[ h ];
486
487 for ( i = 0, il = ahole.length; i < il; i ++ ) {
488
489 vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );
490
491 if ( !extrudeByPath ) {
492
493 v( vert.x, vert.y, amount + z );
494
495 } else {
496
497 v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );
498
499 }
500
501 }
502
503 }
504
505 }
506
507 /* Faces */
508
509 // Top and bottom faces
510
511 buildLidFaces();
512
513 // Sides faces
514
515 buildSideFaces();
516
517
518 ///// Internal functions
519
520 function buildLidFaces() {
521
522 if ( bevelEnabled ) {
523
524 var layer = 0 ; // steps + 1
525 var offset = vlen * layer;
526
527 // Bottom faces
528
529 for ( i = 0; i < flen; i ++ ) {
530
531 face = faces[ i ];
532 f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true );
533
534 }
535
536 layer = steps + bevelSegments * 2;
537 offset = vlen * layer;
538
539 // Top faces
540
541 for ( i = 0; i < flen; i ++ ) {
542
543 face = faces[ i ];
544 f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false );
545
546 }
547
548 } else {
549
550 // Bottom faces
551
552 for ( i = 0; i < flen; i++ ) {
553
554 face = faces[ i ];
555 f3( face[ 2 ], face[ 1 ], face[ 0 ], true );
556
557 }
558
559 // Top faces
560
561 for ( i = 0; i < flen; i ++ ) {
562
563 face = faces[ i ];
564 f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false );
565
566 }
567 }
568
569 }
570
571 // Create faces for the z-sides of the shape
572
573 function buildSideFaces() {
574
575 var layeroffset = 0;
576 sidewalls( contour, layeroffset );
577 layeroffset += contour.length;
578
579 for ( h = 0, hl = holes.length; h < hl; h ++ ) {
580
581 ahole = holes[ h ];
582 sidewalls( ahole, layeroffset );
583
584 //, true
585 layeroffset += ahole.length;
586
587 }
588
589 }
590
591 function sidewalls( contour, layeroffset ) {
592
593 var j, k;
594 i = contour.length;
595
596 while ( --i >= 0 ) {
597
598 j = i;
599 k = i - 1;
600 if ( k < 0 ) k = contour.length - 1;
601
602 //console.log('b', i,j, i-1, k,vertices.length);
603
604 var s = 0, sl = steps + bevelSegments * 2;
605
606 for ( s = 0; s < sl; s ++ ) {
607
608 var slen1 = vlen * s;
609 var slen2 = vlen * ( s + 1 );
610
611 var a = layeroffset + j + slen1,
612 b = layeroffset + k + slen1,
613 c = layeroffset + k + slen2,
614 d = layeroffset + j + slen2;
615
616 f4( a, b, c, d, contour, s, sl, j, k );
617
618 }
619 }
620
621 }
622
623
624 function v( x, y, z ) {
625
626 scope.vertices.push( new THREE.Vector3( x, y, z ) );
627
628 }
629
630 function f3( a, b, c, isBottom ) {
631
632 a += shapesOffset;
633 b += shapesOffset;
634 c += shapesOffset;
635
636 // normal, color, material
637 scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) );
638
639 var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c );
640
641 scope.faceVertexUvs[ 0 ].push( uvs );
642
643 }
644
645 function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) {
646
647 a += shapesOffset;
648 b += shapesOffset;
649 c += shapesOffset;
650 d += shapesOffset;
651
652 scope.faces.push( new THREE.Face3( a, b, d, null, null, extrudeMaterial ) );
653 scope.faces.push( new THREE.Face3( b, c, d, null, null, extrudeMaterial ) );
654
655 var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d,
656 stepIndex, stepsLength, contourIndex1, contourIndex2 );
657
658 scope.faceVertexUvs[ 0 ].push( [ uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ] );
659 scope.faceVertexUvs[ 0 ].push( [ uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ] );
660
661 }
662
663};
664
665THREE.ExtrudeGeometry.WorldUVGenerator = {
666
667 generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
668 var ax = geometry.vertices[ indexA ].x,
669 ay = geometry.vertices[ indexA ].y,
670
671 bx = geometry.vertices[ indexB ].x,
672 by = geometry.vertices[ indexB ].y,
673
674 cx = geometry.vertices[ indexC ].x,
675 cy = geometry.vertices[ indexC ].y;
676
677 return [
678 new THREE.Vector2( ax, ay ),
679 new THREE.Vector2( bx, by ),
680 new THREE.Vector2( cx, cy )
681 ];
682
683 },
684
685 generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) {
686
687 return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC );
688
689 },
690
691 generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions,
692 indexA, indexB, indexC, indexD, stepIndex, stepsLength,
693 contourIndex1, contourIndex2 ) {
694
695 var ax = geometry.vertices[ indexA ].x,
696 ay = geometry.vertices[ indexA ].y,
697 az = geometry.vertices[ indexA ].z,
698
699 bx = geometry.vertices[ indexB ].x,
700 by = geometry.vertices[ indexB ].y,
701 bz = geometry.vertices[ indexB ].z,
702
703 cx = geometry.vertices[ indexC ].x,
704 cy = geometry.vertices[ indexC ].y,
705 cz = geometry.vertices[ indexC ].z,
706
707 dx = geometry.vertices[ indexD ].x,
708 dy = geometry.vertices[ indexD ].y,
709 dz = geometry.vertices[ indexD ].z;
710
711 if ( Math.abs( ay - by ) < 0.01 ) {
712 return [
713 new THREE.Vector2( ax, 1 - az ),
714 new THREE.Vector2( bx, 1 - bz ),
715 new THREE.Vector2( cx, 1 - cz ),
716 new THREE.Vector2( dx, 1 - dz )
717 ];
718 } else {
719 return [
720 new THREE.Vector2( ay, 1 - az ),
721 new THREE.Vector2( by, 1 - bz ),
722 new THREE.Vector2( cy, 1 - cz ),
723 new THREE.Vector2( dy, 1 - dz )
724 ];
725 }
726 }
727};
728
729THREE.ExtrudeGeometry.__v1 = new THREE.Vector2();
730THREE.ExtrudeGeometry.__v2 = new THREE.Vector2();
731THREE.ExtrudeGeometry.__v3 = new THREE.Vector2();
732THREE.ExtrudeGeometry.__v4 = new THREE.Vector2();
733THREE.ExtrudeGeometry.__v5 = new THREE.Vector2();
734THREE.ExtrudeGeometry.__v6 = new THREE.Vector2();
Note: See TracBrowser for help on using the repository browser.