diff options
author | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-29 22:17:57 +0000 |
---|---|---|
committer | petersont@google.com <petersont@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-29 22:17:57 +0000 |
commit | 8258a86b378bc6a41fa8947aee92375ec8ff615c (patch) | |
tree | d5c8cddbbaa23500f951c09cf4f89957459efdc1 /o3d | |
parent | 0380c12a8f7fde532f95da39f48376d256367f9b (diff) | |
download | chromium_src-8258a86b378bc6a41fa8947aee92375ec8ff615c.zip chromium_src-8258a86b378bc6a41fa8947aee92375ec8ff615c.tar.gz chromium_src-8258a86b378bc6a41fa8947aee92375ec8ff615c.tar.bz2 |
Added culling sample.
Review URL: http://codereview.chromium.org/1703014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45993 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d')
-rw-r--r-- | o3d/samples/o3d-webgl-samples/culling.html | 320 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/bitmap.js | 1 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/bounding_box.js | 181 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/client.js | 115 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/draw_list.js | 6 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/element.js | 6 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/field.js | 24 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/pack.js | 1 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param.js | 37 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/primitive.js | 40 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/ray_intersection_info.js | 6 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/shape.js | 31 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/transform.js | 76 | ||||
-rw-r--r-- | o3d/samples/o3djs/webgl.js | 7 |
14 files changed, 772 insertions, 79 deletions
diff --git a/o3d/samples/o3d-webgl-samples/culling.html b/o3d/samples/o3d-webgl-samples/culling.html new file mode 100644 index 0000000..af1031c --- /dev/null +++ b/o3d/samples/o3d-webgl-samples/culling.html @@ -0,0 +1,320 @@ +<!-- +Copyright 2009, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<!-- +O3D Culling. + +Make sure things off screen get culled. + +By default nothing is culled. If you want object to be culled you must setup +bounding boxes in the transform graph that fit the needs of your application +and then turn on culling for those objects you want culled. +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" + "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<title> +Culling. +</title> +<script type="text/javascript" src="../o3d-webgl/base.js"></script> +<script type="text/javascript" src="../o3djs/base.js"></script> +<script type="text/javascript" id="o3dscript"> +o3djs.base.o3d = o3d; +o3djs.require('o3djs.webgl'); +o3djs.require('o3djs.util'); +o3djs.require('o3djs.math'); +o3djs.require('o3djs.rendergraph'); +o3djs.require('o3djs.primitives'); +o3djs.require('o3djs.material'); + +// global variables +var g_timeMult = 1.0; +var g_framesRendered = 0; +var g_o3d; +var g_math; +var g_client; +var g_viewInfo; +var g_pack; +var g_totalTransformsElement; +var g_transformsProcessedElement; +var g_transformsCulledElement; +var g_totalDrawElementsElement; +var g_totalDrawableThingsElement; +var g_drawElementsProcessedElement; +var g_drawElementsCulledElement; +var g_drawElementsRenderedElement; +var g_primitivesRenderedElement; +var g_groupTransforms = []; +var GROUPS_ACROSS = 2; +var UNITS_ACROSS_GROUP = 2; +var TOTAL_ACROSS = GROUPS_ACROSS * UNITS_ACROSS_GROUP; +var HALF_WIDTH = TOTAL_ACROSS * 0.0; +var UNIT_SPACING = 200; + +function createInstances(pack, shape) { + // Make a grid of transforms and put a shape instance on each one. + for (var g = 0; g < GROUPS_ACROSS; g++) { + for (var h = 0; h < GROUPS_ACROSS; h++) { + for (var i = 0; i < GROUPS_ACROSS; i++) { + var groupTransform = pack.createObject('Transform'); + g_groupTransforms[g_groupTransforms.length] = groupTransform; + groupTransform.parent = g_client.root; + // Turn on culling for this transform. + groupTransform.cull = true; + var boundingBox = new g_o3d.BoundingBox([0, 0, 0], + [0, 0, 0]); + groupTransform.translate( + (g * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING, + (h * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING, + (i * UNITS_ACROSS_GROUP - HALF_WIDTH) * UNIT_SPACING); + + for (var x = 0; x < UNITS_ACROSS_GROUP; x++) { + for (var y = 0; y < UNITS_ACROSS_GROUP; y++) { + for (var z = 0; z < UNITS_ACROSS_GROUP; z++) { + var transform = pack.createObject('Transform'); + transform.parent = groupTransform; + // Turn on culling for this transform. + transform.cull = true; + transform.addShape(shape); + // Add up the bounding boxes of all the elements. + var elements = shape.elements; + var box = elements[0].boundingBox; + for (var ee = 1; ee < elements.length; ee++) { + box = box.add(elements[ee].boundingBox); + } + + // Set the transform to have a bounding box that is the sum + // of all the elements under it. + transform.boundingBox = box; + transform.translate( + (x - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING, + (y - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING, + (z - UNITS_ACROSS_GROUP * 0.5) * UNIT_SPACING); + transform.createParam('diffuse', 'ParamFloat4').value = [ + (g * UNITS_ACROSS_GROUP + x) * (1 / TOTAL_ACROSS), + (h * UNITS_ACROSS_GROUP + y) * (1 / TOTAL_ACROSS), + (i * UNITS_ACROSS_GROUP + z) * (1 / TOTAL_ACROSS), + 1]; + // Add the box for this bounding box to the box for the group. + var box = transform.boundingBox.mul(transform.localMatrix); + boundingBox = boundingBox.add(box); + } + } + } + // Set the bounding box for the group transform to encompass all + // the transforms below it. + groupTransform.boundingBox = boundingBox; + } + } + } +} + +/** + * Creates the client area. + */ +function init() { + // These are here so that they are visible to both the browser (so + // selenium sees them) and the embedded V8 engine. + window.g_clock = 0; + window.g_finished = false; // for selenium testing. + + o3djs.webgl.makeClients(initStep2); +} + +/** + * Initializes O3D and creates one shape. + * @param {Array} clientElements Array of o3d object elements. + */ +function initStep2(clientElements) { + // Initializes global variables and libraries. + var o3dElement = clientElements[0]; + g_o3d = o3dElement.o3d; + g_math = o3djs.math; + + // Set window.g_client as well. Otherwise when the sample runs in + // V8, selenium won't be able to find this variable (it can only see + // the browser environment). + window.g_client = g_client = o3dElement.client; + + g_totalDrawableThingsElement = + o3djs.util.getElementById('totalDrawableThings'); + g_totalTransformsElement = + o3djs.util.getElementById('totalTransforms'); + g_transformsProcessedElement = + o3djs.util.getElementById('transformsProcessed'); + g_transformsCulledElement = + o3djs.util.getElementById('transformsCulled'); + g_totalDrawElementsElement = + o3djs.util.getElementById('totalDrawElements'); + g_drawElementsProcessedElement = + o3djs.util.getElementById('drawElementsProcessed'); + g_drawElementsCulledElement = + o3djs.util.getElementById('drawElementsCulled'); + g_drawElementsRenderedElement = + o3djs.util.getElementById('drawElementsRendered'); + g_primitivesRenderedElement = + o3djs.util.getElementById('primitivesRendered'); + + // Creates a pack to manage our resources/assets + g_pack = g_client.createPack(); + + g_viewInfo = o3djs.rendergraph.createBasicView( + g_pack, + g_client.root, + g_client.renderGraphRoot); + + // Create our projection matrix, with a vertical field of view of 45 + // degrees a near clipping plane of 0.1 and far clipping plane of 10000. + g_viewInfo.drawContext.projection = g_math.matrix4.perspective( + g_math.degToRad(45), + g_client.width / g_client.height, + 0.1, + 10000); + + // Create and load the effect. + var material = o3djs.material.createBasicMaterial( + g_pack, + g_viewInfo, + [1, 1, 1, 1]); + + // Create 2 spheres. + var shape1 = o3djs.primitives.createSphere( + g_pack, + material, + 40, + 20, + 20, + g_math.matrix4.translation([-50, 0, 0])); + + var shape2 = o3djs.primitives.createSphere( + g_pack, + material, + 20, + 20, + 20, + g_math.matrix4.translation([50, 0, 0])); + + // Create a shape and move the 2 sphere primitives to the same shape. + // This is done to show that each of the primitives under the shape + // will get culled separately. + var shape = g_pack.createObject('Shape'); + shape1.elements[0].owner = shape; + shape2.elements[0].owner = shape; + // delete the old shapes. + g_pack.removeObject(shape1); + g_pack.removeObject(shape2); + var elements = shape.elements; + // Turn on culling for the two sphere elements. + elements[0].cull = true; + elements[1].cull = true; + + createInstances(g_pack, shape); + + g_totalDrawableThingsElement.innerHTML = + GROUPS_ACROSS * UNITS_ACROSS_GROUP * + GROUPS_ACROSS * UNITS_ACROSS_GROUP * + GROUPS_ACROSS * UNITS_ACROSS_GROUP; + + g_totalDrawElementsElement.innerHTML = g_client.getObjectsByClassName( + 'o3d.DrawElement').length; + g_totalTransformsElement.innerHTML = g_client.getObjectsByClassName( + 'o3d.Transform').length; + + // Setup an onrender callback for animation. + g_client.setRenderCallback(onrender); + + window.g_finished = true; // for selenium testing. +} + +var g_flag = false; + +// spin the camera. +function onrender(renderEvent) { + g_framesRendered++; + // Get the number of seconds since the last render. + var elapsedTime = renderEvent.elapsedTime; + + // Update g_clock in the browser and cache a V8 copy that can be + // accessed efficiently. g_clock must be in the browser for selenium. + var clock = window.g_clock + elapsedTime * window.g_timeMult; + window.g_clock = clock; + + var x = Math.sin(clock * 0.1) * 300; + var z = Math.cos(clock * 0.1) * 300; + var y = Math.sin(clock * 0.2) * 300; + + g_viewInfo.drawContext.view = g_math.matrix4.lookAt( + [x, y, z], + [0, 0, 0], + [0, 1, 0]); + + g_transformsProcessedElement.innerHTML = renderEvent.transformsProcessed; + g_transformsCulledElement.innerHTML = renderEvent.transformsCulled; + g_drawElementsProcessedElement.innerHTML = renderEvent.drawElementsProcessed; + g_drawElementsCulledElement.innerHTML = renderEvent.drawElementsCulled; + g_drawElementsRenderedElement.innerHTML = renderEvent.drawElementsRendered; + g_primitivesRenderedElement.innerHTML = renderEvent.primitivesRendered; +} + +/** + * Remove any callbacks so they don't get called after the page has unloaded. + */ +function unload() { + if (g_client) { + g_client.cleanup(); + } +} + +</script> +</head> +<body onload="init()" onunload="unload()"> +<h1>Culling</h1> +Objects off screen should get culled. +<br/> +<!-- Start of O3D client area --> +<div id="o3d" style="width: 800px; height: 600px;"></div> +<!-- End of O3D client area --> + +<table> +<tr><td>Total Drawable Things:</td><td><span id="totalDrawableThings">-</span></td></tr> +<tr><td>Total Transforms:</td><td><span id="totalTransforms">-</span></td></tr> +<tr><td>Transforms Processed:</td><td><span id="transformsProcessed">-</span></td></tr> +<tr><td>Transforms Culled:</td><td><span id="transformsCulled">-</span></td></tr> +<tr><td>Total DrawElements:</td><td><span id="totalDrawElements">-</span></td></tr> +<tr><td>DrawElements Processed:</td><td><span id="drawElementsProcessed">-</span></td></tr> +<tr><td>DrawElements Culled:</td><td><span id="drawElementsCulled">-</span></td></tr> +<tr><td>DrawElements Rendered:</td><td><span id="drawElementsRendered">-</span></td></tr> +<tr><td>Primitives Rendered:</td><td><span id="primitivesRendered">-</span></td></tr> +</table> +</body> +</html> diff --git a/o3d/samples/o3d-webgl/bitmap.js b/o3d/samples/o3d-webgl/bitmap.js index 94e90f8..444fc0c 100644 --- a/o3d/samples/o3d-webgl/bitmap.js +++ b/o3d/samples/o3d-webgl/bitmap.js @@ -82,6 +82,7 @@ o3d.Bitmap.scratch_canvas_ = null; /** * Gets a canvas to use for scratch work. + * @return {Canvas} The canvas. * @private */ o3d.Bitmap.getScratchCanvas_ = function() { diff --git a/o3d/samples/o3d-webgl/bounding_box.js b/o3d/samples/o3d-webgl/bounding_box.js index 3492868d..b585c31 100644 --- a/o3d/samples/o3d-webgl/bounding_box.js +++ b/o3d/samples/o3d-webgl/bounding_box.js @@ -52,10 +52,52 @@ o3d.inherit('BoundingBox', 'ParamObject'); /** + * Computes a list of 8 3-dimensional vectors for the corners of the box. + * @return {!Array.<Array<numbers>>} The list of corners. + */ +o3d.BoundingBox.prototype.corners_ = function() { + var result = []; + var m = [this.minExtent, this.maxExtent]; + for (var i = 0; i < 2; ++i) { + for (var j = 0; j < 2; ++j) { + for (var k = 0; k < 2; ++k) { + result.push([m[i][0], m[j][1], m[k][2]]); + } + } + } + + return result; +}; + + +/** + * Computes the smallest bounding box containing all the points in the given + * list, and either modifies the optional box passed in to match, or returns + * that box as a new box. + * @param {!Array.<Array<numbers>>} points A non-empty list of points. + * @param {o3d.BoundingBox} opt_targetBox Optional box to modify instead of + * returning a new box. + * @private + */ +o3d.BoundingBox.fitBoxToPoints_ = function(points, opt_targetBox) { + var target = opt_targetBox || new o3d.BoundingBox(); + for (var index = 0; index < 3; ++index) { + target.maxExtent[index] = target.minExtent[index] = points[0][index]; + for (var i = 1; i < points.length; ++i) { + var point = points[i]; + target.minExtent[index] = Math.min(target.minExtent[index], point[index]); + target.maxExtent[index] = Math.max(target.maxExtent[index], point[index]); + } + } + return target; +}; + + +/** * True if this boundingbox has been initialized. * @type {boolean} */ -o3d.BoundingBox.prototype.valid_ = false; +o3d.BoundingBox.prototype.valid = false; /** @@ -65,7 +107,6 @@ o3d.BoundingBox.prototype.valid_ = false; o3d.BoundingBox.prototype.minExtent = [0, 0, 0]; - /** * The max extent of the box. * @type {!o3d.math.Point3} @@ -73,7 +114,6 @@ o3d.BoundingBox.prototype.minExtent = [0, 0, 0]; o3d.BoundingBox.prototype.maxExtent = [0, 0, 0]; - /** * Multiplies the bounding box by the given matrix returning a new bounding * box. @@ -82,7 +122,14 @@ o3d.BoundingBox.prototype.maxExtent = [0, 0, 0]; */ o3d.BoundingBox.prototype.mul = function(matrix) { - o3d.notImplemented(); + var corners = this.corners_(); + var new_corners = []; + + for (var i = 0; i < corners.length; ++i) { + new_corners.push(o3d.Transform.transformPoint(matrix, corners[i])); + } + + return o3d.BoundingBox.fitBoxToPoints_(new_corners); }; @@ -94,7 +141,13 @@ o3d.BoundingBox.prototype.mul = */ o3d.BoundingBox.prototype.add = function(box) { - o3d.notImplemented(); + return new o3d.BoundingBox( + [Math.min(box.minExtent[0], this.minExtent[0]), + Math.min(box.minExtent[1], this.minExtent[1]), + Math.min(box.minExtent[2], this.minExtent[2])], + [Math.max(box.maxExtent[0], this.maxExtent[0]), + Math.max(box.maxExtent[1], this.maxExtent[1]), + Math.max(box.maxExtent[2], this.maxExtent[2])]); }; @@ -112,19 +165,129 @@ o3d.BoundingBox.prototype.add = */ o3d.BoundingBox.prototype.intersectRay = function(start, end) { - o3d.notImplemented(); + var result = new RayIntersectionInfo; + + if (this.valid) { + result.valid = true; + result.intersected = true; // True until proven false. + + var kNumberOfDimensions = 3; + var kRight = 0; + var kLeft = 1; + var kMiddle = 2; + + var dir = [end[0] - start[0], end[1] - start[1], end[2] - start[2]]; + var coord = [0, 0, 0]; + var inside = true; + + var quadrant = []; + var max_t = []; + var candidate_plane = []; + + for (var i = 0; i < kNumberOfDimensions; ++i) { + quadrant.push(0.0); + max_t.push(0.0); + candidate_plane.push(0,0); + } + + var which_plane; + + // Find candidate planes; this loop can be avoided if rays cast all from + // the eye (assumes perpsective view). + for (var i = 0; i < kNumberOfDimensions; ++i) { + if (start[i] < min_extent_[i]) { + quadrant[i] = kLeft; + candidate_plane[i] = min_extent_[i]; + inside = false; + } else if (start[i] > max_extent_[i]) { + quadrant[i] = kRight; + candidate_plane[i] = max_extent_[i]; + inside = false; + } else { + quadrant[i] = kMiddle; + } + } + + // Ray origin inside bounding box. + if (inside) { + result.position = start; + result.inside = true; + } else { + // Calculate T distances to candidate planes. + for (var i = 0; i < kNumberOfDimensions; ++i) { + if (quadrant[i] != kMiddle && dir[i] != 0.0) { + max_t[i] = (candidate_plane[i] - start[i]) / dir[i]; + } else { + max_t[i] = -1.0; + } + } + + // Get largest of the max_t's for final choice of intersection. + which_plane = 0; + for (var i = 1; i < kNumberOfDimensions; ++i) { + if (max_t[which_plane] < max_t[i]) { + which_plane = i; + } + } + + // Check final candidate actually inside box. + if (max_t[which_plane] < 0.0) { + result.intersected = false; + } else { + for (var i = 0; i < kNumberOfDimensions; ++i) { + if (which_plane != i) { + coord[i] = start[i] + max_t[which_plane] * dir[i]; + if (coord[i] < min_extent_[i] || coord[i] > max_extent_[i]) { + result.intersected = false; + break; + } + } else { + coord[i] = candidate_plane[i]; + } + } + + // Ray hits box. + result.position = coord; + } + } + } + + return result; }; /** - * Returns true if the bounding box is inside the frustum. + * Returns true if the bounding box is inside the frustum matrix. + * It checks all 8 corners of the bounding box against the 6 frustum planes + * and determines whether there's at least one plane for which all 6 points lie + * on the outside side of it. In that case it reports that the bounding box + * is outside the frustum. Note that this is a conservative check in that + * it in certain cases it will report that a box is in the frustum even if it + * really isn't. However if it reports that the box is outside then it's + * guaranteed to be outside. * @param {!o3d.math.Matrix4} matrix Matrix to transform the box from its * local space to view frustum space. - * @return {boolean} True if the box is in the frustum. + * @return {boolean} True if the box is in the frustum. */ o3d.BoundingBox.prototype.inFrustum = function(matrix) { - o3d.notImplemented(); + var corners = this.corners_(); + var bb_test = 0x3f; + for (var i = 0; i < corners.length; ++i) { + var corner = corners[i]; + var p = o3d.Transform.transformPoint(matrix, corner); + bb_test &= (((p[0] > 1.0) << 0) | + ((p[0] < -1.0) << 1) | + ((p[1] > 1.0) << 2) | + ((p[1] < -1.0) << 3) | + ((p[2] > 1.0) << 4) | + ((p[2] < 0.0) << 5)); + if (bb_test == 0) { + return true; + } + } + + return (bb_test == 0); }; diff --git a/o3d/samples/o3d-webgl/client.js b/o3d/samples/o3d-webgl/client.js index 5d91416..62ec060 100644 --- a/o3d/samples/o3d-webgl/client.js +++ b/o3d/samples/o3d-webgl/client.js @@ -97,18 +97,8 @@ o3d.Renderer.clients_ = []; o3d.Renderer.renderClients = function() { for (var i = 0; i < o3d.Renderer.clients_.length; ++i) { var client = o3d.Renderer.clients_[i]; - var renderEvent = new o3d.RenderEvent; - var now = (new Date()).getTime() * 0.001; - if(client.then_ == 0.0) - renderEvent.elapsedTime = 0.0; - else - renderEvent.elapsedTime = now - client.then_; - client.updateDisplayInfo_(); - if (client.render_callback) { - client.render_callback(renderEvent); - } - client.then_ = now; - client.renderTree(client.renderGraphRoot); + + client.render(); } }; @@ -213,10 +203,12 @@ o3d.ClientInfo.prototype.non_power_of_two_textures = true; */ o3d.Client = function() { o3d.NamedObject.call(this); - this.root = new o3d.Transform; - this.renderGraphRoot = new o3d.RenderNode; - this.root = new o3d.Transform; + + var tempPack = this.createPack(); + this.root = tempPack.createObject('Transform'); + this.renderGraphRoot = tempPack.createObject('RenderNode'); this.clientId = o3d.Client.nextId++; + this.packs_ = [tempPack]; if (o3d.Renderer.clients_.length == 0) o3d.Renderer.installRenderInterval(); @@ -269,6 +261,13 @@ o3d.Client.prototype.root = null; /** + * A list of all packs for this client. + * @type {!Array.<!o3d.Pack>} + */ +o3d.Client.prototype.packs_ = []; + + +/** * Function that gets called when the client encounters an error. */ o3d.Client.prototype.error_callback = function(error_message) { @@ -313,12 +312,26 @@ o3d.Client.prototype.cleanup = function () { o3d.Client.prototype.createPack = function() { var pack = new o3d.Pack; + pack.client = this; pack.gl = this.gl; + this.packs_.push(pack); return pack; }; /** + * Creates a pack object. + * A pack object. + * @param {!o3d.Pack} pack The pack to remove. + */ +o3d.Client.prototype.destroyPack = + function(pack) { + o3d.removeFromArray(this.packs_, pack); +}; + + + +/** * Searches the Client for an object matching the given id. * * @param {number} id The id of the object to look for. @@ -339,8 +352,14 @@ o3d.Client.prototype.getObjectById = */ o3d.Client.prototype.getObjects = function(name, class_name) { - o3d.notImplemented(); - return []; + var objects = []; + + for (var i = 0; i < this.packs_.length; ++i) { + var pack = this.packs_[i]; + objects = objects.concat(pack.getObjects(name, class_name)); + } + + return objects; }; @@ -351,8 +370,14 @@ o3d.Client.prototype.getObjects = */ o3d.Client.prototype.getObjectsByClassName = function(class_name) { - o3d.notImplemented(); - return []; + var objects = []; + + for (var i = 0; i < this.packs_.length; ++i) { + var pack = this.packs_[i]; + objects = objects.concat(pack.getObjectsByClassName(class_name)); + } + + return objects; }; @@ -387,10 +412,34 @@ o3d.Client.prototype.renderMode = o3d.Client.RENDERMODE_CONTINUOUS; * RENDERMODE_ON_DEMAND. */ o3d.Client.prototype.render = function() { - this.renderTree(); + // Synthesize a render event. + var render_event = new o3d.RenderEvent; + + var now = (new Date()).getTime() * 0.001; + if(this.then_ == 0.0) + render_event.elapsedTime = 0.0; + else + render_event.elapsedTime = now - this.then_; + + if (this.render_callback) { + for (var stat in this.render_stats_) { + render_event[stat] = this.render_stats_[stat]; + } + this.render_callback(render_event); + } + this.then_ = now; + this.renderTree(this.renderGraphRoot); }; +/** + * An object for various statistics that are gather during the render tree + * tranversal. + * + * @type {Object} + */ +o3d.Client.prototype.render_stats = {} + /** * Renders a render graph. @@ -407,6 +456,16 @@ o3d.Client.prototype.render = function() { */ o3d.Client.prototype.renderTree = function(render_node) { + + this.render_stats_ = { + drawElementsCulled: 0, + drawElementsProcessed: 0, + drawElementsRendered: 0, + primitivesRendered: 0, + transformsCulled: 0, + transformsProcessed: 0 + }; + render_node.render(); }; @@ -420,7 +479,6 @@ o3d.Client.prototype.renderTree = o3d.Client.prototype.getDisplayModes = []; - /** * Makes a region of the plugin area that will invoke full-screen mode if * clicked. The developer is responsible for communicating this to the user, @@ -437,7 +495,7 @@ o3d.Client.prototype.getDisplayModes = []; */ o3d.Client.prototype.setFullscreenClickRegion = function(x, y, width, height, mode_id) { - + o3d.notImplemented(); }; @@ -537,9 +595,12 @@ o3d.Client.prototype.initWithCanvas = function(canvas) { } this.gl = gl; + this.root.gl = gl; + this.renderGraphRoot.gl = gl; gl.client = this; - this.updateDisplayInfo_(); + gl.displayInfo = {width: canvas.width, + height: canvas.height}; }; @@ -890,11 +951,3 @@ o3d.Client.prototype.clientId = 0; o3d.Client.prototype.canvas = null; -/** - * Updates the display information attached to the GL. - * @private - */ -o3d.Client.prototype.updateDisplayInfo_ = function() { - this.gl.displayInfo = {width: this.width, - height: this.height}; -}; diff --git a/o3d/samples/o3d-webgl/draw_list.js b/o3d/samples/o3d-webgl/draw_list.js index 46ea458..875f237 100644 --- a/o3d/samples/o3d-webgl/draw_list.js +++ b/o3d/samples/o3d-webgl/draw_list.js @@ -60,7 +60,7 @@ o3d.DrawList.SortMethod = goog.typedef; * BY_PERFORMANCE * BY_Z_ORDER * BY_PRIORITY - * + * * Method to sort DrawList by. */ o3d.DrawList.BY_PERFORMANCE = 0; @@ -77,6 +77,8 @@ o3d.DrawList.prototype.render = function() { var drawElementInfo = this.list_[i]; var world = drawElementInfo.world; var view = drawElementInfo.view; + var viewProjection = drawElementInfo.viewProjection; + var worldViewProjection = drawElementInfo.worldViewProjection; var projection = drawElementInfo.projection; var transform = drawElementInfo.transform; var drawElement = drawElementInfo.drawElement; @@ -88,6 +90,8 @@ o3d.DrawList.prototype.render = function() { o3d.Param.SAS.setWorld(world); o3d.Param.SAS.setView(view); o3d.Param.SAS.setProjection(projection); + o3d.Param.SAS.setViewProjection(viewProjection); + o3d.Param.SAS.setWorldViewProjection(worldViewProjection); var paramObjects = [ transform, diff --git a/o3d/samples/o3d-webgl/element.js b/o3d/samples/o3d-webgl/element.js index 92558b4..d190780 100644 --- a/o3d/samples/o3d-webgl/element.js +++ b/o3d/samples/o3d-webgl/element.js @@ -32,7 +32,7 @@ /** * An Element manages DrawElements for classes inherited from Element. - * + * * @param {!o3d.Material} opt_material The Material used by this Element. * @param {!o3d.BoundingBox} opt_boundingBox The BoundingBox used by this * Element for culling. @@ -145,7 +145,7 @@ o3d.Element.prototype.__defineGetter__('owner', * Creates a DrawElement for this Element. Note that unlike * Shape.createDrawElements and Transform.createDrawElements this one will * create more than one element for the same material. - * + * * @param {!o3d.Pack} pack pack used to manage created DrawElement. * @param {!o3d.Material} material material to use for DrawElement. If you * pass null it will use the material on this Element. This allows you @@ -155,7 +155,7 @@ o3d.Element.prototype.__defineGetter__('owner', */ o3d.Element.prototype.createDrawElement = function(pack, material) { - drawElement = new o3d.DrawElement(); + drawElement = pack.createObject('DrawElement'); drawElement.owner = this; drawElement.material = material || this.material; this.drawElements.push(drawElement); diff --git a/o3d/samples/o3d-webgl/field.js b/o3d/samples/o3d-webgl/field.js index 23058bf..1ae89e4 100644 --- a/o3d/samples/o3d-webgl/field.js +++ b/o3d/samples/o3d-webgl/field.js @@ -72,21 +72,22 @@ o3d.Field.prototype.size = 0; /** * Sets the values of the data stored in the field. - * + * * The buffer for the field must have already been created either through * buffer.set or through buffer.allocateElements. - * + * * The number of values passed in must be a multiple of the number of * components needed for the field. - * + * * @param {number} start_index index of first value to set. - * @param {number} values Values to be stored in the buffer starting at index. + * @param {!Array.<number>} values Values to be stored in the buffer starting at + * index. */ o3d.Field.prototype.setAt = function(start_index, values) { this.buffer.lock(); var l = values.length / this.numComponents; - for (var i = 0; i < l; i++) { + for (var i = 0; i < l; ++i) { for (var c = 0; c < this.numComponents; ++c) { this.buffer.array_[ (start_index + i) * this.buffer.totalComponents + this.offset_ + c] = @@ -100,14 +101,21 @@ o3d.Field.prototype.setAt = /** * Gets the values stored in the field. - * + * * @param {number} start_index index of the first value to get. * @param {number} num_elements number of elements to read from field. - * @return {number} The values of the field. + * @return {!Array.<number>} The values of the field. */ o3d.Field.prototype.getAt = function(start_index, num_elements) { - o3d.notImplemented(); + var values = []; + for (var i = 0; i < num_elements; ++i) { + for (var c = 0; c < this.numComponents; ++c) { + values.push(this.buffer.array_[(start_index + i) * + this.buffer.totalComponents + this.offset_ + c]); + } + } + return values; }; diff --git a/o3d/samples/o3d-webgl/pack.js b/o3d/samples/o3d-webgl/pack.js index 1c211ad..a5336ea 100644 --- a/o3d/samples/o3d-webgl/pack.js +++ b/o3d/samples/o3d-webgl/pack.js @@ -73,6 +73,7 @@ o3d.inherit('Pack', 'NamedObject'); */ o3d.Pack.prototype.destroy = function() { this.objects_ = []; + this.client.removePack(this); }; diff --git a/o3d/samples/o3d-webgl/param.js b/o3d/samples/o3d-webgl/param.js index 4a5ec63..1792eb8 100644 --- a/o3d/samples/o3d-webgl/param.js +++ b/o3d/samples/o3d-webgl/param.js @@ -589,7 +589,7 @@ o3d.inherit('ViewInverseTransposeParamMatrix4', 'CompositionParamMatrix4'); */ o3d.ViewProjectionParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view']; + this.matrix_names_ = ['viewProjection']; }; o3d.inherit('ViewProjectionParamMatrix4', 'CompositionParamMatrix4'); @@ -599,7 +599,7 @@ o3d.inherit('ViewProjectionParamMatrix4', 'CompositionParamMatrix4'); */ o3d.ViewProjectionInverseParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view']; + this.matrix_names_ = ['viewProjection']; this.inverse_ = true; }; o3d.inherit('ViewProjectionInverseParamMatrix4', 'CompositionParamMatrix4'); @@ -610,7 +610,7 @@ o3d.inherit('ViewProjectionInverseParamMatrix4', 'CompositionParamMatrix4'); */ o3d.ViewProjectionTransposeParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view']; + this.matrix_names_ = ['viewProjection']; this.transpose_ = true; }; o3d.inherit('ViewProjectionTransposeParamMatrix4', 'CompositionParamMatrix4'); @@ -621,7 +621,7 @@ o3d.inherit('ViewProjectionTransposeParamMatrix4', 'CompositionParamMatrix4'); */ o3d.ViewProjectionInverseTransposeParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view']; + this.matrix_names_ = ['viewProjection']; this.inverse_ = true; this.transpose_ = true; }; @@ -725,7 +725,7 @@ o3d.inherit('WorldViewInverseTransposeParamMatrix4', */ o3d.WorldViewProjectionParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view', 'world']; + this.matrix_names_ = ['worldViewProjection']; }; o3d.inherit('WorldViewProjectionParamMatrix4', 'CompositionParamMatrix4'); @@ -736,7 +736,7 @@ o3d.inherit('WorldViewProjectionParamMatrix4', */ o3d.WorldViewProjectionInverseParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view', 'world']; + this.matrix_names_ = ['worldViewProjection']; this.inverse_ = true; }; o3d.inherit('WorldViewProjectionInverseParamMatrix4', @@ -748,7 +748,7 @@ o3d.inherit('WorldViewProjectionInverseParamMatrix4', */ o3d.WorldViewProjectionTransposeParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view', 'world']; + this.matrix_names_ = ['worldViewProjection']; this.transpose_ = true; }; o3d.inherit('WorldViewProjectionTransposeParamMatrix4', @@ -760,7 +760,7 @@ o3d.inherit('WorldViewProjectionTransposeParamMatrix4', */ o3d.WorldViewProjectionInverseTransposeParamMatrix4 = function() { o3d.CompositionParamMatrix4.call(this); - this.matrix_names_ = ['projection', 'view', 'world']; + this.matrix_names_ = ['worldViewProjection']; this.inverse_ = true; this.transpose_ = true; }; @@ -911,16 +911,19 @@ o3d.Param.SAS.setView = function(view) { * SAS parameters. */ o3d.Param.SAS.setProjection = function(projection) { - // TODO(petersont): this wasn't being used. Need to adjust all of - // the handwritten GLSL shaders to incorporate the modification of - // gl_Position based on dx_clipping. - /* - var adjustedProjection = - [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, -1, 1]]; - o3d.Transform.compose( - adjustedProjection, projection, adjustedProjection); - */ this['projection'] = projection; }; +/** + * Sets the viewProjection matrix. + */ +o3d.Param.SAS.setViewProjection = function(viewProjection) { + this['viewProjection'] = viewProjection; +}; +/** + * Sets the worldViewProjection matrix. + */ +o3d.Param.SAS.setWorldViewProjection = function(worldViewProjection) { + this['worldViewProjection'] = worldViewProjection; +}; diff --git a/o3d/samples/o3d-webgl/primitive.js b/o3d/samples/o3d-webgl/primitive.js index 5d60f4b..fe31ecb 100644 --- a/o3d/samples/o3d-webgl/primitive.js +++ b/o3d/samples/o3d-webgl/primitive.js @@ -33,7 +33,7 @@ /** * A Primitive is a type of Element that is made from a list of points, * lines or triangles that use a single material. - * + * * @param opt_streamBank o3d.StreamBank The StreamBank used by this * Primitive. * @constructor @@ -138,6 +138,8 @@ o3d.Primitive.prototype.render = function() { } } + this.gl.client.render_stats_['primitivesRendered'] += this.numberPrimitives; + // TODO(petersont): Change the hard-coded 3 and triangles too. this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, indexBuffer.gl_buffer_); this.gl.drawElements(this.gl.TRIANGLES, @@ -149,3 +151,39 @@ o3d.Primitive.prototype.render = function() { this.gl.disableVertexAttribArray(enabled_attribs[i]); } }; + + +/** + * Computes the bounding box in same coordinate system as the specified + * POSITION stream. + * @param {number} position_stream_index Index of POSITION stream. + * @return {!o3d.BoundingBox} The boundingbox for this element in local space. + */ +o3d.Primitive.prototype.getBoundingBox = + function(position_stream_index) { + var streamBank = this.streamBank; + var indexBuffer = this.indexBuffer; + var stream = + this.streamBank.vertexStreams[o3d.Stream.POSITION][position_stream_index]; + + var points = []; + var field = stream.field; + var buffer = field.buffer; + var numPoints = buffer.array_.length / buffer.totalComponents; + + var elements = field.getAt(0, numPoints); + + for (var index = 0; index < numPoints; ++index) { + var p = [0, 0, 0]; + for (var i = 0; i < field.numComponents; ++i) { + p[i] = elements[field.numComponents * index + i]; + } + points.push(p); + } + + o3d.BoundingBox.fitBoxToPoints_(points, this.boundingBox); + return this.boundingBox; +}; + + + diff --git a/o3d/samples/o3d-webgl/ray_intersection_info.js b/o3d/samples/o3d-webgl/ray_intersection_info.js index db37c4d..ae9f22e 100644 --- a/o3d/samples/o3d-webgl/ray_intersection_info.js +++ b/o3d/samples/o3d-webgl/ray_intersection_info.js @@ -51,6 +51,12 @@ o3d.inherit('RayIntersectionInfo', 'NamedObject'); o3d.RayIntersectionInfo.prototype.valid = false; +/** + * True if the origin of the ray is found to be inside the box. + * @type {boolean} + */ +o3d.RayIntersectionInfo.prototype.inside = false; + /** * True if this ray intersection intersected something. diff --git a/o3d/samples/o3d-webgl/shape.js b/o3d/samples/o3d-webgl/shape.js index caf8cf6..e58f1f4 100644 --- a/o3d/samples/o3d-webgl/shape.js +++ b/o3d/samples/o3d-webgl/shape.js @@ -115,28 +115,53 @@ o3d.Shape.prototype.writeToDrawLists = // For each element look at the DrawElements for that element. for (var j = 0; j < element.drawElements.length; ++j) { + this.gl.client.render_stats_['drawElementsProcessed']++; var drawElement = element.drawElements[j]; var material = drawElement.material || drawElement.owner.material; var materialDrawList = material.drawList; + var rendered = false; // Iterate through the drawlists we might write to. for (var k = 0; k < drawListInfos.length; ++k) { var drawListInfo = drawListInfos[k]; var list = drawListInfo.list; - var context = drawListInfo.context; // If any of those drawlists matches the material on the drawElement, // add the drawElement to the list. if (materialDrawList == list) { + var context = drawListInfo.context; + var view = context.view; + var projection = context.projection; + + var worldViewProjection = [[], [], [], []]; + var viewProjection = [[], [], [], []]; + o3d.Transform.compose(projection, view, viewProjection); + o3d.Transform.compose(viewProjection, world, worldViewProjection); + + if (element.cull && element.boundingBox) { + if (!element.boundingBox.inFrustum(worldViewProjection)) { + continue; + } + } + + rendered = true; list.list_.push({ - view: context.view, - projection: context.projection, + view: view, + projection: projection, world: world, + viewProjection: viewProjection, + worldViewProjection: worldViewProjection, transform: transform, drawElement: drawElement }); } } + + if (rendered) { + this.gl.client.render_stats_['drawElementsRendered']++; + } else { + this.gl.client.render_stats_['drawElementsCulled']++; + } } } }; diff --git a/o3d/samples/o3d-webgl/transform.js b/o3d/samples/o3d-webgl/transform.js index 1873c5d..929abfd 100644 --- a/o3d/samples/o3d-webgl/transform.js +++ b/o3d/samples/o3d-webgl/transform.js @@ -225,7 +225,15 @@ o3d.Transform.prototype.getTransformsByNameInTree = */ o3d.Transform.prototype.getUpdatedWorldMatrix = function() { - o3d.notImplemented(); + var parentWorldMatrix; + if (!this.parent) { + parentWorldMatrix = + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; + } else { + parentWorldMatrix = this.parent.getUpdatedWorldMatrix(); + } + o3d.Transform.compose(parentWorldMatrix, this.localMatrix, this.worldMatrix); + return this.worldMatrix; }; @@ -591,6 +599,37 @@ o3d.Transform.prototype.rotateX = }; + +/** + * Takes a 4-by-4 matrix and a vector with 3 entries, + * interprets the vector as a point, transforms that point by the matrix, and + * returns the result as a vector with 3 entries. + * @param {!o3djs.math.Matrix4} m The matrix. + * @param {!o3djs.math.Vector3} v The point. + * @return {!o3djs.math.Vector3} The transformed point. + */ +o3d.Transform.transformPoint = function(m, v) { + var v0 = v[0]; + var v1 = v[1]; + var v2 = v[2]; + + if (!m) { + debugger; + } + + var m0 = m[0]; + var m1 = m[1]; + var m2 = m[2]; + var m3 = m[3]; + + var d = v0 * m0[3] + v1 * m1[3] + v2 * m2[3] + m3[3]; + return [(v0 * m0[0] + v1 * m1[0] + v2 * m2[0] + m3[0]) / d, + (v0 * m0[1] + v1 * m1[1] + v2 * m2[1] + m3[1]) / d, + (v0 * m0[2] + v1 * m1[2] + v2 * m2[2] + m3[2]) / d]; +}; + + + /** * Pre-composes the local matrix of this Transform with a rotation about the * y-axis. For example, if the local matrix is a translation, the new local @@ -926,7 +965,9 @@ o3d.Transform.flattenMatrix4 = function(m) { */ o3d.Transform.prototype.traverse = function(drawListInfos, opt_parentWorldMatrix) { - if (!this.visible) { + + this.gl.client.render_stats_['transformsProcessed']++; + if (drawListInfos.length == 0 || !this.visible) { return; } opt_parentWorldMatrix = @@ -936,15 +977,42 @@ o3d.Transform.prototype.traverse = o3d.Transform.compose( opt_parentWorldMatrix, this.localMatrix, this.worldMatrix); + var remainingDrawListInfos = []; + + if (this.cull) { + if (this.boundingBox) { + for (var i = 0; i < drawListInfos.length; ++i) { + var drawListInfo = drawListInfos[i]; + + var worldViewProjection = [[], [], [], []]; + o3d.Transform.compose(drawListInfo.context.view, + this.worldMatrix, worldViewProjection); + o3d.Transform.compose(drawListInfo.context.projection, + worldViewProjection, worldViewProjection); + + if (this.boundingBox.inFrustum(worldViewProjection)) { + remainingDrawListInfos.push(drawListInfo); + } + } + } + } else { + remainingDrawListInfos = drawListInfos; + } + + if (remainingDrawListInfos.length == 0) { + this.gl.client.render_stats_['transformsCulled']++; + return; + } + var children = this.children; var shapes = this.shapes; for (var i = 0; i < shapes.length; ++i) { - shapes[i].writeToDrawLists(drawListInfos, this.worldMatrix, this); + shapes[i].writeToDrawLists(remainingDrawListInfos, this.worldMatrix, this); } for (var i = 0; i < children.length; ++i) { - children[i].traverse(drawListInfos, this.worldMatrix); + children[i].traverse(remainingDrawListInfos, this.worldMatrix); } }; diff --git a/o3d/samples/o3djs/webgl.js b/o3d/samples/o3djs/webgl.js index d9c53f3..c0594af 100644 --- a/o3d/samples/o3djs/webgl.js +++ b/o3d/samples/o3djs/webgl.js @@ -140,7 +140,7 @@ o3djs.webgl.addDebuggingWrapper = function(context) { /** * Creates a canvas under the given parent element and an o3d.Client * under that. - * + * * @ param {!Element} element The element under which to insert the client. * @ param {string} opt_features Features to turn on. * @ param {boolean} opt_debug Whether gl debugging features should be @@ -161,17 +161,20 @@ o3djs.webgl.createClient = function(element, opt_features, opt_debug) { canvas = document.createElement('canvas'); canvas.style.width = "100%"; canvas.style.height = "100%"; + + var client = new o3d.Client; + var resizeHandler = function() { var width = Math.max(1, canvas.clientWidth); var height = Math.max(1, canvas.clientHeight); canvas.width = width; canvas.height = height; canvas.sizeInitialized_ = true; + client.gl.displayInfo = {width: canvas.width, height: canvas.height}; }; window.addEventListener('resize', resizeHandler, false); setTimeout(resizeHandler, 0); - var client = new o3d.Client; client.initWithCanvas(canvas); canvas.client = client; canvas.o3d = o3d; |