diff options
author | simonrad@chromium.org <simonrad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 06:16:51 +0000 |
---|---|---|
committer | simonrad@chromium.org <simonrad@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-10 06:16:51 +0000 |
commit | 791d861fb0ee9fe2bdb0f62131a34c55a7f312bb (patch) | |
tree | 7bf6736c6c9d348eb5c876ffe23b10727e46e360 /o3d/samples | |
parent | 867125a08f77a22b770f197d90519a8672af83aa (diff) | |
download | chromium_src-791d861fb0ee9fe2bdb0f62131a34c55a7f312bb.zip chromium_src-791d861fb0ee9fe2bdb0f62131a34c55a7f312bb.tar.gz chromium_src-791d861fb0ee9fe2bdb0f62131a34c55a7f312bb.tar.bz2 |
Add line stippling and add functionality to the CameraController.
- Added optional line stippling based on texCoords to the rotate1 line ring shader.
- Redesigned the CameraController and added the ability to zoom, dolly, dolly-zoom, and pan using the mouse.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/486003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34235 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/samples')
-rw-r--r-- | o3d/samples/manipulators/rotate1.html | 67 | ||||
-rw-r--r-- | o3d/samples/manipulators/translate1.html | 67 | ||||
-rw-r--r-- | o3d/samples/manipulators/translate2.html | 67 | ||||
-rw-r--r-- | o3d/samples/o3djs/cameracontroller.js | 205 | ||||
-rw-r--r-- | o3d/samples/o3djs/lineprimitives.js | 13 | ||||
-rw-r--r-- | o3d/samples/o3djs/manipulators.js | 159 |
6 files changed, 406 insertions, 172 deletions
diff --git a/o3d/samples/manipulators/rotate1.html b/o3d/samples/manipulators/rotate1.html index 7f4f27d..cb78ef1 100644 --- a/o3d/samples/manipulators/rotate1.html +++ b/o3d/samples/manipulators/rotate1.html @@ -111,9 +111,22 @@ function main(clientElements) { } function onMouseDown(e) { - if(e.button == 2) { - g_cameraController.mousedown(e); - } else { + if(e.button == 2 && !e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.ZOOM_IN_OUT, e.x, e.y); + } else if(e.button == 2 && !e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_IN_OUT, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_ZOOM, e.x, e.y); + } else if(e.button == 1) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE, e.x, e.y); + } else if(e.button == 0) { g_manager.mousedown(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -123,8 +136,12 @@ function onMouseDown(e) { } function onMouseMove(e) { - g_cameraController.mousemove(e); - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + g_cameraController.mouseMoved(e.x, e.y); + + // You can call this function here, or pass it to the CameraController + // as the onChange callback. + //updateViewAndProjectionMatrices(); + g_manager.mousemove(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -134,12 +151,11 @@ function onMouseMove(e) { } function onMouseUp(e) { - if(e.button == 2) { - g_cameraController.mouseup(e); - } else { - g_manager.mouseup(); - g_manager.updateInactiveManipulators(); - } + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.NONE, e.x, e.y); + + g_manager.mouseup(); + g_manager.updateInactiveManipulators(); } /** @@ -168,20 +184,27 @@ function initGlobals(clientElements) { * Sets up reasonable view and projection matrices. */ function initContext() { + // Set up our CameraController. + g_cameraController = o3djs.cameracontroller.createCameraController( + [0, 2, 0], // centerPos + 23, // backpedal + 0, // heightAngle + 0, // rotationAngle + g_math.degToRad(15), // fieldOfViewAngle + updateViewAndProjectionMatrices); // opt_onChange + + updateViewAndProjectionMatrices(); +} + +function updateViewAndProjectionMatrices() { + g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + // Set up a perspective transformation for the projection. g_mainViewInfo.drawContext.projection = g_math.matrix4.perspective( - g_math.degToRad(30), // 30 degree frustum. + g_cameraController.fieldOfViewAngle * 2, // Frustum angle. g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. - 1, // Near plane. - 5000); // Far plane. - - // Set up our view transformation using our CameraController. - g_cameraController = o3djs.cameracontroller.createCameraController( - [0, 2, 0], // centerPos - 23, // backpedal - 0, // heightAngle - 0); // rotationAngle - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + 1, // Near plane. + 5000); // Far plane. } /** diff --git a/o3d/samples/manipulators/translate1.html b/o3d/samples/manipulators/translate1.html index 06c7193..10b4df6 100644 --- a/o3d/samples/manipulators/translate1.html +++ b/o3d/samples/manipulators/translate1.html @@ -111,9 +111,22 @@ function main(clientElements) { } function onMouseDown(e) { - if(e.button == 2) { - g_cameraController.mousedown(e); - } else { + if(e.button == 2 && !e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.ZOOM_IN_OUT, e.x, e.y); + } else if(e.button == 2 && !e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_IN_OUT, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_ZOOM, e.x, e.y); + } else if(e.button == 1) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE, e.x, e.y); + } else if(e.button == 0) { g_manager.mousedown(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -123,8 +136,12 @@ function onMouseDown(e) { } function onMouseMove(e) { - g_cameraController.mousemove(e); - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + g_cameraController.mouseMoved(e.x, e.y); + + // You can call this function here, or pass it to the CameraController + // as the onChange callback. + //updateViewAndProjectionMatrices(); + g_manager.mousemove(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -134,12 +151,11 @@ function onMouseMove(e) { } function onMouseUp(e) { - if(e.button == 2) { - g_cameraController.mouseup(e); - } else { - g_manager.mouseup(); - g_manager.updateInactiveManipulators(); - } + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.NONE, e.x, e.y); + + g_manager.mouseup(); + g_manager.updateInactiveManipulators(); } /** @@ -168,20 +184,27 @@ function initGlobals(clientElements) { * Sets up reasonable view and projection matrices. */ function initContext() { + // Set up our CameraController. + g_cameraController = o3djs.cameracontroller.createCameraController( + [0, 2, 0], // centerPos + 23, // backpedal + 0, // heightAngle + 0, // rotationAngle + g_math.degToRad(15), // fieldOfViewAngle + updateViewAndProjectionMatrices); // opt_onChange + + updateViewAndProjectionMatrices(); +} + +function updateViewAndProjectionMatrices() { + g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + // Set up a perspective transformation for the projection. g_mainViewInfo.drawContext.projection = g_math.matrix4.perspective( - g_math.degToRad(30), // 30 degree frustum. + g_cameraController.fieldOfViewAngle * 2, // Frustum angle. g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. - 1, // Near plane. - 5000); // Far plane. - - // Set up our view transformation using our CameraController. - g_cameraController = o3djs.cameracontroller.createCameraController( - [0, 2, 0], // centerPos - 23, // backpedal - 0, // heightAngle - 0); // rotationAngle - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + 1, // Near plane. + 5000); // Far plane. } /** diff --git a/o3d/samples/manipulators/translate2.html b/o3d/samples/manipulators/translate2.html index a284f89..48cc6ee 100644 --- a/o3d/samples/manipulators/translate2.html +++ b/o3d/samples/manipulators/translate2.html @@ -111,9 +111,22 @@ function main(clientElements) { } function onMouseDown(e) { - if(e.button == 2) { - g_cameraController.mousedown(e); - } else { + if(e.button == 2 && !e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && !e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.ZOOM_IN_OUT, e.x, e.y); + } else if(e.button == 2 && !e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_IN_OUT, e.x, e.y); + } else if(e.button == 2 && e.shiftKey && e.ctrlKey) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.DOLLY_ZOOM, e.x, e.y); + } else if(e.button == 1) { + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE, e.x, e.y); + } else if(e.button == 0) { g_manager.mousedown(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -123,8 +136,12 @@ function onMouseDown(e) { } function onMouseMove(e) { - g_cameraController.mousemove(e); - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + g_cameraController.mouseMoved(e.x, e.y); + + // You can call this function here, or pass it to the CameraController + // as the onChange callback. + //updateViewAndProjectionMatrices(); + g_manager.mousemove(e.x, e.y, g_mainViewInfo.drawContext.view, g_mainViewInfo.drawContext.projection, @@ -134,12 +151,11 @@ function onMouseMove(e) { } function onMouseUp(e) { - if(e.button == 2) { - g_cameraController.mouseup(e); - } else { - g_manager.mouseup(); - g_manager.updateInactiveManipulators(); - } + g_cameraController.setDragMode( + o3djs.cameracontroller.DragMode.NONE, e.x, e.y); + + g_manager.mouseup(); + g_manager.updateInactiveManipulators(); } /** @@ -168,20 +184,27 @@ function initGlobals(clientElements) { * Sets up reasonable view and projection matrices. */ function initContext() { + // Set up our CameraController. + g_cameraController = o3djs.cameracontroller.createCameraController( + [0, 2, 0], // centerPos + 23, // backpedal + 0, // heightAngle + 0, // rotationAngle + g_math.degToRad(15), // fieldOfViewAngle + updateViewAndProjectionMatrices); // opt_onChange + + updateViewAndProjectionMatrices(); +} + +function updateViewAndProjectionMatrices() { + g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + // Set up a perspective transformation for the projection. g_mainViewInfo.drawContext.projection = g_math.matrix4.perspective( - g_math.degToRad(30), // 30 degree frustum. + g_cameraController.fieldOfViewAngle * 2, // Frustum angle. g_o3dElement.clientWidth / g_o3dElement.clientHeight, // Aspect ratio. - 1, // Near plane. - 5000); // Far plane. - - // Set up our view transformation using our CameraController. - g_cameraController = o3djs.cameracontroller.createCameraController( - [0, 2, 0], // centerPos - 23, // backpedal - 0, // heightAngle - 0); // rotationAngle - g_mainViewInfo.drawContext.view = g_cameraController.calculateViewMatrix(); + 1, // Near plane. + 5000); // Far plane. } /** diff --git a/o3d/samples/o3djs/cameracontroller.js b/o3d/samples/o3djs/cameracontroller.js index 9dcb980..612e1cc 100644 --- a/o3d/samples/o3djs/cameracontroller.js +++ b/o3d/samples/o3djs/cameracontroller.js @@ -46,6 +46,46 @@ o3djs.require('o3djs.math'); o3djs.cameracontroller = o3djs.cameracontroller || {}; /** + * The possible modes that a CameraController can be in. + * One of these is usually set when a mouse button is pressed down, + * and then NONE is set when the mouse button is released. + * When the mouse is moved, the DragMode determines what effect the mouse move + * has on the camera parameters (such as position and orientation). + * If the DragMode is NONE, mouse moves have no effect. + * @enum {number} + */ +o3djs.cameracontroller.DragMode = { + /** + * Dragging the mouse has no effect. + */ + NONE: 0, + /** + * Dragging left or right changes rotationAngle, + * dragging up or down changes heightAngle. + */ + SPIN_ABOUT_CENTER: 1, + /** + * Dragging up or down changes the backpedal. + */ + DOLLY_IN_OUT: 2, + /** + * Dragging up or down changes the fieldOfViewAngle. + */ + ZOOM_IN_OUT: 3, + /** + * Dragging up or down changes the amount of perspective. + * Perspective is focused on the centerPos. + * If backpedal is negative or zero, there is no effect. + */ + DOLLY_ZOOM: 4, + /** + * Dragging moves the centerPos around the plane perpendicular to + * the camera view direction. + */ + MOVE_CENTER_IN_VIEW_PLANE: 5, +}; + +/** * Creates a CameraController. * @param {!o3djs.math.Vector3} centerPos The position that the camera is * looking at and rotating around; or if backpedal is zero, the location @@ -56,8 +96,12 @@ o3djs.cameracontroller = o3djs.cameracontroller || {}; * (about the x axis that passes through the centerPos). In radians. * @param {number} rotationAngle The angle the camera rotates left or right * (about the y axis that passes through the centerPos). In radians. + * @param {number} fieldOfViewAngle The vertical angle of the viewing frustum. + * In radians, between 0 and PI/2. This does not affect the view matrix, + * but it can still be useful to let the CameraController control the + * field of view. * @param {function(!o3djs.cameracontroller.CameraController): void} - * opt_onchange Pointer to a callback to call when the camera changes. + * opt_onChange Pointer to a callback to call when the camera changes. * @return {!o3djs.cameracontroller.CameraController} The created * CameraController. */ @@ -65,16 +109,27 @@ o3djs.cameracontroller.createCameraController = function(centerPos, backpedal, heightAngle, rotationAngle, - opt_onchange) { + fieldOfViewAngle, + opt_onChange) { return new o3djs.cameracontroller.CameraController(centerPos, backpedal, heightAngle, rotationAngle, - opt_onchange); + fieldOfViewAngle, + opt_onChange); }; /** * Class to hold user-controlled camera information and handle user events. + * It can control and output a view matrix, and can also control some aspects + * of a projection matrix. + * + * Most of the parameters it controls affect the view matrix, and it can + * generate a view matrix based on its parameters. + * It can also control certain parameters that affect the projection matrix, + * such as field of view. Rather than deal with all the parameters needed for + * a projection matrix, this class leaves generation of the projection matrix + * up to the user code, and simply exposes the parameters it has. * @constructor * @param {!o3djs.math.Vector3} centerPos The position that the camera is * looking at and rotating around; or if backpedal is zero, the location @@ -85,14 +140,18 @@ o3djs.cameracontroller.createCameraController = function(centerPos, * (about the x axis that passes through the centerPos). In radians. * @param {number} rotationAngle The angle the camera rotates left or right * (about the y axis that passes through the centerPos). In radians. + * @param {number} fieldOfViewAngle The vertical angle of the viewing frustum. + * In radians, between 0 and PI/2. This does not affect the view matrix, + * but it can still be useful to let this class control the field of view. * @param {function(!o3djs.cameracontroller.CameraController): void} - * opt_onchange Pointer to a callback to call when the camera changes. + * opt_onChange Pointer to a callback to call when the camera changes. */ o3djs.cameracontroller.CameraController = function(centerPos, backpedal, heightAngle, rotationAngle, - opt_onchange) { + fieldOfViewAngle, + opt_onChange) { /** * The position that the camera is looking at and rotating around. * Or if backpedal is zero, the location of the camera. In world space. @@ -118,19 +177,28 @@ o3djs.cameracontroller.CameraController = function(centerPos, */ this.rotationAngle = rotationAngle; + /** + * The vertical angle of the perspective viewing frustum. + * In radians, between 0 and PI/2. This does not affect the view matrix. + * The user code can access this value and use it to construct a + * projection matrix, or it can simply ignore it. + * @type {number} + */ + this.fieldOfViewAngle = fieldOfViewAngle; + /** * Points to a callback to call when the camera changes. * @type {function(!o3djs.cameracontroller.CameraController): void} */ - this.onchange = opt_onchange || null; + this.onChange = opt_onChange || null; /** - * Whether the mouse is currently being dragged to control the camera. + * The current mouse-drag mode, ie what happens when you move the mouse. * @private - * @type {boolean} + * @type {o3djs.cameracontroller.DragMode} */ - this.dragging_ = false; + this.dragMode_ = o3djs.cameracontroller.DragMode.NONE; /** * The last X coordinate of the mouse. @@ -146,18 +214,48 @@ o3djs.cameracontroller.CameraController = function(centerPos, */ this.mouseY_ = 0; + + // Some variables to control how quickly the camera changes when you + // move the mouse a certain distance. Feel free to modify these. + // Mouse pixels are converted into arbitrary "units" (for lack of + // a better term), and then "units" are converted into an angle, + // or a distance, etc as the case may be. + + /** + * Controls how quickly the mouse moves the camera (in general). + * Used to convert pixels into "units". + * @type {number} + */ + this.pixelsPerUnit = 300.0; + + /** + * Controls how quickly the mouse affects rotation angles. + * Used to convert "units" into radians. + * @type {number} + */ + this.radiansPerUnit = 1.0; + + /** + * Controls how quickly the mouse affects camera translation. + * Used to convert "units" into world space units of distance. + * @type {number} + */ + this.distancePerUnit = 10.0; + /** - * Controls how quickly the mouse moves the camera. + * Controls how quickly the mouse affects zooming. + * Used to convert "units" into zoom factor. * @type {number} */ - this.dragScaleFactor = 300.0; + this.zoomPerUnit = 1.0; }; /** * Calculates the view matrix for this camera. * @return {!o3djs.math.Matrix4} The view matrix. */ -o3djs.cameracontroller.CameraController.prototype.calculateViewMatrix = function() { +o3djs.cameracontroller.CameraController.prototype.calculateViewMatrix = + function() { var matrix4 = o3djs.math.matrix4; var view = matrix4.translation(o3djs.math.negativeVector(this.centerPos)); view = matrix4.mul(view, matrix4.rotationY(this.rotationAngle)); @@ -167,42 +265,67 @@ o3djs.cameracontroller.CameraController.prototype.calculateViewMatrix = function }; /** - * Method which should be called by end user code upon receiving a - * mouse-down event. - * @param {!o3d.Event} ev The event. - */ -o3djs.cameracontroller.CameraController.prototype.mousedown = function(ev) { - this.dragging_ = true; - this.mouseX_ = ev.x; - this.mouseY_ = ev.y; -}; - -/** - * Method which should be called by end user code upon receiving a - * mouse-up event. - * @param {!o3d.Event} ev The event. + * Change the current mouse-drag mode, ie what happens when you move the mouse. + * Usually you would set it to something when a mouse button is pressed down, + * and then set it to NONE when the button is released. + * @param {o3djs.cameracontroller.DragMode} dragMode The new DragMode. + * @param {number} x The current mouse X coordinate. + * @param {number} y The current mouse Y coordinate. */ -o3djs.cameracontroller.CameraController.prototype.mouseup = function(ev) { - this.dragging_ = false; +o3djs.cameracontroller.CameraController.prototype.setDragMode = + function(dragMode, x, y) { + this.dragMode_ = dragMode; + this.mouseX_ = x; + this.mouseY_ = y; }; /** * Method which should be called by end user code upon receiving a * mouse-move event. - * @param {!o3d.Event} ev The event. + * @param {number} x The new mouse X coordinate. + * @param {number} y The new mouse Y coordinate. */ -o3djs.cameracontroller.CameraController.prototype.mousemove = function(ev) { - if (this.dragging_) { - var curX = ev.x; - var curY = ev.y; - var deltaX = (curX - this.mouseX_) / this.dragScaleFactor; - var deltaY = (curY - this.mouseY_) / this.dragScaleFactor; - this.mouseX_ = curX; - this.mouseY_ = curY; - this.rotationAngle += deltaX; - this.heightAngle += deltaY; - if (this.onchange != null) { - this.onchange(this); +o3djs.cameracontroller.CameraController.prototype.mouseMoved = function(x, y) { + var deltaX = (x - this.mouseX_) / this.pixelsPerUnit; + var deltaY = (y - this.mouseY_) / this.pixelsPerUnit; + this.mouseX_ = x; + this.mouseY_ = y; + + if (this.dragMode_ == o3djs.cameracontroller.DragMode.SPIN_ABOUT_CENTER) { + this.rotationAngle += deltaX * this.radiansPerUnit; + this.heightAngle += deltaY * this.radiansPerUnit; + } + if (this.dragMode_ == o3djs.cameracontroller.DragMode.DOLLY_IN_OUT) { + this.backpedal += deltaY * this.distancePerUnit; + } + if (this.dragMode_ == o3djs.cameracontroller.DragMode.ZOOM_IN_OUT) { + var width = Math.tan(this.fieldOfViewAngle); + width *= Math.pow(2, deltaY * this.zoomPerUnit); + this.fieldOfViewAngle = Math.atan(width); + } + if (this.dragMode_ == o3djs.cameracontroller.DragMode.DOLLY_ZOOM) { + if (this.backpedal > 0) { + var oldWidth = Math.tan(this.fieldOfViewAngle); + this.fieldOfViewAngle += deltaY * this.radiansPerUnit; + this.fieldOfViewAngle = Math.min(this.fieldOfViewAngle, 0.98 * Math.PI/2); + this.fieldOfViewAngle = Math.max(this.fieldOfViewAngle, 0.02 * Math.PI/2); + var newWidth = Math.tan(this.fieldOfViewAngle); + this.backpedal *= oldWidth / newWidth; } } + if (this.dragMode_ == + o3djs.cameracontroller.DragMode.MOVE_CENTER_IN_VIEW_PLANE) { + var matrix4 = o3djs.math.matrix4; + var translationVector = [-deltaX * this.distancePerUnit, + deltaY * this.distancePerUnit, 0]; + var inverseViewMatrix = matrix4.inverse(this.calculateViewMatrix()); + translationVector = matrix4.transformDirection( + inverseViewMatrix, translationVector); + this.centerPos = o3djs.math.addVector(this.centerPos, translationVector); + } + + if (this.onChange != null && + this.dragMode_ != o3djs.cameracontroller.DragMode.NONE) { + this.onChange(this); + } }; diff --git a/o3d/samples/o3djs/lineprimitives.js b/o3d/samples/o3djs/lineprimitives.js index 8e4d923..4d7ae90 100644 --- a/o3d/samples/o3djs/lineprimitives.js +++ b/o3d/samples/o3djs/lineprimitives.js @@ -312,11 +312,14 @@ o3djs.lineprimitives.createLineSphere = function( /** * Creates ring vertices. * The ring is a circle in the XZ plane, centered at the origin. - * The created ring has a position stream and a normal stream. + * The created ring has position, normal, and 1-D texcoord streams. * The normals point outwards from the center of the ring. + * The texture coordinates are based on angle about the center. * * @param {number} radius Radius of the ring. * @param {number} subdivisions Number of steps around the ring. + * @param {number} maxTexCoord 1-D texture coordinates will range from 0 to + * this value, based on angle about the center. * @param {!o3djs.math.Matrix4} opt_matrix A matrix by which to multiply all * the vertices. * @return {!o3djs.lineprimitives.LineVertexInfo} The created ring vertices. @@ -324,6 +327,7 @@ o3djs.lineprimitives.createLineSphere = function( o3djs.lineprimitives.createLineRingVertices = function( radius, subdivisions, + maxTexCoord, opt_matrix) { if (subdivisions < 3) { throw Error('subdivisions must be >= 3'); @@ -334,18 +338,21 @@ o3djs.lineprimitives.createLineRingVertices = function( 3, o3djs.base.o3d.Stream.POSITION); var normalStream = vertexInfo.addStream( 3, o3djs.base.o3d.Stream.NORMAL); + var texCoordStream = vertexInfo.addStream( + 1, o3djs.base.o3d.Stream.TEXCOORD, 0); // Generate the individual vertices in our vertex buffer. - for (var i = 0; i < subdivisions; i++) { + for (var i = 0; i <= subdivisions; i++) { var theta = 2 * Math.PI * i / subdivisions; positionStream.addElement(radius * Math.cos(theta), 0, radius * Math.sin(theta)); normalStream.addElement(Math.cos(theta), 0, Math.sin(theta)); + texCoordStream.addElement(maxTexCoord * i / subdivisions); } // Connect the vertices by simple lines. for (var i = 0; i < subdivisions; i++) { - vertexInfo.addLine(i, (i+1) % subdivisions); + vertexInfo.addLine(i, (i+1)); } if (opt_matrix) { diff --git a/o3d/samples/o3djs/manipulators.js b/o3d/samples/o3djs/manipulators.js index f1ac3ae..1565dbe 100644 --- a/o3d/samples/o3djs/manipulators.js +++ b/o3d/samples/o3djs/manipulators.js @@ -477,6 +477,9 @@ o3djs.manipulators.Manager = function(pack, var state = this.viewInfo.zOrderedState; state.getStateParam('ZComparisonFunction').value = o3djs.base.o3d.State.CMP_GREATER; + // Disable depth writing, otherwise the second pass will have a + // screwed up depth buffer, and will draw when it shouldn't. + state.getStateParam('ZWriteEnable').value = false; // Swap the priorities of the DrawPasses so they get drawn in the // opposite order @@ -493,6 +496,13 @@ o3djs.manipulators.Manager = function(pack, // obscured get drawn as normal. This allows the obscured parts // of the manipulators to be rendered with a different material. + // POTENTIAL PROBLEM: Since we reverse the depth comparison function (and + // disable depth writing) for the obscured rendering pass, those objects will + // not have their proper faces showing. So they will look wrong unless we use + // a constant shader. One possible solution would be to set the stencil + // buffer to indicate obscured/unobscured, so that we are free to use the + // depth buffer normally. + /** * The DrawList we use to render manipulators that are unobscured by the main * scene. @@ -607,7 +617,7 @@ o3djs.manipulators.Manager.prototype.createConstantMaterial_ = * Gets the constant material used for manipulators. * @return {!o3d.Material} A material. */ -o3djs.manipulators.Manager.prototype.getConstantMaterial = function() { +o3djs.manipulators.Manager.prototype.getUnobscuredConstantMaterial = function() { return this.constantMaterial_; }; @@ -623,11 +633,12 @@ o3djs.manipulators.Manager.prototype.getObscuredConstantMaterial = function() { * Gets the material used for the line ring manipulators. * @return {!o3d.Material} A material. */ -o3djs.manipulators.Manager.prototype.getLineRingMaterial = function() { +o3djs.manipulators.Manager.prototype.getUnobscuredLineRingMaterial = + function() { if (!this.lineRingMaterial_) { this.lineRingMaterial_ = o3djs.manipulators.createLineRingMaterial( this.pack, this.unobscuredDrawList_, - [1, 1, 1, 1], [1, 1, 1, 0.4]); + [1, 1, 1, 1], [1, 1, 1, 0.6], false); } return this.lineRingMaterial_; }; @@ -640,7 +651,7 @@ o3djs.manipulators.Manager.prototype.getObscuredLineRingMaterial = function() { if (!this.obscuredLineRingMaterial_) { this.obscuredLineRingMaterial_ = o3djs.manipulators.createLineRingMaterial( this.pack, this.obscuredDrawList_, - [1, 0, 0, 1], [1, 1, 1, 0.4]); // TODO(gman): Pick a color. + [1, 1, 1, 0.5], [1, 1, 1, 0.3], true); } return this.obscuredLineRingMaterial_; }; @@ -1279,7 +1290,7 @@ o3djs.manipulators.Translate1 = function(manager) { o3djs.manipulators.Manip.call(this, manager); var pack = manager.pack; - var material = manager.getConstantMaterial(); + var material = manager.getUnobscuredConstantMaterial(); var shape = manager.translate1Shape_; if (!shape) { @@ -1386,7 +1397,7 @@ o3djs.manipulators.Translate2 = function(manager) { o3djs.manipulators.Manip.call(this, manager); var pack = manager.pack; - var material = manager.getConstantMaterial(); + var material = manager.getUnobscuredConstantMaterial(); var shape = manager.Translate2Shape_; if (!shape) { @@ -1508,7 +1519,7 @@ o3djs.manipulators.Rotate1 = function(manager) { 16, 6, o3djs.math.matrix4.rotationZ(Math.PI / 2)); - pickShape = verts.createShape(pack, manager.getConstantMaterial()); + pickShape = verts.createShape(pack, manager.getUnobscuredConstantMaterial()); manager.Rotate1PickShape_ = pickShape; } @@ -1517,10 +1528,12 @@ o3djs.manipulators.Rotate1 = function(manager) { // Create the line geometry for displaying the manipulator, which looks like // a ring centered at the origin, with the X axis as its vertical axis. var verts = o3djs.lineprimitives.createLineRingVertices( - 1.0, - 32, - o3djs.math.matrix4.rotationZ(Math.PI / 2)); - visibleShape = verts.createShape(pack, manager.getLineRingMaterial()); + 1.0, // radius + 32, // subdivisions + 120, // maxTexCoord (this determines the number of stipples) + o3djs.math.matrix4.rotationZ(Math.PI / 2)); // opt_matrix + visibleShape = verts.createShape(pack, + manager.getUnobscuredLineRingMaterial()); // Add a second DrawElement to this shape to draw it a second time // with a different material when it's obscured. visibleShape.createDrawElements( @@ -1655,54 +1668,72 @@ o3djs.manipulators.Rotate1.prototype.drag = function(startPoint, // TODO(simonrad): Find a better place for these? /** - * The name of the Rotate1 manipulator's line ring effect. - * @type {string} - */ -o3djs.manipulators.LINE_RING_EFFECT_NAME = - 'o3djs.manipulators.lineRingEffect'; - -/** - * An effect string for the Rotate1 manipulator's line ring. - * @type {string} - */ -o3djs.manipulators.LINE_RING_FXSTRING = '' + - 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + - 'float4x4 worldViewProjectionInverseTranspose :\n' + - ' WORLDVIEWPROJECTIONINVERSETRANSPOSE;\n' + - 'float4 color1;\n' + - 'float4 color2;\n' + - 'float4 emissive; // Used for highlighting.\n' + - '\n' + - 'struct VertexShaderInput {\n' + - ' float4 position : POSITION;\n' + - ' float4 normal : NORMAL;\n' + - '};\n' + - '\n' + - 'struct PixelShaderInput {\n' + - ' float4 position : POSITION;\n' + - ' float3 normal : TEXCOORD0;\n' + - '};\n' + - '\n' + - 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + - ' PixelShaderInput output;\n' + - '\n' + - ' output.position = mul(input.position, worldViewProjection);\n' + - ' output.normal = mul(input.normal,\n' + - ' worldViewProjectionInverseTranspose).xyz;\n' + - ' return output;\n' + - '}\n' + - '\n' + - 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' + - ' if (input.normal.z < 0) {\n' + - ' return color1 * emissive;\n' + - ' } else {\n' + - ' return color2 * emissive;\n' + - ' }\n' + - '}\n' + - '\n' + - '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + - '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + - '// #o3d MatrixLoadOrder RowMajor\n'; + * Returns an effect string for the Rotate1 manipulator's line ring. + * @private + * @param {boolean} enableStipple Whether line stippling should be enabled + * in the shader. + * @return {string} The created shader source / effect string. + */ +o3djs.manipulators.getLineRingFXString_ = function(enableStipple) { + var stippleCode = ''; + if (enableStipple) { + stippleCode = '' + + ' // Use the texCoord to do stippling.\n' + + ' if (input.texCoord.x % 2 > 1) return float4(0, 0, 0, 0);\n'; + } + return '' + + 'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' + + '// NOTE: We transform the normals through the\n' + + '// worldViewProjectionInverseTranspose instead of the\n' + + '// worldViewInverseTranspose. The projection matrix warps the\n' + + '// normals in strange ways. One result of this is that the "front\n' + + '// face" color of the ring can extend around more than 50% of the\n' + + '// ring. This may be good or bad. If we dont include the projection\n' + + '// matrix, we always get a 50% split, but we do not account for\n' + + '// perspective. An alternative would be to get a little more\n' + + '// complicated, using the positions of the camera and the center\n' + + '// of the ring.\n' + + 'float4x4 worldViewProjectionInverseTranspose :\n' + + ' WORLDVIEWPROJECTIONINVERSETRANSPOSE;\n' + + 'float4 color1;\n' + + 'float4 color2;\n' + + 'float4 emissive; // Used for highlighting.\n' + + '\n' + + 'struct VertexShaderInput {\n' + + ' float4 position : POSITION;\n' + + ' float4 normal : NORMAL;\n' + + ' float1 texCoord : TEXCOORD0;\n' + + '};\n' + + '\n' + + 'struct PixelShaderInput {\n' + + ' float4 position : POSITION;\n' + + ' float3 normal : TEXCOORD0;\n' + + ' float1 texCoord : TEXCOORD1;\n' + + '};\n' + + '\n' + + 'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' + + ' PixelShaderInput output;\n' + + '\n' + + ' output.position = mul(input.position, worldViewProjection);\n' + + ' output.normal = mul(input.normal,\n' + + ' worldViewProjectionInverseTranspose).xyz;\n' + + ' output.texCoord = input.texCoord;\n' + + ' return output;\n' + + '}\n' + + '\n' + + 'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' + + stippleCode + + ' if (input.normal.z < 0) {\n' + + ' return color1 * emissive; // Front face of the ring.\n' + + ' } else {\n' + + ' return color2 * emissive; // Back face of the ring.\n' + + ' }\n' + + '}\n' + + '\n' + + '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' + + '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' + + '// #o3d MatrixLoadOrder RowMajor\n'; +}; /** * Creates the Rotate1 manipulator's line ring material. @@ -1712,16 +1743,19 @@ o3djs.manipulators.LINE_RING_FXSTRING = '' + * the material is created. * @param {!o3djs.math.Vector4} color1 A color in the format [r, g, b, a]. * @param {!o3djs.math.Vector4} color2 A color in the format [r, g, b, a]. + * @param {boolean} enableStipple Whether line stippling should be enabled + * in the shader. * @return {!o3d.Material} The created material. */ o3djs.manipulators.createLineRingMaterial = function(pack, drawList, color1, - color2) { + color2, + enableStipple) { var material = pack.createObject('Material'); material.effect = pack.createObject('Effect'); - material.effect.loadFromFXString(o3djs.manipulators.LINE_RING_FXSTRING); - material.effect.name = o3djs.manipulators.LINE_RING_EFFECT_NAME; + material.effect.loadFromFXString( + o3djs.manipulators.getLineRingFXString_(enableStipple)); material.drawList = drawList; material.createParam('color1', 'ParamFloat4').value = color1; material.createParam('color2', 'ParamFloat4').value = color2; @@ -1736,6 +1770,7 @@ o3djs.manipulators.createLineRingMaterial = function(pack, material.state.getStateParam('AlphaTestEnable').value = true; material.state.getStateParam('AlphaComparisonFunction').value = o3djs.base.o3d.State.CMP_GREATER; + material.state.getStateParam('AlphaReference').value = 0; return material; }; |