From a444d6a2a26abef25959b2f392ee3e280f03028f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Jul 2015 13:19:59 -0700 Subject: [PATCH] remove hungarian notation --- examples/grab.js | 424 ++++++++++++++++++++++++++--------------------- 1 file changed, 235 insertions(+), 189 deletions(-) diff --git a/examples/grab.js b/examples/grab.js index 13c91bebb8..05bcf128e2 100644 --- a/examples/grab.js +++ b/examples/grab.js @@ -10,97 +10,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var MOVE_TIMESCALE = 0.1; -var INV_MOVE_TIMESCALE = 1.0 / MOVE_TIMESCALE; var MAX_SOLID_ANGLE = 0.01; // objects that appear smaller than this can't be grabbed -var CLOSE_ENOUGH = 0.001; -var ZERO_VEC3 = { x: 0, y: 0, z: 0 }; -var ANGULAR_DAMPING_RATE = 0.40; - -// NOTE: to improve readability global variable names start with 'g' -var gIsGrabbing = false; -var gGrabbedEntity = null; -var gActionID = null; -var gEntityProperties; -var gStartPosition; -var gStartRotation; -var gCurrentPosition; -var gOriginalGravity = ZERO_VEC3; -var gPlaneNormal = ZERO_VEC3; - -// gMaxGrabDistance is a function of the size of the object. -var gMaxGrabDistance; - -// gGrabMode defines the degrees of freedom of the grab target positions -// relative to gGrabStartPosition options include: -// xzPlane (default) -// verticalCylinder (SHIFT) -// rotate (CONTROL) -// Modes to eventually support?: -// xyPlane -// yzPlane -// polar -// elevationAzimuth -var gGrabMode = "xzplane"; - -// gGrabOffset allows the user to grab an object off-center. It points from the object's center -// to the point where the ray intersects the grab plane (at the moment the grab is initiated). -// Future target positions of the ray intersection are on the same plane, and the offset is subtracted -// to compute the target position of the object's center. -var gGrabOffset = { x: 0, y: 0, z: 0 }; - -var gTargetPosition; -var gTargetRotation; -var gLiftKey = false; // SHIFT -var gRotateKey = false; // CONTROL - -var gInitialMouse = { x: 0, y: 0 }; -var gPreviousMouse = { x: 0, y: 0 }; -var gMouseCursorLocation = { x: 0, y: 0 }; -var gMouseAtRotateStart = { x: 0, y: 0 }; - -var gBeaconHeight = 0.10; - -// var gAngularVelocity = ZERO_VEC3; - -// TODO: play sounds again when we aren't leaking AudioInjector threads -// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); -// var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); -// var VOLUME = 0.0; - -var gBeaconHeight = 0.10; -var BEACON_COLOR = { - red: 200, - green: 200, - blue: 200 -}; -var BEACON_WIDTH = 2; +var ZERO_VEC3 = {x: 0, y: 0, z: 0}; +var IDENTITY_QUAT = {x: 0, y: 0, z: 0, w: 0}; -var gBeacon = Overlays.addOverlay("line3d", { - color: BEACON_COLOR, - alpha: 1, - visible: false, - lineWidth: BEACON_WIDTH -}); - -function updateDropLine(position) { - Overlays.editOverlay(gBeacon, { - visible: true, - start: { - x: position.x, - y: position.y + gBeaconHeight, - z: position.z - }, - end: { - x: position.x, - y: position.y - gBeaconHeight, - z: position.z - } - }); -} - -function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) { +// helper function +function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event, maxDistance) { var cameraPosition = Camera.getPosition(); var localPointOnPlane = Vec3.subtract(pointOnPlane, cameraPosition); var distanceFromPlane = Vec3.dot(localPointOnPlane, planeNormal); @@ -117,7 +33,7 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) { var useMaxForwardGrab = false; if (Math.abs(dirDotNorm) > MIN_RAY_PLANE_DOT) { var distanceToIntersection = distanceFromPlane / dirDotNorm; - if (distanceToIntersection > 0 && distanceToIntersection < gMaxGrabDistance) { + if (distanceToIntersection > 0 && distanceToIntersection < maxDistance) { // ray points into the plane localIntersection = Vec3.multiply(pickRay.direction, distanceFromPlane / dirDotNorm); } else { @@ -134,52 +50,160 @@ function mouseIntersectionWithPlane(pointOnPlane, planeNormal, event) { // we re-route the intersection to be in front at max distance. var rayDirection = Vec3.subtract(pickRay.direction, Vec3.multiply(planeNormal, dirDotNorm)); rayDirection = Vec3.normalize(rayDirection); - localIntersection = Vec3.multiply(rayDirection, gMaxGrabDistance); + localIntersection = Vec3.multiply(rayDirection, maxDistance); localIntersection = Vec3.sum(localIntersection, Vec3.multiply(planeNormal, distanceFromPlane)); } var worldIntersection = Vec3.sum(cameraPosition, localIntersection); return worldIntersection; } -function computeNewGrabPlane() { - if (!gIsGrabbing) { +// Mouse class stores mouse click and drag info +Mouse = function() { + this.current = {x: 0, y: 0 }; + this.previous = {x: 0, y: 0 }; + this.rotateStart = {x: 0, y: 0 }; + this.cursorRestore = {x: 0, y: 0}; +} + +Mouse.prototype.startDrag = function(position) { + this.current = {x: position.x, y: position.y}; + this.startRotateDrag(); +} + +Mouse.prototype.updateDrag = function(position) { + this.current = {x: position.x, y: position.y }; +} + +Mouse.prototype.startRotateDrag = function() { + this.previous = {x: this.current.x, y: this.current.y}; + this.rotateStart = {x: this.current.x, y: this.current.y}; + this.cursorRestore = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() }; +} + +Mouse.prototype.getDrag = function() { + var delta = {x: this.current.x - this.previous.x, y: this.current.y - this.previous.y}; + this.previous = {x: this.current.x, y: this.current.y}; + return delta; +} + +Mouse.prototype.restoreRotateCursor = function() { + Window.setCursorPosition(this.cursorRestore.x, this.cursorRestore.y); + this.current = {x: this.rotateStart.x, y: this.rotateStart.y}; +} + +var mouse = new Mouse(); + + +// Beacon class stores info for drawing a line at object's target position +Beacon = function() { + this.height = 0.10; + this.overlayID = Overlays.addOverlay("line3d", { + color: {red: 200, green: 200, blue: 200}, + alpha: 1, + visible: false, + lineWidth: 2 + }); +} + +Beacon.prototype.enable = function() { + Overlays.editOverlay(this.overlayID, { visible: true }); +} + +Beacon.prototype.disable = function() { + Overlays.editOverlay(this.overlayID, { visible: false }); +} + +Beacon.prototype.updatePosition = function(position) { + Overlays.editOverlay(this.overlayID, { + visible: true, + start: { + x: position.x, + y: position.y + this.height, + z: position.z + }, + end: { + x: position.x, + y: position.y - this.height, + z: position.z + } + }); +} + +var beacon = new Beacon(); + + +// TODO: play sounds again when we aren't leaking AudioInjector threads +// var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/CloseClamp.wav"); +// var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); +// var VOLUME = 0.0; + + +// Grabber class stores and computes info for grab behavior +Grabber = function() { + this.isGrabbing = false; + this.entityID = null; + this.actionID = null; + this.startPosition = ZERO_VEC3; + this.lastRotation = IDENTITY_QUAT; + this.currentPosition = ZERO_VEC3; + this.planeNormal = ZERO_VEC3; + + this.originalGravity = ZERO_VEC3; + // maxDistance is a function of the size of the object. + this.maxDistance; + + // mode defines the degrees of freedom of the grab target positions + // relative to startPosition options include: + // xzPlane (default) + // verticalCylinder (SHIFT) + // rotate (CONTROL) + this.mode = "xzplane"; + + // offset allows the user to grab an object off-center. It points from the object's center + // to the point where the ray intersects the grab plane (at the moment the grab is initiated). + // Future target positions of the ray intersection are on the same plane, and the offset is subtracted + // to compute the target position of the object's center. + this.offset = {x: 0, y: 0, z: 0 }; + + this.targetPosition; + this.targetRotation; + + this.liftKey = false; // SHIFT + this.rotateKey = false; // CONTROL +} + +Grabber.prototype.computeNewGrabPlane = function() { + if (!this.isGrabbing) { return; } - var maybeResetMousePosition = false; - if (gGrabMode !== "rotate") { - gMouseAtRotateStart = gMouseCursorLocation; + var modeWasRotate = (this.mode == "rotate"); + this.mode = "xzPlane"; + this.planeNormal = {x: 0, y: 1, z: 0 }; + if (this.rotateKey) { + this.mode = "rotate"; + mouse.startRotateDrag(); } else { - maybeResetMousePosition = true; - } - gGrabMode = "xzPlane"; - gPlaneNormal = { x: 0, y: 1, z: 0 }; - if (gLiftKey) { - if (!gRotateKey) { - gGrabMode = "verticalCylinder"; - // a new planeNormal will be computed each move + if (modeWasRotate) { + // we reset the mouse screen position whenever we stop rotating + mouse.restoreRotateCursor(); } - } else if (gRotateKey) { - gGrabMode = "rotate"; - } + if (this.liftKey) { + this.mode = "verticalCylinder"; + // NOTE: during verticalCylinder mode a new planeNormal will be computed each move + } + } - gPointOnPlane = Vec3.sum(gCurrentPosition, gGrabOffset); - var xzOffset = Vec3.subtract(gPointOnPlane, Camera.getPosition()); + this.pointOnPlane = Vec3.sum(this.currentPosition, this.offset); + var xzOffset = Vec3.subtract(this.pointOnPlane, Camera.getPosition()); xzOffset.y = 0; - gXzDistanceToGrab = Vec3.length(xzOffset); - - if (gGrabMode !== "rotate" && maybeResetMousePosition) { - // we reset the mouse position whenever we stop rotating - Window.setCursorPosition(gMouseAtRotateStart.x, gMouseAtRotateStart.y); - } + this.xzDistanceToGrab = Vec3.length(xzOffset); } -function mousePressEvent(event) { +Grabber.prototype.pressEvent = function(event) { if (!event.isLeftButton) { return; } - gInitialMouse = {x: event.x, y: event.y }; - gPreviousMouse = {x: event.x, y: event.y }; var pickRay = Camera.computePickRay(event.x, event.y); var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking @@ -193,150 +217,172 @@ function mousePressEvent(event) { return; } + mouse.startDrag(event); + var clickedEntity = pickResults.entityID; var entityProperties = Entities.getEntityProperties(clickedEntity) - gStartPosition = entityProperties.position; - gStartRotation = entityProperties.rotation; + this.startPosition = entityProperties.position; + this.lastRotation = entityProperties.rotation; var cameraPosition = Camera.getPosition(); - gBeaconHeight = Vec3.length(entityProperties.dimensions); - gMaxGrabDistance = gBeaconHeight / MAX_SOLID_ANGLE; - if (Vec3.distance(gStartPosition, cameraPosition) > gMaxGrabDistance) { + var objectBoundingDiameter = Vec3.length(entityProperties.dimensions); + beacon.height = objectBoundingDiameter; + this.maxDistance = objectBoundingDiameter / MAX_SOLID_ANGLE; + if (Vec3.distance(this.startPosition, cameraPosition) > this.maxDistance) { // don't allow grabs of things far away return; } Entities.editEntity(clickedEntity, { gravity: ZERO_VEC3 }); - gIsGrabbing = true; + this.isGrabbing = true; - gGrabbedEntity = clickedEntity; - gCurrentPosition = entityProperties.position; - gOriginalGravity = entityProperties.gravity; - gTargetPosition = gStartPosition; + this.entityID = clickedEntity; + this.currentPosition = entityProperties.position; + this.originalGravity = entityProperties.gravity; + this.targetPosition = {x: this.startPosition.x, y: this.startPosition.y, z: this.startPosition.z}; // compute the grab point - var nearestPoint = Vec3.subtract(gStartPosition, cameraPosition); + var nearestPoint = Vec3.subtract(this.startPosition, cameraPosition); var distanceToGrab = Vec3.dot(nearestPoint, pickRay.direction); nearestPoint = Vec3.multiply(distanceToGrab, pickRay.direction); - gPointOnPlane = Vec3.sum(cameraPosition, nearestPoint); + this.pointOnPlane = Vec3.sum(cameraPosition, nearestPoint); // compute the grab offset (points from object center to point of grab) - gGrabOffset = Vec3.subtract(gPointOnPlane, gStartPosition); + this.offset = Vec3.subtract(this.pointOnPlane, this.startPosition); - computeNewGrabPlane(); + this.computeNewGrabPlane(); - updateDropLine(gStartPosition); + beacon.updatePosition(this.startPosition); // TODO: play sounds again when we aren't leaking AudioInjector threads //Audio.playSound(grabSound, { position: entityProperties.position, volume: VOLUME }); } -function mouseReleaseEvent() { - if (gIsGrabbing) { - if (Vec3.length(gOriginalGravity) != 0) { - Entities.editEntity(gGrabbedEntity, { gravity: gOriginalGravity }); +Grabber.prototype.releaseEvent = function() { + if (this.isGrabbing) { + if (Vec3.length(this.originalGravity) != 0) { + Entities.editEntity(this.entityID, { gravity: this.originalGravity}); } - gIsGrabbing = false - Entities.deleteAction(gGrabbedEntity, gActionID); - gActionID = null; + this.isGrabbing = false + Entities.deleteAction(this.entityID, this.actionID); + this.actionID = null; - Overlays.editOverlay(gBeacon, { visible: false }); + beacon.disable(); // TODO: play sounds again when we aren't leaking AudioInjector threads //Audio.playSound(releaseSound, { position: entityProperties.position, volume: VOLUME }); } } -function mouseMoveEvent(event) { - if (!gIsGrabbing) { +Grabber.prototype.moveEvent = function(event) { + if (!this.isGrabbing) { return; } + mouse.updateDrag(event); // see if something added/restored gravity - var entityProperties = Entities.getEntityProperties(gGrabbedEntity); + var entityProperties = Entities.getEntityProperties(this.entityID); if (Vec3.length(entityProperties.gravity) != 0) { - gOriginalGravity = entityProperties.gravity; + this.originalGravity = entityProperties.gravity; } - gCurrentPosition = entityProperties.position; + this.currentPosition = entityProperties.position; var actionArgs = {}; - if (gGrabMode === "rotate") { - var deltaMouse = { x: 0, y: 0 }; - var dx = event.x - gInitialMouse.x; - var dy = event.y - gInitialMouse.y; + if (this.mode === "rotate") { + var drag = mouse.getDrag(); var orientation = Camera.getOrientation(); - var dragOffset = Vec3.multiply(dx, Quat.getRight(orientation)); - dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-dy, Quat.getUp(orientation))); + var dragOffset = Vec3.multiply(drag.x, Quat.getRight(orientation)); + dragOffset = Vec3.sum(dragOffset, Vec3.multiply(-drag.y, Quat.getUp(orientation))); var axis = Vec3.cross(dragOffset, Quat.getFront(orientation)); axis = Vec3.normalize(axis); var ROTATE_STRENGTH = 0.4; // magic number tuned by hand - var angle = ROTATE_STRENGTH * Math.sqrt((dx * dx) + (dy * dy)); + var angle = ROTATE_STRENGTH * Math.sqrt((drag.x * drag.x) + (drag.y * drag.y)); var deltaQ = Quat.angleAxis(angle, axis); // var qZero = entityProperties.rotation; - var qZero = gStartRotation; - var qOne = Quat.multiply(deltaQ, qZero); - actionArgs = {targetRotation: qOne, angularTimeScale: 0.1}; + //var qZero = this.lastRotation; + this.lastRotation = Quat.multiply(deltaQ, this.lastRotation); + actionArgs = {targetRotation: this.lastRotation, angularTimeScale: 0.1}; } else { - var newTargetPosition; - if (gGrabMode === "verticalCylinder") { + var newPointOnPlane; + if (this.mode === "verticalCylinder") { // for this mode we recompute the plane based on current Camera var planeNormal = Quat.getFront(Camera.getOrientation()); planeNormal.y = 0; planeNormal = Vec3.normalize(planeNormal); - var pointOnCylinder = Vec3.multiply(planeNormal, gXzDistanceToGrab); + var pointOnCylinder = Vec3.multiply(planeNormal, this.xzDistanceToGrab); pointOnCylinder = Vec3.sum(Camera.getPosition(), pointOnCylinder); - newTargetPosition = mouseIntersectionWithPlane(pointOnCylinder, planeNormal, event); - gPointOnPlane = Vec3.sum(newTargetPosition, gGrabOffset); + this.pointOnPlane = mouseIntersectionWithPlane(pointOnCylinder, planeNormal, mouse.current, this.maxDistance); + newPointOnPlane = {x: this.pointOnPlane.x, y: this.pointOnPlane.y, z: this.pointOnPlane.z}; } else { var cameraPosition = Camera.getPosition(); - newTargetPosition = mouseIntersectionWithPlane(gPointOnPlane, gPlaneNormal, event); - var relativePosition = Vec3.subtract(newTargetPosition, cameraPosition); + newPointOnPlane = mouseIntersectionWithPlane(this.pointOnPlane, this.planeNormal, mouse.current, this.maxDistance); + var relativePosition = Vec3.subtract(newPointOnPlane, cameraPosition); var distance = Vec3.length(relativePosition); - if (distance > gMaxGrabDistance) { + if (distance > this.maxDistance) { // clamp distance - relativePosition = Vec3.multiply(relativePosition, gMaxGrabDistance / distance); - newTargetPosition = Vec3.sum(relativePosition, cameraPosition); + relativePosition = Vec3.multiply(relativePosition, this.maxDistance / distance); + newPointOnPlane = Vec3.sum(relativePosition, cameraPosition); } } - gTargetPosition = Vec3.subtract(newTargetPosition, gGrabOffset); - actionArgs = {targetPosition: gTargetPosition, linearTimeScale: 0.1}; - } - gPreviousMouse = { x: event.x, y: event.y }; - gMouseCursorLocation = { x: Window.getCursorPositionX(), y: Window.getCursorPositionY() }; + this.targetPosition = Vec3.subtract(newPointOnPlane, this.offset); + actionArgs = {targetPosition: this.targetPosition, linearTimeScale: 0.1}; - if (!gActionID) { - gActionID = Entities.addAction("spring", gGrabbedEntity, actionArgs); + beacon.updatePosition(this.targetPosition); + } + + if (!this.actionID) { + this.actionID = Entities.addAction("spring", this.entityID, actionArgs); } else { - Entities.updateAction(gGrabbedEntity, gActionID, actionArgs); + Entities.updateAction(this.entityID, this.actionID, actionArgs); } - - updateDropLine(gTargetPosition); } -function keyReleaseEvent(event) { +Grabber.prototype.keyReleaseEvent = function(event) { if (event.text === "SHIFT") { - gLiftKey = false; + this.liftKey = false; } if (event.text === "CONTROL") { - gRotateKey = false; + this.rotateKey = false; } - computeNewGrabPlane(); + this.computeNewGrabPlane(); +} + +Grabber.prototype.keyPressEvent = function(event) { + if (event.text === "SHIFT") { + this.liftKey = true; + } + if (event.text === "CONTROL") { + this.rotateKey = true; + } + this.computeNewGrabPlane(); +} + +var grabber = new Grabber(); + +function pressEvent(event) { + grabber.pressEvent(event); +} + +function moveEvent(event) { + grabber.moveEvent(event); +} + +function releaseEvent(event) { + grabber.releaseEvent(event); } function keyPressEvent(event) { - if (event.text === "SHIFT") { - gLiftKey = true; - } - if (event.text === "CONTROL") { - gRotateKey = true; - } - computeNewGrabPlane(); + grabber.keyPressEvent(event); } -Controller.mouseMoveEvent.connect(mouseMoveEvent); -Controller.mousePressEvent.connect(mousePressEvent); -Controller.mouseReleaseEvent.connect(mouseReleaseEvent); +function keyReleaseEvent(event) { + grabber.keyReleaseEvent(event); +} + +Controller.mousePressEvent.connect(pressEvent); +Controller.mouseMoveEvent.connect(moveEvent); +Controller.mouseReleaseEvent.connect(releaseEvent); Controller.keyPressEvent.connect(keyPressEvent); Controller.keyReleaseEvent.connect(keyReleaseEvent);