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.curveType = '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, mesh;
  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 ( mesh !== undefined ) {
  98. parent.remove( mesh );
  99. mesh.geometry.dispose();
  100. }
  101. var extrudePath = splines[ params.spline ];
  102. tubeGeometry = new THREE.TubeBufferGeometry( extrudePath, params.extrusionSegments, 2, params.radiusSegments, params.closed );
  103. addGeometry( tubeGeometry );
  104. setScale();
  105. }
  106. function setScale() {
  107. mesh.scale.set( params.scale, params.scale, params.scale );
  108. }
  109. function addGeometry( geometry ) {
  110. // 3D shape
  111. mesh = new THREE.Mesh( geometry, material );
  112. var wireframe = new THREE.Mesh( geometry, wireframeMaterial );
  113. mesh.add( wireframe );
  114. parent.add( mesh );
  115. }
  116. function animateCamera() {
  117. cameraHelper.visible = params.cameraHelper;
  118. cameraEye.visible = params.cameraHelper;
  119. }
  120. init();
  121. animate();
  122. function init() {
  123. container = document.getElementById( 'container' );
  124. // camera
  125. camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.01, 10000 );
  126. camera.position.set( 0, 50, 500 );
  127. // scene
  128. scene = new THREE.Scene();
  129. scene.background = new THREE.Color( 0xf0f0f0 );
  130. // light
  131. var light = new THREE.DirectionalLight( 0xffffff );
  132. light.position.set( 0, 0, 1 );
  133. scene.add( light );
  134. // tube
  135. parent = new THREE.Object3D();
  136. scene.add( parent );
  137. splineCamera = new THREE.PerspectiveCamera( 84, window.innerWidth / window.innerHeight, 0.01, 1000 );
  138. parent.add( splineCamera );
  139. cameraHelper = new THREE.CameraHelper( splineCamera );
  140. scene.add( cameraHelper );
  141. addTube();
  142. // debug camera
  143. cameraEye = new THREE.Mesh( new THREE.SphereBufferGeometry( 5 ), new THREE.MeshBasicMaterial( { color: 0xdddddd } ) );
  144. parent.add( cameraEye );
  145. cameraHelper.visible = params.cameraHelper;
  146. cameraEye.visible = params.cameraHelper;
  147. // renderer
  148. renderer = new THREE.WebGLRenderer( { antialias: true } );
  149. renderer.setPixelRatio( window.devicePixelRatio );
  150. renderer.setSize( window.innerWidth, window.innerHeight );
  151. container.appendChild( renderer.domElement );
  152. // stats
  153. stats = new Stats();
  154. container.appendChild( stats.dom );
  155. // dat.GUI
  156. var gui = new dat.GUI( { width: 300 } );
  157. var folderGeometry = gui.addFolder( 'Geometry' );
  158. folderGeometry.add( params, 'spline', Object.keys( splines ) ).onChange( function( value ) { addTube(); } );
  159. folderGeometry.add( params, 'scale', 2, 10 ).step( 2 ).onChange( function( value ) { setScale(); } );
  160. folderGeometry.add( params, 'extrusionSegments', 50, 500 ).step( 50 ).onChange( function( value ) { addTube(); } );
  161. folderGeometry.add( params, 'radiusSegments', 2, 12 ).step( 1 ).onChange( function( value ) { addTube(); } );
  162. folderGeometry.add( params, 'closed').onChange( function( value ) { addTube(); } );
  163. folderGeometry.open();
  164. var folderCamera = gui.addFolder( 'Camera' );
  165. folderCamera.add( params, 'animationView').onChange( function( value ) { animateCamera(); } );
  166. folderCamera.add( params, 'lookAhead').onChange( function( value ) { animateCamera(); } );
  167. folderCamera.add( params, 'cameraHelper').onChange( function( value ) { animateCamera(); } );
  168. folderCamera.open();
  169. // controls
  170. var controls = new THREE.OrbitControls( camera, renderer.domElement );
  171. // event listener
  172. window.addEventListener( 'resize', onWindowResize, false );
  173. }
  174. function onWindowResize() {
  175. camera.aspect = window.innerWidth / window.innerHeight;
  176. camera.updateProjectionMatrix();
  177. renderer.setSize( window.innerWidth, window.innerHeight );
  178. }
  179. //
  180. function animate() {
  181. requestAnimationFrame( animate );
  182. render();
  183. stats.update();
  184. }
  185. function render() {
  186. // animate camera along spline
  187. var time = Date.now();
  188. var looptime = 20 * 1000;
  189. var t = ( time % looptime ) / looptime;
  190. var pos = tubeGeometry.parameters.path.getPointAt( t );
  191. pos.multiplyScalar( params.scale );
  192. // interpolation
  193. var segments = tubeGeometry.tangents.length;
  194. var pickt = t * segments;
  195. var pick = Math.floor( pickt );
  196. var pickNext = ( pick + 1 ) % segments;
  197. binormal.subVectors( tubeGeometry.binormals[ pickNext ], tubeGeometry.binormals[ pick ] );
  198. binormal.multiplyScalar( pickt - pick ).add( tubeGeometry.binormals[ pick ] );
  199. var dir = tubeGeometry.parameters.path.getTangentAt( t );
  200. var offset = 15;
  201. normal.copy( binormal ).cross( dir );
  202. // we move on a offset on its binormal
  203. pos.add( normal.clone().multiplyScalar( offset ) );
  204. splineCamera.position.copy( pos );
  205. cameraEye.position.copy( pos );
  206. // using arclength for stablization in look ahead
  207. var lookAt = tubeGeometry.parameters.path.getPointAt( ( t + 30 / tubeGeometry.parameters.path.getLength() ) % 1 ).multiplyScalar( params.scale );
  208. // camera orientation 2 - up orientation via normal
  209. if ( ! params.lookAhead ) lookAt.copy( pos ).add( dir );
  210. splineCamera.matrix.lookAt( splineCamera.position, lookAt, normal );
  211. splineCamera.rotation.setFromRotationMatrix( splineCamera.matrix, splineCamera.rotation.order );
  212. cameraHelper.update();
  213. renderer.render( scene, params.animationView === true ? splineCamera : camera );
  214. }
  215. </script>
  216. </body>
  217. </html>