2 * @author Eric Haines / http://erichaines.com/
4 * Tessellates the famous Utah teapot database by Martin Newell into triangles.
6 * THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn )
8 * defaults: size = 50, segments = 10, bottom = true, lid = true, body = true,
9 * fitLid = false, blinn = true
11 * size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1.
12 * Think of it as a "radius".
13 * segments - number of line segments to subdivide each patch edge;
14 * 1 is possible but gives degenerates, so two is the real minimum.
15 * bottom - boolean, if true (default) then the bottom patches are added. Some consider
16 * adding the bottom heresy, so set this to "false" to adhere to the One True Way.
17 * lid - to remove the lid and look inside, set to true.
18 * body - to remove the body and leave the lid, set this and "bottom" to false.
19 * fitLid - the lid is a tad small in the original. This stretches it a bit so you can't
20 * see the teapot's insides through the gap.
21 * blinn - Jim Blinn scaled the original data vertically by dividing by about 1.3 to look
22 * nicer. If you want to see the original teapot, similar to the real-world model, set
23 * this to false. True by default.
24 * See http://en.wikipedia.org/wiki/File:Original_Utah_Teapot.jpg for the original
25 * real-world teapot (from http://en.wikipedia.org/wiki/Utah_teapot).
27 * Note that the bottom (the last four patches) is not flat - blame Frank Crow, not me.
29 * The teapot should normally be rendered as a double sided object, since for some
30 * patches both sides can be seen, e.g., the gap around the lid and inside the spout.
32 * Segments 'n' determines the number of triangles output.
33 * Total triangles = 32*2*n*n - 8*n [degenerates at the top and bottom cusps are deleted]
35 * size_factor # triangles
45 * Code converted from my ancient SPD software, http://tog.acm.org/resources/SPD/
46 * Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
47 * Lesson: https://www.udacity.com/course/viewer#!/c-cs291/l-68866048/m-106482448
48 * YouTube video on teapot history: https://www.youtube.com/watch?v=DxMfblPzFNc
50 * See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot
55 THREE.TeapotBufferGeometry = function ( size, segments, bottom, lid, body, fitLid, blinn ) {
59 // 32 * 4 * 4 Bezier spline patches
62 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
63 3,16,17,18,7,19,20,21,11,22,23,24,15,25,26,27,
64 18,28,29,30,21,31,32,33,24,34,35,36,27,37,38,39,
65 30,40,41,0,33,42,43,4,36,44,45,8,39,46,47,12,
67 12,13,14,15,48,49,50,51,52,53,54,55,56,57,58,59,
68 15,25,26,27,51,60,61,62,55,63,64,65,59,66,67,68,
69 27,37,38,39,62,69,70,71,65,72,73,74,68,75,76,77,
70 39,46,47,12,71,78,79,48,74,80,81,52,77,82,83,56,
71 56,57,58,59,84,85,86,87,88,89,90,91,92,93,94,95,
72 59,66,67,68,87,96,97,98,91,99,100,101,95,102,103,104,
73 68,75,76,77,98,105,106,107,101,108,109,110,104,111,112,113,
74 77,82,83,56,107,114,115,84,110,116,117,88,113,118,119,92,
76 120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,
77 123,136,137,120,127,138,139,124,131,140,141,128,135,142,143,132,
78 132,133,134,135,144,145,146,147,148,149,150,151,68,152,153,154,
79 135,142,143,132,147,155,156,144,151,157,158,148,154,159,160,68,
81 161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,
82 164,177,178,161,168,179,180,165,172,181,182,169,176,183,184,173,
83 173,174,175,176,185,186,187,188,189,190,191,192,193,194,195,196,
84 176,183,184,173,188,197,198,185,192,199,200,189,196,201,202,193,
86 203,203,203,203,204,205,206,207,208,208,208,208,209,210,211,212,
87 203,203,203,203,207,213,214,215,208,208,208,208,212,216,217,218,
88 203,203,203,203,215,219,220,221,208,208,208,208,218,222,223,224,
89 203,203,203,203,221,225,226,204,208,208,208,208,224,227,228,209,
90 209,210,211,212,229,230,231,232,233,234,235,236,237,238,239,240,
91 212,216,217,218,232,241,242,243,236,244,245,246,240,247,248,249,
92 218,222,223,224,243,250,251,252,246,253,254,255,249,256,257,258,
93 224,227,228,209,252,259,260,229,255,261,262,233,258,263,264,237,
95 265,265,265,265,266,267,268,269,270,271,272,273,92,119,118,113,
96 265,265,265,265,269,274,275,276,273,277,278,279,113,112,111,104,
97 265,265,265,265,276,280,281,282,279,283,284,285,104,103,102,95,
98 265,265,265,265,282,286,287,266,285,288,289,270,95,94,93,92
101 var teapotVertices = [
107 1.3375,-0.749,2.53125,
108 0.749,-1.3375,2.53125,
111 1.4375,-0.805,2.53125,
112 0.805,-1.4375,2.53125,
121 -0.749,-1.3375,2.53125,
122 -1.3375,-0.749,2.53125,
124 -0.805,-1.4375,2.53125,
125 -1.4375,-0.805,2.53125,
133 -1.3375,0.749,2.53125,
134 -0.749,1.3375,2.53125,
136 -1.4375,0.805,2.53125,
137 -0.805,1.4375,2.53125,
144 0.749,1.3375,2.53125,
145 1.3375,0.749,2.53125,
146 0.805,1.4375,2.53125,
147 1.4375,0.805,2.53125,
394 THREE.BufferGeometry.call( this );
396 this.type = 'TeapotBufferGeometry';
410 // number of segments per patch
411 segments = segments !== undefined ? Math.max( 2, Math.floor( segments ) || 10 ) : 10;
413 // which parts should be visible
414 bottom = bottom === undefined ? true : bottom;
415 lid = lid === undefined ? true : lid;
416 body = body === undefined ? true : body;
418 // Should the lid be snug? It's not traditional, but we make it snug by default
419 fitLid = fitLid === undefined ? true : fitLid;
421 // Jim Blinn scaled the teapot down in size by about 1.3 for
422 // some rendering tests. He liked the new proportions that he kept
423 // the data in this form. The model was distributed with these new
424 // proportions and became the norm. Trivia: comparing images of the
425 // real teapot and the computer model, the ratio for the bowl of the
426 // real teapot is more like 1.25, but since 1.3 is the traditional
427 // value given, we use it here.
428 var blinnScale = 1.3;
429 blinn = blinn === undefined ? true : blinn;
431 // scale the size to be the real scaling factor
432 var maxHeight = 3.15 * ( blinn ? 1 : blinnScale );
434 var maxHeight2 = maxHeight / 2;
435 var trueSize = size / maxHeight2;
437 // Number of elements depends on what is needed. Subtract degenerate
438 // triangles at tip of bottom and lid out in advance.
439 var numTriangles = bottom ? ( 8 * segments - 4 ) * segments : 0;
440 numTriangles += lid ? ( 16 * segments - 4 ) * segments : 0;
441 numTriangles += body ? 40 * segments * segments : 0;
443 var indices = new Uint32Array( numTriangles * 3 );
445 var numVertices = bottom ? 4 : 0;
446 numVertices += lid ? 8 : 0;
447 numVertices += body ? 20 : 0;
448 numVertices *= ( segments + 1 ) * ( segments + 1 );
450 var vertices = new Float32Array( numVertices * 3 );
451 var normals = new Float32Array( numVertices * 3 );
452 var uvs = new Float32Array( numVertices * 2 );
455 var ms = new THREE.Matrix4();
456 ms.set( -1.0, 3.0, -3.0, 1.0,
459 1.0, 0.0, 0.0, 0.0 ) ;
469 // M * G * M matrix, sort of see
470 // http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
477 var norm = new THREE.Vector3();
484 var s, t, sval, tval, p;
488 var normOut = new THREE.Vector3();
491 var gmx = new THREE.Matrix4();
492 var tmtx = new THREE.Matrix4();
494 var vsp = new THREE.Vector4();
495 var vtp = new THREE.Vector4();
496 var vdsp = new THREE.Vector4();
497 var vdtp = new THREE.Vector4();
499 var vsdir = new THREE.Vector3();
500 var vtdir = new THREE.Vector3();
502 var mst = ms.clone();
505 // internal function: test if triangle has any matching vertices;
506 // if so, don't save triangle, since it won't display anything.
507 var notDegenerate = function ( vtx1, vtx2, vtx3 ) {
509 // if any vertex matches, return false
510 return ! ( ( ( vertices[ vtx1 * 3 ] === vertices[ vtx2 * 3 ] ) &&
511 ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx2 * 3 + 1 ] ) &&
512 ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx2 * 3 + 2 ] ) ) ||
513 ( ( vertices[ vtx1 * 3 ] === vertices[ vtx3 * 3 ] ) &&
514 ( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
515 ( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) ||
516 ( ( vertices[ vtx2 * 3 ] === vertices[ vtx3 * 3 ] ) &&
517 ( vertices[ vtx2 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
518 ( vertices[ vtx2 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) );
523 for ( i = 0; i < 3; i ++ )
526 mgm[ i ] = new THREE.Matrix4();
530 var minPatches = body ? 0 : 20;
531 var maxPatches = bottom ? 32 : 28;
533 vertPerRow = segments + 1;
543 for ( var surf = minPatches ; surf < maxPatches ; surf ++ ) {
545 // lid is in the middle of the data, patches 20-27,
546 // so ignore it for this part of the loop if the lid is not desired
547 if ( lid || ( surf < 20 || surf >= 28 ) ) {
549 // get M * G * M matrix for x,y,z
550 for ( i = 0 ; i < 3 ; i ++ ) {
552 // get control patches
553 for ( r = 0 ; r < 4 ; r ++ ) {
555 for ( c = 0 ; c < 4 ; c ++ ) {
558 g[ c * 4 + r ] = teapotVertices[ teapotPatches[ surf * 16 + r * 4 + c ] * 3 + i ] ;
560 // is the lid to be made larger, and is this a point on the lid
562 if ( fitLid && ( surf >= 20 && surf < 28 ) && ( i !== 2 ) ) {
564 // increase XY size by 7.7%, found empirically. I don't
565 // increase Z so that the teapot will continue to fit in the
566 // space -1 to 1 for Y (Y is up for the final model).
567 g[ c * 4 + r ] *= 1.077;
571 // Blinn "fixed" the teapot by dividing Z by blinnScale, and that's the
572 // data we now use. The original teapot is taller. Fix it:
573 if ( ! blinn && ( i === 2 ) ) {
575 g[ c * 4 + r ] *= blinnScale;
583 gmx.set( g[ 0 ], g[ 1 ], g[ 2 ], g[ 3 ], g[ 4 ], g[ 5 ], g[ 6 ], g[ 7 ], g[ 8 ], g[ 9 ], g[ 10 ], g[ 11 ], g[ 12 ], g[ 13 ], g[ 14 ], g[ 15 ] );
585 tmtx.multiplyMatrices( gmx, ms );
586 mgm[ i ].multiplyMatrices( mst, tmtx );
590 // step along, get points, and output
591 for ( sstep = 0 ; sstep <= segments ; sstep ++ ) {
593 s = sstep / segments;
595 for ( tstep = 0 ; tstep <= segments ; tstep ++ ) {
597 t = tstep / segments;
600 // get power vectors and their derivatives
601 for ( p = 4, sval = tval = 1.0 ; p -- ; ) {
610 dsp[ p ] = dtp[ p ] = 0.0 ;
611 dsval = dtval = 1.0 ;
615 dsp[ p ] = dsval * ( 3 - p ) ;
616 dtp[ p ] = dtval * ( 3 - p ) ;
626 vdsp.fromArray( dsp );
627 vdtp.fromArray( dtp );
630 for ( i = 0 ; i < 3 ; i ++ ) {
632 // multiply power vectors times matrix to get value
633 tcoord = vsp.clone();
634 tcoord.applyMatrix4( mgm[ i ] );
635 vert[ i ] = tcoord.dot( vtp );
637 // get s and t tangent vectors
638 tcoord = vdsp.clone();
639 tcoord.applyMatrix4( mgm[ i ] );
640 sdir[ i ] = tcoord.dot( vtp ) ;
642 tcoord = vsp.clone();
643 tcoord.applyMatrix4( mgm[ i ] );
644 tdir[ i ] = tcoord.dot( vdtp ) ;
649 vsdir.fromArray( sdir );
650 vtdir.fromArray( tdir );
651 norm.crossVectors( vtdir, vsdir );
654 // if X and Z length is 0, at the cusp, so point the normal up or down, depending on patch number
655 if ( vert[ 0 ] === 0 && vert[ 1 ] === 0 )
658 // if above the middle of the teapot, normal points up, else down
659 normOut.set( 0, vert[ 2 ] > maxHeight2 ? 1 : - 1, 0 );
665 // standard output: rotate on X axis
666 normOut.set( norm.x, norm.z, - norm.y );
671 vertices[ vertCount ++ ] = trueSize * vert[ 0 ];
672 vertices[ vertCount ++ ] = trueSize * ( vert[ 2 ] - maxHeight2 );
673 vertices[ vertCount ++ ] = - trueSize * vert[ 1 ];
675 normals[ normCount ++ ] = normOut.x;
676 normals[ normCount ++ ] = normOut.y;
677 normals[ normCount ++ ] = normOut.z;
679 uvs[ uvCount ++ ] = 1 - t;
680 uvs[ uvCount ++ ] = 1 - s;
687 for ( sstep = 0 ; sstep < segments ; sstep ++ ) {
689 for ( tstep = 0 ; tstep < segments ; tstep ++ ) {
691 v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
693 v3 = v2 + vertPerRow;
694 v4 = v1 + vertPerRow;
696 // Normals and UVs cannot be shared. Without clone(), you can see the consequences
697 // of sharing if you call geometry.applyMatrix( matrix ).
698 if ( notDegenerate ( v1, v2, v3 ) ) {
700 indices[ indexCount ++ ] = v1;
701 indices[ indexCount ++ ] = v2;
702 indices[ indexCount ++ ] = v3;
705 if ( notDegenerate ( v1, v3, v4 ) ) {
707 indices[ indexCount ++ ] = v1;
708 indices[ indexCount ++ ] = v3;
709 indices[ indexCount ++ ] = v4;
717 // increment only if a surface was used
724 this.setIndex( new THREE.BufferAttribute( indices, 1 ) );
725 this.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
726 this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
727 this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
729 this.computeBoundingSphere();
734 THREE.TeapotBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
735 THREE.TeapotBufferGeometry.prototype.constructor = THREE.TeapotBufferGeometry;
737 THREE.TeapotBufferGeometry.prototype.clone = function () {
739 var bufferGeometry = new THREE.TeapotBufferGeometry(
740 this.parameters.size,
741 this.parameters.segments,
742 this.parameters.bottom,
744 this.parameters.body,
745 this.parameters.fitLid,
746 this.parameters.blinn
749 return bufferGeometry;