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(); } }
Comments