Browse Source

make primitives article use threejsLessonUtils

Gregg Tavares 7 years ago
parent
commit
f9031f02fc

+ 41 - 1
threejs/lessons/resources/lesson.css

@@ -24,6 +24,37 @@ pre.prettyprint li {
     white-space: pre;
     white-space: pre;
 }
 }
 
 
+
+div[data-diagram] {
+  height: 100%;
+}
+.spread {
+  display: flex;
+  text-align: center;
+  margin: 2em auto 3em;
+}
+.spread div[data-diagram] {
+    height: 150px;
+}
+.spread>div {
+  flex: 1 1 auto;
+}
+.spread .code {
+  font-family: monospace;
+}
+.spread .code>div {
+  text-align: left;
+}
+#c {
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: -100;
+}
+
+
 .threejs_navbar>div,
 .threejs_navbar>div,
 .lesson-title,
 .lesson-title,
 .lesson-comments,
 .lesson-comments,
@@ -94,7 +125,16 @@ pre.prettyprint li {
 .home-lang select {
 .home-lang select {
     font-size: large;
     font-size: large;
 }
 }
-
+.checkerboard {
+  background-color: #404040;
+  background-image: 
+     linear-gradient(45deg, #808080 25%, transparent 25%), 
+     linear-gradient(-45deg, #808080 25%, transparent 25%), 
+     linear-gradient(45deg, transparent 75%, #808080 75%), 
+     linear-gradient(-45deg, transparent 75%, #808080 75%);
+  background-size: 20px 20px;
+  background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
+}
 .fullscreen {
 .fullscreen {
     position: fixed !important;
     position: fixed !important;
     left: 0;
     left: 0;

+ 168 - 0
threejs/lessons/resources/threejs-lesson-utils.js

@@ -0,0 +1,168 @@
+'use strict';
+
+window.threejsLessonUtils = {
+  init() {
+    if (this.renderer) {
+      return;
+    }
+    const canvas = document.querySelector('#c');
+    const renderer = new THREE.WebGLRenderer({canvas: canvas, alpha: true});
+    this.pixelRatio = Math.max(2, window.devicePixelRatio);
+
+    this.renderer = renderer;
+    this.renderFuncs = [];
+
+    const resizeRendererToDisplaySize = (renderer) => {
+      const canvas = renderer.domElement;
+      const width = canvas.clientWidth * this.pixelRatio;
+      const height = canvas.clientHeight * this.pixelRatio;
+      const needResize = canvas.width !== width || canvas.height !== height;
+      if (needResize) {
+        renderer.setSize(width, height, false);
+      }
+      return needResize;
+    };
+
+    // Three r93 needs to render at least once for some reason.
+    const scene = new THREE.Scene();
+    const camera = new THREE.Camera();
+
+    const render = (time) => {
+      time *= 0.001;
+
+      resizeRendererToDisplaySize(renderer);
+
+      renderer.setScissorTest(false);
+
+      // Three r93 needs to render at least once for some reason.
+      renderer.render(scene, camera);
+
+      renderer.setScissorTest(true);
+
+      // maybe there is another way. Originally I used `position: fixed`
+      // but the problem is if we can't render as fast as the browser
+      // scrolls then our shapes lag. 1 or 2 frames of lag isn't too
+      // horrible but iOS would often been 1/2 a second or worse.
+      // By doing it this way the canvas will scroll which means the
+      // worse that happens is part of the shapes scrolling on don't
+      // get drawn for a few frames but the shapes that are on the screen
+      // scroll perfectly.
+      //
+      // I'm using `transform` on the voodoo that it doesn't affect
+      // layout as much as `top` since AFAIK setting `top` is in
+      // the flow but `transform` is not though thinking about it
+      // the given we're `position: absolute` maybe there's no difference?
+      const transform = `translateY(${window.scrollY}px)`;
+      renderer.domElement.style.transform = transform;
+
+      this.renderFuncs.forEach((fn) => {
+          fn(renderer, time);
+      });
+
+      requestAnimationFrame(render);
+    };
+
+    requestAnimationFrame(render);
+  },
+  addDiagrams(diagrams) {
+    [...document.querySelectorAll('[data-diagram]')].forEach((elem) => {
+      const name = elem.dataset.diagram;
+      const info = diagrams[name];
+      if (!info) {
+        throw new Error(`no diagram: ${name}`);
+      }
+      this.addDiagram(elem, info);
+    });
+  },
+  addDiagram(elem, info) {
+    this.init();
+
+    const scene = new THREE.Scene();
+    const fov = 60;
+    const aspect = 1;
+    const zNear = 0.1;
+    const zFar = 50;
+    const camera = new THREE.PerspectiveCamera(fov, aspect, zNear, zFar);
+    camera.position.z = 15;
+    scene.add(camera);
+
+    const obj3D = info.create({scene, camera});
+    const promise = (obj3D instanceof Promise) ? obj3D : Promise.resolve(obj3D);
+
+    const root = new THREE.Object3D();
+    scene.add(root);
+
+    const controls = new THREE.TrackballControls(camera, elem);
+    controls.noZoom = true;
+    controls.noPan = true;
+
+    // add the lights as children of the camera.
+    // this is because TrackbacllControls move the camera.
+    // We really want to rotate the object itself but there's no
+    // controls for that so we fake it by putting all the lights
+    // on the camera so they move with it.
+    camera.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, .5));
+    const light = new THREE.DirectionalLight(0xffffff, 1);
+    light.position.set(-1, 2, 4 - 15);
+    camera.add(light);
+
+    let updateFunction;
+
+    promise.then((result) => {
+      const info = result instanceof THREE.Object3D ? {
+        obj3D: result,
+      } : result;
+      const { obj3D, update } = info;
+      root.add(obj3D);
+      updateFunction = update;
+    });
+
+    let oldWidth = -1;
+    let oldHeight = -1;
+
+    const render = (renderer, time) => {
+      root.rotation.x = time * .1;
+      root.rotation.y = time * .11;
+
+      const rect = elem.getBoundingClientRect();
+      if (rect.bottom < 0 || rect.top  > renderer.domElement.clientHeight ||
+          rect.right  < 0 || rect.left > renderer.domElement.clientWidth) {
+        return;
+      }
+
+      const width  = (rect.right - rect.left) * this.pixelRatio;
+      const height = (rect.bottom - rect.top) * this.pixelRatio;
+      const left   = rect.left * this.pixelRatio;
+      const top    = rect.top * this.pixelRatio;
+
+      if (width !== oldWidth || height !== oldHeight) {
+        oldWidth = width;
+        oldHeight = height;
+        controls.handleResize();
+      }
+      controls.update();
+
+      if (updateFunction) {
+        updateFunction(time);
+      }
+
+      const aspect = width / height;
+      const targetFov = THREE.Math.degToRad(60);
+      const fov = aspect >= 1
+        ? targetFov
+        : (2 * Math.atan(Math.tan(targetFov * .5) / aspect));
+
+      camera.fov = THREE.Math.radToDeg(fov);
+      camera.aspect = aspect;
+      camera.updateProjectionMatrix();
+
+      renderer.setViewport(left, top, width, height);
+      renderer.setScissor(left, top, width, height);
+      renderer.render(scene, camera);
+    };
+
+    this.renderFuncs.push(render);
+  },
+};
+
+

+ 16 - 144
threejs/lessons/resources/threejs-primitives.js

@@ -1,11 +1,9 @@
 'use strict';
 'use strict';
 
 
-function main() {
+/* global threejsLessonUtils */
 
 
-  // even on low-res we want hi-res rendering so the lines are small
-  const pixelRatio = 2;
-
-  const primitives = {
+{
+  const diagrams = {
     BoxBufferGeometry: {
     BoxBufferGeometry: {
       create() {
       create() {
         const width = 8;
         const width = 8;
@@ -368,9 +366,6 @@ function main() {
     },
     },
   };
   };
 
 
-  const canvas = document.querySelector('#c');
-  const renderer = new THREE.WebGLRenderer({canvas: canvas, alpha: true});
-
   function addLink(parent, name) {
   function addLink(parent, name) {
     const a = document.createElement('a');
     const a = document.createElement('a');
     a.href = `https://threejs.org/docs/#api/geometries/${name}`;
     a.href = `https://threejs.org/docs/#api/geometries/${name}`;
@@ -395,14 +390,12 @@ function main() {
     return addElem(parent, 'div', className);
     return addElem(parent, 'div', className);
   }
   }
 
 
-  const primRenderFuncs = [
-    ...[...document.querySelectorAll('[data-primitive]')].map(createPrimitiveDOM),
-    ...[...document.querySelectorAll('[data-primitive-diagram]')].map(createPrimitiveDiagram),
-  ];
+  [...document.querySelectorAll('[data-diagram]')].forEach(createDiagram);
+  [...document.querySelectorAll('[data-primitive]')].forEach(createPrimitiveDOM);
 
 
   function createPrimitiveDOM(base) {
   function createPrimitiveDOM(base) {
     const name = base.dataset.primitive;
     const name = base.dataset.primitive;
-    const info = primitives[name];
+    const info = diagrams[name];
     if (!info) {
     if (!info) {
       throw new Error(`no primitive ${name}`);
       throw new Error(`no primitive ${name}`);
     }
     }
@@ -420,48 +413,21 @@ function main() {
     }
     }
     addDiv(right, '.note').innerHTML = text;
     addDiv(right, '.note').innerHTML = text;
 
 
-    return createPrimitive(elem, info);
+    return createLiveImage(elem, info);
   }
   }
 
 
-  function createPrimitiveDiagram(base) {
-    const name = base.dataset.primitiveDiagram;
-    const info = primitives[name];
+  function createDiagram(base) {
+    const name = base.dataset.diagram;
+    const info = diagrams[name];
     if (!info) {
     if (!info) {
       throw new Error(`no primitive ${name}`);
       throw new Error(`no primitive ${name}`);
     }
     }
-    return createPrimitive(base, info);
+    return createLiveImage(base, info);
   }
   }
 
 
-  function createPrimitive(elem, info) {
+  function createLiveImage(elem, info) {
     const geometry = info.create();
     const geometry = info.create();
     const promise = (geometry instanceof Promise) ? geometry : Promise.resolve(geometry);
     const promise = (geometry instanceof Promise) ? geometry : Promise.resolve(geometry);
-    const scene = new THREE.Scene();
-
-    const root = new THREE.Object3D();
-    scene.add(root);
-
-    const fov = 60;
-    const aspect = 1;
-    const zNear = 0.1;
-    const zFar = 50;
-    const camera = new THREE.PerspectiveCamera(fov, aspect, zNear, zFar);
-    camera.position.z = 15;
-
-    const controls = new THREE.TrackballControls(camera, elem);
-    controls.noZoom = true;
-    controls.noPan = true;
-
-    // add the lights as children of the camera.
-    // this is because TrackbacllControls move the camera.
-    // We really want to rotate the object itself but there's no
-    // controls for that so we fake it by putting all the lights
-    // on the camera so they move with it.
-    camera.add(new THREE.HemisphereLight(0xaaaaaa, 0x444444, .5));
-    const light = new THREE.DirectionalLight(0xffffff, 1);
-    light.position.set(-1, 2, 4 - 15);
-    camera.add(light);
-    scene.add(camera);
-
     promise.then((geometryInfo) => {
     promise.then((geometryInfo) => {
       if (geometryInfo instanceof THREE.BufferGeometry ||
       if (geometryInfo instanceof THREE.BufferGeometry ||
           geometryInfo instanceof THREE.Geometry) {
           geometryInfo instanceof THREE.Geometry) {
@@ -471,6 +437,8 @@ function main() {
         };
         };
       }
       }
 
 
+      const root = new THREE.Object3D();
+
       const boxGeometry = geometryInfo.geometry || geometryInfo.lineGeometry;
       const boxGeometry = geometryInfo.geometry || geometryInfo.lineGeometry;
       boxGeometry.computeBoundingBox();
       boxGeometry.computeBoundingBox();
       const centerOffset = new THREE.Vector3();
       const centerOffset = new THREE.Vector3();
@@ -497,104 +465,8 @@ function main() {
         lineMesh.position.copy(centerOffset);
         lineMesh.position.copy(centerOffset);
         root.add(lineMesh);
         root.add(lineMesh);
       }
       }
-    });
-
-    let oldWidth = -1;
-    let oldHeight = -1;
-
-    function render(renderer, time) {
-      root.rotation.x = time * .1;
-      root.rotation.y = time * .11;
-
-      const rect = elem.getBoundingClientRect();
-      if (rect.bottom < 0 || rect.top  > renderer.domElement.clientHeight ||
-          rect.right  < 0 || rect.left > renderer.domElement.clientWidth) {
-        return;
-      }
 
 
-      const width  = (rect.right - rect.left) * pixelRatio;
-      const height = (rect.bottom - rect.top) * pixelRatio;
-      const left   = rect.left * pixelRatio;
-      const top    = rect.top * pixelRatio;
-
-      if (width !== oldWidth || height !== oldHeight) {
-        oldWidth = width;
-        oldHeight = height;
-        controls.handleResize();
-      }
-      controls.update();
-
-      const aspect = width / height;
-      const targetFov = THREE.Math.degToRad(60);
-      const fov = aspect >= 1
-        ? targetFov
-        : (2 * Math.atan(Math.tan(targetFov * .5) / aspect));
-
-      camera.fov = THREE.Math.radToDeg(fov);
-      camera.aspect = aspect;
-      camera.updateProjectionMatrix();
-
-      renderer.setViewport(left, top, width, height);
-      renderer.setScissor(left, top, width, height);
-      renderer.render(scene, camera);
-    }
-
-    return render;
-  }
-
-  function resizeRendererToDisplaySize(renderer) {
-    const canvas = renderer.domElement;
-    const width = canvas.clientWidth * pixelRatio;
-    const height = canvas.clientHeight * pixelRatio;
-    const needResize = canvas.width !== width || canvas.height !== height;
-    if (needResize) {
-      renderer.setSize(width, height, false);
-    }
-    return needResize;
-  }
-
-  // Three r93 needs to render at least once for some reason.
-  const scene = new THREE.Scene();
-  const camera = new THREE.Camera();
-
-  function render(time) {
-    time *= 0.001;
-
-    resizeRendererToDisplaySize(renderer);
-
-    renderer.setScissorTest(false);
-
-    // Three r93 needs to render at least once for some reason.
-    renderer.render(scene, camera);
-
-    renderer.setScissorTest(true);
-
-    // maybe there is another way. Originally I used `position: fixed`
-    // but the problem is if we can't render as fast as the browser
-    // scrolls then our shapes lag. 1 or 2 frames of lag isn't too
-    // horrible but iOS would often been 1/2 a second or worse.
-    // By doing it this way the canvas will scroll which means the
-    // worse that happens is part of the shapes scrolling on don't
-    // get drawn for a few frames but the shapes that are on the screen
-    // scroll perfectly.
-    //
-    // I'm using `transform` on the voodoo that it doesn't affect
-    // layout as much as `top` since AFAIK setting `top` is in
-    // the flow but `transform` is not though thinking about it
-    // the given we're `position: absolute` maybe there's no difference?
-    const transform = `translateY(${window.scrollY}px)`;
-    renderer.domElement.style.transform = transform;
-
-    primRenderFuncs.forEach((fn) => {
-        fn(renderer, time);
+      threejsLessonUtils.addDiagram(elem, {create: () => root});
     });
     });
-
-    requestAnimationFrame(render);
   }
   }
-
-  requestAnimationFrame(render);
-}
-
-main();
-
-
+}

+ 10 - 24
threejs/lessons/threejs-primitives.md

@@ -1,7 +1,7 @@
 Title: Three.js Primitives
 Title: Three.js Primitives
 Description: A tour of three.js primitives.
 Description: A tour of three.js primitives.
 
 
-This article one in a series of articles about three.js.
+This article is one in a series of articles about three.js.
 The first article was [about fundamentals](threejs-fundamentals.html).
 The first article was [about fundamentals](threejs-fundamentals.html).
 If you haven't read that yet you might want to start there.
 If you haven't read that yet you might want to start there.
 
 
@@ -289,9 +289,9 @@ might be the sphere geometries. Spheres take parameters for
 how many divisions to make around and how many top to bottom. For example
 how many divisions to make around and how many top to bottom. For example
 
 
 <div class="spread">
 <div class="spread">
-<div data-primitive-diagram="SphereBufferGeometryLow"></div>
-<div data-primitive-diagram="SphereBufferGeometryMedium"></div>
-<div data-primitive-diagram="SphereBufferGeometryHigh"></div>
+<div data-diagram="SphereBufferGeometryLow"></div>
+<div data-diagram="SphereBufferGeometryMedium"></div>
+<div data-diagram="SphereBufferGeometryHigh"></div>
 </div>
 </div>
 
 
 The first sphere has 5 segments around and 3 high which is 15 segments
 The first sphere has 5 segments around and 3 high which is 15 segments
@@ -303,9 +303,9 @@ look like you need a high number of segments but remove the lines
 and the flat shading and we get this
 and the flat shading and we get this
 
 
 <div class="spread">
 <div class="spread">
-<div data-primitive-diagram="SphereBufferGeometryLowSmooth"></div>
-<div data-primitive-diagram="SphereBufferGeometryMediumSmooth"></div>
-<div data-primitive-diagram="SphereBufferGeometryHighSmooth"></div>
+<div data-diagram="SphereBufferGeometryLowSmooth"></div>
+<div data-diagram="SphereBufferGeometryMediumSmooth"></div>
+<div data-diagram="SphereBufferGeometryHighSmooth"></div>
 </div>
 </div>
 
 
 It's now not so clear that the one on the right with 5000 triangles
 It's now not so clear that the one on the right with 5000 triangles
@@ -322,8 +322,8 @@ Sometimes it's easy to choose. For example you can also choose
 to subdivide a plane.
 to subdivide a plane.
 
 
 <div class="spread">
 <div class="spread">
-<div data-primitive-diagram="PlaneBufferGeometryLow"></div>
-<div data-primitive-diagram="PlaneBufferGeometryHigh"></div>
+<div data-diagram="PlaneBufferGeometryLow"></div>
+<div data-diagram="PlaneBufferGeometryHigh"></div>
 </div>
 </div>
 
 
 The plane on the left is 2 triangles. The plane on the right
 The plane on the left is 2 triangles. The plane on the right
@@ -343,15 +343,9 @@ to use it](threejs-scenegraph.html).
 <canvas id="c"></canvas>
 <canvas id="c"></canvas>
 <script src="../resources/threejs/r94/three.min.js"></script>
 <script src="../resources/threejs/r94/three.min.js"></script>
 <script src="../resources/threejs/r94/js/controls/TrackballControls.js"></script>
 <script src="../resources/threejs/r94/js/controls/TrackballControls.js"></script>
+<script src="resources/threejs-lesson-utils.js"></script>
 <script src="resources/threejs-primitives.js"></script>
 <script src="resources/threejs-primitives.js"></script>
 <style>
 <style>
-.spread {
-  display: flex;
-}
-.spread>div {
-  flex: 1 1 auto;
-  height: 150px;
-}
 .primitives {
 .primitives {
 }
 }
 .primitives>div {
 .primitives>div {
@@ -381,14 +375,6 @@ to use it](threejs-scenegraph.html).
 .primitives .desc {
 .primitives .desc {
   flex: 1 1 auto;
   flex: 1 1 auto;
 }
 }
-#c {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100vw;
-  height: 100vh;
-  z-index: -100;
-}
 </style>
 </style>