diff options
author | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-02 04:34:35 +0000 |
---|---|---|
committer | kbr@google.com <kbr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-02 04:34:35 +0000 |
commit | 3782c85ed2f6f2183e3eb90fae339745f981b73a (patch) | |
tree | 272add5e3a7020b03d3182624bf6446241942d51 | |
parent | fa17362aa4fc2cb24a3d907738425d19e5decacb (diff) | |
download | chromium_src-3782c85ed2f6f2183e3eb90fae339745f981b73a.zip chromium_src-3782c85ed2f6f2183e3eb90fae339745f981b73a.tar.gz chromium_src-3782c85ed2f6f2183e3eb90fae339745f981b73a.tar.bz2 |
Beginnings of direct manipulator support in o3djs library. Added
Translate1 manipulator, which implements dragging along a line, and
helper and base classes. This is a work in progress; feedback
appreciated.
Fixed problem in recently changed documentation in primitives.js.
Minor documentation cleanup in primitives.html.
Review URL: http://codereview.chromium.org/178044
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25152 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | o3d/samples/manipulators/translate1.html | 273 | ||||
-rw-r--r-- | o3d/samples/o3djs/manipulators.js | 923 | ||||
-rw-r--r-- | o3d/samples/o3djs/primitives.js | 4 | ||||
-rw-r--r-- | o3d/samples/primitives.html | 7 |
4 files changed, 1202 insertions, 5 deletions
diff --git a/o3d/samples/manipulators/translate1.html b/o3d/samples/manipulators/translate1.html new file mode 100644 index 0000000..dd3eadb --- /dev/null +++ b/o3d/samples/manipulators/translate1.html @@ -0,0 +1,273 @@ +<!--
+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.
+-->
+
+<!--
+Translate1 manipulator
+
+This sample shows how to use the o3djs.manipulators.Translate1 class
+to interactively drag objects around the scene.
+-->
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>
+Translate1 Manipulator
+</title>
+<script type="text/javascript" src="../o3djs/base.js"></script>
+<script type="text/javascript" id="o3dscript">
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.manipulators');
+o3djs.require('o3djs.effect');
+
+// global variables
+var g_o3dElement;
+var g_client;
+var g_o3d;
+var g_math;
+var g_pack;
+var g_viewInfo;
+
+var g_lightPosition = [5, 5, 7];
+var g_eyePosition = [1, 5, 23];
+
+var g_primitives = [];
+var g_manager;
+
+/**
+ * Creates the client area.
+ */
+function initClient() {
+ window.g_finished = false; // for selenium testing.
+
+ // Runs the sample in V8. Comment out this line to run it in the browser
+ // JavaScript engine, for example if you want to debug it.
+ // TODO(kbr): we need to investigate why turning this on is
+ // significantly slower than leaving it disabled.
+ // o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.makeClients(main);
+}
+
+/**
+ * Initializes global variables, positions camera, draws shapes.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function main(clientElements) {
+ var o3dElement = clientElements[0];
+
+ // Init global variables.
+ initGlobals(clientElements);
+
+ // Set up the view and projection transformations.
+ initContext();
+
+ // Add the shapes to the transform heirarchy.
+ createShapes();
+
+ // Add the manipulators to the transform hierarchy.
+ createManipulators();
+
+ // Start picking; it won't do anything until the scene finishes loading.
+ o3djs.event.addEventListener(o3dElement, 'mousedown', onMouseDown);
+ o3djs.event.addEventListener(o3dElement, 'mousemove', onMouseMove);
+ o3djs.event.addEventListener(o3dElement, 'mouseup', onMouseUp);
+
+ window.g_finished = true; // for selenium testing.
+}
+
+function onMouseDown(e) {
+ g_manager.mousedown(e.x, e.y,
+ g_viewInfo.drawContext.view,
+ g_viewInfo.drawContext.projection,
+ g_client.width,
+ g_client.height);
+}
+
+function onMouseMove(e) {
+ g_manager.mousemove(e.x, e.y,
+ g_viewInfo.drawContext.view,
+ g_viewInfo.drawContext.projection,
+ g_client.width,
+ g_client.height);
+ g_manager.updateInactiveManipulators();
+}
+
+function onMouseUp(e) {
+ g_manager.mouseup();
+ g_manager.updateInactiveManipulators();
+}
+
+/**
+ * Initializes global variables and libraries.
+ */
+function initGlobals(clientElements) {
+ g_o3dElement = clientElements[0];
+ window.g_client = g_client = g_o3dElement.client;
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+
+ // Create a pack to manage the objects created.
+ g_pack = g_client.createPack();
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_pack,
+ g_client.root,
+ g_client.renderGraphRoot);
+}
+
+/**
+ * Sets up reasonable view and projection matrices.
+ */
+function initContext() {
+ // Set up a perspective transformation for the projection.
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_math.degToRad(30), // 30 degree frustum.
+ g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio.
+ 1, // Near plane.
+ 5000); // Far plane.
+
+ // Set up our view transformation to look towards the world origin where the
+ // primitives are located.
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(
+ g_eyePosition, // eye
+ [0, 2, 0], // target
+ [0, 1, 0]); // up
+}
+
+/**
+ * Creates shapes using the primitives utility library, and adds them to the
+ * transform graph at the root node.
+ */
+function createShapes() {
+ // Create a little tree-like hierarchy of cubes
+ createCubeTree(2, 1.5, [0, 0, 0], g_client.root);
+}
+
+/**
+ * Creates a small tree of cubes to demonstrate interaction with a
+ * hierarchy of shapes.
+ */
+function createCubeTree(depth, edgeLength, translation, parent) {
+ var cur = createCube(edgeLength, translation, parent);
+ if (depth > 0) {
+ createCubeTree(depth - 1,
+ edgeLength / 1.5,
+ o3djs.math.addVector(translation,
+ [-1.2 * edgeLength,
+ 1.0 * edgeLength,
+ 0]),
+ cur);
+ createCubeTree(depth - 1,
+ edgeLength / 1.5,
+ o3djs.math.addVector(translation,
+ [1.2 * edgeLength,
+ 1.0 * edgeLength,
+ 0]),
+ cur);
+ }
+ return cur;
+}
+
+/**
+ * Creates a cube shape using the primitives utility library, with an
+ * optional translation and parent. Returns the newly-created
+ * transform for the cube.
+ */
+function createCube(edgeLength, opt_translation, opt_parent) {
+ var cube = o3djs.primitives.createCube(
+ g_pack,
+ // A green phong-shaded material.
+ o3djs.material.createBasicMaterial(g_pack,
+ g_viewInfo,
+ [0, 1, 0, 1]),
+ edgeLength);
+ var transform = g_pack.createObject('Transform');
+ transform.addShape(cube);
+ if (opt_translation) {
+ transform.translate(opt_translation);
+ }
+ if (opt_parent) {
+ transform.parent = opt_parent;
+ } else {
+ transform.parent = g_client.root;
+ }
+ g_primitives.push(transform);
+ return transform;
+}
+
+/**
+ * Creates manipulators attached to the objects we've just created.
+ */
+function createManipulators() {
+ g_manager = o3djs.manipulators.createManager(g_pack,
+ g_viewInfo.performanceDrawList,
+ g_client.root,
+ null,
+ 0);
+ var jj = 2;
+ for (var ii = 0; ii < g_primitives.length; ii++) {
+ var manip = g_manager.createTranslate1();
+ manip.attachTo(g_primitives[ii]);
+ manip.setOffsetTranslation([0, -1.5, 0]);
+ // Demonstrate that we can drag along arbitrary directions.
+ if ((++jj % 4) == 0) {
+ manip.setOffsetRotation(o3djs.quaternions.rotationY(Math.PI / 4));
+ }
+ }
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function unload() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+</script>
+</head>
+<body onload="initClient()" onunload="unload()">
+<h1>Translate1 Manipulator Sample</h1>
+This example shows how to move objects around the scene using the
+Translate1 manipulator.
+<br/>
+<!-- Start of O3D plugin -->
+<div id="o3d" style="width: 600px; height: 600px;"></div>
+<!-- End of O3D plugin -->
+</body>
+</html>
diff --git a/o3d/samples/o3djs/manipulators.js b/o3d/samples/o3djs/manipulators.js new file mode 100644 index 0000000..bc37de1 --- /dev/null +++ b/o3d/samples/o3djs/manipulators.js @@ -0,0 +1,923 @@ +/*
+ * 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.
+ */
+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) {
+ 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/primitives.js b/o3d/samples/o3djs/primitives.js index 54c9cfd..0a062f8 100644 --- a/o3d/samples/o3djs/primitives.js +++ b/o3d/samples/o3djs/primitives.js @@ -1208,7 +1208,7 @@ o3djs.primitives.createDisc = function(pack, material, * cylinder. * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply * all the vertices. - * @return {o3djs.primitives.VertexInfo} The created cylinder vertices. + * @return {!o3djs.primitives.VertexInfo} The created cylinder vertices. */ o3djs.primitives.createCylinderVertices = function(radius, height, @@ -1273,7 +1273,7 @@ o3djs.primitives.createCylinder = function(pack, * truncated cone. * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply * all the vertices. - * @return {o3djs.primitives.VertexInfo} The created truncated cone vertices. + * @return {!o3djs.primitives.VertexInfo} The created truncated cone vertices. */ o3djs.primitives.createTruncatedConeVertices = function(bottomRadius, topRadius, diff --git a/o3d/samples/primitives.html b/o3d/samples/primitives.html index 89337a9..e61cdae 100644 --- a/o3d/samples/primitives.html +++ b/o3d/samples/primitives.html @@ -130,9 +130,10 @@ function initContext() { /** * Creates a material based on the given single color. - * @param {Array} baseColor An array with 4 entries, the R,G,B, and A components - * of a color. - * @return {Material} A material whose overall pigment is baseColor. + * @param {!o3djs.math.Vector4} baseColor A 4-component vector with + * the R,G,B, and A components of a color. + * @return {!o3d.Material} A phong material whose overall pigment is + * baseColor. */ function createMaterial(baseColor) { // Create a new, empty Material object. |