Merge branch 'master' of https://github.com/highfidelity/hifi into collision-sound-tuning

This commit is contained in:
Howard Stearns 2015-05-15 10:25:03 -07:00
commit 3600a5def8
5 changed files with 84 additions and 64 deletions

View file

@ -18,6 +18,7 @@
var entityProps, currentPosition, currentVelocity, currentRotation, distanceToTarget, velocityTowardTarget, desiredVelocity; var entityProps, currentPosition, currentVelocity, currentRotation, distanceToTarget, velocityTowardTarget, desiredVelocity;
var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance;
var LEFT = 0;
var RIGHT = 1; var RIGHT = 1;
var LASER_WIDTH = 3; var LASER_WIDTH = 3;
var LASER_COLOR = { var LASER_COLOR = {
@ -50,7 +51,7 @@ var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/s
var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
function getRayIntersection(pickRay) { function getRayIntersection(pickRay) {
var intersection = Entities.findRayIntersection(pickRay); var intersection = Entities.findRayIntersection(pickRay, true);
return intersection; return intersection;
} }
@ -194,7 +195,7 @@ function controller(side) {
origin: this.palmPosition, origin: this.palmPosition,
direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition))
}; };
var intersection = getRayIntersection(pickRay); var intersection = getRayIntersection(pickRay, true);
if (intersection.intersects && intersection.properties.collisionsWillMove) { if (intersection.intersects && intersection.properties.collisionsWillMove) {
this.laserWasHovered = true; this.laserWasHovered = true;
if (this.triggerHeld && !this.grabbing) { if (this.triggerHeld && !this.grabbing) {
@ -286,10 +287,12 @@ function controller(side) {
function update(deltaTime) { function update(deltaTime) {
rightController.update(deltaTime); rightController.update(deltaTime);
leftController.update(deltaTime);
} }
function scriptEnding() { function scriptEnding() {
rightController.cleanup(); rightController.cleanup();
leftController.cleanup();
} }
function vectorIsZero(v) { function vectorIsZero(v) {
@ -297,6 +300,7 @@ function vectorIsZero(v) {
} }
var rightController = new controller(RIGHT); var rightController = new controller(RIGHT);
var leftController = new controller(LEFT);
Script.update.connect(update); Script.update.connect(update);

View file

@ -19,20 +19,20 @@ var EDGE_THICKESS = 0.10;
var EDGE_HEIGHT = 0.10; var EDGE_HEIGHT = 0.10;
var DROP_HEIGHT = 0.3; var DROP_HEIGHT = 0.3;
var PUCK_SIZE = 0.15; var PUCK_SIZE = 0.15;
var PUCK_THICKNESS = 0.03; var PUCK_THICKNESS = 0.05;
var PADDLE_SIZE = 0.12; var PADDLE_SIZE = 0.15;
var PADDLE_THICKNESS = 0.03; var PADDLE_THICKNESS = 0.05;
var GOAL_WIDTH = 0.35; var GOAL_WIDTH = 0.35;
var GRAVITY = -9.8; var GRAVITY = -9.8;
var LIFETIME = 6000; var LIFETIME = 6000;
var PUCK_DAMPING = 0.03; var PUCK_DAMPING = 0.02;
var PADDLE_DAMPING = 0.35; var PADDLE_DAMPING = 0.35;
var ANGULAR_DAMPING = 0.10; var ANGULAR_DAMPING = 0.4;
var PADDLE_ANGULAR_DAMPING = 0.35; var PADDLE_ANGULAR_DAMPING = 0.75;
var MODEL_SCALE = 1.52; var MODEL_SCALE = 1.52;
var MODEL_OFFSET = { x: 0, y: -0.18, z: 0 }; var MODEL_OFFSET = { x: 0, y: -0.19, z: 0 };
var scoreSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_score.wav"); var scoreSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_score.wav");
@ -41,9 +41,16 @@ var normalTable = "https://hifi-public.s3.amazonaws.com/ozan/props/airHockeyTabl
var hitSound1 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav" var hitSound1 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav"
var hitSound2 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit2.wav" var hitSound2 = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit2.wav"
var hitSideSound = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit3.wav" var hitSideSound = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit3.wav"
var puckModel = "https://hifi-public.s3.amazonaws.com/ozan/props/airHockeyTable/airHockeyPuck.fbx"
var puckCollisionModel = "http://headache.hungry.com/~seth/hifi/airHockeyPuck-hull.obj"
var paddleModel = "https://hifi-public.s3.amazonaws.com/ozan/props/airHockeyTable/airHockeyPaddle.obj"
var paddleCollisionModel = "http://headache.hungry.com/~seth/hifi/paddle-hull.obj"
var center = Vec3.sum(MyAvatar.position, Vec3.multiply((FIELD_WIDTH + FIELD_LENGTH) * 0.60, Quat.getFront(Camera.getOrientation()))); var center = Vec3.sum(MyAvatar.position, Vec3.multiply((FIELD_WIDTH + FIELD_LENGTH) * 0.60, Quat.getFront(Camera.getOrientation())));
var edgeRestitution = 0.9;
var floorFriction = 0.01;
var floor = Entities.addEntity( var floor = Entities.addEntity(
{ type: "Box", { type: "Box",
position: Vec3.subtract(center, { x: 0, y: 0, z: 0 }), position: Vec3.subtract(center, { x: 0, y: 0, z: 0 }),
@ -52,6 +59,7 @@ var floor = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
locked: true, locked: true,
friction: floorFriction,
visible: debugVisible, visible: debugVisible,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -64,6 +72,7 @@ var edge1 = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -76,6 +85,7 @@ var edge2 = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -88,6 +98,7 @@ var edge3a = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -100,6 +111,7 @@ var edge3b = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -112,6 +124,7 @@ var edge4a = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -124,6 +137,7 @@ var edge4b = Entities.addEntity(
gravity: { x: 0, y: 0, z: 0 }, gravity: { x: 0, y: 0, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
visible: debugVisible, visible: debugVisible,
restitution: edgeRestitution,
locked: true, locked: true,
lifetime: LIFETIME }); lifetime: LIFETIME });
@ -146,8 +160,8 @@ function makeNewProp(which) {
if (which == "puck") { if (which == "puck") {
return Entities.addEntity( return Entities.addEntity(
{ type: "Model", { type: "Model",
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", modelURL: puckModel,
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", compoundShapeURL: puckCollisionModel,
collisionSoundURL: hitSound1, collisionSoundURL: hitSound1,
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: 0 }), position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: 0 }),
dimensions: { x: PUCK_SIZE, y: PUCK_THICKNESS, z: PUCK_SIZE }, dimensions: { x: PUCK_SIZE, y: PUCK_THICKNESS, z: PUCK_SIZE },
@ -162,13 +176,13 @@ function makeNewProp(which) {
else if (which == "paddle1") { else if (which == "paddle1") {
return Entities.addEntity( return Entities.addEntity(
{ type: "Model", { type: "Model",
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", modelURL: paddleModel,
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", compoundShapeURL: paddleCollisionModel,
collisionSoundURL: hitSound2, collisionSoundURL: hitSound2,
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: FIELD_LENGTH * 0.35 }), position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT * 1.5, z: FIELD_LENGTH * 0.35 }),
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE }, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE },
gravity: { x: 0, y: GRAVITY, z: 0 }, gravity: { x: 0, y: GRAVITY, z: 0 },
velocity: { x: 0, y: 0.05, z: 0 }, velocity: { x: 0, y: 0.07, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
damping: PADDLE_DAMPING, damping: PADDLE_DAMPING,
angularDamping: PADDLE_ANGULAR_DAMPING, angularDamping: PADDLE_ANGULAR_DAMPING,
@ -178,13 +192,13 @@ function makeNewProp(which) {
else if (which == "paddle2") { else if (which == "paddle2") {
return Entities.addEntity( return Entities.addEntity(
{ type: "Model", { type: "Model",
modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", modelURL: paddleModel,
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", compoundShapeURL: paddleCollisionModel,
collisionSoundURL: hitSound2, collisionSoundURL: hitSound2,
position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: -FIELD_LENGTH * 0.35 }), position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT * 1.5, z: -FIELD_LENGTH * 0.35 }),
dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE }, dimensions: { x: PADDLE_SIZE, y: PADDLE_THICKNESS, z: PADDLE_SIZE },
gravity: { x: 0, y: GRAVITY, z: 0 }, gravity: { x: 0, y: GRAVITY, z: 0 },
velocity: { x: 0, y: 0.05, z: 0 }, velocity: { x: 0, y: 0.07, z: 0 },
ignoreCollisions: false, ignoreCollisions: false,
damping: PADDLE_DAMPING, damping: PADDLE_DAMPING,
angularDamping: PADDLE_ANGULAR_DAMPING, angularDamping: PADDLE_ANGULAR_DAMPING,

View file

@ -16,8 +16,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance;
var LEFT = 0;
var RIGHT = 1; var RIGHT = 1;
var LASER_WIDTH = 3; var LASER_WIDTH = 3;
var LASER_COLOR = { var LASER_COLOR = {
@ -50,10 +50,11 @@ var grabSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/s
var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav"); var releaseSound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/eric/sounds/ReleaseClamp.wav");
function getRayIntersection(pickRay) { function getRayIntersection(pickRay) {
var intersection = Entities.findRayIntersection(pickRay); var intersection = Entities.findRayIntersection(pickRay, true);
return intersection; return intersection;
} }
function controller(side) { function controller(side) {
this.triggerHeld = false; this.triggerHeld = false;
this.triggerThreshold = 0.9; this.triggerThreshold = 0.9;
@ -190,7 +191,7 @@ function controller(side) {
origin: this.palmPosition, origin: this.palmPosition,
direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition))
}; };
var intersection = getRayIntersection(pickRay); var intersection = getRayIntersection(pickRay, true);
if (intersection.intersects && intersection.properties.collisionsWillMove) { if (intersection.intersects && intersection.properties.collisionsWillMove) {
this.laserWasHovered = true; this.laserWasHovered = true;
if (this.triggerHeld && !this.grabbing) { if (this.triggerHeld && !this.grabbing) {
@ -282,10 +283,12 @@ function controller(side) {
function update(deltaTime) { function update(deltaTime) {
rightController.update(deltaTime); rightController.update(deltaTime);
leftController.update(deltaTime);
} }
function scriptEnding() { function scriptEnding() {
rightController.cleanup(); rightController.cleanup();
leftController.cleanup();
} }
function vectorIsZero(v) { function vectorIsZero(v) {
@ -293,6 +296,7 @@ function vectorIsZero(v) {
} }
var rightController = new controller(RIGHT); var rightController = new controller(RIGHT);
var leftController = new controller(LEFT);
Script.update.connect(update); Script.update.connect(update);

View file

@ -15,36 +15,48 @@
#include "BulletUtil.h" #include "BulletUtil.h"
// find the average point on a convex shape
glm::vec3 findCenter(const QVector<glm::vec3>& points) { btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector<glm::vec3>& points) {
glm::vec3 result = glm::vec3(0); assert(points.size() > 0);
for (int i = 0; i < points.size(); i++) {
result += points[i]; btConvexHullShape* hull = new btConvexHullShape();
glm::vec3 center = points[0];
glm::vec3 maxCorner = center;
glm::vec3 minCorner = center;
for (int i = 1; i < points.size(); i++) {
center += points[i];
maxCorner = glm::max(maxCorner, points[i]);
minCorner = glm::min(minCorner, points[i]);
} }
return result * (1.0f / points.size()); center /= (float)(points.size());
}
float margin = hull->getMargin();
// bullet puts "margins" around all the collision shapes. This can cause shapes will hulls // Bullet puts "margins" around all the collision shapes. This can cause objects that use ConvexHull shapes
// to float a bit above what they are sitting on, etc. One option is to call: // to have visible gaps between them and the surface they touch. One option is to reduce the size of the margin
// // but this can reduce the performance and stability of the simulation (e.g. the GJK algorithm will fail to provide
// compound->setMargin(0.01); // nearest contact points and narrow-phase collisions will fall into more expensive code paths). Alternatively
// // one can shift the geometry of the shape to make the margin surface approximately close to the visible surface.
// to reduce the size of the margin, but this has some consequences for the // This is the strategy we try, but if the object is too small then we start to reduce the margin down to some minimum.
// performance and stability of the simulation. Instead, we clench in all the points of
// the hull by the margin. These clenched points + bullets margin will but the actual const float MIN_MARGIN = 0.01f;
// collision hull fairly close to the visual edge of the object. glm::vec3 diagonal = maxCorner - minCorner;
QVector<glm::vec3> shrinkByMargin(const QVector<glm::vec3>& points, const glm::vec3 center, float margin) { float minDimension = glm::min(diagonal[0], diagonal[1]);
QVector<glm::vec3> result(points.size()); minDimension = glm::min(minDimension, diagonal[2]);
for (int i = 0; i < points.size(); i++) { margin = glm::min(glm::max(0.5f * minDimension, MIN_MARGIN), margin);
glm::vec3 pVec = points[ i ] - center; hull->setMargin(margin);
glm::vec3 pVecNorm = glm::normalize(pVec);
result[ i ] = center + pVec - (pVecNorm * margin); // add the points, correcting for margin
glm::vec3 relativeScale = (diagonal - glm::vec3(2.0f * margin)) / diagonal;
glm::vec3 correctedPoint;
for (int i = 0; i < points.size(); ++i) {
correctedPoint = (points[i] - center) * relativeScale + center;
hull->addPoint(btVector3(correctedPoint[0], correctedPoint[1], correctedPoint[2]), false);
} }
return result; hull->recalcLocalAabb();
return hull;
} }
btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
btCollisionShape* shape = NULL; btCollisionShape* shape = NULL;
switch(info.getType()) { switch(info.getType()) {
@ -68,30 +80,14 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) {
const QVector<QVector<glm::vec3>>& points = info.getPoints(); const QVector<QVector<glm::vec3>>& points = info.getPoints();
uint32_t numSubShapes = info.getNumSubShapes(); uint32_t numSubShapes = info.getNumSubShapes();
if (numSubShapes == 1) { if (numSubShapes == 1) {
auto hull = new btConvexHullShape(); shape = createConvexHull(info.getPoints()[0]);
const QVector<QVector<glm::vec3>>& points = info.getPoints();
glm::vec3 center = findCenter(points[0]);
QVector<glm::vec3> shrunken = shrinkByMargin(points[0], center, hull->getMargin());
foreach (glm::vec3 point, shrunken) {
btVector3 btPoint(point[0], point[1], point[2]);
hull->addPoint(btPoint, false);
}
hull->recalcLocalAabb();
shape = hull;
} else { } else {
assert(numSubShapes > 1); assert(numSubShapes > 1);
auto compound = new btCompoundShape(); auto compound = new btCompoundShape();
btTransform trans; btTransform trans;
trans.setIdentity(); trans.setIdentity();
foreach (QVector<glm::vec3> hullPoints, points) { foreach (QVector<glm::vec3> hullPoints, points) {
auto hull = new btConvexHullShape(); btConvexHullShape* hull = createConvexHull(hullPoints);
glm::vec3 center = findCenter(points[0]);
QVector<glm::vec3> shrunken = shrinkByMargin(hullPoints, center, hull->getMargin());
foreach (glm::vec3 point, shrunken) {
btVector3 btPoint(point[0], point[1], point[2]);
hull->addPoint(btPoint, false);
}
hull->recalcLocalAabb();
compound->addChildShape (trans, hull); compound->addChildShape (trans, hull);
} }
shape = compound; shape = compound;

View file

@ -22,6 +22,8 @@
// TODO: rename this to ShapeFactory // TODO: rename this to ShapeFactory
namespace ShapeInfoUtil { namespace ShapeInfoUtil {
btConvexHullShape* createConvexHull(const QVector<glm::vec3>& points);
btCollisionShape* createShapeFromInfo(const ShapeInfo& info); btCollisionShape* createShapeFromInfo(const ShapeInfo& info);
}; };