webgl_geometry_extrude_splines.html 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>three.js webgl - geometry - spline extrusion</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  7. <style>
  8. body {
  9. font-family: Monospace;
  10. background-color: #f0f0f0;
  11. margin: 0px;
  12. overflow: hidden;
  13. }
  14. #info {
  15. position: absolute;
  16. top: 0px;
  17. width: 100%;
  18. padding: 5px;
  19. font-family:Monospace;
  20. font-size:13px;
  21. text-align:center;
  22. }
  23. </style>
  24. </head>
  25. <body>
  26. <div id="container"></div>
  27. <div id="info"><a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - geometry - spline extrusion examples by <a href="http://www.lab4games.net/zz85/blog" target="_blank" rel="noopener">zz85</a></div>
  28. <script src="../build/three.js"></script>
  29. <script src="js/controls/OrbitControls.js"></script>
  30. <!-- where curves formulas are defined -->
  31. <script src="js/CurveExtras.js"></script>
  32. <script src="js/libs/stats.min.js"></script>
  33. <script src="js/libs/dat.gui.min.js"></script>
  34. <script>
  35. var container, stats;
  36. var camera, scene, renderer, splineCamera, cameraHelper, cameraEye;
  37. var binormal = new THREE.Vector3();
  38. var normal = new THREE.Vector3();
  39. var pipeSpline = new THREE.CatmullRomCurve3( [
  40. new THREE.Vector3( 0, 10, -10 ), new THREE.Vector3( 10, 0, -10 ),
  41. new THREE.Vector3( 20, 0, 0 ), new THREE.Vector3( 30, 0, 10 ),
  42. new THREE.Vector3( 30, 0, 20 ), new THREE.Vector3( 20, 0, 30 ),
  43. new THREE.Vector3( 10, 0, 30 ), new THREE.Vector3( 0, 0, 30 ),
  44. new THREE.Vector3( -10, 10, 30 ), new THREE.Vector3( -10, 20, 30 ),
  45. new THREE.Vector3( 0, 30, 30 ), new THREE.Vector3( 10, 30, 30 ),
  46. new THREE.Vector3( 20, 30, 15 ), new THREE.Vector3( 10, 30, 10 ),
  47. new THREE.Vector3( 0, 30, 10 ), new THREE.Vector3( -10, 20, 10 ),
  48. new THREE.Vector3( -10, 10, 10 ), new THREE.Vector3( 0, 0, 10 ),
  49. new THREE.Vector3( 10, -10, 10 ), new THREE.Vector3( 20, -15, 10 ),
  50. new THREE.Vector3( 30, -15, 10 ), new THREE.Vector3( 40, -15, 10 ),
  51. new THREE.Vector3( 50, -15, 10 ), new THREE.Vector3( 60, 0, 10 ),
  52. new THREE.Vector3( 70, 0, 0 ), new THREE.Vector3( 80, 0, 0 ),
  53. new THREE.Vector3( 90, 0, 0 ), new THREE.Vector3( 100, 0, 0 )
  54. ] );
  55. var sampleClosedSpline = new THREE.CatmullRomCurve3( [
  56. new THREE.Vector3( 0, -40, -40 ),
  57. new THREE.Vector3( 0, 40, -40 ),
  58. new THREE.Vector3( 0, 140, -40 ),
  59. new THREE.Vector3( 0, 40, 40 ),
  60. new THREE.Vector3( 0, -40, 40 )
  61. ] );
  62. sampleClosedSpline.type = 'catmullrom';
  63. sampleClosedSpline.closed = true;
  64. // Keep a dictionary of Curve instances
  65. var splines = {
  66. GrannyKnot: new THREE.Curves.GrannyKnot(),
  67. HeartCurve: new THREE.Curves.HeartCurve( 3.5 ),
  68. VivianiCurve: new THREE.Curves.VivianiCurve( 70 ),
  69. KnotCurve: new THREE.Curves.KnotCurve(),
  70. HelixCurve: new THREE.Curves.HelixCurve(),
  71. TrefoilKnot: new THREE.Curves.TrefoilKnot(),
  72. TorusKnot: new THREE.Curves.TorusKnot( 20 ),
  73. CinquefoilKnot: new THREE.Curves.CinquefoilKnot( 20 ),
  74. TrefoilPolynomialKnot: new THREE.Curves.TrefoilPolynomialKnot( 14 ),
  75. FigureEightPolynomialKnot: new THREE.Curves.FigureEightPolynomialKnot(),
  76. DecoratedTorusKnot4a: new THREE.Curves.DecoratedTorusKnot4a(),
  77. DecoratedTorusKnot4b: new THREE.Curves.DecoratedTorusKnot4b(),
  78. DecoratedTorusKnot5a: new THREE.Curves.DecoratedTorusKnot5a(),
  79. DecoratedTorusKnot5c: new THREE.Curves.DecoratedTorusKnot5c(),
  80. PipeSpline: pipeSpline,
  81. SampleClosedSpline: sampleClosedSpline
  82. };
  83. var parent, tubeGeometry, group;
  84. var params = {
  85. spline: 'GrannyKnot',
  86. scale: 4,
  87. extrusionSegments: 100,
  88. radiusSegments: 3,
  89. closed: true,
  90. animationView: false,
  91. lookAhead: false,
  92. cameraHelper: false,
  93. };
  94. var material = new THREE.MeshLambertMaterial( { color: 0xff00ff } );
  95. var wireframeMaterial = new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.3, wireframe: true, transparent: true } );
  96. function addTube() {
  97. if ( group !== undefined ) {
  98. parent.remove( group );
  99. group.children[ 0 ].geometry.dispose();
  100. group.children[ 1 ].geometry.dispose();
  101. }
  102. var extrudePath = splines[ params.spline ];
  103. tubeGeometry = new THREE.TubeBufferGeometry( extrudePath, params.extrusionSegments, 2, params.radiusSegments, params.closed );
  104. addGeometry( tubeGeometry );
  105. setScale();
  106. }
  107. function setScale() {
  108. group.scale.set( params.scale, params.scale, params.scale );
  109. }
  110. function addGeometry( geometry ) {
  111. // 3D shape
  112. group = THREE.SceneUtils.createMultiMaterialObject( geometry, [ material, wireframeMaterial ] );
  113. parent.add( group );
  114. }
  115. function animateCamera() {
  116. cameraHelper.visible = params.cameraHelper;
  117. cameraEye.visible = params.cameraHelper;
  118. }
  119. init();
  120. animate();
  121. function init() {
  122. container = document.getElementById( 'container' );
  123. // camera
  124. camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 10000 );
  125. camera.position.set( 0, 50, 500 );
  126. // scene
  127. scene = new THREE.Scene();
  128. // light
  129. var light = new THREE.DirectionalLight( 0xffffff );
  130. light.position.set( 0, 0, 1 );
  131. scene.add( light );
  132. // tube
  133. parent = new THREE.Object3D();
  134. scene.add( parent );
  135. splineCamera = new THREE.PerspectiveCamera( 84, window.innerWidth / window.innerHeight, 0.01, 1000 );
  136. parent.add( splineCamera );
  137. cameraHelper = new THREE.CameraHelper( splineCamera );
  138. scene.add( cameraHelper );
  139. addTube();
  140. // debug camera
  141. cameraEye = new THREE.Mesh( new THREE.SphereGeometry( 5 ), new THREE.MeshBasicMaterial( { color: 0xdddddd } ) );
  142. parent.add( cameraEye );
  143. cameraHelper.visible = params.cameraHelper;
  144. cameraEye.visible = params.cameraHelper;
  145. // renderer
  146. renderer = new THREE.WebGLRenderer( { antialias: true } );
  147. renderer.setClearColor( 0xf0f0f0 );
  148. renderer.setPixelRatio( window.devicePixelRatio );
  149. renderer.setSize( window.innerWidth, window.innerHeight );
  150. container.appendChild( renderer.domElement );
  151. // stats
  152. stats = new Stats();
  153. container.appendChild( stats.dom );
  154. // dat.GUI
  155. var gui = new dat.GUI( { width: 300 } );
  156. var folderGeometry = gui.addFolder( 'Geometry' );
  157. folderGeometry.add( params, 'spline', Object.keys( splines ) ).onChange( function( value ) { addTube(); } );
  158. folderGeometry.add( params, 'scale', 2, 10 ).step( 2 ).onChange( function( value ) { setScale(); } );
  159. folderGeometry.add( params, 'extrusionSegments', 50, 500 ).step( 50 ).onChange( function( value ) { addTube(); } );
  160. folderGeometry.add( params, 'radiusSegments', 2, 12 ).step( 1 ).onChange( function( value ) { addTube(); } );
  161. folderGeometry.add( params, 'closed').onChange( function( value ) { addTube(); } );
  162. folderGeometry.open();
  163. var folderCamera = gui.addFolder( 'Camera' );
  164. folderCamera.add( params, 'animationView').onChange( function( value ) { animateCamera( true ); } );
  165. folderCamera.add( params, 'lookAhead').onChange( function( value ) { animateCamera(); } );
  166. folderCamera.add( params, 'cameraHelper').onChange( function( value ) { animateCamera(); } );
  167. folderCamera.open();
  168. // controls
  169. controls = new THREE.OrbitControls( camera, renderer.domElement );
  170. // event listener
  171. window.addEventListener( 'resize', onWindowResize, false );
  172. }
  173. function onWindowResize() {
  174. camera.aspect = window.innerWidth / window.innerHeight;
  175. camera.updateProjectionMatrix();
  176. renderer.setSize( window.innerWidth, window.innerHeight );
  177. }
  178. //
  179. function animate() {
  180. requestAnimationFrame( animate );
  181. render();
  182. stats.update();
  183. }
  184. function render() {
  185. // animate camera along spline
  186. var time = Date.now();
  187. var looptime = 20 * 1000;
  188. var t = ( time % looptime ) / looptime;
  189. var pos = tubeGeometry.parameters.path.getPointAt( t );
  190. pos.multiplyScalar( params.scale );
  191. // interpolation
  192. var segments = tubeGeometry.tangents.length;
  193. var pickt = t * segments;
  194. var pick = Math.floor( pickt );
  195. var pickNext = ( pick + 1 ) % segments;
  196. binormal.subVectors( tubeGeometry.binormals[ pickNext ], tubeGeometry.binormals[ pick ] );
  197. binormal.multiplyScalar( pickt - pick ).add( tubeGeometry.binormals[ pick ] );
  198. var dir = tubeGeometry.parameters.path.getTangentAt( t );
  199. var offset = 15;
  200. normal.copy( binormal ).cross( dir );
  201. // we move on a offset on its binormal
  202. pos.add( normal.clone().multiplyScalar( offset ) );
  203. splineCamera.position.copy( pos );
  204. cameraEye.position.copy( pos );
  205. // using arclength for stablization in look ahead
  206. var lookAt = tubeGeometry.parameters.path.getPointAt( ( t + 30 / tubeGeometry.parameters.path.getLength() ) % 1 ).multiplyScalar( params.scale );
  207. // camera orientation 2 - up orientation via normal
  208. if ( ! params.lookAhead ) lookAt.copy( pos ).add( dir );
  209. splineCamera.matrix.lookAt( splineCamera.position, lookAt, normal );
  210. splineCamera.rotation.setFromRotationMatrix( splineCamera.matrix, splineCamera.rotation.order );
  211. cameraHelper.update();
  212. renderer.render( scene, params.animationView === true ? splineCamera : camera );
  213. }
  214. </script>
  215. </body>
  216. </html>