webgl_geometry_extrude_splines.html 8.9 KB

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