webgl_geometry_extrude_splines.html 9.0 KB

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