webgl_geometry_extrude_splines.html 9.2 KB

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