summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3djs
diff options
context:
space:
mode:
authorapatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 19:56:24 +0000
committerapatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 19:56:24 +0000
commitccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b (patch)
treefc6e4a0552431c7297798fad46f57fb76c35a3b3 /o3d/samples/o3djs
parentdfe9d1d2de7fd8d244599b79a22898cce8df7c81 (diff)
downloadchromium_src-ccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b.zip
chromium_src-ccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b.tar.gz
chromium_src-ccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b.tar.bz2
Made all line endings consistently LF and added svn:eol-style=LF property to files with these names / extensions.
c cc h mm txt idl py js html css gyp gypi xml shader json htm README DEPS git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31811 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples/o3djs')
-rw-r--r--o3d/samples/o3djs/manipulators.js1924
-rw-r--r--o3d/samples/o3djs/performance.js404
-rw-r--r--o3d/samples/o3djs/texture.js478
3 files changed, 1403 insertions, 1403 deletions
diff --git a/o3d/samples/o3djs/manipulators.js b/o3d/samples/o3djs/manipulators.js
index e1ddce6..cade671 100644
--- a/o3d/samples/o3djs/manipulators.js
+++ b/o3d/samples/o3djs/manipulators.js
@@ -1,962 +1,962 @@
-/*
- * 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.
- */
-
-
-/**
- * @fileoverview This file contains classes that implement several
- * forms of 2D and 3D manipulation.
- */
-
-o3djs.provide('o3djs.manipulators');
-
-o3djs.require('o3djs.material');
-
-o3djs.require('o3djs.math');
-
-o3djs.require('o3djs.picking');
-
-o3djs.require('o3djs.primitives');
-
-o3djs.require('o3djs.quaternions');
-
-/**
- * A module implementing several forms of 2D and 3D manipulation.
- * @namespace
- */
-o3djs.manipulators = o3djs.manipulators || {};
-
-/**
- * Creates a new manipulator manager, which maintains multiple
- * manipulators in the same scene. The manager is implicitly
- * associated with a particular O3D client via the Pack which is
- * passed in, although multiple managers can be created for a given
- * client. The manipulators are positioned in world coordinates and
- * are placed in the scene graph underneath the parent transform which
- * is passed in.
- * @param {!o3d.Pack} pack Pack in which manipulators' geometry and
- * materials will be created.
- * @param {!o3d.DrawList} drawList The draw list against which
- * internal materials are created.
- * @param {!o3d.Transform} parentTransform The parent transform under
- * which the manipulators' geometry should be parented.
- * @param {!o3d.RenderNode} parentRenderNode The parent render node
- * under which the manipulators' draw elements should be placed.
- * @param {number} renderNodePriority The priority that the
- * manipulators' geometry should use for rendering.
- * @return {!o3djs.manipulators.Manager} The created manipulator
- * manager.
- */
-o3djs.manipulators.createManager = function(pack,
- drawList,
- parentTransform,
- parentRenderNode,
- renderNodePriority) {
- return new o3djs.manipulators.Manager(pack,
- drawList,
- parentTransform,
- parentRenderNode,
- renderNodePriority);
-}
-
-//
-// Some linear algebra classes.
-// TODO(kbr): find a better home for these.
-//
-
-/**
- * Creates a new Line object, which implements projection and
- * closest-point operations.
- * @constructor
- * @private
- * @param {o3djs.math.Vector3} opt_direction The direction of the
- * line. Does not need to be normalized but must not be the zero
- * vector. Defaults to [1, 0, 0] if not specified.
- * @param {o3djs.math.Vector3} opt_point A point through which the
- * line goes. Defaults to [0, 0, 0] if not specified.
- */
-o3djs.manipulators.Line_ = function(opt_direction,
- opt_point) {
- if (opt_direction) {
- this.direction_ = o3djs.math.copyVector(opt_direction);
- } else {
- this.direction_ = [1, 0, 0];
- }
-
- if (opt_point) {
- this.point_ = o3djs.math.copyVector(opt_point);
- } else {
- this.point_ = [0, 0, 0];
- }
-
- // Helper for computing projections along the line
- this.alongVec_ = [0, 0, 0];
- this.recalc_();
-}
-
-/**
- * Sets the direction of this line.
- * @private
- * @param {!o3djs.math.Vector3} direction The new direction of the
- * line. Does not need to be normalized but must not be the zero
- * vector.
- */
-o3djs.manipulators.Line_.prototype.setDirection = function(direction) {
- this.direction_ = o3djs.math.copyVector(direction);
- this.recalc_();
-}
-
-/**
- * Gets the direction of this line.
- * @private
- * @return {!o3djs.math.Vector3} The direction of the line.
- */
-o3djs.manipulators.Line_.prototype.getDirection = function() {
- return this.direction_;
-}
-
-/**
- * Sets one point through which this line travels.
- * @private
- * @param {!o3djs.math.Vector3} point A point which through the line
- * will travel.
- */
-o3djs.manipulators.Line_.prototype.setPoint = function(point) {
- this.point_ = o3djs.math.copyVector(point);
- this.recalc_();
-}
-
-/**
- * Gets one point through which this line travels.
- * @private
- * @return {!o3djs.math.Vector3} A point which through the line
- * travels.
- */
-o3djs.manipulators.Line_.prototype.getPoint = function() {
- return this.point_;
-}
-
-/**
- * Projects a point onto the line.
- * @private
- * @param {!o3djs.math.Vector3} point Point to be projected.
- * @return {!o3djs.math.Vector3} Point on the line closest to the
- * passed point.
- */
-o3djs.manipulators.Line_.prototype.projectPoint = function(point) {
- var dotp = o3djs.math.dot(this.direction_, point);
- return o3djs.math.addVector(this.alongVec_,
- o3djs.math.mulScalarVector(dotp,
- this.direction_));
-}
-
-o3djs.manipulators.EPSILON = 0.00001;
-o3djs.manipulators.X_AXIS = [1, 0, 0];
-
-
-/**
- * Returns the closest point on this line to the given ray, which is
- * specified by start and end points. If the ray is parallel to the
- * line, returns null.
- * @private
- * @param {!o3djs.math.Vector3} startPoint Start point of ray.
- * @param {!o3djs.math.Vector3} endPoint End point of ray.
- * @return {o3djs.math.Vector3} The closest point on the line to the
- * ray, or null if the ray is parallel to the line.
- */
-o3djs.manipulators.Line_.prototype.closestPointToRay = function(startPoint,
- endPoint) {
- // Consider a two-sided line and a one-sided ray, both in in 3D
- // space, and assume they are not parallel. Their parametric
- // formulation is:
- //
- // p1 = point + t * dir
- // p2 = raystart + u * raydir
- //
- // Here t and u are scalar parameter values, and the other values
- // are three-dimensional vectors. p1 and p2 are arbitrary points on
- // the line and ray, respectively.
- //
- // At the points cp1 and cp2 on these two lines where the line and
- // the ray are closest together, the line segment between cp1 and
- // cp2 is perpendicular to both of the lines.
- //
- // We can therefore write the following equations:
- //
- // dot( dir, (cp2 - cp1)) = 0
- // dot(raydir, (cp2 - cp1)) = 0
- //
- // Define t' and u' as the parameter values for cp1 and cp2,
- // respectively. Expanding, these equations become
- //
- // dot( dir, ((raystart + u' * raydir) - (point + t' * dir))) = 0
- // dot(raydir, ((raystart + u' * raydir) - (point + t' * dir))) = 0
- //
- // With some reshuffling, these can be expressed in vector/matrix
- // form:
- //
- // [ dot( dir, raystart) - dot( dir, point) ]
- // [ dot(raydir, raystart) - dot(raydir, point) ] + (continued)
- //
- // [ -dot( dir, dir) dot( dir, raydir) ] [ t' ] [0]
- // [ -dot(raydir, dir) dot(raydir, raydir) ] * [ u' ] = [0]
- //
- // u' is the parameter for the world space ray being cast into the
- // screen. We can deduce whether the starting point of the ray is
- // actually the closest point to the infinite 3D line by whether the
- // value of u' is less than zero.
- var rayDirection = o3djs.math.subVector(endPoint, startPoint);
- var ddrd = o3djs.math.dot(this.direction_, rayDirection);
- var A = [[-o3djs.math.lengthSquared(this.direction_), ddrd],
- [ddrd, -o3djs.math.lengthSquared(rayDirection)]];
- var det = o3djs.math.det2(A);
- if (Math.abs(det) < o3djs.manipulators.EPSILON) {
- return null;
- }
- var Ainv = o3djs.math.inverse2(A);
- var b = [o3djs.math.dot(this.point_, this.direction_) -
- o3djs.math.dot(startPoint, this.direction_),
- o3djs.math.dot(startPoint, rayDirection) -
- o3djs.math.dot(this.point_, rayDirection)];
- var x = o3djs.math.mulMatrixVector(Ainv, b);
- if (x[1] < 0) {
- // Means that start point is closest point to this line
- return startPoint;
- } else {
- return o3djs.math.addVector(this.point_,
- o3djs.math.mulScalarVector(
- x[0],
- this.direction_));
- }
-}
-
-/**
- * Performs internal recalculations when the parameters of the line change.
- * @private
- */
-o3djs.manipulators.Line_.prototype.recalc_ = function() {
- var denom = o3djs.math.lengthSquared(this.direction_);
- if (denom == 0.0) {
- throw 'Line_.recalc: ERROR: direction was the zero vector (not allowed)';
- }
- this.alongVec_ =
- o3djs.math.subVector(this.point_,
- o3djs.math.mulScalarVector(
- o3djs.math.dot(this.point_,
- this.direction_),
- this.direction_));
-}
-
-o3djs.manipulators.DEFAULT_COLOR = [0.8, 0.8, 0.8, 1.0];
-o3djs.manipulators.HIGHLIGHTED_COLOR = [0.9, 0.9, 0.0, 1.0];
-
-/**
- * Constructs a new manipulator manager. Do not call this directly;
- * use o3djs.manipulators.createManager instead.
- * @constructor
- * @param {!o3d.Pack} pack Pack in which manipulators' geometry and
- * materials will be created.
- * @param {!o3d.DrawList} drawList The draw list against which
- * internal materials are created.
- * @param {!o3d.Transform} parentTransform The parent transform under
- * which the manipulators' geometry should be parented.
- * @param {!o3d.RenderNode} parentRenderNode The parent render node
- * under which the manipulators' draw elements should be placed.
- * @param {number} renderNodePriority The priority that the
- * manipulators' geometry should use for rendering.
- */
-o3djs.manipulators.Manager = function(pack,
- drawList,
- parentTransform,
- parentRenderNode,
- renderNodePriority) {
- this.pack = pack;
- this.drawList = drawList;
- this.parentTransform = parentTransform;
- this.parentRenderNode = parentRenderNode;
- this.renderNodePriority = renderNodePriority;
-
- this.lightPosition = [10, 10, 10];
-
- // Create the default and highlighted materials.
- this.defaultMaterial =
- this.createPhongMaterial_(o3djs.manipulators.DEFAULT_COLOR);
- this.highlightedMaterial =
- this.createPhongMaterial_(o3djs.manipulators.HIGHLIGHTED_COLOR);
-
- // This is a map from the manip's parent Transform clientId to the manip.
- this.manipsByClientId = [];
-
- // Presumably we need a TransformInfo for the parentTransform.
- this.transformInfo =
- o3djs.picking.createTransformInfo(this.parentTransform, null);
-
- /**
- * The currently-highlighted manipulator.
- * @type {o3djs.manipulators.Manip}
- */
- this.highlightedManip = null;
-
- /**
- * The manipulator currently being dragged.
- * @private
- * @type {o3djs.manipulators.Manip}
- */
- this.draggedManip_ = null;
-}
-
-/**
- * Creates a phong material based on the given single color.
- * @private
- * @param {!o3djs.math.Vector4} baseColor A vector with 4 entries, the
- * R,G,B, and A components of a color.
- * @return {!o3d.Material} A phong material whose overall pigment is baseColor.
- */
-o3djs.manipulators.Manager.prototype.createPhongMaterial_ =
- function(baseColor) {
- // Create a new, empty Material object.
- var material = this.pack.createObject('Material');
-
- o3djs.effect.attachStandardShader(
- this.pack, material, this.lightPosition, 'phong');
-
- material.drawList = this.drawList;
-
- // Assign parameters to the phong material.
- material.getParam('emissive').value = [0, 0, 0, 1];
- material.getParam('ambient').value =
- o3djs.math.mulScalarVector(0.1, baseColor);
- material.getParam('diffuse').value = baseColor;
- material.getParam('specular').value = [.2, .2, .2, 1];
- material.getParam('shininess').value = 20;
-
- return material;
-}
-
-/**
- * Creates a new Translate1 manipulator. A Translate1 moves along the
- * X axis in its local coordinate system.
- * @return {!o3djs.manipulators.Translate1} A new Translate1 manipulator.
- */
-o3djs.manipulators.Manager.prototype.createTranslate1 = function() {
- var manip = new o3djs.manipulators.Translate1(this);
- this.add_(manip);
- return manip;
-}
-
-/**
- * Adds a manipulator to this manager's set.
- * @private
- * @param {!o3djs.manipulators.Manip} manip The manipulator to add.
- */
-o3djs.manipulators.Manager.prototype.add_ = function(manip) {
- // Generate draw elements for the manipulator's transform
- manip.getTransform().createDrawElements(this.pack, null);
- // Add the manipulator's transform to the parent transform
- manip.getBaseTransform_().parent = this.parentTransform;
- // Add the manipulator into our managed list
- this.manipsByClientId[manip.getTransform().clientId] = manip;
-}
-
-/**
- * Event handler for multiple kinds of mouse events.
- * @private
- * @param {number} x The x coordinate of the mouse event.
- * @param {number} y The y coordinate of the mouse event.
- * @param {!o3djs.math.Matrix4} view The current view matrix.
- * @param {!o3djs.math.Matrix4} projection The current projection matrix.
- * @param {number} width The width of the viewport.
- * @param {number} height The height of the viewport.
- * @param {!function(!o3djs.manipulators.Manager,
- * o3djs.picking.PickInfo, o3djs.manipulators.Manip): void} func
- * Callback function. Always receives the manager as argument; if
- * a manipulator was picked, receives non-null PickInfo and Manip
- * arguments, otherwise receives null for both of these arguments.
- */
-o3djs.manipulators.Manager.prototype.handleMouse_ = function(x,
- y,
- view,
- projection,
- width,
- height,
- func) {
- this.transformInfo.update();
-
- // Create the world ray
- var worldRay =
- o3djs.picking.clientPositionToWorldRayEx(x, y,
- view, projection,
- width, height);
-
- // Pick against all of the manipulators' geometry
- var pickResult = this.transformInfo.pick(worldRay);
- if (pickResult != null) {
- // Find which manipulator we picked.
- // NOTE this assumes some things about the transform graph
- // structure of the manipulators.
- var manip =
- this.manipsByClientId[pickResult.shapeInfo.parent.transform.clientId];
- func(this, pickResult, manip);
- } else {
- func(this, null, null);
- }
-}
-
-/**
- * Callback handling the mouse-down event on a manipulator.
- * @private
- * @param {!o3djs.manipulators.Manager} manager The manipulator
- * manager owning the given manipulator.
- * @param {o3djs.picking.PickInfo} pickResult The picking information
- * associated with the mouse-down event.
- * @param {o3djs.manipulators.Manip} manip The manipulator to be
- * selected.
- */
-o3djs.manipulators.mouseDownCallback_ = function(manager,
- pickResult,
- manip) {
- if (manip != null) {
- manager.draggedManip_ = manip;
- manip.makeActive(pickResult);
- }
-}
-
-/**
- * Callback handling the mouse-over event on a manipulator.
- * @private
- * @param {!o3djs.manipulators.Manager} manager The manipulator
- * manager owning the given manipulator.
- * @param {o3djs.picking.PickInfo} pickResult The picking information
- * associated with the mouse-over event.
- * @param {o3djs.manipulators.Manip} manip The manipulator to be
- * highlighted.
- */
-o3djs.manipulators.hoverCallback_ = function(manager,
- pickResult,
- manip) {
- if (manager.highlightedManip != null &&
- manager.highlightedManip != manip) {
- // Un-highlight the previously highlighted manipulator
- manager.highlightedManip.clearHighlight();
- manager.highlightedManip = null;
- }
-
- if (manip != null) {
- manip.highlight(pickResult);
- manager.highlightedManip = manip;
- }
-}
-
-/**
- * Method which should be called by end user code upon receiving a
- * mouse-down event.
- * @param {number} x The x coordinate of the mouse event.
- * @param {number} y The y coordinate of the mouse event.
- * @param {!o3djs.math.Matrix4} view The current view matrix.
- * @param {!o3djs.math.Matrix4} projection The current projection matrix.
- * @param {number} width The width of the viewport.
- * @param {number} height The height of the viewport.
- */
-o3djs.manipulators.Manager.prototype.mousedown = function(x,
- y,
- view,
- projection,
- width,
- height) {
- this.handleMouse_(x, y, view, projection, width, height,
- o3djs.manipulators.mouseDownCallback_);
-}
-
-/**
- * Method which should be called by end user code upon receiving a
- * mouse motion event.
- * @param {number} x The x coordinate of the mouse event.
- * @param {number} y The y coordinate of the mouse event.
- * @param {!o3djs.math.Matrix4} view The current view matrix.
- * @param {!o3djs.math.Matrix4} projection The current projection matrix.
- * @param {number} width The width of the viewport.
- * @param {number} height The height of the viewport.
- */
-o3djs.manipulators.Manager.prototype.mousemove = function(x,
- y,
- view,
- projection,
- width,
- height) {
- if (this.draggedManip_ != null) {
- var worldRay =
- o3djs.picking.clientPositionToWorldRayEx(x, y,
- view, projection,
- width, height);
- this.draggedManip_.drag(worldRay.near, worldRay.far);
- } else {
- this.handleMouse_(x, y, view, projection, width, height,
- o3djs.manipulators.hoverCallback_);
- }
-}
-
-/**
- * Method which should be called by end user code upon receiving a
- * mouse-up event.
- */
-o3djs.manipulators.Manager.prototype.mouseup = function() {
- if (this.draggedManip_ != null) {
- this.draggedManip_.makeInactive();
- this.draggedManip_ = null;
- }
-}
-
-/**
- * Method which should be called by end user code, typically in
- * response to mouse move events, to update the transforms of
- * manipulators which might have been moved either because of
- * manipulators further up the hierarchy, or programmatic changes to
- * transforms.
- */
-o3djs.manipulators.Manager.prototype.updateInactiveManipulators = function() {
- for (var ii in this.manipsByClientId) {
- var manip = this.manipsByClientId[ii];
- if (!manip.isActive()) {
- manip.updateBaseTransformFromAttachedTransform_();
- }
- }
-}
-
-/**
- * Base class for all manipulators.
- * @constructor
- * @param {!o3djs.manipulators.Manager} manager The manager of this
- * manipulator.
- */
-o3djs.manipulators.Manip = function(manager) {
- this.manager_ = manager;
- var pack = manager.pack;
- // This transform holds the local transformation of the manipulator,
- // which is either applied to the transform to which it is attached,
- // or (see below) consumed by the user in the manipulator's
- // callbacks. After each interaction, if there is an attached
- // transform, this local transform is added in to it and reset to
- // the identity.
- // TODO(kbr): add support for user callbacks on manipulators.
- this.localTransform_ = pack.createObject('Transform');
-
- // This transform provides an offset, if desired, between the
- // manipulator's geometry and the transform (and, implicitly, the
- // shape) to which it is attached. This allows the manipulator to be
- // easily placed below an object, for example.
- this.offsetTransform_ = pack.createObject('Transform');
-
- // This transform is the one which is actually parented to the
- // manager's parentTransform. It is used to place the manipulator in
- // world space, regardless of the world space location of the
- // parentTransform supplied to the manager. If this manipulator is
- // attached to a given transform, then upon completion of a
- // particular drag interaction, this transform is adjusted to take
- // into account the attached transform's new value.
- this.baseTransform_ = pack.createObject('Transform');
-
- // Hook up these transforms
- this.localTransform_.parent = this.offsetTransform_;
- this.offsetTransform_.parent = this.baseTransform_;
-
- // This is the transform in the scene graph to which this
- // manipulator is conceptually "attached", and whose local transform
- // we are modifying.
- this.attachedTransform_ = null;
-
- this.active_ = false;
-}
-
-/**
- * Adds shapes to the internal transform of this manipulator.
- * @private
- * @param {!Array.<!o3d.Shape>} shapes Array of shapes to add.
- */
-o3djs.manipulators.Manip.prototype.addShapes_ = function(shapes) {
- for (var ii = 0; ii < shapes.length; ii++) {
- this.localTransform_.addShape(shapes[ii]);
- }
-}
-
-/**
- * Returns the "base" transform of this manipulator, which places the
- * origin of the manipulator at the local origin of the attached
- * transform.
- * @private
- * @return {!o3d.Transform} The base transform of this manipulator.
- */
-o3djs.manipulators.Manip.prototype.getBaseTransform_ = function() {
- return this.baseTransform_;
-}
-
-/**
- * Returns the "offset" transform of this manipulator, which allows
- * the manipulator's geometry to be moved or rotated with respect to
- * the local origin of the attached transform.
- * @return {!o3d.Transform} The offset transform of this manipulator.
- */
-o3djs.manipulators.Manip.prototype.getOffsetTransform = function() {
- return this.offsetTransform_;
-}
-
-/**
- * Returns the local transform of this manipulator, which contains the
- * changes that have been made in response to the current drag
- * operation. Upon completion of the drag, this transform's effects
- * are composed in to the attached transform, and this transform is
- * reset to the identity.
- * @return {!o3d.Transform} The local transform of this manipulator.
- */
-o3djs.manipulators.Manip.prototype.getTransform = function() {
- return this.localTransform_;
-}
-
-/**
- * Sets the translation component of the offset transform. This is
- * useful for moving the manipulator's geometry with respect to the
- * local origin of the attached transform.
- * @param {!o3djs.math.Vector3} translation The offset translation for
- * this manipulator.
- */
-o3djs.manipulators.Manip.prototype.setOffsetTranslation =
- function(translation) {
- this.getOffsetTransform().localMatrix =
- o3djs.math.matrix4.setTranslation(this.getOffsetTransform().localMatrix,
- translation);
-
-}
-
-/**
- * Sets the rotation component of the offset transform. This is useful
- * for orienting the manipulator's geometry with respect to the local
- * origin of the attached transform.
- * @param {!o3djs.quaternions.Quaternion} quaternion The offset
- * rotation for this manipulator.
- */
-o3djs.manipulators.Manip.prototype.setOffsetRotation = function(quaternion) {
- var rot = o3djs.quaternions.quaternionToRotation(quaternion);
- this.getOffsetTransform().localMatrix =
- o3djs.math.matrix4.setUpper3x3(this.getOffsetTransform().localMatrix,
- rot);
-
-}
-
-/**
- * Explicitly sets the local translation of this manipulator.
- * (TODO(kbr): it is not clear that this capability should be in the
- * API.)
- * @param {!o3djs.math.Vector3} translation The local translation for
- * this manipulator.
- */
-o3djs.manipulators.Manip.prototype.setTranslation = function(translation) {
- this.getTransform().localMatrix =
- o3djs.math.matrix4.setTranslation(this.getTransform().localMatrix,
- translation);
-
-}
-
-/**
- * Explicitly sets the local rotation of this manipulator. (TODO(kbr):
- * it is not clear that this capability should be in the API.)
- * @param {!o3djs.quaternions.Quaternion} quaternion The local
- * rotation for this manipulator.
- */
-o3djs.manipulators.Manip.prototype.setRotation = function(quaternion) {
- var rot = o3djs.quaternions.quaternionToRotation(quaternion);
- this.getTransform().localMatrix =
- o3djs.math.matrix4.setUpper3x3(this.getTransform().localMatrix,
- rot);
-
-}
-
-/**
- * Attaches this manipulator to the given transform. Interactions with
- * the manipulator will cause this transform's local matrix to be
- * modified appropriately.
- * @param {!o3d.Transform} transform The transform to which this
- * manipulator should be attached.
- */
-o3djs.manipulators.Manip.prototype.attachTo = function(transform) {
- this.attachedTransform_ = transform;
- // Update our base transform to place the manipulator at exactly the
- // location of the attached transform.
- this.updateBaseTransformFromAttachedTransform_();
-}
-
-/**
- * Highlights this manipulator according to the given pick result.
- * @param {o3djs.picking.PickInfo} pickResult The pick result which
- * caused this manipulator to become highlighted.
- */
-o3djs.manipulators.Manip.prototype.highlight = function(pickResult) {
-}
-
-/**
- * Clears any highlight for this manipulator.
- */
-o3djs.manipulators.Manip.prototype.clearHighlight = function() {
-}
-
-/**
- * Activates this manipulator according to the given pick result. In
- * complex manipulators, picking different portions of the manipulator
- * may result in different forms of interaction.
- * @param {o3djs.picking.PickInfo} pickResult The pick result which
- * caused this manipulator to become active.
- */
-o3djs.manipulators.Manip.prototype.makeActive = function(pickResult) {
- this.active_ = true;
-}
-
-/**
- * Deactivates this manipulator.
- */
-o3djs.manipulators.Manip.prototype.makeInactive = function() {
- this.active_ = false;
-}
-
-/**
- * Drags this manipulator according to the world-space ray specified
- * by startPoint and endPoint. makeActive must already have been
- * called with the initial pick result causing this manipulator to
- * become active. This method exists only to be overridden.
- * @param {!o3djs.math.Vector3} startPoint Start point of the
- * world-space ray through the current mouse position.
- * @param {!o3djs.math.Vector3} endPoint End point of the world-space
- * ray through the current mouse position.
- */
-o3djs.manipulators.Manip.prototype.drag = function(startPoint,
- endPoint) {
-}
-
-/**
- * Indicates whether this manipulator is active.
- * @return {boolean} Whether this manipulator is active.
- */
-o3djs.manipulators.Manip.prototype.isActive = function() {
- return this.active_;
-}
-
-/**
- * Updates the base transform of this manipulator from the state of
- * its attached transform, resetting the local transform of this
- * manipulator to the identity.
- * @private
- */
-o3djs.manipulators.Manip.prototype.updateBaseTransformFromAttachedTransform_ =
- function() {
- if (this.attachedTransform_ != null) {
- var attWorld = this.attachedTransform_.worldMatrix;
- var parWorld = this.manager_.parentTransform.worldMatrix;
- var parWorldInv = o3djs.math.matrix4.inverse(parWorld);
- this.baseTransform_.localMatrix =
- o3djs.math.matrix4.mul(attWorld, parWorldInv);
- // Reset the manipulator's local matrix to the identity.
- this.localTransform_.localMatrix = o3djs.math.matrix4.identity();
- }
-}
-
-/**
- * Updates this manipulator's attached transform based on the values
- * in the local transform.
- * @private
- */
-o3djs.manipulators.Manip.prototype.updateAttachedTransformFromLocalTransform_ =
- function() {
- if (this.attachedTransform_ != null) {
- // Compute the composition of the base and local transforms.
- // The offset transform is skipped except for transforming the
- // local matrix's translation by the rotation component of the
- // offset transform.
- var base = this.baseTransform_.worldMatrix;
- var local = this.localTransform_.localMatrix;
- var xlate = o3djs.math.matrix4.getTranslation(local);
- var transformedXlate =
- o3djs.math.matrix4.transformDirection(
- this.offsetTransform_.localMatrix,
- o3djs.math.matrix4.getTranslation(local));
- o3djs.math.matrix4.setTranslation(local, transformedXlate);
- var totalMat = o3djs.math.matrix4.mul(base, local);
-
- // Set this into the attached transform, taking into account its
- // parent's transform, if any.
- // Note that we can not query the parent's transform directly, so
- // we compute it using a little trick.
- var attWorld = this.attachedTransform_.worldMatrix;
- var attLocal = this.attachedTransform_.localMatrix;
- var attParentMat =
- o3djs.math.matrix4.mul(attWorld,
- o3djs.math.matrix4.inverse(attLocal));
- // Now we can take the inverse of this matrix
- var attParentMatInv = o3djs.math.matrix4.inverse(attParentMat);
- totalMat = o3djs.math.matrix4.mul(attParentMatInv, totalMat);
- this.attachedTransform_.localMatrix = totalMat;
- }
-}
-
-/**
- * Sets the material of the given shape's draw elements.
- * @private
- */
-o3djs.manipulators.Manip.prototype.setMaterial_ = function(shape, material) {
- var elements = shape.elements;
- for (var ii = 0; ii < elements.length; ii++) {
- var drawElements = elements[ii].drawElements;
- for (var jj = 0; jj < drawElements.length; jj++) {
- drawElements[jj].material = material;
- }
- }
-}
-
-/**
- * Sets the materials of the given shapes' draw elements.
- * @private
- */
-o3djs.manipulators.Manip.prototype.setMaterials_ = function(shapes, material) {
- for (var ii = 0; ii < shapes.length; ii++) {
- this.setMaterial_(shapes[ii], material);
- }
-}
-
-/**
- * A manipulator allowing an object to be dragged along a line.
- * @constructor
- * @extends {o3djs.manipulators.Manip}
- * @param {!o3djs.manipulators.Manager} manager The manager for the
- * new Translate1 manipulator.
- */
-o3djs.manipulators.Translate1 = function(manager) {
- o3djs.manipulators.Manip.call(this, manager);
-
- var pack = manager.pack;
- var material = manager.defaultMaterial;
-
- var shape = manager.translate1Shape_;
- if (!shape) {
- // Create the geometry for the manipulator, which looks like a
- // two-way arrow going from (-1, 0, 0) to (1, 0, 0).
- var matrix4 = o3djs.math.matrix4;
- var zRot = matrix4.rotationZ(Math.PI / 2);
-
- var verts = o3djs.primitives.createTruncatedConeVertices(
- 0.15, // Bottom radius.
- 0.0, // Top radius.
- 0.3, // Height.
- 4, // Number of radial subdivisions.
- 1, // Number of vertical subdivisions.
- matrix4.mul(matrix4.translation([0, 0.85, 0]), zRot));
-
- verts.append(o3djs.primitives.createCylinderVertices(
- 0.06, // Radius.
- 1.4, // Height.
- 4, // Number of radial subdivisions.
- 1, // Number of vertical subdivisions.
- zRot));
-
- verts.append(o3djs.primitives.createTruncatedConeVertices(
- 0.0, // Bottom radius.
- 0.15, // Top radius.
- 0.3, // Height.
- 4, // Number of radial subdivisions.
- 1, // Number of vertical subdivisions.
- matrix4.mul(matrix4.translation([0, -0.85, 0]), zRot)));
-
- shape = verts.createShape(pack, material);
- manager.translate1Shape_ = shape;
- }
-
- this.addShapes_([ shape ]);
-
- this.transformInfo = o3djs.picking.createTransformInfo(this.getTransform(),
- manager.transformInfo);
-
- // Add a parameter to our transform to be able to change the
- // material's color for highlighting.
- this.colorParam = this.getTransform().createParam('diffuse', 'ParamFloat4');
- this.clearHighlight();
-
- /** Dragging state */
- this.dragLine = new o3djs.manipulators.Line_();
-}
-
-o3djs.base.inherit(o3djs.manipulators.Translate1, o3djs.manipulators.Manip);
-
-o3djs.manipulators.Translate1.prototype.highlight = function(pickResult) {
- // We can use instanced geometry for the entire Translate1 since its
- // entire color changes during highlighting.
- // TODO(kbr): support custom user geometry and associated callbacks.
- this.colorParam.value = o3djs.manipulators.HIGHLIGHTED_COLOR;
-}
-
-o3djs.manipulators.Translate1.prototype.clearHighlight = function() {
- this.colorParam.value = o3djs.manipulators.DEFAULT_COLOR;
-}
-
-o3djs.manipulators.Translate1.prototype.makeActive = function(pickResult) {
- o3djs.manipulators.Manip.prototype.makeActive.call(this, pickResult);
- this.highlight(pickResult);
- var worldMatrix = this.getTransform().worldMatrix;
- this.dragLine.setDirection(
- o3djs.math.matrix4.transformDirection(worldMatrix,
- o3djs.manipulators.X_AXIS));
- this.dragLine.setPoint(pickResult.worldIntersectionPosition);
-}
-
-o3djs.manipulators.Translate1.prototype.makeInactive = function() {
- o3djs.manipulators.Manip.prototype.makeInactive.call(this);
- this.clearHighlight();
- this.updateAttachedTransformFromLocalTransform_();
- this.updateBaseTransformFromAttachedTransform_();
-}
-
-o3djs.manipulators.Translate1.prototype.drag = function(startPoint,
- endPoint) {
- // Algorithm: Find closest point of ray to dragLine. Subtract this
- // point from the line's point to find difference vector; transform
- // from world to local coordinates to find new local offset of
- // manipulator.
- var closestPoint = this.dragLine.closestPointToRay(startPoint, endPoint);
- if (closestPoint == null) {
- // Drag axis is parallel to ray. Punt.
- return;
- }
- // Need to do a world-to-local transformation on the difference vector.
- // Note that we also incorporate the translation portion of the matrix.
- var diffVector =
- o3djs.math.subVector(closestPoint, this.dragLine.getPoint());
- var worldToLocal =
- o3djs.math.matrix4.inverse(this.getTransform().worldMatrix);
- this.getTransform().localMatrix =
- o3djs.math.matrix4.setTranslation(
- this.getTransform().localMatrix,
- o3djs.math.matrix4.transformDirection(worldToLocal,
- diffVector));
- this.updateAttachedTransformFromLocalTransform_();
-}
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains classes that implement several
+ * forms of 2D and 3D manipulation.
+ */
+
+o3djs.provide('o3djs.manipulators');
+
+o3djs.require('o3djs.material');
+
+o3djs.require('o3djs.math');
+
+o3djs.require('o3djs.picking');
+
+o3djs.require('o3djs.primitives');
+
+o3djs.require('o3djs.quaternions');
+
+/**
+ * A module implementing several forms of 2D and 3D manipulation.
+ * @namespace
+ */
+o3djs.manipulators = o3djs.manipulators || {};
+
+/**
+ * Creates a new manipulator manager, which maintains multiple
+ * manipulators in the same scene. The manager is implicitly
+ * associated with a particular O3D client via the Pack which is
+ * passed in, although multiple managers can be created for a given
+ * client. The manipulators are positioned in world coordinates and
+ * are placed in the scene graph underneath the parent transform which
+ * is passed in.
+ * @param {!o3d.Pack} pack Pack in which manipulators' geometry and
+ * materials will be created.
+ * @param {!o3d.DrawList} drawList The draw list against which
+ * internal materials are created.
+ * @param {!o3d.Transform} parentTransform The parent transform under
+ * which the manipulators' geometry should be parented.
+ * @param {!o3d.RenderNode} parentRenderNode The parent render node
+ * under which the manipulators' draw elements should be placed.
+ * @param {number} renderNodePriority The priority that the
+ * manipulators' geometry should use for rendering.
+ * @return {!o3djs.manipulators.Manager} The created manipulator
+ * manager.
+ */
+o3djs.manipulators.createManager = function(pack,
+ drawList,
+ parentTransform,
+ parentRenderNode,
+ renderNodePriority) {
+ return new o3djs.manipulators.Manager(pack,
+ drawList,
+ parentTransform,
+ parentRenderNode,
+ renderNodePriority);
+}
+
+//
+// Some linear algebra classes.
+// TODO(kbr): find a better home for these.
+//
+
+/**
+ * Creates a new Line object, which implements projection and
+ * closest-point operations.
+ * @constructor
+ * @private
+ * @param {o3djs.math.Vector3} opt_direction The direction of the
+ * line. Does not need to be normalized but must not be the zero
+ * vector. Defaults to [1, 0, 0] if not specified.
+ * @param {o3djs.math.Vector3} opt_point A point through which the
+ * line goes. Defaults to [0, 0, 0] if not specified.
+ */
+o3djs.manipulators.Line_ = function(opt_direction,
+ opt_point) {
+ if (opt_direction) {
+ this.direction_ = o3djs.math.copyVector(opt_direction);
+ } else {
+ this.direction_ = [1, 0, 0];
+ }
+
+ if (opt_point) {
+ this.point_ = o3djs.math.copyVector(opt_point);
+ } else {
+ this.point_ = [0, 0, 0];
+ }
+
+ // Helper for computing projections along the line
+ this.alongVec_ = [0, 0, 0];
+ this.recalc_();
+}
+
+/**
+ * Sets the direction of this line.
+ * @private
+ * @param {!o3djs.math.Vector3} direction The new direction of the
+ * line. Does not need to be normalized but must not be the zero
+ * vector.
+ */
+o3djs.manipulators.Line_.prototype.setDirection = function(direction) {
+ this.direction_ = o3djs.math.copyVector(direction);
+ this.recalc_();
+}
+
+/**
+ * Gets the direction of this line.
+ * @private
+ * @return {!o3djs.math.Vector3} The direction of the line.
+ */
+o3djs.manipulators.Line_.prototype.getDirection = function() {
+ return this.direction_;
+}
+
+/**
+ * Sets one point through which this line travels.
+ * @private
+ * @param {!o3djs.math.Vector3} point A point which through the line
+ * will travel.
+ */
+o3djs.manipulators.Line_.prototype.setPoint = function(point) {
+ this.point_ = o3djs.math.copyVector(point);
+ this.recalc_();
+}
+
+/**
+ * Gets one point through which this line travels.
+ * @private
+ * @return {!o3djs.math.Vector3} A point which through the line
+ * travels.
+ */
+o3djs.manipulators.Line_.prototype.getPoint = function() {
+ return this.point_;
+}
+
+/**
+ * Projects a point onto the line.
+ * @private
+ * @param {!o3djs.math.Vector3} point Point to be projected.
+ * @return {!o3djs.math.Vector3} Point on the line closest to the
+ * passed point.
+ */
+o3djs.manipulators.Line_.prototype.projectPoint = function(point) {
+ var dotp = o3djs.math.dot(this.direction_, point);
+ return o3djs.math.addVector(this.alongVec_,
+ o3djs.math.mulScalarVector(dotp,
+ this.direction_));
+}
+
+o3djs.manipulators.EPSILON = 0.00001;
+o3djs.manipulators.X_AXIS = [1, 0, 0];
+
+
+/**
+ * Returns the closest point on this line to the given ray, which is
+ * specified by start and end points. If the ray is parallel to the
+ * line, returns null.
+ * @private
+ * @param {!o3djs.math.Vector3} startPoint Start point of ray.
+ * @param {!o3djs.math.Vector3} endPoint End point of ray.
+ * @return {o3djs.math.Vector3} The closest point on the line to the
+ * ray, or null if the ray is parallel to the line.
+ */
+o3djs.manipulators.Line_.prototype.closestPointToRay = function(startPoint,
+ endPoint) {
+ // Consider a two-sided line and a one-sided ray, both in in 3D
+ // space, and assume they are not parallel. Their parametric
+ // formulation is:
+ //
+ // p1 = point + t * dir
+ // p2 = raystart + u * raydir
+ //
+ // Here t and u are scalar parameter values, and the other values
+ // are three-dimensional vectors. p1 and p2 are arbitrary points on
+ // the line and ray, respectively.
+ //
+ // At the points cp1 and cp2 on these two lines where the line and
+ // the ray are closest together, the line segment between cp1 and
+ // cp2 is perpendicular to both of the lines.
+ //
+ // We can therefore write the following equations:
+ //
+ // dot( dir, (cp2 - cp1)) = 0
+ // dot(raydir, (cp2 - cp1)) = 0
+ //
+ // Define t' and u' as the parameter values for cp1 and cp2,
+ // respectively. Expanding, these equations become
+ //
+ // dot( dir, ((raystart + u' * raydir) - (point + t' * dir))) = 0
+ // dot(raydir, ((raystart + u' * raydir) - (point + t' * dir))) = 0
+ //
+ // With some reshuffling, these can be expressed in vector/matrix
+ // form:
+ //
+ // [ dot( dir, raystart) - dot( dir, point) ]
+ // [ dot(raydir, raystart) - dot(raydir, point) ] + (continued)
+ //
+ // [ -dot( dir, dir) dot( dir, raydir) ] [ t' ] [0]
+ // [ -dot(raydir, dir) dot(raydir, raydir) ] * [ u' ] = [0]
+ //
+ // u' is the parameter for the world space ray being cast into the
+ // screen. We can deduce whether the starting point of the ray is
+ // actually the closest point to the infinite 3D line by whether the
+ // value of u' is less than zero.
+ var rayDirection = o3djs.math.subVector(endPoint, startPoint);
+ var ddrd = o3djs.math.dot(this.direction_, rayDirection);
+ var A = [[-o3djs.math.lengthSquared(this.direction_), ddrd],
+ [ddrd, -o3djs.math.lengthSquared(rayDirection)]];
+ var det = o3djs.math.det2(A);
+ if (Math.abs(det) < o3djs.manipulators.EPSILON) {
+ return null;
+ }
+ var Ainv = o3djs.math.inverse2(A);
+ var b = [o3djs.math.dot(this.point_, this.direction_) -
+ o3djs.math.dot(startPoint, this.direction_),
+ o3djs.math.dot(startPoint, rayDirection) -
+ o3djs.math.dot(this.point_, rayDirection)];
+ var x = o3djs.math.mulMatrixVector(Ainv, b);
+ if (x[1] < 0) {
+ // Means that start point is closest point to this line
+ return startPoint;
+ } else {
+ return o3djs.math.addVector(this.point_,
+ o3djs.math.mulScalarVector(
+ x[0],
+ this.direction_));
+ }
+}
+
+/**
+ * Performs internal recalculations when the parameters of the line change.
+ * @private
+ */
+o3djs.manipulators.Line_.prototype.recalc_ = function() {
+ var denom = o3djs.math.lengthSquared(this.direction_);
+ if (denom == 0.0) {
+ throw 'Line_.recalc: ERROR: direction was the zero vector (not allowed)';
+ }
+ this.alongVec_ =
+ o3djs.math.subVector(this.point_,
+ o3djs.math.mulScalarVector(
+ o3djs.math.dot(this.point_,
+ this.direction_),
+ this.direction_));
+}
+
+o3djs.manipulators.DEFAULT_COLOR = [0.8, 0.8, 0.8, 1.0];
+o3djs.manipulators.HIGHLIGHTED_COLOR = [0.9, 0.9, 0.0, 1.0];
+
+/**
+ * Constructs a new manipulator manager. Do not call this directly;
+ * use o3djs.manipulators.createManager instead.
+ * @constructor
+ * @param {!o3d.Pack} pack Pack in which manipulators' geometry and
+ * materials will be created.
+ * @param {!o3d.DrawList} drawList The draw list against which
+ * internal materials are created.
+ * @param {!o3d.Transform} parentTransform The parent transform under
+ * which the manipulators' geometry should be parented.
+ * @param {!o3d.RenderNode} parentRenderNode The parent render node
+ * under which the manipulators' draw elements should be placed.
+ * @param {number} renderNodePriority The priority that the
+ * manipulators' geometry should use for rendering.
+ */
+o3djs.manipulators.Manager = function(pack,
+ drawList,
+ parentTransform,
+ parentRenderNode,
+ renderNodePriority) {
+ this.pack = pack;
+ this.drawList = drawList;
+ this.parentTransform = parentTransform;
+ this.parentRenderNode = parentRenderNode;
+ this.renderNodePriority = renderNodePriority;
+
+ this.lightPosition = [10, 10, 10];
+
+ // Create the default and highlighted materials.
+ this.defaultMaterial =
+ this.createPhongMaterial_(o3djs.manipulators.DEFAULT_COLOR);
+ this.highlightedMaterial =
+ this.createPhongMaterial_(o3djs.manipulators.HIGHLIGHTED_COLOR);
+
+ // This is a map from the manip's parent Transform clientId to the manip.
+ this.manipsByClientId = [];
+
+ // Presumably we need a TransformInfo for the parentTransform.
+ this.transformInfo =
+ o3djs.picking.createTransformInfo(this.parentTransform, null);
+
+ /**
+ * The currently-highlighted manipulator.
+ * @type {o3djs.manipulators.Manip}
+ */
+ this.highlightedManip = null;
+
+ /**
+ * The manipulator currently being dragged.
+ * @private
+ * @type {o3djs.manipulators.Manip}
+ */
+ this.draggedManip_ = null;
+}
+
+/**
+ * Creates a phong material based on the given single color.
+ * @private
+ * @param {!o3djs.math.Vector4} baseColor A vector with 4 entries, the
+ * R,G,B, and A components of a color.
+ * @return {!o3d.Material} A phong material whose overall pigment is baseColor.
+ */
+o3djs.manipulators.Manager.prototype.createPhongMaterial_ =
+ function(baseColor) {
+ // Create a new, empty Material object.
+ var material = this.pack.createObject('Material');
+
+ o3djs.effect.attachStandardShader(
+ this.pack, material, this.lightPosition, 'phong');
+
+ material.drawList = this.drawList;
+
+ // Assign parameters to the phong material.
+ material.getParam('emissive').value = [0, 0, 0, 1];
+ material.getParam('ambient').value =
+ o3djs.math.mulScalarVector(0.1, baseColor);
+ material.getParam('diffuse').value = baseColor;
+ material.getParam('specular').value = [.2, .2, .2, 1];
+ material.getParam('shininess').value = 20;
+
+ return material;
+}
+
+/**
+ * Creates a new Translate1 manipulator. A Translate1 moves along the
+ * X axis in its local coordinate system.
+ * @return {!o3djs.manipulators.Translate1} A new Translate1 manipulator.
+ */
+o3djs.manipulators.Manager.prototype.createTranslate1 = function() {
+ var manip = new o3djs.manipulators.Translate1(this);
+ this.add_(manip);
+ return manip;
+}
+
+/**
+ * Adds a manipulator to this manager's set.
+ * @private
+ * @param {!o3djs.manipulators.Manip} manip The manipulator to add.
+ */
+o3djs.manipulators.Manager.prototype.add_ = function(manip) {
+ // Generate draw elements for the manipulator's transform
+ manip.getTransform().createDrawElements(this.pack, null);
+ // Add the manipulator's transform to the parent transform
+ manip.getBaseTransform_().parent = this.parentTransform;
+ // Add the manipulator into our managed list
+ this.manipsByClientId[manip.getTransform().clientId] = manip;
+}
+
+/**
+ * Event handler for multiple kinds of mouse events.
+ * @private
+ * @param {number} x The x coordinate of the mouse event.
+ * @param {number} y The y coordinate of the mouse event.
+ * @param {!o3djs.math.Matrix4} view The current view matrix.
+ * @param {!o3djs.math.Matrix4} projection The current projection matrix.
+ * @param {number} width The width of the viewport.
+ * @param {number} height The height of the viewport.
+ * @param {!function(!o3djs.manipulators.Manager,
+ * o3djs.picking.PickInfo, o3djs.manipulators.Manip): void} func
+ * Callback function. Always receives the manager as argument; if
+ * a manipulator was picked, receives non-null PickInfo and Manip
+ * arguments, otherwise receives null for both of these arguments.
+ */
+o3djs.manipulators.Manager.prototype.handleMouse_ = function(x,
+ y,
+ view,
+ projection,
+ width,
+ height,
+ func) {
+ this.transformInfo.update();
+
+ // Create the world ray
+ var worldRay =
+ o3djs.picking.clientPositionToWorldRayEx(x, y,
+ view, projection,
+ width, height);
+
+ // Pick against all of the manipulators' geometry
+ var pickResult = this.transformInfo.pick(worldRay);
+ if (pickResult != null) {
+ // Find which manipulator we picked.
+ // NOTE this assumes some things about the transform graph
+ // structure of the manipulators.
+ var manip =
+ this.manipsByClientId[pickResult.shapeInfo.parent.transform.clientId];
+ func(this, pickResult, manip);
+ } else {
+ func(this, null, null);
+ }
+}
+
+/**
+ * Callback handling the mouse-down event on a manipulator.
+ * @private
+ * @param {!o3djs.manipulators.Manager} manager The manipulator
+ * manager owning the given manipulator.
+ * @param {o3djs.picking.PickInfo} pickResult The picking information
+ * associated with the mouse-down event.
+ * @param {o3djs.manipulators.Manip} manip The manipulator to be
+ * selected.
+ */
+o3djs.manipulators.mouseDownCallback_ = function(manager,
+ pickResult,
+ manip) {
+ if (manip != null) {
+ manager.draggedManip_ = manip;
+ manip.makeActive(pickResult);
+ }
+}
+
+/**
+ * Callback handling the mouse-over event on a manipulator.
+ * @private
+ * @param {!o3djs.manipulators.Manager} manager The manipulator
+ * manager owning the given manipulator.
+ * @param {o3djs.picking.PickInfo} pickResult The picking information
+ * associated with the mouse-over event.
+ * @param {o3djs.manipulators.Manip} manip The manipulator to be
+ * highlighted.
+ */
+o3djs.manipulators.hoverCallback_ = function(manager,
+ pickResult,
+ manip) {
+ if (manager.highlightedManip != null &&
+ manager.highlightedManip != manip) {
+ // Un-highlight the previously highlighted manipulator
+ manager.highlightedManip.clearHighlight();
+ manager.highlightedManip = null;
+ }
+
+ if (manip != null) {
+ manip.highlight(pickResult);
+ manager.highlightedManip = manip;
+ }
+}
+
+/**
+ * Method which should be called by end user code upon receiving a
+ * mouse-down event.
+ * @param {number} x The x coordinate of the mouse event.
+ * @param {number} y The y coordinate of the mouse event.
+ * @param {!o3djs.math.Matrix4} view The current view matrix.
+ * @param {!o3djs.math.Matrix4} projection The current projection matrix.
+ * @param {number} width The width of the viewport.
+ * @param {number} height The height of the viewport.
+ */
+o3djs.manipulators.Manager.prototype.mousedown = function(x,
+ y,
+ view,
+ projection,
+ width,
+ height) {
+ this.handleMouse_(x, y, view, projection, width, height,
+ o3djs.manipulators.mouseDownCallback_);
+}
+
+/**
+ * Method which should be called by end user code upon receiving a
+ * mouse motion event.
+ * @param {number} x The x coordinate of the mouse event.
+ * @param {number} y The y coordinate of the mouse event.
+ * @param {!o3djs.math.Matrix4} view The current view matrix.
+ * @param {!o3djs.math.Matrix4} projection The current projection matrix.
+ * @param {number} width The width of the viewport.
+ * @param {number} height The height of the viewport.
+ */
+o3djs.manipulators.Manager.prototype.mousemove = function(x,
+ y,
+ view,
+ projection,
+ width,
+ height) {
+ if (this.draggedManip_ != null) {
+ var worldRay =
+ o3djs.picking.clientPositionToWorldRayEx(x, y,
+ view, projection,
+ width, height);
+ this.draggedManip_.drag(worldRay.near, worldRay.far);
+ } else {
+ this.handleMouse_(x, y, view, projection, width, height,
+ o3djs.manipulators.hoverCallback_);
+ }
+}
+
+/**
+ * Method which should be called by end user code upon receiving a
+ * mouse-up event.
+ */
+o3djs.manipulators.Manager.prototype.mouseup = function() {
+ if (this.draggedManip_ != null) {
+ this.draggedManip_.makeInactive();
+ this.draggedManip_ = null;
+ }
+}
+
+/**
+ * Method which should be called by end user code, typically in
+ * response to mouse move events, to update the transforms of
+ * manipulators which might have been moved either because of
+ * manipulators further up the hierarchy, or programmatic changes to
+ * transforms.
+ */
+o3djs.manipulators.Manager.prototype.updateInactiveManipulators = function() {
+ for (var ii in this.manipsByClientId) {
+ var manip = this.manipsByClientId[ii];
+ if (!manip.isActive()) {
+ manip.updateBaseTransformFromAttachedTransform_();
+ }
+ }
+}
+
+/**
+ * Base class for all manipulators.
+ * @constructor
+ * @param {!o3djs.manipulators.Manager} manager The manager of this
+ * manipulator.
+ */
+o3djs.manipulators.Manip = function(manager) {
+ this.manager_ = manager;
+ var pack = manager.pack;
+ // This transform holds the local transformation of the manipulator,
+ // which is either applied to the transform to which it is attached,
+ // or (see below) consumed by the user in the manipulator's
+ // callbacks. After each interaction, if there is an attached
+ // transform, this local transform is added in to it and reset to
+ // the identity.
+ // TODO(kbr): add support for user callbacks on manipulators.
+ this.localTransform_ = pack.createObject('Transform');
+
+ // This transform provides an offset, if desired, between the
+ // manipulator's geometry and the transform (and, implicitly, the
+ // shape) to which it is attached. This allows the manipulator to be
+ // easily placed below an object, for example.
+ this.offsetTransform_ = pack.createObject('Transform');
+
+ // This transform is the one which is actually parented to the
+ // manager's parentTransform. It is used to place the manipulator in
+ // world space, regardless of the world space location of the
+ // parentTransform supplied to the manager. If this manipulator is
+ // attached to a given transform, then upon completion of a
+ // particular drag interaction, this transform is adjusted to take
+ // into account the attached transform's new value.
+ this.baseTransform_ = pack.createObject('Transform');
+
+ // Hook up these transforms
+ this.localTransform_.parent = this.offsetTransform_;
+ this.offsetTransform_.parent = this.baseTransform_;
+
+ // This is the transform in the scene graph to which this
+ // manipulator is conceptually "attached", and whose local transform
+ // we are modifying.
+ this.attachedTransform_ = null;
+
+ this.active_ = false;
+}
+
+/**
+ * Adds shapes to the internal transform of this manipulator.
+ * @private
+ * @param {!Array.<!o3d.Shape>} shapes Array of shapes to add.
+ */
+o3djs.manipulators.Manip.prototype.addShapes_ = function(shapes) {
+ for (var ii = 0; ii < shapes.length; ii++) {
+ this.localTransform_.addShape(shapes[ii]);
+ }
+}
+
+/**
+ * Returns the "base" transform of this manipulator, which places the
+ * origin of the manipulator at the local origin of the attached
+ * transform.
+ * @private
+ * @return {!o3d.Transform} The base transform of this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.getBaseTransform_ = function() {
+ return this.baseTransform_;
+}
+
+/**
+ * Returns the "offset" transform of this manipulator, which allows
+ * the manipulator's geometry to be moved or rotated with respect to
+ * the local origin of the attached transform.
+ * @return {!o3d.Transform} The offset transform of this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.getOffsetTransform = function() {
+ return this.offsetTransform_;
+}
+
+/**
+ * Returns the local transform of this manipulator, which contains the
+ * changes that have been made in response to the current drag
+ * operation. Upon completion of the drag, this transform's effects
+ * are composed in to the attached transform, and this transform is
+ * reset to the identity.
+ * @return {!o3d.Transform} The local transform of this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.getTransform = function() {
+ return this.localTransform_;
+}
+
+/**
+ * Sets the translation component of the offset transform. This is
+ * useful for moving the manipulator's geometry with respect to the
+ * local origin of the attached transform.
+ * @param {!o3djs.math.Vector3} translation The offset translation for
+ * this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.setOffsetTranslation =
+ function(translation) {
+ this.getOffsetTransform().localMatrix =
+ o3djs.math.matrix4.setTranslation(this.getOffsetTransform().localMatrix,
+ translation);
+
+}
+
+/**
+ * Sets the rotation component of the offset transform. This is useful
+ * for orienting the manipulator's geometry with respect to the local
+ * origin of the attached transform.
+ * @param {!o3djs.quaternions.Quaternion} quaternion The offset
+ * rotation for this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.setOffsetRotation = function(quaternion) {
+ var rot = o3djs.quaternions.quaternionToRotation(quaternion);
+ this.getOffsetTransform().localMatrix =
+ o3djs.math.matrix4.setUpper3x3(this.getOffsetTransform().localMatrix,
+ rot);
+
+}
+
+/**
+ * Explicitly sets the local translation of this manipulator.
+ * (TODO(kbr): it is not clear that this capability should be in the
+ * API.)
+ * @param {!o3djs.math.Vector3} translation The local translation for
+ * this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.setTranslation = function(translation) {
+ this.getTransform().localMatrix =
+ o3djs.math.matrix4.setTranslation(this.getTransform().localMatrix,
+ translation);
+
+}
+
+/**
+ * Explicitly sets the local rotation of this manipulator. (TODO(kbr):
+ * it is not clear that this capability should be in the API.)
+ * @param {!o3djs.quaternions.Quaternion} quaternion The local
+ * rotation for this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.setRotation = function(quaternion) {
+ var rot = o3djs.quaternions.quaternionToRotation(quaternion);
+ this.getTransform().localMatrix =
+ o3djs.math.matrix4.setUpper3x3(this.getTransform().localMatrix,
+ rot);
+
+}
+
+/**
+ * Attaches this manipulator to the given transform. Interactions with
+ * the manipulator will cause this transform's local matrix to be
+ * modified appropriately.
+ * @param {!o3d.Transform} transform The transform to which this
+ * manipulator should be attached.
+ */
+o3djs.manipulators.Manip.prototype.attachTo = function(transform) {
+ this.attachedTransform_ = transform;
+ // Update our base transform to place the manipulator at exactly the
+ // location of the attached transform.
+ this.updateBaseTransformFromAttachedTransform_();
+}
+
+/**
+ * Highlights this manipulator according to the given pick result.
+ * @param {o3djs.picking.PickInfo} pickResult The pick result which
+ * caused this manipulator to become highlighted.
+ */
+o3djs.manipulators.Manip.prototype.highlight = function(pickResult) {
+}
+
+/**
+ * Clears any highlight for this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.clearHighlight = function() {
+}
+
+/**
+ * Activates this manipulator according to the given pick result. In
+ * complex manipulators, picking different portions of the manipulator
+ * may result in different forms of interaction.
+ * @param {o3djs.picking.PickInfo} pickResult The pick result which
+ * caused this manipulator to become active.
+ */
+o3djs.manipulators.Manip.prototype.makeActive = function(pickResult) {
+ this.active_ = true;
+}
+
+/**
+ * Deactivates this manipulator.
+ */
+o3djs.manipulators.Manip.prototype.makeInactive = function() {
+ this.active_ = false;
+}
+
+/**
+ * Drags this manipulator according to the world-space ray specified
+ * by startPoint and endPoint. makeActive must already have been
+ * called with the initial pick result causing this manipulator to
+ * become active. This method exists only to be overridden.
+ * @param {!o3djs.math.Vector3} startPoint Start point of the
+ * world-space ray through the current mouse position.
+ * @param {!o3djs.math.Vector3} endPoint End point of the world-space
+ * ray through the current mouse position.
+ */
+o3djs.manipulators.Manip.prototype.drag = function(startPoint,
+ endPoint) {
+}
+
+/**
+ * Indicates whether this manipulator is active.
+ * @return {boolean} Whether this manipulator is active.
+ */
+o3djs.manipulators.Manip.prototype.isActive = function() {
+ return this.active_;
+}
+
+/**
+ * Updates the base transform of this manipulator from the state of
+ * its attached transform, resetting the local transform of this
+ * manipulator to the identity.
+ * @private
+ */
+o3djs.manipulators.Manip.prototype.updateBaseTransformFromAttachedTransform_ =
+ function() {
+ if (this.attachedTransform_ != null) {
+ var attWorld = this.attachedTransform_.worldMatrix;
+ var parWorld = this.manager_.parentTransform.worldMatrix;
+ var parWorldInv = o3djs.math.matrix4.inverse(parWorld);
+ this.baseTransform_.localMatrix =
+ o3djs.math.matrix4.mul(attWorld, parWorldInv);
+ // Reset the manipulator's local matrix to the identity.
+ this.localTransform_.localMatrix = o3djs.math.matrix4.identity();
+ }
+}
+
+/**
+ * Updates this manipulator's attached transform based on the values
+ * in the local transform.
+ * @private
+ */
+o3djs.manipulators.Manip.prototype.updateAttachedTransformFromLocalTransform_ =
+ function() {
+ if (this.attachedTransform_ != null) {
+ // Compute the composition of the base and local transforms.
+ // The offset transform is skipped except for transforming the
+ // local matrix's translation by the rotation component of the
+ // offset transform.
+ var base = this.baseTransform_.worldMatrix;
+ var local = this.localTransform_.localMatrix;
+ var xlate = o3djs.math.matrix4.getTranslation(local);
+ var transformedXlate =
+ o3djs.math.matrix4.transformDirection(
+ this.offsetTransform_.localMatrix,
+ o3djs.math.matrix4.getTranslation(local));
+ o3djs.math.matrix4.setTranslation(local, transformedXlate);
+ var totalMat = o3djs.math.matrix4.mul(base, local);
+
+ // Set this into the attached transform, taking into account its
+ // parent's transform, if any.
+ // Note that we can not query the parent's transform directly, so
+ // we compute it using a little trick.
+ var attWorld = this.attachedTransform_.worldMatrix;
+ var attLocal = this.attachedTransform_.localMatrix;
+ var attParentMat =
+ o3djs.math.matrix4.mul(attWorld,
+ o3djs.math.matrix4.inverse(attLocal));
+ // Now we can take the inverse of this matrix
+ var attParentMatInv = o3djs.math.matrix4.inverse(attParentMat);
+ totalMat = o3djs.math.matrix4.mul(attParentMatInv, totalMat);
+ this.attachedTransform_.localMatrix = totalMat;
+ }
+}
+
+/**
+ * Sets the material of the given shape's draw elements.
+ * @private
+ */
+o3djs.manipulators.Manip.prototype.setMaterial_ = function(shape, material) {
+ var elements = shape.elements;
+ for (var ii = 0; ii < elements.length; ii++) {
+ var drawElements = elements[ii].drawElements;
+ for (var jj = 0; jj < drawElements.length; jj++) {
+ drawElements[jj].material = material;
+ }
+ }
+}
+
+/**
+ * Sets the materials of the given shapes' draw elements.
+ * @private
+ */
+o3djs.manipulators.Manip.prototype.setMaterials_ = function(shapes, material) {
+ for (var ii = 0; ii < shapes.length; ii++) {
+ this.setMaterial_(shapes[ii], material);
+ }
+}
+
+/**
+ * A manipulator allowing an object to be dragged along a line.
+ * @constructor
+ * @extends {o3djs.manipulators.Manip}
+ * @param {!o3djs.manipulators.Manager} manager The manager for the
+ * new Translate1 manipulator.
+ */
+o3djs.manipulators.Translate1 = function(manager) {
+ o3djs.manipulators.Manip.call(this, manager);
+
+ var pack = manager.pack;
+ var material = manager.defaultMaterial;
+
+ var shape = manager.translate1Shape_;
+ if (!shape) {
+ // Create the geometry for the manipulator, which looks like a
+ // two-way arrow going from (-1, 0, 0) to (1, 0, 0).
+ var matrix4 = o3djs.math.matrix4;
+ var zRot = matrix4.rotationZ(Math.PI / 2);
+
+ var verts = o3djs.primitives.createTruncatedConeVertices(
+ 0.15, // Bottom radius.
+ 0.0, // Top radius.
+ 0.3, // Height.
+ 4, // Number of radial subdivisions.
+ 1, // Number of vertical subdivisions.
+ matrix4.mul(matrix4.translation([0, 0.85, 0]), zRot));
+
+ verts.append(o3djs.primitives.createCylinderVertices(
+ 0.06, // Radius.
+ 1.4, // Height.
+ 4, // Number of radial subdivisions.
+ 1, // Number of vertical subdivisions.
+ zRot));
+
+ verts.append(o3djs.primitives.createTruncatedConeVertices(
+ 0.0, // Bottom radius.
+ 0.15, // Top radius.
+ 0.3, // Height.
+ 4, // Number of radial subdivisions.
+ 1, // Number of vertical subdivisions.
+ matrix4.mul(matrix4.translation([0, -0.85, 0]), zRot)));
+
+ shape = verts.createShape(pack, material);
+ manager.translate1Shape_ = shape;
+ }
+
+ this.addShapes_([ shape ]);
+
+ this.transformInfo = o3djs.picking.createTransformInfo(this.getTransform(),
+ manager.transformInfo);
+
+ // Add a parameter to our transform to be able to change the
+ // material's color for highlighting.
+ this.colorParam = this.getTransform().createParam('diffuse', 'ParamFloat4');
+ this.clearHighlight();
+
+ /** Dragging state */
+ this.dragLine = new o3djs.manipulators.Line_();
+}
+
+o3djs.base.inherit(o3djs.manipulators.Translate1, o3djs.manipulators.Manip);
+
+o3djs.manipulators.Translate1.prototype.highlight = function(pickResult) {
+ // We can use instanced geometry for the entire Translate1 since its
+ // entire color changes during highlighting.
+ // TODO(kbr): support custom user geometry and associated callbacks.
+ this.colorParam.value = o3djs.manipulators.HIGHLIGHTED_COLOR;
+}
+
+o3djs.manipulators.Translate1.prototype.clearHighlight = function() {
+ this.colorParam.value = o3djs.manipulators.DEFAULT_COLOR;
+}
+
+o3djs.manipulators.Translate1.prototype.makeActive = function(pickResult) {
+ o3djs.manipulators.Manip.prototype.makeActive.call(this, pickResult);
+ this.highlight(pickResult);
+ var worldMatrix = this.getTransform().worldMatrix;
+ this.dragLine.setDirection(
+ o3djs.math.matrix4.transformDirection(worldMatrix,
+ o3djs.manipulators.X_AXIS));
+ this.dragLine.setPoint(pickResult.worldIntersectionPosition);
+}
+
+o3djs.manipulators.Translate1.prototype.makeInactive = function() {
+ o3djs.manipulators.Manip.prototype.makeInactive.call(this);
+ this.clearHighlight();
+ this.updateAttachedTransformFromLocalTransform_();
+ this.updateBaseTransformFromAttachedTransform_();
+}
+
+o3djs.manipulators.Translate1.prototype.drag = function(startPoint,
+ endPoint) {
+ // Algorithm: Find closest point of ray to dragLine. Subtract this
+ // point from the line's point to find difference vector; transform
+ // from world to local coordinates to find new local offset of
+ // manipulator.
+ var closestPoint = this.dragLine.closestPointToRay(startPoint, endPoint);
+ if (closestPoint == null) {
+ // Drag axis is parallel to ray. Punt.
+ return;
+ }
+ // Need to do a world-to-local transformation on the difference vector.
+ // Note that we also incorporate the translation portion of the matrix.
+ var diffVector =
+ o3djs.math.subVector(closestPoint, this.dragLine.getPoint());
+ var worldToLocal =
+ o3djs.math.matrix4.inverse(this.getTransform().worldMatrix);
+ this.getTransform().localMatrix =
+ o3djs.math.matrix4.setTranslation(
+ this.getTransform().localMatrix,
+ o3djs.math.matrix4.transformDirection(worldToLocal,
+ diffVector));
+ this.updateAttachedTransformFromLocalTransform_();
+}
diff --git a/o3d/samples/o3djs/performance.js b/o3d/samples/o3djs/performance.js
index 92865e5..c5faeaa 100644
--- a/o3d/samples/o3djs/performance.js
+++ b/o3d/samples/o3djs/performance.js
@@ -1,202 +1,202 @@
-/*
- * 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.
- */
-
-
-/**
- * @fileoverview This file contains a utility that helps adjust rendering
- * quality [or any other setting, really] based on rendering performance.
- *
- */
-
-o3djs.provide('o3djs.performance');
-
-/**
- * A Module to help with adjusting performance.
- * @namespace
- */
-o3djs.performance = o3djs.performance || {};
-
-/**
- * Creates a utility that monitors performance [in terms of FPS] and helps to
- * adjust the rendered scene accordingly.
- * @param {number} targetFPSMin the minimum acceptable frame rate; if we're
- * under this, try to decrease quality to improve performance.
- * @param {number} targetFPSMax if we're over this, try to increase quality.
- * @param {!function(): void} increaseQuality a function to increase
- * quality because we're rendering at high-enough FPS to afford it.
- * @param {!function(): void} decreaseQuality a function to decrease
- * quality to try to raise our rendering speed.
- * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options.
- * @return {!o3djs.performance.PerformanceMonitor} The created
- * PerformanceMonitor.
- */
-o3djs.performance.createPerformanceMonitor = function(
- targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) {
- return new o3djs.performance.PerformanceMonitor(targetFPSMin, targetFPSMax,
- increaseQuality, decreaseQuality, opt_options);
-};
-
-/**
- * A class that monitors performance [in terms of FPS] and helps to adjust the
- * rendered scene accordingly.
- * @constructor
- * @param {number} targetFPSMin the minimum acceptable frame rate; if we're
- * under this, try to decrease quality to improve performance.
- * @param {number} targetFPSMax if we're over this, try to increase quality.
- * @param {function(): void} increaseQuality a function to increase
- * quality/lower FPS.
- * @param {function(): void} decreaseQuality a function to decrease
- * quality/raise FPS.
- * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options.
- */
-o3djs.performance.PerformanceMonitor = function(
- targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) {
- opt_options = opt_options || {};
-
- /**
- * A function to increase quality/lower FPS.
- * @type {function(): void}
- */
- this.increaseQuality = increaseQuality;
-
- /**
- * A function to decrease quality/raise FPS.
- * @type {function(): void}
- */
- this.decreaseQuality = decreaseQuality;
-
- /**
- * The mean time taken per frame so far, in seconds. This is only valid once
- * we've collected at least minSamples samples.
- * @type {number}
- */
- this.meanFrameTime = 0;
-
- /**
- * The number of samples we've collected so far, when that number is less than
- * or equal to this.damping. After that point, we no longer update
- * this.sampleCount, so it will clip at this.damping.
- *
- * @type {number}
- */
- this.sampleCount = 0;
-
- /**
- * The minimum number of samples to collect before trying to adjust quality.
- *
- * @type {number}
- */
- this.minSamples = opt_options.opt_minSamples || 60;
-
- /**
- * A number that controls the rate at which the effects of any given sample
- * fade away. Higher is slower, but also means that each individual sample
- * counts for less at its most-influential. Damping defaults to 120; anywhere
- * between 60 and 600 are probably reasonable values, depending on your needs,
- * but the number must be no less than minSamples.
- *
- * @type {number}
- */
- this.damping = opt_options.opt_damping || 120;
-
- /**
- * The minimum number of samples to take in between adjustments, to cut down
- * on overshoot. It defaults to 2 * minSamples.
- *
- * @type {number}
- */
- this.delayCycles = opt_options.opt_delayCycles || 2 * this.minSamples;
-
- this.targetFrameTimeMax_ = 1 / targetFPSMin;
- this.targetFrameTimeMin_ = 1 / targetFPSMax;
- this.scaleInput_ = 1 / this.minSamples;
- this.scaleMean_ = 1;
- this.delayCyclesLeft_ = 0;
- if (this.damping < this.minSamples) {
- throw Error('Damping must be at least minSamples.');
- }
-};
-
-/**
- * Options for the PerformanceMonitor.
- *
- * opt_minSamples is the minimum number of samples to take before making any
- * performance adjustments.
- * opt_damping is a number that controls the rate at which the effects of any
- * given sample fade away. Higher is slower, but also means that each
- * individual sample counts for less at its most-influential. Damping
- * defaults to 120; anywhere between 60 and 600 are probably reasonable values,
- * depending on your needs, but the number must be no less than minSamples.
- * opt_delayCycles is the minimum number of samples to take in between
- * adjustments, to cut down on overshoot. It defaults to 2 * opt_minSamples.
- *
- * @type {{
- * opt_minSamples: number,
- * opt_damping: number,
- * opt_delayCycles: number
- * }}
- */
-o3djs.performance.PerformanceMonitor.Options = goog.typedef;
-
-/**
- * Call this once per frame with the elapsed time since the last call, and it
- * will attempt to adjust your rendering quality as needed.
- *
- * @param {number} seconds the elapsed time since the last frame was rendered,
- * in seconds.
- */
-o3djs.performance.PerformanceMonitor.prototype.onRender = function(seconds) {
- var test = true;
- if (this.sampleCount < this.damping) {
- if (this.sampleCount >= this.minSamples) {
- this.scaleInput_ = 1 / (this.sampleCount + 1);
- this.scaleMean_ = this.sampleCount * this.scaleInput_;
- } else {
- test = false;
- }
- this.sampleCount += 1;
- }
- this.meanFrameTime = this.meanFrameTime * this.scaleMean_ +
- seconds * this.scaleInput_;
- if (this.delayCyclesLeft_ > 0) {
- this.delayCyclesLeft_ -= 1;
- } else if (test) {
- if (this.meanFrameTime < this.targetFrameTimeMin_) {
- this.increaseQuality();
- this.delayCyclesLeft_ = this.delayCycles;
- } else if (this.meanFrameTime > this.targetFrameTimeMax_) {
- this.decreaseQuality();
- this.delayCyclesLeft_ = this.delayCycles;
- }
- }
-};
-
-
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains a utility that helps adjust rendering
+ * quality [or any other setting, really] based on rendering performance.
+ *
+ */
+
+o3djs.provide('o3djs.performance');
+
+/**
+ * A Module to help with adjusting performance.
+ * @namespace
+ */
+o3djs.performance = o3djs.performance || {};
+
+/**
+ * Creates a utility that monitors performance [in terms of FPS] and helps to
+ * adjust the rendered scene accordingly.
+ * @param {number} targetFPSMin the minimum acceptable frame rate; if we're
+ * under this, try to decrease quality to improve performance.
+ * @param {number} targetFPSMax if we're over this, try to increase quality.
+ * @param {!function(): void} increaseQuality a function to increase
+ * quality because we're rendering at high-enough FPS to afford it.
+ * @param {!function(): void} decreaseQuality a function to decrease
+ * quality to try to raise our rendering speed.
+ * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options.
+ * @return {!o3djs.performance.PerformanceMonitor} The created
+ * PerformanceMonitor.
+ */
+o3djs.performance.createPerformanceMonitor = function(
+ targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) {
+ return new o3djs.performance.PerformanceMonitor(targetFPSMin, targetFPSMax,
+ increaseQuality, decreaseQuality, opt_options);
+};
+
+/**
+ * A class that monitors performance [in terms of FPS] and helps to adjust the
+ * rendered scene accordingly.
+ * @constructor
+ * @param {number} targetFPSMin the minimum acceptable frame rate; if we're
+ * under this, try to decrease quality to improve performance.
+ * @param {number} targetFPSMax if we're over this, try to increase quality.
+ * @param {function(): void} increaseQuality a function to increase
+ * quality/lower FPS.
+ * @param {function(): void} decreaseQuality a function to decrease
+ * quality/raise FPS.
+ * @param {!o3djs.performance.PerformanceMonitor.Options} opt_options Options.
+ */
+o3djs.performance.PerformanceMonitor = function(
+ targetFPSMin, targetFPSMax, increaseQuality, decreaseQuality, opt_options) {
+ opt_options = opt_options || {};
+
+ /**
+ * A function to increase quality/lower FPS.
+ * @type {function(): void}
+ */
+ this.increaseQuality = increaseQuality;
+
+ /**
+ * A function to decrease quality/raise FPS.
+ * @type {function(): void}
+ */
+ this.decreaseQuality = decreaseQuality;
+
+ /**
+ * The mean time taken per frame so far, in seconds. This is only valid once
+ * we've collected at least minSamples samples.
+ * @type {number}
+ */
+ this.meanFrameTime = 0;
+
+ /**
+ * The number of samples we've collected so far, when that number is less than
+ * or equal to this.damping. After that point, we no longer update
+ * this.sampleCount, so it will clip at this.damping.
+ *
+ * @type {number}
+ */
+ this.sampleCount = 0;
+
+ /**
+ * The minimum number of samples to collect before trying to adjust quality.
+ *
+ * @type {number}
+ */
+ this.minSamples = opt_options.opt_minSamples || 60;
+
+ /**
+ * A number that controls the rate at which the effects of any given sample
+ * fade away. Higher is slower, but also means that each individual sample
+ * counts for less at its most-influential. Damping defaults to 120; anywhere
+ * between 60 and 600 are probably reasonable values, depending on your needs,
+ * but the number must be no less than minSamples.
+ *
+ * @type {number}
+ */
+ this.damping = opt_options.opt_damping || 120;
+
+ /**
+ * The minimum number of samples to take in between adjustments, to cut down
+ * on overshoot. It defaults to 2 * minSamples.
+ *
+ * @type {number}
+ */
+ this.delayCycles = opt_options.opt_delayCycles || 2 * this.minSamples;
+
+ this.targetFrameTimeMax_ = 1 / targetFPSMin;
+ this.targetFrameTimeMin_ = 1 / targetFPSMax;
+ this.scaleInput_ = 1 / this.minSamples;
+ this.scaleMean_ = 1;
+ this.delayCyclesLeft_ = 0;
+ if (this.damping < this.minSamples) {
+ throw Error('Damping must be at least minSamples.');
+ }
+};
+
+/**
+ * Options for the PerformanceMonitor.
+ *
+ * opt_minSamples is the minimum number of samples to take before making any
+ * performance adjustments.
+ * opt_damping is a number that controls the rate at which the effects of any
+ * given sample fade away. Higher is slower, but also means that each
+ * individual sample counts for less at its most-influential. Damping
+ * defaults to 120; anywhere between 60 and 600 are probably reasonable values,
+ * depending on your needs, but the number must be no less than minSamples.
+ * opt_delayCycles is the minimum number of samples to take in between
+ * adjustments, to cut down on overshoot. It defaults to 2 * opt_minSamples.
+ *
+ * @type {{
+ * opt_minSamples: number,
+ * opt_damping: number,
+ * opt_delayCycles: number
+ * }}
+ */
+o3djs.performance.PerformanceMonitor.Options = goog.typedef;
+
+/**
+ * Call this once per frame with the elapsed time since the last call, and it
+ * will attempt to adjust your rendering quality as needed.
+ *
+ * @param {number} seconds the elapsed time since the last frame was rendered,
+ * in seconds.
+ */
+o3djs.performance.PerformanceMonitor.prototype.onRender = function(seconds) {
+ var test = true;
+ if (this.sampleCount < this.damping) {
+ if (this.sampleCount >= this.minSamples) {
+ this.scaleInput_ = 1 / (this.sampleCount + 1);
+ this.scaleMean_ = this.sampleCount * this.scaleInput_;
+ } else {
+ test = false;
+ }
+ this.sampleCount += 1;
+ }
+ this.meanFrameTime = this.meanFrameTime * this.scaleMean_ +
+ seconds * this.scaleInput_;
+ if (this.delayCyclesLeft_ > 0) {
+ this.delayCyclesLeft_ -= 1;
+ } else if (test) {
+ if (this.meanFrameTime < this.targetFrameTimeMin_) {
+ this.increaseQuality();
+ this.delayCyclesLeft_ = this.delayCycles;
+ } else if (this.meanFrameTime > this.targetFrameTimeMax_) {
+ this.decreaseQuality();
+ this.delayCyclesLeft_ = this.delayCycles;
+ }
+ }
+};
+
+
diff --git a/o3d/samples/o3djs/texture.js b/o3d/samples/o3djs/texture.js
index 33678b2..5041dde 100644
--- a/o3d/samples/o3djs/texture.js
+++ b/o3d/samples/o3djs/texture.js
@@ -1,239 +1,239 @@
-/*
- * 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.
- */
-
-
-/**
- * @fileoverview This file contains functions helping to manipulate and manage
- * textures.
- */
-
-o3djs.provide('o3djs.texture');
-
-/**
- * A Module for bitmaps.
- * @namespace
- */
-o3djs.texture = o3djs.texture || {};
-
-/**
- * The maximum dimension of a texture.
- * @type {number}
- */
-o3djs.texture.MAX_TEXTURE_DIMENSION = 2048;
-
-/**
- * Computes the maximum number of levels of mips a given width and height could
- * use.
- * @param {number} width Width of texture.
- * @param {number} height Height of texture.
- * @return {number} The maximum number of levels for the given width and height.
- */
-o3djs.texture.computeNumLevels = function(width, height) {
- if (width == 0 || height == 0) {
- return 0;
- }
- var max = Math.max(width, height);
- var levels = 0;
- while (max > 0) {
- ++levels;
- max = max >> 1;
- }
- return levels;
-};
-
-/**
- * Creates a texture from a RawData object.
- * @param {!o3d.Pack} pack The pack to create the texture in.
- * @param {!o3d.RawData} rawData The raw data to create the texture from.
- * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
- * can not be generated for DXT textures although they will be loaded if they
- * exist in the RawData.
- * @param {boolean} opt_flip Whether or not to flip the texture. Most DCC tools
- * Like Maya, Max, etc expect the textures to be flipped. Note that only
- * 2D (image) textures will be flipped. Cube textures will not be flipped.
- * Default = true.
- * @param {number} opt_maxWidth The maximum width of the texture. If the RawData
- * is larger than this size it will be scaled down to this size. Note that
- * DXT format textures can not be scaled. Default = 2048.
- * @param {number} opt_maxHeight The maximum width of the texture. If the
- * RawData is larger than this size it will be scaled down to this size. Note
- * that DXT format textures can not be scaled. Default = 2048.
- * @return {!o3d.Texture} The created texture.
- */
-o3djs.texture.createTextureFromRawData = function(
- pack,
- rawData,
- opt_generateMips,
- opt_flip,
- opt_maxWidth,
- opt_maxHeight) {
- // Make a bitmaps from the raw data.
- var bitmaps = pack.createBitmapsFromRawData(rawData);
- if (opt_flip || typeof opt_flip === 'undefined') {
- for (var ii = 0; ii < bitmaps.length; ++ii) {
- var bitmap = bitmaps[ii];
- if (bitmap.semantic == o3djs.base.o3d.Bitmap.IMAGE) {
- bitmaps[ii].flipVertically();
- }
- }
- }
-
- // Create a texture from the bitmaps.
- var texture = o3djs.texture.createTextureFromBitmaps(
- pack, bitmaps, opt_generateMips);
-
- // Delete the bitmaps.
- for (var ii = 0; ii < bitmaps.length; ++ii) {
- pack.removeObject(bitmaps[ii]);
- }
-
- return texture;
-};
-
-/**
- * Returns whether or not a given texture format can be scaled.
- * @param {!o3d.Texture.Format} format The format to check.
- * @return {boolean} True if you can scale and make mips for the given format.
- */
-o3djs.texture.canMakeMipsAndScale = function(format) {
- switch (format) {
- case o3djs.base.o3d.Texture.XRGB8:
- case o3djs.base.o3d.Texture.ARGB8:
- case o3djs.base.o3d.Texture.ABGR16F:
- case o3djs.base.o3d.Texture.R32F:
- case o3djs.base.o3d.Texture.ABGR32F:
- return true;
- case o3djs.base.o3d.Texture.DXT1:
- case o3djs.base.o3d.Texture.DXT3:
- case o3djs.base.o3d.Texture.DXT5:
- return false;
- }
- return false;
-};
-
-/**
- * Creates a Texture from an array of bitmaps.
- * @param {!o3d.Pack} pack The pack to create the texture in.
- * @param {!Array.<!o3d.Bitmap>} bitmaps An array of bitmaps to create the
- * texture from. For a 2D texture this would be 1 bitmap. For a cubemap this
- * would be 6 bitmaps.
- * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
- * can not be generated for DXT textures although they will be loaded if they
- * exist in the RawData. Default = true.
- * @return {!o3d.Texture} The created texture.
- */
-o3djs.texture.createTextureFromBitmaps = function(
- pack,
- bitmaps,
- opt_generateMips) {
-
- if (bitmaps.length == 0) {
- throw 'no bitmaps';
- }
-
- var srcWidth = bitmaps[0].width;
- var srcHeight = bitmaps[0].height;
- var format = bitmaps[0].format;
- var mipMaps = bitmaps[0].numMipmaps;
- var maxMips = o3djs.texture.computeNumLevels(srcWidth, srcHeight);
- var targetMips = mipMaps;
- var dstWidth = srcWidth;
- var dstHeight = srcHeight;
- if ((typeof opt_generateMips === 'undefined' || opt_generateMips) &&
- o3djs.texture.canMakeMipsAndScale(format) &&
- mipMaps == 1 && maxMips > 1) {
- targetMips = maxMips;
- }
-
- // Check that all the bitmaps are the same size and make mips
- for (var ii = 0; ii < bitmaps.length; ++ii) {
- var bitmap = bitmaps[ii];
- if (bitmap.width != srcWidth ||
- bitmap.height != srcHeight ||
- bitmap.format != format ||
- bitmap.numMipmaps != mipMaps) {
- throw 'bitmaps must all be the same width, height, mips and format';
- }
- if (targetMips != mipMaps) {
- bitmap.generateMips(0, targetMips - 1);
- }
- }
-
- var levels = bitmap.numMipmaps > 1 ? bitmap.numMipmaps :
- o3djs.texture.computeNumLevels(dstWidth, dstHeight);
- var texture;
- if (bitmaps.length == 6 &&
- bitmaps[0].semantic != o3djs.base.o3d.Bitmap.SLICE) {
- if (srcWidth != srcHeight ||
- srcWidth != dstWidth ||
- srcHeight != dstHeight) {
- throw 'Cubemaps must be square';
- }
- texture = pack.createTextureCUBE(dstWidth, format, targetMips, false);
- for (var ii = 0; ii < 6; ++ii) {
- texture.setFromBitmap(
- /** @type {o3d.TextureCUBE.CubeFace} */ (ii),
- bitmaps[ii]);
- }
- } else if (bitmaps.length == 1) {
- texture = pack.createTexture2D(
- dstWidth, dstHeight, format, targetMips, false);
- texture.setFromBitmap(bitmaps[0]);
- }
-
- return /** @type{!o3d.Texture} */ (texture);
-};
-
-/**
- * Creates a TextureCUBE from 6 bitmaps. The bitmaps do not have to be the same
- * size thought they do have to be the same format.
- *
- * @param {!o3d.Pack} pack The pack to create the texture in.
- * @param {number} edgeLength The size of the cubemap.
- * @param {!Array.<!o3d.Bitmap>} bitmaps An array of 6 bitmaps in the order
- * FACE_POSITIVE_X, FACE_NEGATIVE_X, FACE_POSITIVE_Y, FACE_NEGATIVE_Y,
- * FACE_POSITIVE_Z, FACE_NEGATIVE_Z.
- * @return {!o3d.Texture} The created texture.
- */
-o3djs.texture.createCubeTextureFrom6Bitmaps = function(
- pack, edgeLength, bitmaps) {
- var numMips = o3djs.texture.computeNumLevels(edgeLength, edgeLength);
- var texture = pack.createTextureCUBE(
- edgeLength, bitmaps[0].format, numMips, false);
- for (var ii = 0; ii < 6; ++ii) {
- var bitmap = bitmaps[ii];
- texture.drawImage(bitmap, 0, 0, 0, bitmap.width, bitmap.height,
- ii, 0, 0, edgeLength, edgeLength);
- }
- texture.generateMips(0, numMips - 1);
- return texture;
-};
-
+/*
+ * 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.
+ */
+
+
+/**
+ * @fileoverview This file contains functions helping to manipulate and manage
+ * textures.
+ */
+
+o3djs.provide('o3djs.texture');
+
+/**
+ * A Module for bitmaps.
+ * @namespace
+ */
+o3djs.texture = o3djs.texture || {};
+
+/**
+ * The maximum dimension of a texture.
+ * @type {number}
+ */
+o3djs.texture.MAX_TEXTURE_DIMENSION = 2048;
+
+/**
+ * Computes the maximum number of levels of mips a given width and height could
+ * use.
+ * @param {number} width Width of texture.
+ * @param {number} height Height of texture.
+ * @return {number} The maximum number of levels for the given width and height.
+ */
+o3djs.texture.computeNumLevels = function(width, height) {
+ if (width == 0 || height == 0) {
+ return 0;
+ }
+ var max = Math.max(width, height);
+ var levels = 0;
+ while (max > 0) {
+ ++levels;
+ max = max >> 1;
+ }
+ return levels;
+};
+
+/**
+ * Creates a texture from a RawData object.
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {!o3d.RawData} rawData The raw data to create the texture from.
+ * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
+ * can not be generated for DXT textures although they will be loaded if they
+ * exist in the RawData.
+ * @param {boolean} opt_flip Whether or not to flip the texture. Most DCC tools
+ * Like Maya, Max, etc expect the textures to be flipped. Note that only
+ * 2D (image) textures will be flipped. Cube textures will not be flipped.
+ * Default = true.
+ * @param {number} opt_maxWidth The maximum width of the texture. If the RawData
+ * is larger than this size it will be scaled down to this size. Note that
+ * DXT format textures can not be scaled. Default = 2048.
+ * @param {number} opt_maxHeight The maximum width of the texture. If the
+ * RawData is larger than this size it will be scaled down to this size. Note
+ * that DXT format textures can not be scaled. Default = 2048.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createTextureFromRawData = function(
+ pack,
+ rawData,
+ opt_generateMips,
+ opt_flip,
+ opt_maxWidth,
+ opt_maxHeight) {
+ // Make a bitmaps from the raw data.
+ var bitmaps = pack.createBitmapsFromRawData(rawData);
+ if (opt_flip || typeof opt_flip === 'undefined') {
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ var bitmap = bitmaps[ii];
+ if (bitmap.semantic == o3djs.base.o3d.Bitmap.IMAGE) {
+ bitmaps[ii].flipVertically();
+ }
+ }
+ }
+
+ // Create a texture from the bitmaps.
+ var texture = o3djs.texture.createTextureFromBitmaps(
+ pack, bitmaps, opt_generateMips);
+
+ // Delete the bitmaps.
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ pack.removeObject(bitmaps[ii]);
+ }
+
+ return texture;
+};
+
+/**
+ * Returns whether or not a given texture format can be scaled.
+ * @param {!o3d.Texture.Format} format The format to check.
+ * @return {boolean} True if you can scale and make mips for the given format.
+ */
+o3djs.texture.canMakeMipsAndScale = function(format) {
+ switch (format) {
+ case o3djs.base.o3d.Texture.XRGB8:
+ case o3djs.base.o3d.Texture.ARGB8:
+ case o3djs.base.o3d.Texture.ABGR16F:
+ case o3djs.base.o3d.Texture.R32F:
+ case o3djs.base.o3d.Texture.ABGR32F:
+ return true;
+ case o3djs.base.o3d.Texture.DXT1:
+ case o3djs.base.o3d.Texture.DXT3:
+ case o3djs.base.o3d.Texture.DXT5:
+ return false;
+ }
+ return false;
+};
+
+/**
+ * Creates a Texture from an array of bitmaps.
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {!Array.<!o3d.Bitmap>} bitmaps An array of bitmaps to create the
+ * texture from. For a 2D texture this would be 1 bitmap. For a cubemap this
+ * would be 6 bitmaps.
+ * @param {boolean} opt_generateMips Whether or not to generate mips. Note, mips
+ * can not be generated for DXT textures although they will be loaded if they
+ * exist in the RawData. Default = true.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createTextureFromBitmaps = function(
+ pack,
+ bitmaps,
+ opt_generateMips) {
+
+ if (bitmaps.length == 0) {
+ throw 'no bitmaps';
+ }
+
+ var srcWidth = bitmaps[0].width;
+ var srcHeight = bitmaps[0].height;
+ var format = bitmaps[0].format;
+ var mipMaps = bitmaps[0].numMipmaps;
+ var maxMips = o3djs.texture.computeNumLevels(srcWidth, srcHeight);
+ var targetMips = mipMaps;
+ var dstWidth = srcWidth;
+ var dstHeight = srcHeight;
+ if ((typeof opt_generateMips === 'undefined' || opt_generateMips) &&
+ o3djs.texture.canMakeMipsAndScale(format) &&
+ mipMaps == 1 && maxMips > 1) {
+ targetMips = maxMips;
+ }
+
+ // Check that all the bitmaps are the same size and make mips
+ for (var ii = 0; ii < bitmaps.length; ++ii) {
+ var bitmap = bitmaps[ii];
+ if (bitmap.width != srcWidth ||
+ bitmap.height != srcHeight ||
+ bitmap.format != format ||
+ bitmap.numMipmaps != mipMaps) {
+ throw 'bitmaps must all be the same width, height, mips and format';
+ }
+ if (targetMips != mipMaps) {
+ bitmap.generateMips(0, targetMips - 1);
+ }
+ }
+
+ var levels = bitmap.numMipmaps > 1 ? bitmap.numMipmaps :
+ o3djs.texture.computeNumLevels(dstWidth, dstHeight);
+ var texture;
+ if (bitmaps.length == 6 &&
+ bitmaps[0].semantic != o3djs.base.o3d.Bitmap.SLICE) {
+ if (srcWidth != srcHeight ||
+ srcWidth != dstWidth ||
+ srcHeight != dstHeight) {
+ throw 'Cubemaps must be square';
+ }
+ texture = pack.createTextureCUBE(dstWidth, format, targetMips, false);
+ for (var ii = 0; ii < 6; ++ii) {
+ texture.setFromBitmap(
+ /** @type {o3d.TextureCUBE.CubeFace} */ (ii),
+ bitmaps[ii]);
+ }
+ } else if (bitmaps.length == 1) {
+ texture = pack.createTexture2D(
+ dstWidth, dstHeight, format, targetMips, false);
+ texture.setFromBitmap(bitmaps[0]);
+ }
+
+ return /** @type{!o3d.Texture} */ (texture);
+};
+
+/**
+ * Creates a TextureCUBE from 6 bitmaps. The bitmaps do not have to be the same
+ * size thought they do have to be the same format.
+ *
+ * @param {!o3d.Pack} pack The pack to create the texture in.
+ * @param {number} edgeLength The size of the cubemap.
+ * @param {!Array.<!o3d.Bitmap>} bitmaps An array of 6 bitmaps in the order
+ * FACE_POSITIVE_X, FACE_NEGATIVE_X, FACE_POSITIVE_Y, FACE_NEGATIVE_Y,
+ * FACE_POSITIVE_Z, FACE_NEGATIVE_Z.
+ * @return {!o3d.Texture} The created texture.
+ */
+o3djs.texture.createCubeTextureFrom6Bitmaps = function(
+ pack, edgeLength, bitmaps) {
+ var numMips = o3djs.texture.computeNumLevels(edgeLength, edgeLength);
+ var texture = pack.createTextureCUBE(
+ edgeLength, bitmaps[0].format, numMips, false);
+ for (var ii = 0; ii < 6; ++ii) {
+ var bitmap = bitmaps[ii];
+ texture.drawImage(bitmap, 0, 0, 0, bitmap.width, bitmap.height,
+ ii, 0, 0, edgeLength, edgeLength);
+ }
+ texture.generateMips(0, numMips - 1);
+ return texture;
+};
+