O3D makes it very easy to load scenes. A scene, in O3D, is just a collection files which can render an object, or even multiple objects. The scene file normally has the extension .o3dtgz and is essentially just a tgz archive which contains the various files making up the scene.

A scene can contain the shaders, textures and the data making up the meshes as well. This means is that when O3D loads the scene file, it is able to set up everything associated with the models in the scene, and the application does not need to worry about setting up materials or shaders for the models making up the scene. This doesn’t mean that you can’t override these components though, which makes modifying models once they in the scene an easy job to do too.

So anyway, let’s look at the code. The first thing is to include the pack and scene libraries.

o3djs.require('o3djs.pack');
o3djs.require('o3djs.scene');



In the intialisation function we call the loadScene() function, which we will define next. I have used the teapot from the O3D samples as the scene to load, but this could be any valid scene file.

  loadScene(g_pack, 'tutorial14/teapot.o3dtgz', g_3dRoot);



Now, we get to the function that does the work. We pass the pack (which in our case is g_pack), the filename of the scene file to load, and g_3DRoot which the scene will be attached to.

The first thing we do here is load the scene using o3djs.scene.loadscene(), which then calls the callback function once the load has completed. In the callback function, if no error occured, we then make a call to o3djs.pack.preparePack() which sets up the pack with the details of the view, so that the camera angle, etc is set up correctly.

function loadScene(pack, fileName, parent) {
  var scenePath = o3djs.util.getCurrentURI() + fileName;
  o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);

  function callback(pack, parent, exception) {
    if (exception) {
      alert('Could not load: ' + fileName + '\n' + exception);
      return;
    }
    o3djs.pack.preparePack(pack, g_viewInfo);
  }
}



One more small change I made in the code from prevous tutorials, is that I moved the primitives that I am creating out of the way a bit, as the scene is loaded at the origin. I could just as easily moved the scene though.

Here is the full listing

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');
o3djs.require('o3djs.picking');
o3djs.require('o3djs.fps');
o3djs.require('o3djs.io');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.scene');

// 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_lightPosition = [5, 5, 7];
var g_camera = {
  eye: [0, 0, 10],
  target: [0, 0, 0]
};
var g_mouseX = 0;
var g_mouseY = 0;
var g_spinningObject = false;
var g_pickedInfo;
var g_treeInfo;  
var g_fpsManager;
var g_sampler;
var g_backgroundCanvas;
var g_backgroundRoot;
var g_backgroundViewInfo;
var g_backgroundCanvasLib;
var g_backgroundTexture;

//Event handler for the mousedown event
function mouseDown(e) {
  if (e.button == 0) {
      var worldRay = o3djs.picking.clientPositionToWorldRay(
          e.x,
          e.y,
          g_viewInfo.drawContext,
          g_client.width,
          g_client.height);  
      g_treeInfo.update();
      g_pickedInfo = g_treeInfo.pick(worldRay);
      if (g_pickedInfo) {
        g_spinningObject = true;

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

//Event handler for the mousemove event
function mouseMove(e) {
  g_mouseX = e.x;
  g_mouseY = e.y;

  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;
   }

}

//Event handler for the mouseup event
function mouseUp(e) {
  g_dragging = false;
  g_spinningObject = false;
}

//Even handler for the scroll button
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]);
 }
}         

// Updates the transform info for our scene
function updateInfo() {
  if (!g_treeInfo) {
    g_treeInfo = o3djs.picking.createTransformInfo(g_3dRoot, null);
  }
  g_treeInfo.update();
}

function drawText(str) {
  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_fpsManager.update(renderEvent);
  g_clock += renderEvent.elapsedTime * g_timeMult;
  drawText("(" + g_mouseX  + "," + g_mouseY + ")");
  if (g_spinningObject) {

     var pickTrans = g_pickedInfo.shapeInfo.parent.transform;
     pickTrans.rotateX(0.05);  
     pickTrans.rotateY(0.05);  
  }  
  updateInfo();  
}

/**
 * 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);
  }
}         

/**
 * Creates a phong material based on the given single color.
 * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components
 *   of a color.
 * @return {Material} A phong material whose overall pigment is baseColor.
 */
function createPhongMaterial(baseColor) {
  // Create a new, empty Material object.
  var material = g_pack.createObject('Material');

  o3djs.effect.attachStandardShader(
      g_pack, material, g_lightPosition, 'phong');

  material.drawList = g_viewInfo.performanceDrawList;

  // Assign parameters to the phong material.
  material.getParam('emissive').value = [0, 0, 0, 1];
  material.getParam('ambient').value = g_math.mulScalarVector(0.1, baseColor);
  material.getParam('diffuse').value = g_math.mulScalarVector(0.9, baseColor);
  material.getParam('specular').value = [.2, .2, .2, 1];
  material.getParam('shininess').value = 20;

  return material;
}

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,
      createPhongMaterial([1, 1, 0, 1]), 
      1.0,   // Radius of the sphere.
      30,    // Number of meridians.
      20);    // Number of parallels.

  var cylinder = o3djs.primitives.createCylinder(
      g_pack,
      createPhongMaterial([0, 1, 0, 1]), 
      0.5,   // Radius.
      1.5,   // Depth.
      20,    // Number of radial subdivisions.
      20);   // Number of vertical subdivisions.

  var plane = o3djs.primitives.createPlane(
      g_pack,
      createPhongMaterial([0, 0, 1, 1]), 
      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,
      createPhongMaterial([0, 1, 1, 1]), 
      polygon,  // The profile polygon to be extruded.
      1);       // The depth of the extrusion.

  var disc = o3djs.primitives.createDisc(
      g_pack,
      createPhongMaterial([1, 0, 1, 1]), 
      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, -3]},
    {shape: sphere, translation: [0, 1, -3]},
    {shape: cylinder, translation: [2, 1, -3]},
    {shape: plane, translation: [-2, -1, -3]},
    {shape: prism, translation: [0, -1, -3]},
    {shape: disc, translation: [2, -1, -3]}
  ];

  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;
  }
}

/**
 * Loads a scene into the transform graph.
 * @param {!o3d.Pack} pack Pack to load scene into.
 * @param {string} fileName filename of the scene.
 * @param {!o3d.Transform} parent parent node in the transform graph to
 *      which to load the scene into.
 */
function loadScene(pack, fileName, parent) {
  // Get our full path to the scene
  var scenePath = o3djs.util.getCurrentURI() + fileName;

  // Load the file given the full path, and call the callback function
  // when its done loading.
  o3djs.scene.loadScene(g_client, pack, parent, scenePath, callback);

  /**
   * Our callback is called once the scene has been loaded into memory
   * from the web or locally.
   * @param {!o3d.Pack} pack The pack that was passed in above.
   * @param {!o3d.Transform} parent The parent that was passed in above.
   * @param {*} exception null if loading succeeded.
   */
  function callback(pack, parent, exception) {
    if (exception) {
      alert('Could not load: ' + fileName + '\n' + exception);
      return;
    }
    // Generate draw elements and setup material draw lists.
    o3djs.pack.preparePack(pack, g_viewInfo);

  }
}

/**
 * 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');
  g_backgroundRoot = g_pack.createObject('Transform');

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

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

  // 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;
  g_viewInfo.clearBuffer.clearColorFlag = false;

  // Create a view for the background. 
  g_backgroundViewInfo = o3djs.rendergraph.createBasicView(
       g_pack,
       g_backgroundRoot,
       g_client.renderGraphRoot);

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

  //g_backgroundViewInfo.root.priority + 5;
  // 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                                                     

  //Set up the 2d orthographic view
  g_backgroundViewInfo.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_backgroundViewInfo.drawContext.view = g_math.matrix4.lookAt(
      [0, 0, 1],   // eye
      [0, 0, 0],   // target
      [0, 1, 0]);  // up     

  // Create an FPS manager.
  g_fpsManager = o3djs.fps.createFPSManager(g_pack,
                                            g_client.width,
                                            g_client.height,
                                            g_client.renderGraphRoot);

  // Load the scene into the transform graph as a child of the g_sceneRoot.
  loadScene(g_pack, 'tutorial14/teapot.o3dtgz', g_3dRoot);

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

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

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

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

  // Create an O3D Param on the material for every uniform used by the
  // shader.
  cubeEffect.createUniformParameters(cubeMaterial);

  // Get the material's sampler parameter so that we can set the texture value
  // to it.

  var samplerParam = cubeMaterial.getParam('texSampler0');

  // Create a Sampler object and set the min filtering to ANISOTROPIC.  This
  // will improve the quality of the rendered texture when viewed at an angle.
  g_sampler = g_pack.createObject('Sampler');
  g_sampler.minFilter = g_o3d.Sampler.ANISOTROPIC;
  g_sampler.maxAnisotropy = 4;
  samplerParam.value = g_sampler; 

  o3djs.io.loadTexture(g_pack, 'tutorial14/image.png', 
      function(texture, exception) {
        if (exception) {
          g_sampler.texture = null;
        } else {
          // Set the texture on the sampler object to the newly created texture
          // object returned by the request.
          g_sampler.texture = texture;

          // We can add shapes now that the texture is loaded
          createShapes(cubeMaterial);
        }
      });

  // 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(200, 0, 0, 100, 50, true);

  g_backgroundCanvasLib = o3djs.canvas.create(g_pack, g_backgroundRoot, g_backgroundViewInfo);
  g_backgroundCanvas = g_backgroundCanvasLib.createXYQuad(0, 0, 0, g_client.width, g_client.height, true);

  g_backgroundCanvas.canvas.clear([0, 0, 0, 1]);

  o3djs.io.loadTexture(g_pack, 'tutorial14/bg.jpg', function(texture, exception) {
    if (exception) {
      alert(exception);
    } else {
      if (g_backgroundTexture) {
        g_pack.removeObject(g_backgroundTexture);
      }

      g_backgroundTexture = texture;
      g_backgroundCanvas.canvas.drawBitmap(g_backgroundTexture, 0, g_client.width);
      g_backgroundCanvas.updateTexture();
    }
  });  

  // 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', mouseDown);
  o3djs.event.addEventListener(g_o3dElement, 'mousemove', mouseMove);
  o3djs.event.addEventListener(g_o3dElement, 'mouseup', mouseUp);
  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