<!doctype html>
<html>
  <head>
    <title>Snowperson</title>
    <style>
      body { max-width: 100%; }
      /* feel free to style the canvas any way you want. If you want it to
      use the entire window, set width: 100% and height: 100%. */
      
      canvas {
          width: 80%;
          height: 500px;
          display: block;
          margin: 10px auto;
      }
    </style>
  </head>
<body>

  <h1>Snowperson</h1>
  
  <p>Demonstrates the use of nested objects and transforms</p>

</body>
</html>
function createSnowPerson (wireframe, radiusBottom, radiusMiddle, radiusTop) {
    /* Returns a three-sphere snowperson. The args are the radii of the
    spheres, starting from the bottom. The snowperson sits on the origin,
    and the spheres are stacked along the y axis. */

    // these could also be parameters
    var bodyColor = 0xFFFFFF;
    var bodyMaterial = new THREE.MeshBasicMaterial({color: bodyColor});
    bodyMaterial.wireframe = wireframe;
    var sphereDetail = 10;

    // here is our container
    var frosty = new THREE.Object3D();

    // function to add one snowball to frosty.  
    // height is distance of origin to sphere center along y.
    function addSphere(radius,height) {
        var sg = new THREE.SphereGeometry(radius,sphereDetail,sphereDetail);
        var s = new THREE.Mesh(sg, bodyMaterial);
        console.log("adding sphere: " + radius + " at " + height);
        s.position.y = height;   // specify where along Y to add it
        frosty.add(s);
    }

    // ================================================================
    // main code of function
    
    var y = radiusBottom;  // center of bottom sphere
    addSphere(radiusBottom,y);
    y += radiusBottom + radiusMiddle;  // center of middle sphere
    addSphere(radiusMiddle,y);
    y += radiusMiddle + radiusTop;  // center of top sphere
    addSphere(radiusTop,y);
    
    return frosty;
}

// has to be global, hence the long name.
var guiParametersSnowperson = {
    radiusBottom: 3,
    radiusMiddle: 2,
    radiusTop: 1,
    X: 0,
    Y: 0,
    Z: 0,
    wireframe: true
}

function addSnowPersonGUI(scene) {
    var sp = createSnowPerson( guiParametersSnowperson.wireframe,
                               guiParametersSnowperson.radiusBottom,
                               guiParametersSnowperson.radiusMiddle,
                               guiParametersSnowperson.radiusTop);
    // instance transform to place it in the scene.
    sp.position.set( guiParametersSnowperson.X,
                     guiParametersSnowperson.Y,
                     guiParametersSnowperson.Z );
    // don't forget this!
    scene.add(sp);
    return sp;
}

var renderer = new THREE.WebGLRenderer();
var scene = new THREE.Scene();
                        

var snowperson = addSnowPersonGUI(scene);

TW.mainInit(renderer, scene);
var state = TW.cameraSetup(renderer,
               scene,
               // maybe a worst-case scenario?
               {minx: -10, maxx: +10,
                miny: 0, maxy: 30,
                minz: -10, maxz: +10});
TW.toggleAxes("show");

var render = state.render;
TW.setKeyboardCallback("w",
                       function () {
                           guiParametersSnowperson.wireframe = !guiParametersSnowperson.wireframe;
                           console.log("wireframe is "+(guiParametersSnowperson.wireframe?"on":"off"));
                           redraw();
                       },
                       "toggle wireframe");

function redraw() {
    // since all three spheres share exactly the same object for the material,
    // we can just set any one of the three children's material to wireframe.
    /*
    snowperson.children[0].material.wireframe = 
        snowperson.children[1].material.wireframe = 
        snowperson.children[2].material.wireframe = guiParametersSnowperson.wireframe;
        */
    snowperson.children[0].material.wireframe = guiParametersSnowperson.wireframe;

    snowperson.position.x = guiParametersSnowperson.X;
    snowperson.position.y = guiParametersSnowperson.Y;
    snowperson.position.z = guiParametersSnowperson.Z;
    render();
}

function rebuildAndRedraw() {
    scene.remove(snowperson);
    snowperson = addSnowPersonGUI(scene);
    redraw();
}

var gui = new dat.GUI();
gui.add(guiParametersSnowperson,'wireframe').onChange(redraw);
gui.add(guiParametersSnowperson,'X', -3, +3).onChange(redraw);
gui.add(guiParametersSnowperson,'Y', -3, +3).onChange(redraw);
gui.add(guiParametersSnowperson,'Z', -3, +3).onChange(redraw);
gui.add(guiParametersSnowperson,'radiusTop', 0.1, 5).step(0.1).onChange(rebuildAndRedraw);
gui.add(guiParametersSnowperson,'radiusMiddle', 0.1, 5).step(0.1).onChange(rebuildAndRedraw);
gui.add(guiParametersSnowperson,'radiusBottom', 0.1, 5).step(0.1).onChange(rebuildAndRedraw);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/three.min.js
  2. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/OrbitControls.js
  3. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/tw.js
  4. https://s3.amazonaws.com/m.mr-pc.org/t/cisc3620/2020sp/dat.gui.min.js