webgl_geometry_extrude_splines.html 12.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

<!doctype html>
<html lang="en">
  <head>
    <title>three.js webgl - geometry - shapes</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
      body {
        font-family: Monospace;
        background-color: #f0f0f0;
        margin: 0px;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
18
  
19 20 21
    <script src="../build/Three.js"></script>
    <script src="../src/extras/core/Curve.js"></script>
    <script src="../src/extras/geometries/TubeGeometry.js"></script>
22
    <script src="../src/extras/helpers/CameraHelper.js"></script>
23 24 25

    <!-- where curves formulas are defined -->
    <script src="js/CurveExtras.js"></script>
26

27 28 29 30 31 32
    <script src="js/Stats.js"></script>


    <script>
    var container, stats;

33
    var camera, scene, renderer, splineCamera, cameraHelper, cameraEye;
34 35 36 37 38 39 40 41 42 43 44 45

    var text, plane;

    var targetRotation = 0;
    var targetRotationOnMouseDown = 0;

    var mouseX = 0;
    var mouseXOnMouseDown = 0;

    var windowHalfX = window.innerWidth / 2;
    var windowHalfY = window.innerHeight / 2;

46 47 48 49
    var binormal = new THREE.Vector3();
    var normal = new THREE.Vector3();


50
    var pipeSpline = new THREE.SplineCurve3([
51 52 53 54 55 56 57 58 59 60
        new THREE.Vector3(0, 10, -10), new THREE.Vector3(10, 0, -10), new THREE.Vector3(20, 0, 0), new THREE.Vector3(30, 0, 10), new THREE.Vector3(30, 0, 20), new THREE.Vector3(20, 0, 30), new THREE.Vector3(10, 0, 30), new THREE.Vector3(0, 0, 30), new THREE.Vector3(-10, 10, 30), new THREE.Vector3(-10, 20, 30), new THREE.Vector3(0, 30, 30), new THREE.Vector3(10, 30, 30), new THREE.Vector3(20, 30, 15), new THREE.Vector3(10, 30, 10), new THREE.Vector3(0, 30, 10), new THREE.Vector3(-10, 20, 10), new THREE.Vector3(-10, 10, 10), new THREE.Vector3(0, 0, 10), new THREE.Vector3(10, -10, 10), new THREE.Vector3(20, -15, 10), new THREE.Vector3(30, -15, 10), new THREE.Vector3(40, -15, 10), new THREE.Vector3(50, -15, 10), new THREE.Vector3(60, 0, 10), new THREE.Vector3(70, 0, 0), new THREE.Vector3(80, 0, 0), new THREE.Vector3(90, 0, 0), new THREE.Vector3(100, 0, 0)]);

    var sampleClosedSpline = new THREE.ClosedSplineCurve3([
      new THREE.Vector3(0, -40, -40),
      new THREE.Vector3(0, 40, -40),
      new THREE.Vector3(0, 140, -40),
      new THREE.Vector3(0, 40, 40),
      new THREE.Vector3(0, -40, 40),
    ]);

61
    // Keep a diction of Curve instances
62
    var splines = {
Z
zz85 已提交
63
      GrannyKnot: new THREE.Curves.GrannyKnot(),
64 65 66 67 68 69 70 71 72 73 74 75
      HeartCurve: new THREE.Curves.HeartCurve(3.5),
      VivianiCurve: new THREE.Curves.VivianiCurve(70),
      KnotCurve: new THREE.Curves.KnotCurve(),
      HelixCurve: new THREE.Curves.HelixCurve(),
      TrefoilKnot: new THREE.Curves.TrefoilKnot(),
      TorusKnot: new THREE.Curves.TorusKnot(20),
      CinquefoilKnot: new THREE.Curves.CinquefoilKnot(20),
      TrefoilPolynomialKnot: new THREE.Curves.TrefoilPolynomialKnot(14),
      FigureEightPolynomialKnot: new THREE.Curves.FigureEightPolynomialKnot(),
      DecoratedTorusKnot4a: new THREE.Curves.DecoratedTorusKnot4a(),
      DecoratedTorusKnot4b: new THREE.Curves.DecoratedTorusKnot4b(),
      DecoratedTorusKnot5a: new THREE.Curves.DecoratedTorusKnot5a(),
76 77 78
      DecoratedTorusKnot5c: new THREE.Curves.DecoratedTorusKnot5c(),
      PipeSpline: pipeSpline,
      SampleClosedSpline: sampleClosedSpline
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    };




    extrudePath = new THREE.Curves.TrefoilKnot();

    var dropdown = '<select id="dropdown" onchange="addTube(this.value)">';

    var s;
    for ( s in splines ) {
      dropdown += '<option value="' + s + '"';
      dropdown += '>' + s + '</option>';
    }

    dropdown += '</select>';

96
    var closed2 = true;
97 98
    var debug = true;
    var parent;
99
    var tube, tubeMesh;
100
    var animation = false, lookAhead = false;
101
    var scale;
Z
zz85 已提交
102
    var showCameraHelper = false;
103 104 105 106

    function addTube() {

      var value = document.getElementById('dropdown').value;
107
      
108
      var segments = parseInt(document.getElementById('segments').value);
109
      closed2 = document.getElementById('closed').checked;
110
      debug = document.getElementById('debug').checked;
111

112 113 114
      var radiusSegments = parseInt(document.getElementById('radiusSegments').value);

      console.log('adding tube', value, closed2, debug, radiusSegments);
115 116 117 118
      if (tubeMesh) parent.remove(tubeMesh);

      extrudePath = splines[value];
      
119 120 121 122 123 124 125 126 127 128 129
      tube = new THREE.TubeGeometry(extrudePath, segments, 2, radiusSegments, closed2, debug);

      addGeometry(tube, 0xff00ff);
      setScale();
    
    }

    function setScale() {

      scale = parseInt(document.getElementById('scale').value);
      tubeMesh.scale.set(scale, scale, scale);
130 131 132

    }

133 134

    function addGeometry(geometry, color) {
135 136 137 138 139

        // 3d shape
        tubeMesh = THREE.SceneUtils.createMultiMaterialObject(geometry, [
          new THREE.MeshLambertMaterial({
              color: color,
Z
zz85 已提交
140
              opacity: (geometry.debug) ? 0.2 : 0.8,
141
              transparent: true
142 143 144
          }),
         new THREE.MeshBasicMaterial({
            color: 0x000000,
Z
zz85 已提交
145
            opacity: 0.5,
146
            wireframe: true
147 148 149 150 151 152 153
        })]);

        if (geometry.debug) tubeMesh.add(geometry.debug);

        //mesh.children[0].doubleSided = true;
        parent.add(tubeMesh);

154 155
    }

156 157 158 159 160 161 162
    function animateCamera(toggle) {

      if (toggle) {
        animation = !animation;
        document.getElementById('animation').value = 'Camera Spline Animation View: ' + (animation? 'ON': 'OFF');
      }
      
163
      lookAhead = document.getElementById('lookAhead').checked;
164 165 166 167 168

      showCameraHelper = document.getElementById('cameraHelper').checked;

      cameraHelper.children[0].visible = showCameraHelper;
      cameraEye.visible = showCameraHelper;
169
    }
170 171


172 173 174 175 176 177 178 179 180 181 182 183 184
    init();
    animate();

    function init() {

      container = document.createElement('div');
      document.body.appendChild(container);

      var info = document.createElement('div');
      info.style.position = 'absolute';
      info.style.top = '10px';
      info.style.width = '100%';
      info.style.textAlign = 'center';
185 186 187 188
      info.innerHTML = 'Spline Extrusion Examples by <a href="http://www.lab4games.net/zz85/blog">zz85</a><br/>Select spline:';

      info.innerHTML += dropdown;

189
      info.innerHTML += '<br/>Scale: <select id="scale" onchange="setScale()"><option>1</option><option>2</option><option selected>4</option><option>6</option><option>10</option></select>';
190
      info.innerHTML += '<br/>Extrusion Segments: <select onchange="addTube()" id="segments"><option>50</option><option selected>100</option><option>200</option><option>400</option></select>';
191
      info.innerHTML += '<br/>Radius Segments: <select id="radiusSegments" onchange="addTube()"><option>1</option><option>2</option><option selected>3</option><option>4</option><option>5</option><option>6</option><option>8</option><option>12</option></select>';
Z
zz85 已提交
192
      info.innerHTML += '<br/>Debug normals: <input id="debug" type="checkbox" onchange="addTube()"  /> Closed:<input id="closed" onchange="addTube()" type="checkbox" checked />';
193

Z
zz85 已提交
194
      info.innerHTML += '<br/><br/><input id="animation" type="button" onclick="animateCamera(true)" value="Camera Spline Animation View: OFF"/><br/> Look Ahead <input id="lookAhead" type="checkbox" onchange="animateCamera()" /> Camera Helper <input id="cameraHelper" type="checkbox" onchange="animateCamera()" />';
195

196
      container.appendChild(info);
A
alteredq 已提交
197

198
      scene = new THREE.Scene();
A
alteredq 已提交
199

200 201 202 203 204
      // 
      camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 1000);
      splineCamera = new THREE.PerspectiveCamera(84, window.innerWidth / window.innerHeight, 0.01, 1000);
      cameraHelper = new THREE.CameraHelper(splineCamera);

205
      camera.position.set(0, 50, 500);
206
      
207
      scene.add(camera);
A
alteredq 已提交
208

209
      
210 211 212
      var light = new THREE.DirectionalLight(0xffffff);
      light.position.set(0, 0, 1);
      scene.add(light);
A
alteredq 已提交
213

214
      parent = new THREE.Object3D();
A
alteredq 已提交
215
      parent.position.y = 100;
216
      scene.add(parent);
A
alteredq 已提交
217

218
      addTube();
A
alteredq 已提交
219

220
      // Debug point
221 222 223
      cameraEye = new THREE.Mesh(new THREE.SphereGeometry(5), new THREE.MeshBasicMaterial({
          color: 0xdddddd
      }));
224

225 226 227 228
      cameraHelper.children[0].visible = showCameraHelper;
      cameraEye.visible = showCameraHelper;

      parent.add(cameraEye);
229 230 231 232 233

      cameraHelper.scale.multiplyScalar(0.1);
      splineCamera.add(cameraHelper);
      parent.add(splineCamera);

234 235 236 237 238
      //
      renderer = new THREE.WebGLRenderer({
          antialias: true
      });
      renderer.setSize(window.innerWidth, window.innerHeight);
A
alteredq 已提交
239

240
      container.appendChild(renderer.domElement);
A
alteredq 已提交
241

242 243 244 245
      stats = new Stats();
      stats.domElement.style.position = 'absolute';
      stats.domElement.style.top = '0px';
      container.appendChild(stats.domElement);
A
alteredq 已提交
246

247 248 249
      renderer.domElement.addEventListener('mousedown', onDocumentMouseDown, false);
      renderer.domElement.addEventListener('touchstart', onDocumentTouchStart, false);
      renderer.domElement.addEventListener('touchmove', onDocumentTouchMove, false);
250 251 252 253 254 255 256 257 258

    }

    //

    function onDocumentMouseDown(event) {

      event.preventDefault();

259 260 261
      renderer.domElement.addEventListener('mousemove', onDocumentMouseMove, false);
      renderer.domElement.addEventListener('mouseup', onDocumentMouseUp, false);
      renderer.domElement.addEventListener('mouseout', onDocumentMouseOut, false);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277

      mouseXOnMouseDown = event.clientX - windowHalfX;
      targetRotationOnMouseDown = targetRotation;

    }

    function onDocumentMouseMove(event) {

      mouseX = event.clientX - windowHalfX;

      targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.02;

    }

    function onDocumentMouseUp(event) {

278 279 280
      renderer.domElement.removeEventListener('mousemove', onDocumentMouseMove, false);
      renderer.domElement.removeEventListener('mouseup', onDocumentMouseUp, false);
      renderer.domElement.removeEventListener('mouseout', onDocumentMouseOut, false);
281 282 283 284 285

    }

    function onDocumentMouseOut(event) {

286 287 288
      renderer.domElement.removeEventListener('mousemove', onDocumentMouseMove, false);
      renderer.domElement.removeEventListener('mouseup', onDocumentMouseUp, false);
      renderer.domElement.removeEventListener('mouseout', onDocumentMouseOut, false);
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

    }

    function onDocumentTouchStart(event) {

      if (event.touches.length == 1) {

          event.preventDefault();

          mouseXOnMouseDown = event.touches[0].pageX - windowHalfX;
          targetRotationOnMouseDown = targetRotation;

      }

    }

    function onDocumentTouchMove(event) {

      if (event.touches.length == 1) {

          event.preventDefault();

          mouseX = event.touches[0].pageX - windowHalfX;
          targetRotation = targetRotationOnMouseDown + (mouseX - mouseXOnMouseDown) * 0.05;

      }

    }

    //

    function animate() {

      requestAnimationFrame(animate);

      render();
      stats.update();

    }

    function render() {

331 332 333 334
      // Try Animate Camera Along Spline
      var time = Date.now();
      var looptime = 20 * 1000;
      var t = (time % looptime) / looptime;
335

336 337
      var pos = tube.path.getPointAt(t);
      pos.multiplyScalar(scale);
338

339 340 341 342 343
      // interpolation
      var segments = tube.tangents.length;
      var pickt = t * segments;
      var pick = Math.floor(pickt);
      var pickNext = (pick + 1) % segments;
344

345 346
      binormal.sub(tube.binormals[pickNext], tube.binormals[pick]);
      binormal.multiplyScalar(pickt - pick).addSelf(tube.binormals[pick]);
347 348


349 350
      var dir = tube.path.getTangentAt(t);

351 352 353
      var offset = 15;

      normal.copy(binormal).crossSelf(dir);
354 355

      // We move on a offset on its binormal
356 357
      pos.addSelf(normal.clone().multiplyScalar(offset));

358
      splineCamera.position = pos;
359
      cameraEye.position = pos;
360 361


362 363
      // Camera Orientation 1 - default look at
      // splineCamera.lookAt(lookAt);
364

Z
zz85 已提交
365 366 367
      // Using arclength for stablization in look ahead.
      var lookAt = tube.path.getPointAt((t + 30/tube.path.getLength()) % 1).multiplyScalar(scale);
      
368
      // Camera Orientation 2 - up orientation via normal
369
      if (!lookAhead)
370 371
      lookAt.copy(pos).addSelf(dir);
      splineCamera.matrix.lookAt(splineCamera.position, lookAt, normal);
W
WestLangley 已提交
372
      splineCamera.rotation.setEulerFromRotationMatrix(splineCamera.matrix, splineCamera.eulerOrder);
373

374 375
      cameraHelper.update();

376 377
      parent.rotation.y += (targetRotation - parent.rotation.y) * 0.05;

378 379 380 381 382 383 384 385 386 387 388 389
      if (animation) {

        renderer.render(scene, splineCamera);

      } else {

        renderer.render(scene, camera);

      }

      
      
A
alteredq 已提交
390 391
    }
	</script>
392 393 394

  </body>
</html>