webgl_geometry_extrude_splines.html 9.0 KB

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