Browse Source

remove articles so we can have a PR

Gregg Tavares 7 years ago
parent
commit
80cd2e575f

+ 0 - 316
threejs/lessons/threejs-fundamentals.md

@@ -1,316 +0,0 @@
-Title: Three.js Fundamentals
-Description: Your first Three.js lesson starting with the fundamentals
-
-This is the first article in a series of articles about three.js.
-[Three.js](http://threejs.org) is a 3D library that tries to make
-it as easy as possible to get 3D content on a webpage.
-
-Three.js is often confused with WebGL since more often than
-not, but not always, three.js uses WebGL to draw 3D.
-WebGL is a very low-level
-system that only draws points, lines, and triangles. To do
-anything useful with WebGL generally requires quite a bit of
-code and that is where three.js comes in. It handlings things
-like scenes, lights, shadows, materials, textures, all things that you'd
-have to write yourself if you were to use WebGL directly.
-
-These tutorials assume you already know JavaScript and, for the
-most part they will use ES6 style JavaScript. Most browsers
-that support three.js are auto-updated so most users should
-be able to run this code. If you'd like to make this code run
-on older browsers look into a transpiler like [Babel](http://babel.io).
-
-When learning most programming languages the first thing people
-do is make the computer print `"Hello World!"`. For 3D one
-of the most common first things to do is to make a 3D cube.
-so let's start with "Hello Cube!"
-
-The first thing we need is a `<canvas>` tag so
-
-```
-<body>
-  <canvas id="c"></canvas>
-</body>
-```
-
-Three.js will draw into that canvas so we need to look it up
-and pass it to three.js.
-
-```
-<script>
-'use strict';
-
-function main() {
-  const canvas = document.querySelector('#c');
-  const renderer = new THREE.WebGLRenderer({canvas: canvas});
-  ...
-</script>
-```
-
-Note there are some esoteric details here. If you don't pass a canvas
-into three.js it will create one for you but then you have to add it
-to your document. Where to add it may change depending on your use case
-and you'll have to change your code so I find that passing a canvas
-to three.js feels a little more flexible. I can put the canvas anywhere
-and the code will find it where as if I had code to insert the canvas
-into to the document I'd likely have to change that code if my use case
-changed.
-
-After we look up the canvas we create a `WebGLRenderer`. The renderer
-is the thing responsible for actually taking all the data you provide
-and rendering it to the canvas. In the past there have been other renderers
-like `CSSRenderer`, a `CanvasRenderer` and in the future there may be a
-`WebGL2Renderer` or `WebGPURenderer`. For now there's the `WebGLRenderer`
-that uses WebGL to render 3D to the canvas.
-
-Next up we need a camera.
-
-```
-const fov = 75;
-const aspect = 2;  // the canvas default
-const zNear = 0.1;
-const zFar = 5;
-const camera = new THREE.PerspectiveCamera(fov, aspect, zNear, zFar);
-```
-
-`fov` is short for `field of view`. In this case 75 degrees in the vertical
-dimension. Note that most angles in three.js are in radians but for some
-reason the perspective camera takes degrees.
-
-`aspect` is the display aspect of the canvas. We'll go over the details
-in another article but by default a canvas is 300x150 pixels which makes
-the aspect 300/150 or 2.
-
-`zNear` and `zFar` represent the space in front of the camera
-that will be rendered. Anything before that range or after that range
-will be clipped (not drawn).
-
-Those 4 settings define a *"frustum"*. A *frustum* is the name of
-a 3d shape that is like a pyramid with the tip sliced off. In other
-words think of the word "frustum" as another 3D shape like sphere,
-cube, prism, frustum.
-
-<img src="resources/frustum-3d.svg" width="500" class="threejs_center"/>
-
-The height of the zNear and zFar planes are determined by the field of view.
-The width of both planes is determined by the field of view and the aspect.
-
-Anything inside the defined frustum will be be drawn. Anything outside
-will not.
-
-The camera defaults to looking down the -Z axis with +Y up. We'll put our cube
-at the origin so we need to move the camera back a litte from the origin
-in order to see anything.
-
-```
-camera.position.z = 2;
-```
-
-Here's what we're aiming for.
-
-<img src="resources/scene-down.svg" width="500" class="threejs_center"/>
-
-In the diagram above we can see our camera is at `z = 2`. It's looking
-down the -Z axis. Our frustum starts 0.1 units from the front of the camera
-and goes to 5 units in front of the camera. Because in this diagram we are looking down,
-the field of view is affected by the aspect. Our canvas is twice as wide
-as it is tall so across view the field of view will be much wider than
-our specified 75 degrees which is the vertical field of view.
-
-Next we make a `Scene`. A `Scene` in three.js is a form of scene graph.
-Anything you want three.js to draw needs to be added to the scene. We'll
-cover more details of how scenes work in a future article.
-
-```
-const scene = new THREE.Scene();
-```
-
-Next up we create a `BoxGeometry` which contains the data for a box.
-Almost anything we want to display in Three.js needs geometry which defines
-the vertices that make up our 3D object.
-
-```
-const boxWidth = 1;
-const boxHeight = 1;
-const boxDepth = 1;
-const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
-```
-
-We then create a basic material and set its color. Colors can
-be specified using standard CSS style 6 digit hex color values.
-
-```
-const material = new THREE.MeshBasicMaterial({color: 0x44aa88});
-```
-
-We then create a `Mesh`. A `Mesh` in three represents the combination
-of a `Geometry` (the shape of the object) and a `Material` (how to draw
-the object, shiny or flat, what color, what texture(s) to apply. Etc.)
-as well as the position, orientation, and scale of that
-object in the scene.
-
-```
-const cube = new THREE.Mesh(geometry, material);
-```
-
-And finally we add that mesh to the scene
-
-```
-scene.add(cube);
-```
-
-We can then render the scene by calling the renderer's render function
-and passing it the scene and the camera
-
-```
-renderer.render(scene, camera);
-```
-
-Here's a working exmaple
-
-{{{example url="../threejs-fundamentals.html" }}}
-
-It's kind of hard to tell that is a 3D cube since we're viewing
-it directly down the -Z axis and the cube itself is axis aligned
-so we're only seeing a single face.
-
-Let's animate it spinning and hopefully that will make
-it clear it's being drawn in 3D. To animate it we'll render inside a render loop using
-[`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
-
-Here's our loop
-
-```
-function render(time) {
-  time *= 0.001;  // convert time to seconds
-
-  cube.rotation.x = time;
-  cube.rotation.y = time;
-
-  renderer.render(scene, camera);
-
-  requestAnimationFrame(render);
-}
-requestAnimationFrame(render);
-```
-
-`requestAnimationFrame` is a request to the browser that you want to animate something.
-You pass it a function to be called. In our case that function is `render`. The browser
-will call your function and if you update anything related to the display of the
-page the browser will re-render the page. In our case we are calling three's
-`renderer.render` function which will draw our scene.
-
-`requestAnimationFrame` passes the time since the page started rendering to
-the our function. That time is passed in milliseconds. I find it's much
-easier to work with seconds so here we're converting that to seconds.
-
-We then set the cube's X and Y rotation to the current time. These rotations
-are in [radians](https://en.wikipedia.org/wiki/Radian). There are 2 pi radians
-in a circle so our cube should turn around once on each axis in about 6.28
-seconds.
-
-We then render the scene and request another animation frame to continue
-our loop.
-
-Outside the loop we call `requestAnimationFrame` one time to start the loop.
-
-{{{example url="../threejs-fundamentals-with-animation.html" }}}
-
-It's a little better but it's still hard to see the 3d. What would help is to add some lighting
-so let's add a light. There are many kinds of lights in three.js which
-we'll go over in a future article. For now let's create a directional
-light.
-
-```
-{
-  const color = 0xFFFFFF;
-  const intensity = 1;
-  const light = new THREE.DirectionalLight(color, intensity);
-  light.position.set(-1, 2, 4);
-  scene.add(light);
-}
-```
-
-Directional lights have a position and a target. Both default to 0, 0, 0. In our
-case we're setting the light's position to -1, 2, 4 so it's slightly on the left,
-above, and behind our camera. The target is still 0, 0, 0 so it will shine
-toward the origin.
-
-We also need to change the material. The `MeshBasicMaterial` is not affected by
-lights. Let's change it to a `MeshPhongMaterial` which is affected by lights.
-
-```
--const material = new THREE.MeshBasicMaterial({color: 0x44aa88});  // greenish blue
-+const material = new THREE.MeshPhongMaterial({color: 0x44aa88});  // greenish blue
-```
-
-And here it is working.
-
-{{{example url="../threejs-fundamentals-with-light.html" }}}
-
-It should now be pretty clearly 3D.
-
-Just for the fun of it let's add 2 more cubes.
-
-We'll use the same geometry for each cube but make a different
-material so each cube can be a different color.
-
-First we'll make a function that creates a new material
-with the specified color. Then it creates a mesh using
-the specified geometry and adds it to the scene and
-sets its X position.
-
-```
-function makeInstance(geometry, color, x) {
-  const material = new THREE.MeshPhongMaterial({color});
-
-  const cube = new THREE.Mesh(geometry, material);
-  scene.add(cube);
-
-  cube.position.x = x;
-
-  return cube;
-}
-```
-
-Then we'll call it 3 times with 3 different colors and X positions
-saving the `Mesh` instances in an array.
-
-```
-const cubes = [
-  makeInstance(geometry, 0x44aa88,  0),
-  makeInstance(geometry, 0x8844aa, -2),
-  makeInstance(geometry, 0xaa8844,  2),
-];
-```
-
-Finally we'll spin all 3 cubes in our render function. We
-compute a slightly different rotation for each one.
-
-```
-function render(time) {
-  time *= 0.001;  // convert time to seconds
-
-  cubes.forEach((cube, ndx) => {
-    const speed = 1 + ndx * .1;
-    const rot = time * speed;
-    cube.rotation.x = rot;
-    cube.rotation.y = rot;
-  });
-
-  ...
-```
-
-and here's that.
-
-{{{example url="../threejs-fundamentals-3-cubes.html" }}}
-
-If you compare it to the top down diagram above you can see
-it matches our expectections. With cubes at X = -2 and X = +2
-they are partially outside our frustum. They are also
-somewhat exaggeratedly warped since the field of view
-across the canvas is so extreme.
-
-I hope this short intro helps to get things started. [Next up we'll cover
-making our code responsive so it is adaptable to multiple situations](threejs-responsive.html).
-

+ 0 - 8
threejs/lessons/threejs-materials-and-lights.md

@@ -1,8 +0,0 @@
-Title: Three.js Materials and Lights
-Description: Materials and Lights
-
-This article is part of a series of articles about three.js. The
-first article is [three.js fundamentals](three-fundamentals.html). If
-you haven't read yet you might want to consider starting there.
-
-TBD

+ 0 - 394
threejs/lessons/threejs-primitives.md

@@ -1,394 +0,0 @@
-Title: Three.js Primitives
-Description: A tour of three.js primitives.
-
-This article one in a series of articles about three.js.
-The first article was [about fundamentals](threejs-fundamentals.html).
-If you haven't read that yet you might want to start there.
-
-Three.js has a large number of primitives. Primitives
-are generally 3D shapes that are generated at runtime
-with a bunch of parameters.
-
-It's common to use primitives for things like a sphere
-for globe or a bunch of boxes to draw a 3D graph. It's
-especially common to use primitives to experiment
-and get started with 3D. For the majority if 3D apps
-it's more common to have an artist make 3D models
-in a 3D modeling program. Later in this series we'll
-cover making and loading data from several 3D modeling
-programs. For now let's go over some of the available
-primitives.
-
-<div class="primitives">
-<div data-primitive="BoxBufferGeometry">A Box</div>
-<div data-primitive="CircleBufferGeometry">A flat circle</div>
-<div data-primitive="ConeBufferGeometry">A Cone</div>
-<div data-primitive="CylinderBufferGeometry">A Cylinder</div>
-<div data-primitive="DodecahedronBufferGeometry">A dodecahedron (12 sides)</div>
-<div data-primitive="ExtrudeBufferGeometry">An extruded 2d shape with optional bevelling.
-Here we are extruding a heart shape. Note this is the basis
-for <code>TextBufferGeometry</code> and <code>TextGeometry</code> respectively.</div>
-<div data-primitive="IcosahedronBufferGeometry">An icosahedron (20 sides)</div>
-<div data-primitive="LatheBufferGeometry">A shape generated by spinning a line. Examples would lamps, bowling pins, candles, candle holders, wine glasses, drinking glasses, etc... You provide the 2d silhouette as series of points and then tell three.js how many subdivisions to make as it spins the silhouette around an axis.</div>
-<div data-primitive="OctahedronBufferGeometry">An Octahedron (8 sides)</div>
-<div data-primitive="ParametricBufferGeometry">A surface generated by providing a function that takes a 2d point from a grid and returns the corresponding 3d point.</div>
-<div data-primitive="PlaneBufferGeometry">A 2D plane</div>
-<div data-primitive="PolyhedronBufferGeometry">Takes a set of triangles centered around a point and projects them onto a sphere</div>
-<div data-primitive="RingBufferGeometry">A 2D disc with a hole in the center</div>
-<div data-primitive="ShapeBufferGeometry">A 2d outline that gets trianglulated</div>
-<div data-primitive="SphereBufferGeometry">A sphere</div>
-<div data-primitive="TetrahedronBufferGeometry">A terahedron (4 sides)</div>
-<div data-primitive="TextBufferGeometry">3D Text generated from a 3D font and a string</div>
-<div data-primitive="TorusBufferGeometry">A torus (donut)</div>
-<div data-primitive="TorusKnotBufferGeometry">A torus knot</div>
-<div data-primitive="TubeBufferGeometry">A circle traced down a path</div>
-<div data-primitive="EdgesGeometry">A helper object that takes another geometry as input and generates edges only if the angle between faces is greater than some threshold. For example if you look at the box at the top it shows a line going through each face showing every triangle that makes the box. Using an EdgesGeometry instead the middle lines are removed.</div>
-<div data-primitive="WireframeGeometry">Generates geometry that contains one line segment (2 points) per edge in the given geometry. With out this you'd often be missing edges or get extra edges since WebGL generally requires 2 points per line segment. For example if all you had was a single triangle there would only be 3 points. If you tried to draw it using a material with <code>wireframe: true</code> you would only get a single line. Passing that triangle geometry to a <code>WireframeGeometry</code> will generate a new Geometry that has 3 lines segments using 6 points..</div>
-</div>
-
-You might notice of most of them come in pairs of `Geometry`
-or `BufferGeometry`. The difference between the 2 types is effectively flexibility
-vs performance.
-
-`BufferGeometry` based primitves are the performance oriented
-types. The vertices for the geometry are generated directly
-into an efficient typed array format ready to be uploaded to the GPU
-for rendering. This means they are faster to start up
-and take less memory but if you want to modify their
-data they take what is often considered more complex
-programming to manipulate.
-
-`Geometry` based primitives are the more flexible, easier to manipulate
-type. They are built from JavaScript based classes like `Vector3` for
-3D points, `Face3` for triangles.
-They take quite a bit of memory and before they can be rendered three.js will need to
-convert them to something similar to the corresponding `BufferGeometry` representation.
-
-If you know you are not going to manipulate a primitive or
-if you're comfortable doing the math to manipulate their
-internals then it's best to go with the `BufferGeometry`
-based primitives. If on the other hand you want to change
-a few things before rendering you might find the `Geometry`
-based primitives easier to deal with.
-
-As an simple example a `BufferGeometry`
-can not have new vertices easily added. The number of vertices used is
-decided at creation time, storage is created, and then data for vertices
-are filled in. Where as for `Geometry` you can add vertices as you go.
-
-We'll go over creating custom geometry in another article. For now
-let's make an example creating each type of primitive. We'll start
-with the [examples from the previous article](threejs-responsive.html).
-
-Near the top let's set a background color
-
-```
-const canvas = document.querySelector('#c');
-const renderer = new THREE.WebGLRenderer({canvas: canvas});
-+renderer.setClearColor(0xAAAAAA);
-```
-
-This tells three.js to clear to lightish gray.
-
-The camera needs to change position so that we can see all the
-objects.
-
-```
--const fov = 75;
-+const fov = 40;
-const aspect = 2;  // the canvas default
-const zNear = 0.1;
--const zFar = 5;
-+const zFar = 1000;
-const camera = new THREE.PerspectiveCamera(fov, aspect, zNear, zFar);
--camera.position.z = 2;
-+camera.position.z = 120;
-```
-
-Let's add a function, `addObject`, that takes an x, y position and an `Object3D` and adds
-the object to the scene.
-
-```
-const objects = [];
-const spread = 15;
-
-function addObject(x, y, obj) {
-  obj.position.x = x * spread;
-  obj.position.y = y * spread;
-
-  scene.add(obj);
-  objects.push(obj);
-}
-```
-
-Let's also make a function to create a random colored material.
-We'll use a feature of `Color` that lets you set a color
-based on hue, saturation, and luminance.
-
-`hue` goes from 0 to 1 around the color wheel with
-red at 0, green at .33 and blue at .66. `saturation`
-goes from 0 to 1 with 0 having no color and 1 being
-most saturated. `luminance` goes from 0 to 1
-with 0 being black, 1 being white and 0.5 being
-the maximum amount of color. In other words
-as `luminance` goes from 0.0 to 0.5 the color
-will go from black to `hue`. From 0.5 to 1.0
-the color will go from `hue` to white.
-
-```
-function createMaterial() {
-  const material = new THREE.MeshPhongMaterial({
-    side: THREE.DoubleSide,
-  });
-
-  const hue = Math.random();
-  const saturation = 1;
-  const luminance = .5;
-  material.color.setHSL(hue, saturation, luminance);
-
-  return material;
-}
-```
-
-We also passed `side: THREE.DoubleSide` to the material.
-This tells three to draw both sides of the triangles
-that make up a shape. For a solid shape like a sphere
-or a cube there's usually no reason to draw the
-back sides of triangles as they all face inside the
-shape. In our case though we are drawing a few things
-like the `PlaneBufferGeometry` and the `ShapeBufferGeometry`
-which are 2 dimensional and so have no inside. Without
-setting `side: THREE.DoubleSide` they would disappear
-when looking at their back sides.
-
-I should note that it's faster to draw when **not** setting
-`side: THREE.DoubleSide` so ideally we'd set it only on
-the materials that really need it but in this case we
-are not drawing too much so there isn't much reason to
-worry about it.
-
-Let's make a function, `addSolidGeometry`, that
-we pass a geometry and it creates a random colored
-material via `createMaterial` and adds it to the scene
-via `addObject`.
-
-```
-function addSolidGeometry(x, y, geometry) {
-  const mesh = new THREE.Mesh(geometry, createMaterial());
-  addObject(x, y, mesh);
-}
-```
-
-Now we can use this for the majority of the primitves we create.
-For example creating a box
-
-```
-{
-  const width = 8;
-  const height = 8;
-  const depth = 8;
-  addSolidGeometry(-2, -2, new THREE.BoxBufferGeometry(width, height, depth));
-}
-```
-
-If you look in the code below you'll see a similar section for each type of geometry.
-
-Here's the result:
-
-{{{example url="../threejs-primitives.html" }}}
-
-There are a couple of notable exceptions to the pattern above.
-The biggest is probably the `TextBufferGeometry`. It needs to load
-3D font data before it can generate a mesh for the text.
-That data loads asynchronously so we need to wait for it
-to load before trying to create the geometry. You can see below
-we create a `FontLoader` and pass it the url to our font
-and a callback. The callback is called after the font loads.
-In the callback we create the geometry
-and call `addObject` to add it the scene.
-
-```
-{
-  const loader = new THREE.FontLoader();
-  loader.load('resources/threejs/fonts/helvetiker_regular.typeface.json', (font) => {
-    const geometry = new THREE.TextBufferGeometry('three.js', {
-      font: font,
-      size: 3.0,
-      height: .2,
-      curveSegments: 12,
-      bevelEnabled: true,
-      bevelThickness: 0.15,
-      bevelSize: .3,
-      bevelSegments: 5,
-    });
-    const mesh = new THREE.Mesh(geometry, createMaterial());
-    geometry.computeBoundingBox();
-    geometry.boundingBox.getCenter(mesh.position).multiplyScalar(-1);
-
-    const parent = new THREE.Object3D();
-    parent.add(mesh);
-
-    addObject(-1, 1, parent);
-  });
-}
-```
-
-There's one other difference. We want to spin the text around its
-center but by default three.js creates the text such that its center of rotation
-is on the left edge. To work around this we can ask three.js to compute the bounding
-box of the geometry. We can then call the `getCenter` method
-of the bounding box and pass it our mesh's position object.
-`getCenter` copies the center of the box into the position.
-It also returns the position object so we can call `multiplyScaler(-1)`
-to position the entire object such that its center of rotation
-is at the center of the object.
-
-If we then just called `addSolidGeometry` like with previous
-examples it would set the position again which is
-no good. So, in this case we create an `Object3D` which
-is the standard node for the three.js scene graph. `Mesh`
-is inherited from `Object3D` as well. We'll cover [how the scene graph
-works in another article](threejs-scenegraph.html).
-For now it's enough to know that
-like DOM nodes, children are drawn relative to their parent.
-By making an `Object3D` and making our mesh a child of that
-we can position the `Object3D` where ever we want and still
-keep the center offset we set earilier.
-
-If we didn't do this the text would spin off center.
-
-{{{example url="../threejs-primitives-text.html" }}}
-
-Notice the one on the left is not spinning around its center
-where as the one on the right is.
-
-The other exceptions are the 2 line based examples for `EdgesGeometry`
-and `WireframeGeometry`. Instead of calling `addSolidGeometry` they call
-`addLineGeomtry` which looks like this
-
-```
-function addLineGeometry(x, y, geometry) {
-  const material = new THREE.LineBasicMaterial({color: 0x000000});
-  const mesh = new THREE.LineSegments(geometry, material);
-  addObject(x, y, mesh);
-}
-```
-
-It creates a black `LineBasicMaterial` and then creates a `LineSegments`
-object which is a wrapper for `Mesh` that helps three know you're rendering
-line segments (2 points per segment).
-
-Each of the primitives has several parameters you can pass on creation
-and it's best to [look in the documentation](https://threejs.org/docs/) for all of them rather than
-repeat them here. You can also click the links above next to each shape
-to take you directly to the docs for that shape.
-
-One other thing that's important to cover is that almost all shapes
-have various settings for how much to subdivde them. A good example
-might be the sphere geometries. Spheres take parameters for
-how many divisions to make around and how many top to bottom. For example
-
-<div class="spread">
-<div data-primitive-diagram="SphereBufferGeometryLow"></div>
-<div data-primitive-diagram="SphereBufferGeometryMedium"></div>
-<div data-primitive-diagram="SphereBufferGeometryHigh"></div>
-</div>
-
-The first sphere has 5 segments around and 3 high which is 15 segments
-or 30 triangles. The second sphere has 24 segments by 10. That's 240 segments
-or 480 triangles. The last one has 50 by 50 which is 2500 segments or 5000 triangles.
-
-It's up to you to decide how many subdivisions you need. It might
-look like you need a high number of segments but remove the lines
-and the flat shading and we get this
-
-<div class="spread">
-<div data-primitive-diagram="SphereBufferGeometryLowSmooth"></div>
-<div data-primitive-diagram="SphereBufferGeometryMediumSmooth"></div>
-<div data-primitive-diagram="SphereBufferGeometryHighSmooth"></div>
-</div>
-
-It's now not so clear that the one on the right with 5000 triangles
-is entirely better than the one in the middle with only 480.
-If you're only drawing a few spheres, like say a single globe for
-a map of the earth, then a single 10000 triangle sphere is not a bad
-choice. If on the otherhand you're trying to draw 1000 spheres
-then 1000 spheres times 10000 triangles each is 10 million triangles.
-To animate smoothly you need the browser to draw at 60 frames a
-second so you'd be asking the browser to draw 600 million triangles
-per second. That's a lot of computing.
-
-Sometimes it's easy to choose. For example you can also choose
-to subdivide a plane.
-
-<div class="spread">
-<div data-primitive-diagram="PlaneBufferGeometryLow"></div>
-<div data-primitive-diagram="PlaneBufferGeometryHigh"></div>
-</div>
-
-The plane on the left is 2 triangles. The plane on the right
-is 200 triangles. Unlike the sphere there is really no trade off in quality for most
-use cases of a plane. You'd most likely only subdivide a plane
-if you expected to want to modify or warp it in some way. A box
-is similar.
-
-So, choose whatever is appropriate for your situation. The less
-subdivisions you choose the more likely things will run smoothly and the less
-memory they'll take. You'll have to decide for yourself what the correct
-tradeoff is for your particular siutation.
-
-Next up let's go over [how three's scene graph works and how
-to use it](threejs-scenegraph.html).
-
-<canvas id="c"></canvas>
-<script src="../resources/threejs/r94/three.min.js"></script>
-<script src="../resources/threejs/r94/js/controls/TrackballControls.js"></script>
-<script src="resources/threejs-primitives.js"></script>
-<style>
-.spread {
-  display: flex;
-}
-.spread>div {
-  flex: 1 1 auto;
-  height: 150px;
-}
-.primitives {
-}
-.primitives>div {
-  display: flex;
-  align-items: center;
-  margin-bottom: 1em;
-}
-.primitives .shape {
-  flex: 0 0 auto;
-  width: 200px;
-  height: 200px;
-}
-.primitives .desc {
-  word-wrap: break-word;
-  padding: 1em;
-  min-width: 0;
-}
-.primitives .desc code {
-  white-space: normal;
-}
-@media (max-width: 550px) {
-  .primitives .shape {
-      width: 120px;
-      height: 120px;
-  }
-}
-.primitives .desc {
-  flex: 1 1 auto;
-}
-#c {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 100vw;
-  height: 100vh;
-  z-index: -100;
-}
-</style>
-
-

+ 0 - 271
threejs/lessons/threejs-responsive.md

@@ -1,271 +0,0 @@
-Title: Three.js Responsive Design
-Description: How to make your three.js fit different sized displays.
-
-This is the second article in a series of articles about three.js.
-The first article was [about fundamentals](threejs-fundamentals.html).
-If you haven't read that yet you might want to start there.
-
-This article is about how to make your three.js app be responsive
-to any situation. Making a webpage responsive generally refers
-to the page displaying well on different sized displays from
-desktops to tablets to phones.
-
-For three.js there are even more situations to consider. For
-example a 3D editor with controls on the left, right, top, or
-bottom is something we might want to handle. A live diagram
-in the middle of a document is another example.
-
-The last sample we had used a plain canvas with no css and
-no size
-
-```
-<canvas id="c"></canvas>
-```
-
-That canvas defaults to 300x150 css pixels in size.
-
-In the web platform the recommended way to set the size
-of something is to use CSS.
-
-Let's make the canvas fill the page by adding CSS
-
-```
-<style>
-html, body {
-   margin: 0;
-   height: 100%;
-}
-#c {
-   width: 100%;
-   height: 100%;
-   display: block;
-}
-</style>
-```
-
-In HTML the body has a margin of 5px pixels by default so setting the
-margin to 0 removes the margin. Setting the html and body height to 100%
-makes them fill the window. Otherwise they are only as large
-as the content that fills them.
-
-Next we tell the `id=c` element to be
-100% the size of its container which in this case is the body of
-the document.
-
-Finally we set its `display` mode to `block`. A canvas's
-default display mode is `inline`. Inline
-elements can end up adding whitespace to what is displayed. By
-setting the canvas to `block` that issue goes away.
-
-Here's the result
-
-{{{example url="../threejs-responsive-no-resize.html" }}}
-
-You can see the canvas is now filling the page but there are 2
-problems. One our cubes are stretched. They are not cubes they
-are more like boxes. Too tall or too wide. Open the
-example in its own window and resize it. You'll see how
-the cubes get stretched wide and tall.
-
-<img src="resources/images/resize-incorrect-aspect.png" width="407" class="threejs_center">
-
-The second problem is they look low resolution or blocky and
-blurry. Stretch the window really large and you'll really see
-the issue.
-
-<img src="resources/images/resize-low-res.png" class="threejs_center">
-
-Let's fix the stretchy problem first. To do that we need
-to set the aspect of the camera to the aspect of the canvas's
-display size. We can do that by looking at the canvas's
-`clientWidth` and `clientHeight` properties.
-
-We'll update our render loop like this
-
-```
-function render(time) {
-  time *= 0.001;
-
-+  const canvas = renderer.domElement;
-+  camera.aspect = client.clientWidth / client.clientHeight;
-+  camera.updateProjectionMatrix();
-
-  ...
-```
-
-Now the cubes should stop being distorted.
-
-{{{example url="../threejs-responsive-update-camera.html" }}}
-
-Open the example in a separate window and resize the window
-and you should see the cubes are no longer stretched tall or wide.
-They stay the correct aspect regardless of window size.
-
-<img src="resources/images/resize-correct-aspect.png" width="407" class="threejs_center">
-
-Now let's fix the blockiness.
-
-Canvas elements have 2 sizes. One size is the size the canvas is displayed
-on the page. That's what we set with CSS. The other size is the
-number of pixels in the canvas itself. This is no different than an image.
-For example we might have a 128x64 pixel image and using
-css we might display as 400x200 pixels.
-
-```
-<img src="some128x64image.jpg" style="width:400px; height:200px">
-```
-
-A canvas's internal size, its resolution, is often called its drawingbuffer size.
-In three.js we can set the canvas's drawingbuffer size by calling `renderer.setSize`.
-What size should we pick? The most obvious answer is "the same size the canvas is displayed".
-Again, to do that we can look at the canvas's `clientWidth` and `clientHeight`
-attributes.
-
-Let's write a function that checks if the renderer's canvas is not
-already the size it is being displayed as and if so set its size.
-
-```
-function resizeRendererToDisplaySize(renderer) {
-  const canvas = renderer.domElement;
-  const width = canvas.clientWidth;
-  const height = canvas.clientHeight;
-  const needResize = canvas.width !== width || canvas.height !== height;
-  if (needResize) {
-    renderer.setSize(width, height, false);
-  }
-  return needResize;
-}
-```
-
-Notice we check if the canvas actually needs to be resized. Resizing the canvas
-is an interesting part of the canvas spec and it's best not to set the same
-size if it's already the size we want.
-
-Once we know if we need to resize or not we then call `renderer.setSize` and
-pass in the new width and height. It's important to pass `false` at the end.
-`render.setSize` by default sets the canvas's CSS size but doing so is not
-what we want. We want the browser to continue to work how it does for all other
-elements which is to use CSS to determine the display size of the element. We don't
-want canvases used by three to be different than other elements.
-
-Note that our function returns true if the canvas was resized. We can use
-this to check if there are other things we should update. Let's modify
-our render loop to use the new function
-
-```
-function render(time) {
-  time *= 0.001;
-
-+  if (resizeRendererToDisplaySize(renderer)) {
-+    const canvas = renderer.domElement;
-+    camera.aspect = client.clientWidth / client.clientHeight;
-+    camera.updateProjectionMatrix();
-+  }
-
-  ...
-```
-
-Since the apsect is only going to change if the canvas's display size
-changed we only set the camera's aspect if `resizeRendererToDisplaySize`
-returns `true`.
-
-{{{example url="../threejs-responsive.html" }}}
-
-It should now render with a resolution that matches the display
-size of the canvas.
-
-To make the point about letting CSS handle the resizing let's take
-our code and put it in a [separate `.js` file](../resources/threejs-responsive.js).
-Here then are a few more examples where we let CSS choose the size and notice we had
-to change zero code for them to work.
-
-Let's put our cubes in the middle of a paragraph of text.
-
-{{{example url="../threejs-responsive-paragraph.html" startPane="html" }}}
-
-and here's our same code used in an editor style layout
-where the control area on the right can be resized.
-
-{{{example url="../threejs-responsive-editor.html" startPane="html" }}}
-
-The important part to notice is no code changed. Only our HTML and CSS
-changed.
-
-## Handling HD-DPI displays
-
-HD-DPI stands for high-density dot per inch displays.
-That's most Mac's now a days and many windows machines
-as well as pretty much all smartphones.
-
-The way this works in the browser is they use
-CSS pixels to set the sizes which are suppose to be the same
-regardless of how high res the display is. The browser
-will the just render text with more detail but the
-same physical size.
-
-There are various ways to handle HD-DPI with three.js.
-
-The first one is just not to do anything special. This
-is arguably the most common. Rendering 3D graphics
-takes a lot of GPU processing power. Mobile GPUs have
-less power than desktops, at least as of 2018, and yet
-mobile phones often have very high resolution displays.
-The current top of the line phones have a HD-DPI ratio
-of 3x meaning for every one pixel from a non-HD-DPI display
-those phones have 9 pixels. That means they have to do 9x
-the rendering.
-
-Computing 9x the pixels is a lot of work so if we just
-leave the code as it is we'll compute 1x the pixels and the
-browser will just draw it at 3x the size (3x by 3x = 9x pixels).
-
-For any heavy three.js app that's probably what you want
-otherwise you're likely to get a slow framerate.
-
-That said if you actually do want to render at the resolution
-of the device there are a couple of ways to do this in three.js.
-
-One is to tell three.js a resolution multiplier using `renderer.setPixelRatio`.
-You ask the browser what the multiplier is from CSS pixels to device pixels
-and pass that to three.js
-
-     renderer.setPixelRatio(window.devicePixelRatio);
-
-After that any calls to `renderer.setSize` will magicially
-use the size you request multiplied by whatever pixel ratio
-you passed in.
-
-The other way is to do it yourself when you resize the canvas.
-
-```
-    function resizeRendererToDisplaySize(renderer) {
-      const canvas = renderer.domElement;
-      const pixelRatio = window.devicePixelRatio;
-      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;
-    }
-```
-
-I prefer this second way. Why? Because it means I get what I ask for.
-There are many cases when using three.js where we need to know the actual
-size of the canvas's drawingBuffer. For example when making a post processing filter,
-or if we are making a shader that accesses `gl_FragCoord`, etc...
-By doing it oursevles we always know the size being used is the size we requested.
-There is no special case where magic is happening behind the scenes.
-
-Here's an example using the code above.
-
-{{{example url="../threejs-responsive-hd-dpi.html" }}}
-
-It might be hard to see the difference but if you have an HD-DPI
-display and you compare this sample to those above you should
-notice the edges are more crisp.
-
-This article covered a very basic but fundamental topic. Next up lets quickly
-[go over the basic primitives that three.js provides](threejs-primitives.html).
-

+ 0 - 390
threejs/lessons/threejs-scenegraph.md

@@ -1,390 +0,0 @@
-Title: Three.js Scenegraph
-Description: What's a scene graph?
-
-This article is part of a series of articles about three.js. The
-first article is [three.js fundamentals](three-fundamentals.html). If
-you haven't read yet you might want to consider starting there.
-
-Three.js's core is arguably its scene graph. A scene graph in a 3D
-engine is a hierarchy of nodes in a graph where each node represents
-a local space.
-
-<img src="resources/images/scenegraph-generic.svg" align="center">
-
-That's kind of abstract so let's try to give some examples.
-
-One example might be solar system, sun, earth, moon.
-
-<img src="resources/images/scenegraph-solarsystem.svg" align="center">
-
-The Earth orbits the Sun. The Moon orbits the Earth. The Moon
-moves in a circle around the Earth. From the Moon's point of
-view it's rotating in the "local space" of the Earth. Even though
-its motion relative to the Sun is some crazy spirograph like
-curve from the Moon's point of view it just has to concern itself with rotating
-around the Earth's local space.
-
-{{{diagram url="resources/moon-orbit.html" }}}
-
-To think of it another way, you living on the Earth do not have to think
-about the Earth's rotation on its axis nor its rotation around the
-Sun. You just walk or drive or swim or run as though the Earth is
-not moving or rotating at all. You walk, drive, swim, run, and live
-in the Earth's "local space" even though relative to the sun you are
-spinning around the earth at around 1000 miles per hour and around
-the sun at around 67,000 miles per hour. Your position in the solar
-system is similar to that of the moon above but you don't have to concern
-yourself. You just worry about your position relative to the earth its
-"local space".
-
-Let's take it one step at a time. Imagine we want to make
-a diagram of the sun, earth, and moon. We'll start with the sun by
-just making a sphere and putting it at the origin. Note: We're using
-sun, earth, moon as a demonstration of how to use a scenegraph. Of course
-the real sun, earth, and moon use physics but for our purposes we'll
-fake it with a scenegraph.
-
-```
-// an array of objects who's rotation to update
-const objects = [];
-
-// use just one sphere for everything
-const radius = 1;
-const widthSegments = 6;
-const heightSegments = 6;
-const sphereGeometry = new THREE.SphereBufferGeometry(
-    radius, widthSegments, heightSegments);
-
-const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
-const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
-sunMesh.scale.set(5, 5, 5);  // make the sun large
-scene.add(sunMesh);
-objects.push(sunMesh);
-```
-
-We're using a really low-polygon sphere. Only 6 subdivisions around its equator.
-This is so it's easy to see the rotation.
-
-We're going to reuse the same sphere for everything so we'll set a scale
-for the sun mesh of 5x.
-
-We also set the phong material's `emissive` property to yellow. A phong material's
-emissive property is basically the color that will be drawn with no light hitting
-the surface. Light is added to that color.
-
-Let's also put a single point light in the center of the scene. We'll go into more
-details about point lights later but for now the simple version is a point light
-represents light that eminates from a single point.
-
-```
-{
-  const color = 0xFFFFFF;
-  const intensity = 3;
-  const light = new THREE.PointLight(color, intensity);
-  scene.add(light);
-}
-```
-
-To make it easy to see we're going to put the camera directly above the origin
-looking down. The easist way to do that us to use the `lookAt` function. The `lookAt`
-function will orient the camera from its position to "lookAt the position
-we pass to `lookAt`. Before we do that though we need to tell the camera
-which way the top of the camera is facing or rather which way is "up" for the
-camera. For most situations positive Y being up is good enough but since 
-we are looking straight down we need to tell the camera that positive Z is up.
-
-```
-const camera = new THREE.PerspectiveCamera(fov, aspect, zNear, zFar);
-camera.position.set(0, 50, 0);
-camera.up.set(0, 0, 1);
-camera.lookAt(0, 0, 0);
-```
-
-In the render loop, adapted from previous examples, we're rotating all
-objects in our `objects` array with this code.
-
-```
-objects.forEach((obj) => {
-  obj.rotation.y = time;
-});
-```
-
-Since we added the `sunMesh` to the `objects` array it will rotate.
-
-{{{example url="../threejs-scenegraph-sun.html" }}}
-
-Now let's add an the earth.
-
-```
-const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
-const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
-earthMesh.position.x = 10;
-scene.add(earthMesh);
-objects.push(earthMesh);
-```
-
-We make a material that is blue but we gave it a small amount of *emissive* blue
-so that it will show up against our black background.
-
-We use the same `sphereGeometry` with our new blue `earthMaterial` to make
-an `earthMesh`. We position that 10 units to the left of the sun
-and add it to the scene.  Since we added it to our `objects` array it will
-rotate too.
-
-{{{example url="../threejs-scenegraph-sun-earth.html" }}}
-
-You can see both the sun and the earth are rotating but the earth is not
-going around the sun. Let's make the earth a child of the sun
-
-```
--scene.add(earthMesh);
-+sunMesh.add(earthMesh);
-``` 
-
-and...
-
-{{{example url="../threejs-scenegraph-sun-earth-orbit.html" }}}
-
-What happened? Why is the earth the same size as the sun and why is it so far away?
-I actually had to move the camera from 50 units above to 150 units above to see the earth.
-
-We made the `earthMesh` a child of the `sunMesh`. The `sunMesh` has
-its scale set to 5x with `sunMesh.scale.set(5, 5, 5)`. That means the
-`sunMesh`s local space is 5 times as big. Anything put in that space
- will be multiplied by 5. That means the earth is now 5x larger and
- it's distance from the sun (`earthMesh.position.x = 10`) is also
- 5x as well.
-
- Our scene graph currently looks like this
-
-<img src="resources/images/scenegraph-sun-earth.svg" align="center">
-
-To fix it let's add an empty scene graph node. We'll parent both the sun and the earth
-to that node.
-
-```
-+const solarSystem = new THREE.Object3D();
-+scene.add(solarSystem);
-+objects.push(solarSystem);
-
-const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
-const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
-sunMesh.scale.set(5, 5, 5);
--scene.add(sunMesh);
-+solarSystem.add(sunMesh);
-objects.push(sunMesh);
-
-const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
-const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
-earthMesh.position.x = 10;
--sunMesh.add(earthMesh);
-+solarSystem.add(earthMesh);
-objects.push(earthMesh);
-```
-
-Here we made a `Object3D`. Like a `Mesh` it is also a node in the scene graph
-but unlike a `Mesh` it has no material or geometry. It just represents a local space.
-
-Our new scene graph looks like this
-
-<img src="resources/images/scenegraph-sun-earth-fixed.svg" align="center">
-
-Both the `sunMesh` and the `earthMesh` are children of the `solarSystem`. All 3
-are being rotated and now because the `earthMesh` is not a child of the `sunMesh`
-it is no longer scaled by 5x.
-
-{{{example url="../threejs-scenegraph-sun-earth-orbit-fixed.html" }}}
-
-Much better. The earth is smaller than the sun and it's rotating around the sun
-and rotating itself.
-
-Continuing that same pattern let's add a moon.
-
-```
-+const earthOrbit = new THREE.Object3D();
-+earthOrbit.position.x = 10;
-+solarSystem.add(earthOrbit);
-+objects.push(earthOrbit);
-
-const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
-const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
--solarSystem.add(earthMesh);
-+earthOrbit.add(earthMesh);
-objects.push(earthMesh);
-
-+const moonOrbit = new THREE.Object3D();
-+moonOrbit.position.x = 2;
-+earthOrbit.add(moonOrbit);
-
-+const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
-+const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
-+moonMesh.scale.set(.5, .5, .5);
-+moonOrbit.add(moonMesh);
-+objects.push(moonMesh);
-```
-
-Again we added another invisible scene graph node, a `Object3D` called `earthOrbit`
-and added both the `earthMesh` and the `moonMesh` to it. The new scene graph looks like
-this.
-
-<img src="resources/images/scenegraph-sun-earth-moon.svg" align="center">
-
-and here's that
-
-{{{example url="../threejs-scenegraph-sun-earth-moon.html" }}}
-
-You can see the moon follows the spirograph pattern shown at the top
-of this article but we didn't have to manually compute it. We just
-setup our scene graph to do it for us.
-
-It is often useful to draw something to visualize the nodes in the scene graph.
-Three.js has some helpful ummmm, helpers to ummm, ... help with this.
-
-One is called an `AxesHelper`. It draws 3 lines representing the local
-<span style="color:red">X</span>,
-<span style="color:green">Y</span>, and
-<span style="color:blue">Z</span> axes. Let's add one to every node we
-created.
-
-```
-// add an AxesHelper to each node
-objects.forEach((node) => {
-  const axes = new THREE.AxesHelper();
-  axes.material.depthTest = false;
-  axes.renderOrder = 1;
-  node.add(axes);
-});
-```
-
-On our case we want the axes to appear even though they are inside the spheres.
-To do this we set their material's `depthTest` to false which means they will
-not check to see if they are drawing behind something else. We also
-set their `renderOrder` to 1 (the default is 0) so that they get drawn after
-all the spheres. Otherwise a sphere might draw over them and cover them up.
-
-{{{example url="../threejs-scenegraph-sun-earth-moon-axes.html" }}}
-
-We can see the 
-<span style="color:red">x (red)</span> and
-<span style="color:blue">z (blue)</span> axes. Since we are looking
-straight down and each of our objects is only rotating around its
-y axis we don't see much of the <span style="color:green">y (green)</span> axes.
-
-It might be hard to see some of them as there are 2 pairs of overlapping axes. Both the `sunMesh`
-and the `solarSystem` are at the same position. Similarly the `earthMesh` and
-`earthOrbit` are at the same position. Let's add some simple controls to allow us
-to turn them on/off for each node. 
-While we're at it let's also add another helper called the `GridHelper`. It
-makes a 2D grid on the X,Z plane. By default the grid is 10x10 units.
-
-We're also going to use [dat.GUI](https://github.com/dataarts/dat.gui) which is
-a UI library that is very popular with three.js projects. dat.GUI takes an
-object and a property name on that object and based on the type of the property
-automatically makes a UI to manipulate that property.
-
-We want to make both a `GridHelper` and an `AxesHelper` for each node. We need
-a label for each node so we'll get rid of the old loop and switch to calling
-some function to add the helpers for each node
-
-```
--// add an AxesHelper to each node
--objects.forEach((node) => {
--  const axes = new THREE.AxesHelper();
--  axes.material.depthTest = false;
--  axes.renderOrder = 1;
--  node.add(axes);
--});
-
-+function makeAxisGrid(node, label, units) {
-+  const helper = new AxisGridHelper(node, units);
-+  gui.add(helper, 'visible').name(label);
-+}
-+
-+makeAxisGrid(solarSystem, 'solarSystem', 25);
-+makeAxisGrid(sunMesh, 'sunMesh');
-+makeAxisGrid(earthOrbit, 'earthOrbit');
-+makeAxisGrid(earthMesh, 'earthMesh');
-+makeAxisGrid(moonMesh, 'moonMesh');
-```
-
-`makeAxisGrid` makes a `AxisGridHelper` which is class we'll create
-to make dat.GUI happy. Like it says above dat.GUI
-will automagically make a UI that manipulates the named property
-of some object. It will create a different UI depending on the type
-of property. We want it to create a checkbox so we need to specify
-a `bool` property. But, we want both the axes and the grid
-to appear/disappear based on a single property so we'll make a class
-that has a getter and setter for a property. That way we can let dat.GUI
-think it's manipulating a single property but internally we can set
-the visible property of both the `AxesHelper` and `GridHelper` for a node.
-
-```
-// Turns both axes and grid visible on/off
-// dat.GUI requires a property that returns a bool
-// to decide to make a checkbox so we make a setter
-// can getter for `visible` which we can tell dat.GUI
-// to look at.
-class AxisGridHelper {
-  constructor(node, units = 10) {
-    const axes = new THREE.AxesHelper();
-    axes.material.depthTest = false;
-    axes.renderOrder = 2;  // after the grid
-    node.add(axes);
-
-    const grid = new THREE.GridHelper(units, units);
-    grid.material.depthTest = false;
-    grid.renderOrder = 1;
-    node.add(grid);
-
-    this.grid = grid;
-    this.axes = axes;
-    this.visible = false;
-  }
-  get visible() {
-    return this._visible;
-  }
-  set visible(v) {
-    this._visible = v;
-    this.grid.visible = v;
-    this.axes.visible = v;
-  }
-}
-```
-
-One thing to notice is we set the `renderOrder` of the `AxesHelper`
-to 2 and for the `GridHelper` to 1 so that the axes get drawn after the grid.
-Otherwise the grid might overwrite the axes.
-
-{{{example url="../threejs-scenegraph-sun-earth-moon-axes-grids.html" }}}
-
-Turn on the `solarSystem` and you'll see how the earth is exactly 10
-units out from the center just like we set above. You can see how the
-earth is in the *local space* of the `solarSystem`. Similary if you
-turn on the `earthOrbit` you'll see how the moon is exactly 2 units
-from the center of the *local space* of the `earthOrbit`.
-
-A few more examples of scene graphs. An automobile in a simple game world might have a scene graph like this
-
-<img src="resources/images/scenegraph-car.svg" align="center">
-
-If you move the car's body all the wheels will move with it. If you wanted the body
-to bounce separate from the wheels you might parent the body and the wheels to a "frame" node
-that represents the car's frame.
-
-Another example is a human in a game world.
-
-<img src="resources/images/scenegraph-human.svg" align="center">
-
-You can see the scene graph gets pretty complex for a human. In fact
-that scene graph above is simplified. For example you might extend it
-to cover the every finger (at least another 28 nodes) and every toe
-(yet another 28 nodes) plus ones for the and jaw, the eyes and maybe more.
-
-I hope this gives some idea of how scene graphs work and how you might use them.
-Making `Object3D` nodes and parenting things to them is an important step to using
-a 3D engine like three.js well. Often it might seem like some complex math is necessary
-to make something move and rotate the way you want. For example without a scene graph
-computing the motion of the moon or where to put the wheels of the car relative to its
-body would be very complicated but using a scene graph it becomes much easier.
-
-[Next up we'll go over materials and lights](threejs-materials-and-lights.html).