Up until now, the cube we have drawn was created by a custom object. There is an easier way of creating basic shapes. This is using primitives.

What primitives are, essentially, is a set of pre-configured objects that you can create instances of to display on your screen.

I have removed all code from the previous tutorial that created the cube, and instead have created a new function that draws a cube, accompanied by a few new friends. First the code creates a fewprimitives. Since objects are created at the origin, we need to translate the object to a spot we want them or else they will all be on top of each other, therefore after we create the objects, we apply a translation translation to the objects, and add them to the 3D root, so that they will be displayed.

function createShapes(material) {
  var cube = o3djs.primitives.createCube(
      g_pack,
      material, 
      Math.sqrt(2));   // The length of each side of the cube.

  var sphere = o3djs.primitives.createSphere(
      g_pack,
      material,
      1.0,   // Radius of the sphere.
      30,    // Number of meridians.
      20);    // Number of parallels.

  var cylinder = o3djs.primitives.createCylinder(
      g_pack,
      material,
      0.5,   // Radius.
      1.5,   // Depth.
      20,    // Number of radial subdivisions.
      20);   // Number of vertical subdivisions.

  var plane = o3djs.primitives.createPlane(
      g_pack,
      material,
      1,      // Width.
      1.618,  // Depth.
      3,      // Horizontal subdivisions.
      3);     // Vertical subdivisions.

  // Make a polygon to extrude for the prism.
  var polygon = [];
  var n = 10;
  for (var i = 0; i < n; ++i) {
    var theta = 2.0 * i * Math.PI / n;
    var radius = (i % 2) ? 1 : 0.382;
    polygon.push([radius * Math.cos(theta), radius * Math.sin(theta)]);
  }

  var prism = o3djs.primitives.createPrism(
      g_pack,
      material,
      polygon,  // The profile polygon to be extruded.
      1);       // The depth of the extrusion.

  var disc = o3djs.primitives.createDisc(
      g_pack,
      material,
      1,   // Radius.
      7,   // Divisions.
      2,   // Stacks (optional).
      0,   // Start Stack (optional).
      2);  // Stack Power (optional).

  // Add the shapes to the transforms.
  var transformTable = [
    {shape: cube, translation: [-2, 1, 0]},
    {shape: sphere, translation: [0, 1, 0]},
    {shape: cylinder, translation: [2, 1, 0]},
    {shape: plane, translation: [-2, -1, 0]},
    {shape: prism, translation: [0, -1, 0]},
    {shape: disc, translation: [2, -1, 0]}
  ];

  for (var i = 0; i < transformTable.length; i++) {
    var transform = g_pack.createObject('Transform');
    transform.addShape(transformTable[i].shape);
    transform.translate(transformTable[i].translation);
    transform.parent = g_3dRoot;
  }
}


When you run the code, you may notice that the objects are all a rather boring red, with no shading or lighting, which is something that I will address in the next tutorial…

Here is the full listing of the javascript file

o3djs.require('o3djs.util');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.canvas');
o3djs.require('o3djs.quaternions');
o3djs.require('o3djs.event');
o3djs.require('o3djs.arcball');
o3djs.require('o3djs.primitives');

// Events
// Run the init() function once the page has finished loading.
// Run the uninit() function when the page has is unloaded.
window.onload = init;
window.onunload = uninit;

// global variables
var g_o3dElement;
var g_o3d;
var g_math;
var g_client;
var g_pack;
var g_clock = 0;
var g_timeMult = 1;
var g_cubeTransform;
var g_textCanvas;
var g_paint;
var g_canvasLib;
var g_3dRoot;
var g_hudRoot;
var g_viewInfo;
var g_hudViewInfo;
var g_keyPressDelta = 0.05;

var g_quaternions;
var g_aball;
var g_thisRot;
var g_lastRot;
var g_dragging = false;
var g_camera = {
  eye: [0, 0, 10],
  target: [0, 0, 0]
};

function startDragging(e) {
  g_lastRot = g_thisRot;
  g_aball.click([e.x, e.y]);
  g_dragging = true;
}

function drag(e) {
  if (g_dragging) {
    var rotationQuat = g_aball.drag([e.x, e.y]);
    var rot_mat = g_quaternions.quaternionToRotation(rotationQuat);
    g_thisRot = g_math.matrix4.mul(g_lastRot, rot_mat);
    var m = g_3dRoot.localMatrix;
    g_math.matrix4.setUpper3x3(m, g_thisRot);
    g_3dRoot.localMatrix = m;
   }
}

function stopDragging(e) {
  g_dragging = false;
}

function scrollMe(e) {
  if (e.deltaY) {
    g_camera.eye =
        g_math.mulScalarVector((e.deltaY < 0 ? 11 : 13) / 12, g_camera.eye);
    g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_camera.eye,
                                                       g_camera.target,
                                                       [0, 1, 0]);
 }
}         

function drawText(str) {
  // Clear to completely transparent.
  g_textCanvas.canvas.clear([0.5, 0.5, 0.5, 0.5]);

  // Reuse the global paint object
  var paint = g_paint;
  paint.color = [1, 1, 1, 1];
  paint.textSize = 12;
  paint.textTypeface = 'Comic Sans MS';
  paint.textAlign = g_o3d.CanvasPaint.LEFT;
  paint.shader = null;
  g_textCanvas.canvas.drawText(str, 10, 30, paint);

  g_textCanvas.updateTexture();
}         

/**
 * This method gets called every time O3D renders a frame.  Here's
 * where we update the cube's transform to make it spin.
 * @param {o3d.RenderEvent} renderEvent The render event object that 
 * gives us the elapsed time since the last time a frame was rendered.
 */
function renderCallback(renderEvent) {
  g_clock += renderEvent.elapsedTime * g_timeMult;
  drawText("Hello world - " + (Math.round(g_clock * 100) / 100) + "s");       
}

/**
 * Function performing the rotate action in response to a key-press.
 * Rotates the scene based on key pressed. (w ,s, a, d). Note that the
 * x,y-axis referenced here are relative to the current view of scene.
 * @param {keyPressed} The letter pressed, in lower case.
 * @param {delta} The angle by which the scene should be rotated.
 * @return true if an action was taken.
 */
function keyPressedAction(keyPressed, delta) {
  var actionTaken = false;
  switch(keyPressed) {
    case 'a':
      g_3dRoot.localMatrix =
          g_math.matrix4.mul(g_3dRoot.localMatrix,
                             g_math.matrix4.rotationY(-delta));
      actionTaken = true;
      break;
    case 'd':
      g_3dRoot.localMatrix =
          g_math.matrix4.mul(g_3dRoot.localMatrix,
                             g_math.matrix4.rotationY(delta));
      actionTaken = true;
      break;
    case 'w':
      g_3dRoot.localMatrix =
          g_math.matrix4.mul(g_3dRoot.localMatrix,
                             g_math.matrix4.rotationX(-delta));
      actionTaken = true;
      break;
    case 's':
      g_3dRoot.localMatrix =
          g_math.matrix4.mul(g_3dRoot.localMatrix,
                             g_math.matrix4.rotationX(delta));
      actionTaken = true;
      break;
  }
  return actionTaken;
}

/**
 * Callback for the keypress event.
 * Invokes the action to be performed for the key pressed.
 * @param {event} keyPress event passed to us by javascript.
 */
function keyPressedCallback(event) {
  event = event || window.event;

  // Ignore accelerator key messages.
  if (event.metaKey)
    return;

  var keyChar =String.fromCharCode(o3djs.event.getEventKeyChar(event));
  // Just in case they have capslock on.
  keyChar = keyChar.toLowerCase();

  if (keyPressedAction(keyChar, g_keyPressDelta)) {
    o3djs.event.cancel(event);
  }
}         

function createShapes(material) {
  var cube = o3djs.primitives.createCube(
      g_pack,
      material, 
      Math.sqrt(2));   // The length of each side of the cube.

  var sphere = o3djs.primitives.createSphere(
      g_pack,
      material,
      1.0,   // Radius of the sphere.
      30,    // Number of meridians.
      20);    // Number of parallels.

  var cylinder = o3djs.primitives.createCylinder(
      g_pack,
      material,
      0.5,   // Radius.
      1.5,   // Depth.
      20,    // Number of radial subdivisions.
      20);   // Number of vertical subdivisions.

  var plane = o3djs.primitives.createPlane(
      g_pack,
      material,
      1,      // Width.
      1.618,  // Depth.
      3,      // Horizontal subdivisions.
      3);     // Vertical subdivisions.

  // Make a polygon to extrude for the prism.
  var polygon = [];
  var n = 10;
  for (var i = 0; i < n; ++i) {
    var theta = 2.0 * i * Math.PI / n;
    var radius = (i % 2) ? 1 : 0.382;
    polygon.push([radius * Math.cos(theta), radius * Math.sin(theta)]);
  }

  var prism = o3djs.primitives.createPrism(
      g_pack,
      material,
      polygon,  // The profile polygon to be extruded.
      1);       // The depth of the extrusion.

  var disc = o3djs.primitives.createDisc(
      g_pack,
      material,
      1,   // Radius.
      7,   // Divisions.
      2,   // Stacks (optional).
      0,   // Start Stack (optional).
      2);  // Stack Power (optional).

  // Add the shapes to the transforms.
  var transformTable = [
    {shape: cube, translation: [-2, 1, 0]},
    {shape: sphere, translation: [0, 1, 0]},
    {shape: cylinder, translation: [2, 1, 0]},
    {shape: plane, translation: [-2, -1, 0]},
    {shape: prism, translation: [0, -1, 0]},
    {shape: disc, translation: [2, -1, 0]}
  ];

  for (var i = 0; i < transformTable.length; i++) {
    var transform = g_pack.createObject('Transform');
    transform.addShape(transformTable[i].shape);
    transform.translate(transformTable[i].translation);
    transform.parent = g_3dRoot;
  }
}

/**
 * Creates the client area.
 */
function init() {
  o3djs.util.makeClients(initStep2);
}

/**
 * Initializes O3D.
 * @param {Array} clientElements Array of o3d object elements.
 */
function initStep2(clientElements) {
  // Initializes global variables and libraries.
  g_o3dElement = clientElements[0];
  g_client = g_o3dElement.client;
  g_o3d = g_o3dElement.o3d;
  g_math = o3djs.math;
  g_quaternions = o3djs.quaternions;

  // Initialize O3D sample libraries.
  o3djs.base.init(g_o3dElement);

  // Create a pack to manage the objects created.
  g_pack = g_client.createPack();

  //Create the arcball which is used for the rotation
  g_aball = o3djs.arcball.create(300, 300);

  //Initialise rotation matrixes
  g_lastRot = g_math.matrix4.identity();
  g_thisRot = g_math.matrix4.identity();

  // Create 2 root transforms, one for the 3d parts and 2d parts.
  // This is not strictly neccassary but it is helpful.
  g_3dRoot = g_pack.createObject('Transform');
  g_hudRoot = g_pack.createObject('Transform');

  // Create the render graph for a view.
  g_viewInfo = o3djs.rendergraph.createBasicView(
      g_pack,
      g_3dRoot,
      g_client.renderGraphRoot);

  // Set the background color to black.
  g_viewInfo.clearBuffer.clearColor = [0, 0, 0, 1];

  // Create a second view for the hud. 
  g_hudViewInfo = o3djs.rendergraph.createBasicView(
       g_pack,
       g_hudRoot,
       g_client.renderGraphRoot);

  // Make sure the hud gets drawn after the 3d stuff
  g_hudViewInfo.root.priority = g_viewInfo.root.priority + 1;

  // Turn off clearing the color for the hud since that would erase the
  // 3d parts but leave clearing the depth and stencil so the HUD is
  //  unaffected by anything done by the 3d parts.
  g_hudViewInfo.clearBuffer.clearColorFlag = false;

  // Set up a perspective view
  g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
      g_math.degToRad(30), // 30 degree fov.
      g_client.width / g_client.height,
      1,                  // Near plane.
      5000);              // Far plane.

  // Set up our view transformation to look towards the world origin 
  // where the cube is located.
  g_viewInfo.drawContext.view = g_math.matrix4.lookAt(g_camera.eye, //eye
                                            g_camera.target,  // target
                                            [0, 1, 0]); // up

  //Set up the 2d orthographic view
  g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
      0 + 0.5,
      g_client.width + 0.5,
      g_client.height + 0.5,
      0 + 0.5,
      0.001,
      1000);

  g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
      [0, 0, 1],   // eye
      [0, 0, 0],   // target
      [0, 1, 0]);  // up                                                     

  // Load effect
  var redEffect = g_pack.createObject('Effect');
  var shaderString = 'shaders/solidred.shader';
  o3djs.effect.loadEffect(redEffect, shaderString);

  // Create a Material for the mesh.
  var redMaterial = g_pack.createObject('Material');

  // Set the material's drawList.
  redMaterial.drawList = g_viewInfo.performanceDrawList;

  // Apply our effect to this material. The effect tells the 3D
  // hardware which shaders to use.
  redMaterial.effect = redEffect;

  createShapes(redMaterial);

  // Create the global paint object that's used by draw operations.
  g_paint = g_pack.createObject('CanvasPaint');

  // Creates an instance of the canvas utilities library.
  g_canvasLib = o3djs.canvas.create(g_pack, g_hudRoot, g_hudViewInfo);

  // Create a canvas that will be used to display the text.
  g_textCanvas = g_canvasLib.createXYQuad(70, 70, 0, 100, 50, true);
  // Set our render callback for animation.
  // This sets a function to be executed every time frame is rendered.
  g_client.setRenderCallback(renderCallback);

  //Set up a callback to interpret keypresses
  window.document.onkeypress = keyPressedCallback;

  //Set up mouse events
  o3djs.event.addEventListener(g_o3dElement, 'mousedown', startDragging);
  o3djs.event.addEventListener(g_o3dElement, 'mousemove', drag);
  o3djs.event.addEventListener(g_o3dElement, 'mouseup', stopDragging);
  o3djs.event.addEventListener(g_o3dElement, 'wheel', scrollMe);           
}

/**
 * Removes callbacks so they aren't called after the page has unloaded.
 */
function uninit() {
  if (g_client) {
    g_client.cleanup();
  }
}
Share