cleanup and stability for grabHockey.js script

This commit is contained in:
Andrew Meadows 2015-05-18 16:57:25 -07:00
parent 85fac8394a
commit bb1fe8f439

View file

@ -10,6 +10,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var adebug = 0;
// these are hand-measured bounds of the AirHockey table
var fieldMaxOffset = {
x: 0.475,
@ -40,13 +41,14 @@ var entityProps;
var moveUpDown = false;
var CLOSE_ENOUGH = 0.001;
var FULL_STRENGTH = 1.0;
var SPRING_RATE = 1.5;
var SPRING_TIMESCALE = 0.05;
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 grabOffset;
var originalGravity = {
x: 0,
y: 0,
@ -94,6 +96,20 @@ function nearLinePoint(targetPosition) {
return Vec3.sum(handPosition, along);
}
function xzPickRayIntersetion(pointOnPlane, mouseX, mouseY) {
var relativePosition = Vec3.subtract(pointOnPlane, Camera.getPosition());
var pickRay = Camera.computePickRay(mouseX, mouseY);
if (Math.abs(pickRay.direction.y) > 0.001) {
var length = relativePosition.y / pickRay.direction.y;
var pickInersection = Vec3.multiply(pickRay.direction, length);
pickInersection = Vec3.sum(Camera.getPosition(), pickInersection);
return pickInersection;
}
// point and line are more-or-less co-planar: compute closest approach of pickRay and pointOnPlane
var length = Vec3.dot(relativePosition, pickRay.direction);
var pickInersection = Vec3.multiply(pickRay.direction, length);
return pickInersection;
}
function mousePressEvent(event) {
if (!event.isLeftButton) {
@ -103,19 +119,23 @@ function mousePressEvent(event) {
prevMouse.y = event.y;
var pickRay = Camera.computePickRay(event.x, event.y);
var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking
if (!intersection.intersects) {
var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking
if (!pickResults.intersects) {
return;
}
if (intersection.properties.collisionsWillMove) {
grabbedEntity = intersection.entityID;
if (pickResults.properties.collisionsWillMove) {
grabbedEntity = pickResults.entityID;
var props = Entities.getEntityProperties(grabbedEntity)
isGrabbing = true;
originalGravity = props.gravity;
targetPosition = props.position;
var objectPosition = props.position;
currentPosition = props.position;
currentVelocity = props.velocity;
updateDropLine(targetPosition);
updateDropLine(objectPosition);
var pointOnPlane = xzPickRayIntersetion(objectPosition, event.x, event.y);
grabOffset = Vec3.subtract(pointOnPlane, objectPosition);
targetPosition = objectPosition;
// remember the height of the object when first grabbed
// we'll try to maintain this height during the rest of this grab
@ -223,53 +243,48 @@ function mouseMoveEvent(event) {
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);
var pointOnPlane = xzPickRayIntersetion(currentPosition, event.x, event.y);
pointOnPlane = Vec3.subtract(pointOnPlane, grabOffset);
// translate mousePosition into local-frame
mousePosition = Vec3.subtract(mousePosition, tablePosition);
// translate pointOnPlane into local-frame
pointOnPlane = Vec3.subtract(pointOnPlane, tablePosition);
// clamp local mousePosition to table field
if (mousePosition.x > fieldMaxOffset.x) {
mousePosition.x = fieldMaxOffset.x;
} else if (mousePosition.x < fieldMinOffset.x) {
mousePosition.x = fieldMinOffset.x;
}
if (mousePosition.z > fieldMaxOffset.z) {
mousePosition.z = fieldMaxOffset.z;
} else if (mousePosition.z < fieldMinOffset.z) {
mousePosition.z = fieldMinOffset.z;
}
// clamp to rotated square (for cut corners)
var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 });
mousePosition = Vec3.multiplyQbyV(rotation, mousePosition);
if (mousePosition.x > halfCornerBoxWidth) {
mousePosition.x = halfCornerBoxWidth;
} else if (mousePosition.x < -halfCornerBoxWidth) {
mousePosition.x = -halfCornerBoxWidth;
}
if (mousePosition.z > halfCornerBoxWidth) {
mousePosition.z = halfCornerBoxWidth;
} else if (mousePosition.z < -halfCornerBoxWidth) {
mousePosition.z = -halfCornerBoxWidth;
}
// rotate back into local frame
rotation.y = -rotation.y;
mousePosition = Vec3.multiplyQbyV(rotation, mousePosition);
// translate into world-frame
mousePosition = Vec3.sum(tablePosition, mousePosition);
mousePosition.y = grabHeight;
targetPosition = mousePosition;
// clamp local pointOnPlane to table field
if (pointOnPlane.x > fieldMaxOffset.x) {
pointOnPlane.x = fieldMaxOffset.x;
} else if (pointOnPlane.x < fieldMinOffset.x) {
pointOnPlane.x = fieldMinOffset.x;
}
if (pointOnPlane.z > fieldMaxOffset.z) {
pointOnPlane.z = fieldMaxOffset.z;
} else if (pointOnPlane.z < fieldMinOffset.z) {
pointOnPlane.z = fieldMinOffset.z;
}
// clamp to rotated square (for cut corners)
var rotation = Quat.angleAxis(45, { x:0, y:1, z:0 });
pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane);
if (pointOnPlane.x > halfCornerBoxWidth) {
pointOnPlane.x = halfCornerBoxWidth;
} else if (pointOnPlane.x < -halfCornerBoxWidth) {
pointOnPlane.x = -halfCornerBoxWidth;
}
if (pointOnPlane.z > halfCornerBoxWidth) {
pointOnPlane.z = halfCornerBoxWidth;
} else if (pointOnPlane.z < -halfCornerBoxWidth) {
pointOnPlane.z = -halfCornerBoxWidth;
}
// rotate back into local frame
rotation.y = -rotation.y;
pointOnPlane = Vec3.multiplyQbyV(rotation, pointOnPlane);
// translate into world-frame
pointOnPlane = Vec3.sum(tablePosition, pointOnPlane);
// clamp to gragHeight
pointOnPlane.y = grabHeight;
targetPosition = pointOnPlane;
}
}
prevMouse.x = event.x;
@ -297,57 +312,32 @@ function keyPressEvent(event) {
function update(deltaTime) {
dT = deltaTime;
if (isGrabbing) {
entityProps = Entities.getEntityProperties(grabbedEntity);
currentPosition = entityProps.position;
currentVelocity = entityProps.velocity;
currentRotation = entityProps.rotation;
var dPosition = Vec3.subtract(targetPosition, currentPosition);
distanceToTarget = Vec3.length(dPosition);
if (distanceToTarget > CLOSE_ENOUGH) {
// compute current velocity in the direction we want to move
velocityTowardTarget = Vec3.dot(currentVelocity, Vec3.normalize(dPosition));
velocityTowardTarget = Vec3.multiply(Vec3.normalize(dPosition), velocityTowardTarget);
// compute the speed we would like to be going toward the target position
desiredVelocity = Vec3.multiply(dPosition, (1.0 / deltaTime) * SPRING_RATE);
// compute how much we want to add to the existing velocity
addedVelocity = Vec3.subtract(desiredVelocity, velocityTowardTarget);
// If target is too far, roll off the force as inverse square of distance
if (distanceToTarget / cameraEntityDistance > FULL_STRENGTH) {
addedVelocity = Vec3.multiply(addedVelocity, Math.pow(FULL_STRENGTH / distanceToTarget, 2.0));
}
newVelocity = Vec3.sum(currentVelocity, addedVelocity);
// Add Damping
newVelocity = Vec3.subtract(newVelocity, Vec3.multiply(newVelocity, DAMPING_RATE));
// Update entity
} else {
newVelocity = {
x: 0,
y: 0,
z: 0
};
}
if (shouldRotate) {
angularVelocity = Vec3.subtract(angularVelocity, Vec3.multiply(angularVelocity, ANGULAR_DAMPING_RATE));
Entities.editEntity(grabbedEntity, { angularVelocity: angularVelocity, });
} else {
angularVelocity = entityProps.angularVelocity;
var dPosition = Vec3.subtract(targetPosition, currentPosition);
var delta = Vec3.length(dPosition);
if (delta > CLOSE_ENOUGH) {
var MAX_POSITION_DELTA = 0.50;
if (delta > MAX_POSITION_DELTA) {
dPosition = Vec3.multiply(dPosition, MAX_POSITION_DELTA / delta);
}
// desired speed is proportional to displacement by the inverse of timescale
// (for critically damped motion)
newVelocity = Vec3.multiply(dPosition, 1.0 / SPRING_TIMESCALE);
} else {
newVelocity = {
x: 0,
y: 0,
z: 0
};
}
Entities.editEntity(grabbedEntity, { velocity: newVelocity, });
}
// 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,
velocity: newVelocity,
angularVelocity: angularVelocity
});
updateDropLine(targetPosition);
}
}