diff --git a/examples/example/games/grabHockey.js b/examples/example/games/grabHockey.js index 5e101e1d48..58664950f0 100644 --- a/examples/example/games/grabHockey.js +++ b/examples/example/games/grabHockey.js @@ -1,4 +1,3 @@ - // grab.js // examples // @@ -11,12 +10,38 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +// these are hand-measured bounds of the AirHockey table +var fieldMaxOffset = { + x: 0.475, + y: 0.315, + z: 0.830 +}; +var fieldMinOffset = { + x: -0.475, + y: 0.315, + z: -0.830 +}; + +// parameters for storing the table playing field +var fieldMax = { + x: 0, + y: 0, + z: 0 +}; +var fieldMin = { + x: 0, + y: 0, + z: 0 +}; + var isGrabbing = false; var grabbedEntity = null; var prevMouse = {}; var deltaMouse = { z: 0 } + +var TABLE_SEARCH_RANGE = 10; var entityProps; var moveUpDown = false; var CLOSE_ENOUGH = 0.001; @@ -26,8 +51,13 @@ var DAMPING_RATE = 0.80; var ANGULAR_DAMPING_RATE = 0.40; var SCREEN_TO_METERS = 0.001; var currentPosition, currentVelocity, cameraEntityDistance, currentRotation; +var grabHeight; var velocityTowardTarget, desiredVelocity, addedVelocity, newVelocity, dPosition, camYaw, distanceToTarget, targetPosition; -var originalGravity = {x: 0, y: 0, z: 0}; +var originalGravity = { + x: 0, + y: 0, + z: 0 +}; var shouldRotate = false; var dQ, theta, axisAngle, dT; var angularVelocity = { @@ -58,11 +88,11 @@ var dropLine = Overlays.addOverlay("line3d", { function vectorIsZero(v) { - return v.x == 0 && v.y == 0 && v.z == 0; + return v.x == 0 && v.y == 0 && v.z == 0; } function nearLinePoint(targetPosition) { - // var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0}); + // var handPosition = Vec3.sum(MyAvatar.position, {x:0, y:0.2, z:0}); var handPosition = MyAvatar.getRightPalmPosition(); var along = Vec3.subtract(targetPosition, handPosition); along = Vec3.normalize(along); @@ -75,9 +105,15 @@ function mousePressEvent(event) { if (!event.isLeftButton) { return; } + prevMouse.x = event.x; + prevMouse.y = event.y; + var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking - if (intersection.intersects && intersection.properties.collisionsWillMove) { + if (!intersection.intersects) { + return; + } + if (intersection.properties.collisionsWillMove) { grabbedEntity = intersection.entityID; var props = Entities.getEntityProperties(grabbedEntity) isGrabbing = true; @@ -87,15 +123,35 @@ function mousePressEvent(event) { currentVelocity = props.velocity; updateDropLine(targetPosition); + // remember the height of the object when first grabbed + // we'll try to maintain this height during the rest of this grab + grabHeight = currentPosition.y; + Entities.editEntity(grabbedEntity, { - gravity: {x: 0, y: 0, z: 0} + gravity: { + x: 0, + y: 0, + z: 0 + } }); Audio.playSound(grabSound, { position: props.position, volume: VOLUME }); - } + + //We want to detect table once user grabs something that may be on a table... + var potentialTables = Entities.findEntities(MyAvatar.position, TABLE_SEARCH_RANGE); + potentialTables.forEach(function(table) { + var props = Entities.getEntityProperties(table); + if (props.name === "table") { + var tablePosition = props.position; + // when we know the table's position we can compute the X-Z bounds of its field + fieldMax = Vec3.sum(tablePosition, fieldMaxOffset); + fieldMin = Vec3.sum(tablePosition, fieldMinOffset); + } + }); + } } function updateDropLine(position) { @@ -127,7 +183,7 @@ function mouseReleaseEvent() { // 5. interface B releases the entity and puts the original gravity back (to zero) if (!vectorIsZero(originalGravity)) { Entities.editEntity(grabbedEntity, { - gravity: originalGravity + gravity: originalGravity }); } @@ -144,6 +200,7 @@ function mouseReleaseEvent() { } } +// new mouseMoveEvent function mouseMoveEvent(event) { if (isGrabbing) { // see if something added/restored gravity @@ -152,24 +209,15 @@ function mouseMoveEvent(event) { originalGravity = props.gravity; } - deltaMouse.x = event.x - prevMouse.x; - if (!moveUpDown) { - deltaMouse.z = event.y - prevMouse.y; - deltaMouse.y = 0; - } else { - deltaMouse.y = (event.y - prevMouse.y) * -1; - deltaMouse.z = 0; - } - // Update the target position by the amount the mouse moved - camYaw = Quat.safeEulerAngles(Camera.getOrientation()).y; - dPosition = Vec3.multiplyQbyV(Quat.fromPitchYawRollDegrees(0, camYaw, 0), deltaMouse); - if (!shouldRotate) { - // Adjust target position for the object by the mouse move - cameraEntityDistance = Vec3.distance(Camera.getPosition(), currentPosition); - // Scale distance we want to move by the distance from the camera to the grabbed object - // TODO: Correct SCREEN_TO_METERS to be correct for the actual FOV, resolution - targetPosition = Vec3.sum(targetPosition, Vec3.multiply(dPosition, cameraEntityDistance * SCREEN_TO_METERS)); - } else if (shouldRotate) { + if (shouldRotate) { + deltaMouse.x = event.x - prevMouse.x; + if (!moveUpDown) { + deltaMouse.z = event.y - prevMouse.y; + deltaMouse.y = 0; + } else { + deltaMouse.y = (event.y - prevMouse.y) * -1; + deltaMouse.z = 0; + } var transformedDeltaMouse = { x: deltaMouse.z, y: deltaMouse.x, @@ -180,15 +228,37 @@ function mouseMoveEvent(event) { theta = 2 * Math.acos(dQ.w); axisAngle = Quat.axis(dQ); angularVelocity = Vec3.multiply((theta / dT), axisAngle); - } + } else { + var relativePosition = Vec3.subtract(currentPosition, Camera.getPosition()); + if (relativePosition.y < 0) { + // grabee is below camera, so movement is valid + // compute intersectionPoint where mouse ray hits grabee's current x-z plane + var pickRay = Camera.computePickRay(event.x, event.y); + var mousePosition = pickRay.direction; + var length = relativePosition.y / mousePosition.y; + mousePosition = Vec3.multiply(mousePosition, length); + mousePosition = Vec3.sum(Camera.getPosition(), mousePosition); + // clamp mousePosition to table field + if (mousePosition.x < fieldMin.x) { + mousePosition.x = fieldMin.x; + } else if (mousePosition.x > fieldMax.x) { + mousePosition.x = fieldMax.x; + } + if (mousePosition.z < fieldMin.z) { + mousePosition.z = fieldMin.z; + } else if (mousePosition.z > fieldMax.z) { + mousePosition.z = fieldMax.z; + } + mousePosition.y = grabHeight; + targetPosition = mousePosition; + } + } } prevMouse.x = event.x; prevMouse.y = event.y; - } - function keyReleaseEvent(event) { if (event.text === "SHIFT") { moveUpDown = false; @@ -237,14 +307,24 @@ function update(deltaTime) { newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE)); // Update entity } else { - newVelocity = {x: 0, y: 0, z: 0}; + newVelocity = { + x: 0, + y: 0, + z: 0 + }; } if (shouldRotate) { angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE)); } else { - angularVelocity = entityProps.angularVelocity; + angularVelocity = entityProps.angularVelocity; } + // enforce that grabee's rotation is only about y-axis while being grabbed + currentRotation.x = 0; + currentRotation.z = 0; + currentRotation.y = Math.sqrt(1.0 - currentRotation.w * currentRotation.w); + // Hrm... slamming the currentRotation doesn't seem to work + Entities.editEntity(grabbedEntity, { position: currentPosition, rotation: currentRotation,