diff options
author | apatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 19:56:24 +0000 |
---|---|---|
committer | apatrick@google.com <apatrick@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 19:56:24 +0000 |
commit | ccf02ac9bd67d4d49ad8264ae7fc6098e361ff4b (patch) | |
tree | fc6e4a0552431c7297798fad46f57fb76c35a3b3 /o3d/samples/o3djs | |
parent | dfe9d1d2de7fd8d244599b79a22898cce8df7c81 (diff) | |
download | chromium_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.js | 1924 | ||||
-rw-r--r-- | o3d/samples/o3djs/performance.js | 404 | ||||
-rw-r--r-- | o3d/samples/o3djs/texture.js | 478 |
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; +}; + |