From b5a28730eac73c5eabe54a582504af0a13e8786d Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 14 May 2015 14:41:32 -0700 Subject: [PATCH 1/8] Filter out low-penetration continuous contact from all entity collision activity including scripts: We don't want sound for these. This appears to avoid the Entities.getEntityProperties() deadlock within entity scripts. Variable names: energyPercentOfFull => energyFactorOfFull, because the range is 0-1, not 0-100. COLLISION_SOUND_COMPRESSION => COLLISION_SOUND_COMPRESSION_RANGE (portion of full dynamic range) Tune constants: COLLISION_ENERGY_AT_FULL_VOLUME 10 => 1 COLLISION_MINIMUM_VOLUME 0.01 => 0.001 COLLISION_SOUND_COMPRESSION_RANGE 0.5 => 0.95 Refer to SoundCache later, after filtering for collision threshold. --- .../src/EntityTreeRenderer.cpp | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3cd5c9a268..158ca17242 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1119,27 +1119,29 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT if (collisionSoundURL.isEmpty()) { return; } - SharedSoundPointer sound = DependencyManager::get().data()->getSound(QUrl(collisionSoundURL)); - if (!sound->isReady()) { - return; - } - const float mass = entity->computeMass(); const float COLLISION_PENTRATION_TO_VELOCITY = 50; // as a subsitute for RELATIVE entity->getVelocity() const float linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = 10.0f; - const float COLLISION_MINIMUM_VOLUME = 0.01f; - const float energyPercentOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); - //qCDebug(entitiesrenderer) << energyPercentOfFull << energy << " " << " " << linearVelocity << " " << mass; - if (energyPercentOfFull < COLLISION_MINIMUM_VOLUME) { + const float COLLISION_ENERGY_AT_FULL_VOLUME = 1.0f; + const float COLLISION_MINIMUM_VOLUME = 0.001f; + const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); + //qCDebug(entitiesrenderer) << collisionSoundURL << energyFactorOfFull << energy << collision.type << "/" << glm::length(collision.penetration); + if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { return; } - // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range 0.5-1.0, if we play it all. - const float COLLISION_SOUND_COMPRESSION = 0.5f; - const float volume = (energyPercentOfFull * COLLISION_SOUND_COMPRESSION) + (1.0f - COLLISION_SOUND_COMPRESSION); - //qCDebug(entitiesrenderer) << collisionSoundURL << " " << volume << " " << position << " " << sound->isStereo(); + + SharedSoundPointer sound = DependencyManager::get().data()->getSound(QUrl(collisionSoundURL)); + if (!sound->isReady()) { + return; + } + + // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all. + const float COLLISION_SOUND_COMPRESSION_RANGE = 0.95f; + float volume = energyFactorOfFull; + volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); + qCDebug(entitiesrenderer) << collisionSoundURL << (volume * 10.0f); // This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored. AudioInjectorOptions options; @@ -1166,13 +1168,19 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons if (!_tree || _shuttingDown) { return; } + // Don't respond to small continuous contacts. It causes deadlocks when locking the entityTree. + // Note that any entity script is likely to Entities.getEntityProperties(), which locks the tree. + const float COLLISION_MINUMUM_PENETRATION = 0.001; + if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { + return; + } // See if we should play sounds EntityTree* entityTree = static_cast(_tree); if (!entityTree->tryLockForRead()) { // I don't know why this can happen, but if it does, // the consequences are a deadlock, so bail. - qCDebug(entitiesrenderer) << "NOTICE: skipping collision."; + qCDebug(entitiesrenderer) << "NOTICE: skipping collision type " << collision.type << " penetration " << glm::length(collision.penetration); return; } const QUuid& myNodeID = DependencyManager::get()->getSessionUUID(); From 7a47e04b9cf1e525b9bf9580ef392dfc38e3a7ea Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 14 May 2015 14:57:18 -0700 Subject: [PATCH 2/8] adjust margin for hulls with small dimensions --- libraries/physics/src/ShapeInfoUtil.cpp | 80 ++++++++++++------------- libraries/physics/src/ShapeInfoUtil.h | 2 + 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/libraries/physics/src/ShapeInfoUtil.cpp b/libraries/physics/src/ShapeInfoUtil.cpp index 768546ddeb..1cccd691c2 100644 --- a/libraries/physics/src/ShapeInfoUtil.cpp +++ b/libraries/physics/src/ShapeInfoUtil.cpp @@ -15,36 +15,48 @@ #include "BulletUtil.h" -// find the average point on a convex shape -glm::vec3 findCenter(const QVector& points) { - glm::vec3 result = glm::vec3(0); - for (int i = 0; i < points.size(); i++) { - result += points[i]; + +btConvexHullShape* ShapeInfoUtil::createConvexHull(const QVector& points) { + assert(points.size() > 0); + + 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 -// to float a bit above what they are sitting on, etc. One option is to call: -// -// compound->setMargin(0.01); -// -// to reduce the size of the margin, but this has some consequences for the -// 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 -// collision hull fairly close to the visual edge of the object. -QVector shrinkByMargin(const QVector& points, const glm::vec3 center, float margin) { - QVector result(points.size()); - for (int i = 0; i < points.size(); i++) { - glm::vec3 pVec = points[ i ] - center; - glm::vec3 pVecNorm = glm::normalize(pVec); - result[ i ] = center + pVec - (pVecNorm * margin); + // Bullet puts "margins" around all the collision shapes. This can cause objects that use ConvexHull shapes + // 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 + // 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. + // This is the strategy we try, but if the object is too small then we start to reduce the margin down to some minimum. + + const float MIN_MARGIN = 0.01f; + glm::vec3 diagonal = maxCorner - minCorner; + float minDimension = glm::min(diagonal[0], diagonal[1]); + minDimension = glm::min(minDimension, diagonal[2]); + margin = glm::min(glm::max(0.5f * minDimension, MIN_MARGIN), margin); + hull->setMargin(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* shape = NULL; switch(info.getType()) { @@ -68,30 +80,14 @@ btCollisionShape* ShapeInfoUtil::createShapeFromInfo(const ShapeInfo& info) { const QVector>& points = info.getPoints(); uint32_t numSubShapes = info.getNumSubShapes(); if (numSubShapes == 1) { - auto hull = new btConvexHullShape(); - const QVector>& points = info.getPoints(); - glm::vec3 center = findCenter(points[0]); - QVector 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; + shape = createConvexHull(info.getPoints()[0]); } else { assert(numSubShapes > 1); auto compound = new btCompoundShape(); btTransform trans; trans.setIdentity(); foreach (QVector hullPoints, points) { - auto hull = new btConvexHullShape(); - glm::vec3 center = findCenter(points[0]); - QVector 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(); + btConvexHullShape* hull = createConvexHull(hullPoints); compound->addChildShape (trans, hull); } shape = compound; diff --git a/libraries/physics/src/ShapeInfoUtil.h b/libraries/physics/src/ShapeInfoUtil.h index 39a897019c..7284cd3550 100644 --- a/libraries/physics/src/ShapeInfoUtil.h +++ b/libraries/physics/src/ShapeInfoUtil.h @@ -22,6 +22,8 @@ // TODO: rename this to ShapeFactory namespace ShapeInfoUtil { + btConvexHullShape* createConvexHull(const QVector& points); + btCollisionShape* createShapeFromInfo(const ShapeInfo& info); }; From 5accb4d0d28a7fd40ba601d70462e6be9510b901 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 14 May 2015 15:12:31 -0700 Subject: [PATCH 3/8] Remove debug logging. --- libraries/entities-renderer/src/EntityTreeRenderer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 158ca17242..49187c3b2f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1127,7 +1127,6 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT const float COLLISION_ENERGY_AT_FULL_VOLUME = 1.0f; const float COLLISION_MINIMUM_VOLUME = 0.001f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); - //qCDebug(entitiesrenderer) << collisionSoundURL << energyFactorOfFull << energy << collision.type << "/" << glm::length(collision.penetration); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { return; } @@ -1141,7 +1140,6 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT const float COLLISION_SOUND_COMPRESSION_RANGE = 0.95f; float volume = energyFactorOfFull; volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); - qCDebug(entitiesrenderer) << collisionSoundURL << (volume * 10.0f); // This is quite similar to AudioScriptingInterface::playSound() and should probably be refactored. AudioInjectorOptions options; From 7a12a4d58e2045d6f8053f28f73215d0a092b31e Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 14 May 2015 17:00:40 -0700 Subject: [PATCH 4/8] added support for both right and left hydras for hydra scripts, and made picking accurate --- examples/controllers/hydra/hydraGrab.js | 8 ++++++-- examples/example/games/hydraGrabHockey.js | 10 +++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/controllers/hydra/hydraGrab.js b/examples/controllers/hydra/hydraGrab.js index 9e49838d88..34484eb9e8 100644 --- a/examples/controllers/hydra/hydraGrab.js +++ b/examples/controllers/hydra/hydraGrab.js @@ -18,6 +18,7 @@ var entityProps, currentPosition, currentVelocity, currentRotation, distanceToTarget, velocityTowardTarget, desiredVelocity; var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; +var LEFT = 0; var RIGHT = 1; var LASER_WIDTH = 3; 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"); function getRayIntersection(pickRay) { - var intersection = Entities.findRayIntersection(pickRay); + var intersection = Entities.findRayIntersection(pickRay, true); return intersection; } @@ -194,7 +195,7 @@ function controller(side) { origin: 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) { this.laserWasHovered = true; if (this.triggerHeld && !this.grabbing) { @@ -286,10 +287,12 @@ function controller(side) { function update(deltaTime) { rightController.update(deltaTime); + leftController.update(deltaTime); } function scriptEnding() { rightController.cleanup(); + leftController.cleanup(); } function vectorIsZero(v) { @@ -297,6 +300,7 @@ function vectorIsZero(v) { } var rightController = new controller(RIGHT); +var leftController = new controller(LEFT); Script.update.connect(update); diff --git a/examples/example/games/hydraGrabHockey.js b/examples/example/games/hydraGrabHockey.js index 9834dbad9f..b9f760fa08 100644 --- a/examples/example/games/hydraGrabHockey.js +++ b/examples/example/games/hydraGrabHockey.js @@ -16,8 +16,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // - var addedVelocity, newVelocity, angularVelocity, dT, cameraEntityDistance; +var LEFT = 0; var RIGHT = 1; var LASER_WIDTH = 3; 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"); function getRayIntersection(pickRay) { - var intersection = Entities.findRayIntersection(pickRay); + var intersection = Entities.findRayIntersection(pickRay, true); return intersection; } + function controller(side) { this.triggerHeld = false; this.triggerThreshold = 0.9; @@ -190,7 +191,7 @@ function controller(side) { origin: 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) { this.laserWasHovered = true; if (this.triggerHeld && !this.grabbing) { @@ -282,10 +283,12 @@ function controller(side) { function update(deltaTime) { rightController.update(deltaTime); + leftController.update(deltaTime); } function scriptEnding() { rightController.cleanup(); + leftController.cleanup(); } function vectorIsZero(v) { @@ -293,6 +296,7 @@ function vectorIsZero(v) { } var rightController = new controller(RIGHT); +var leftController = new controller(LEFT); Script.update.connect(update); From 7be463388bef36ec6d8d79bea9fe7cbcc6b3c9f2 Mon Sep 17 00:00:00 2001 From: Philip Rosedale Date: Thu, 14 May 2015 18:22:30 -0700 Subject: [PATCH 5/8] new paddle models, low friction on field --- examples/example/games/airHockey.js | 48 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/examples/example/games/airHockey.js b/examples/example/games/airHockey.js index 036d6ca5ac..98bab1d57e 100644 --- a/examples/example/games/airHockey.js +++ b/examples/example/games/airHockey.js @@ -19,20 +19,20 @@ var EDGE_THICKESS = 0.10; var EDGE_HEIGHT = 0.10; var DROP_HEIGHT = 0.3; var PUCK_SIZE = 0.15; -var PUCK_THICKNESS = 0.03; -var PADDLE_SIZE = 0.12; -var PADDLE_THICKNESS = 0.03; +var PUCK_THICKNESS = 0.05; +var PADDLE_SIZE = 0.15; +var PADDLE_THICKNESS = 0.05; var GOAL_WIDTH = 0.35; var GRAVITY = -9.8; var LIFETIME = 6000; -var PUCK_DAMPING = 0.03; +var PUCK_DAMPING = 0.02; var PADDLE_DAMPING = 0.35; -var ANGULAR_DAMPING = 0.10; -var PADDLE_ANGULAR_DAMPING = 0.35; +var ANGULAR_DAMPING = 0.4; +var PADDLE_ANGULAR_DAMPING = 0.75; 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"); @@ -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 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 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 edgeRestitution = 0.9; +var floorFriction = 0.01; + var floor = Entities.addEntity( { type: "Box", 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 }, ignoreCollisions: false, locked: true, + friction: floorFriction, visible: debugVisible, lifetime: LIFETIME }); @@ -64,6 +72,7 @@ var edge1 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -76,6 +85,7 @@ var edge2 = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -88,6 +98,7 @@ var edge3a = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -100,6 +111,7 @@ var edge3b = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -112,6 +124,7 @@ var edge4a = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -124,6 +137,7 @@ var edge4b = Entities.addEntity( gravity: { x: 0, y: 0, z: 0 }, ignoreCollisions: false, visible: debugVisible, + restitution: edgeRestitution, locked: true, lifetime: LIFETIME }); @@ -146,8 +160,8 @@ function makeNewProp(which) { if (which == "puck") { return Entities.addEntity( { type: "Model", - modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", - compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", + modelURL: puckModel, + compoundShapeURL: puckCollisionModel, collisionSoundURL: hitSound1, position: Vec3.sum(center, { x: 0, y: DROP_HEIGHT, z: 0 }), dimensions: { x: PUCK_SIZE, y: PUCK_THICKNESS, z: PUCK_SIZE }, @@ -162,13 +176,13 @@ function makeNewProp(which) { else if (which == "paddle1") { return Entities.addEntity( { type: "Model", - modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", - compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", + modelURL: paddleModel, + compoundShapeURL: paddleCollisionModel, 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 }, 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, damping: PADDLE_DAMPING, angularDamping: PADDLE_ANGULAR_DAMPING, @@ -178,13 +192,13 @@ function makeNewProp(which) { else if (which == "paddle2") { return Entities.addEntity( { type: "Model", - modelURL: "http://headache.hungry.com/~seth/hifi/puck.obj", - compoundShapeURL: "http://headache.hungry.com/~seth/hifi/puck.obj", + modelURL: paddleModel, + compoundShapeURL: paddleCollisionModel, 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 }, 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, damping: PADDLE_DAMPING, angularDamping: PADDLE_ANGULAR_DAMPING, From 1161f33b4502cbf51722a851a610878abf43b7f5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 14 May 2015 20:25:29 -0700 Subject: [PATCH 6/8] laser-poiner script for mouse --- examples/pointer.js | 76 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 examples/pointer.js diff --git a/examples/pointer.js b/examples/pointer.js new file mode 100644 index 0000000000..2d7b601baa --- /dev/null +++ b/examples/pointer.js @@ -0,0 +1,76 @@ + +var lineEntityID = null; +var lineIsRezzed = false; + + +function nearLinePoint(targetPosition) { + var handPosition = MyAvatar.getRightPalmPosition(); + var along = Vec3.subtract(targetPosition, handPosition); + along = Vec3.normalize(along); + along = Vec3.multiply(along, 0.4); + return Vec3.sum(handPosition, along); +} + + +function removeLine() { + if (lineIsRezzed) { + Entities.deleteEntity(lineEntityID); + lineEntityID = null; + lineIsRezzed = false; + } +} + + +function createOrUpdateLine(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + var intersection = Entities.findRayIntersection(pickRay, true); // accurate picking + + if (lineIsRezzed) { + Entities.editEntity(lineEntityID, { + position: nearLinePoint(intersection.intersection), + dimensions: Vec3.subtract(intersection.intersection, nearLinePoint(intersection.intersection)), + lifetime: 30 // renew lifetime + }); + + } else { + lineIsRezzed = true; + lineEntityID = Entities.addEntity({ + type: "Line", + position: nearLinePoint(intersection.intersection), + dimensions: Vec3.subtract(intersection.intersection, nearLinePoint(intersection.intersection)), + color: { red: 255, green: 255, blue: 255 }, + lifetime: 30 // if someone crashes while pointing, don't leave the line there forever. + }); + } +} + + +function mousePressEvent(event) { + if (!event.isLeftButton) { + return; + } + if (lineIsRezzed) { + return; + } + createOrUpdateLine(event); + if (lineIsRezzed) { + Controller.mouseMoveEvent.connect(mouseMoveEvent); + } + } + + +function mouseMoveEvent(event) { + createOrUpdateLine(event); +} + + +function mouseReleaseEvent() { + if (lineIsRezzed) { + Controller.mouseMoveEvent.disconnect(mouseMoveEvent); + } + removeLine(); +} + + +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); From e462617a6b39434fab68effbafb666e7f1aa001a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 15 May 2015 10:29:24 -0700 Subject: [PATCH 7/8] add error log debug for script fail --- libraries/script-engine/src/ScriptCache.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 5025d02918..8f854cfdc3 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -35,7 +36,7 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is isPending = true; bool alreadyWaiting = _scriptUsers.contains(url); _scriptUsers.insert(url, scriptUser); - + if (alreadyWaiting) { qCDebug(scriptengine) << "Already downloading script at:" << url.toString(); } else { @@ -43,7 +44,7 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); - qCDebug(scriptengine) << "Downloading script at:" << url.toString(); + qCDebug(scriptengine) << "Downloading script at" << url.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &ScriptCache::scriptDownloaded); } @@ -56,7 +57,7 @@ void ScriptCache::scriptDownloaded() { QUrl url = reply->url(); QList scriptUsers = _scriptUsers.values(url); _scriptUsers.remove(url); - + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) { _scriptCache[url] = reply->readAll(); qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); @@ -65,7 +66,9 @@ void ScriptCache::scriptDownloaded() { user->scriptContentsAvailable(url, _scriptCache[url]); } } else { - qCDebug(scriptengine) << "ERROR Loading file:" << reply->url().toString(); + qCWarning(scriptengine) << "Error loading script from URL " << reply->url().toString() + << "- HTTP status code is" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() + << "and error from QNetworkReply is" << reply->errorString(); foreach(ScriptUser* user, scriptUsers) { user->errorInLoadingScript(url); } @@ -73,4 +76,4 @@ void ScriptCache::scriptDownloaded() { reply->deleteLater(); } - + From 9f7a56e3e54dd7bb2355c250036220c23cdc5cd2 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Fri, 15 May 2015 11:00:28 -0700 Subject: [PATCH 8/8] Ask audio injector to delete itself. Put in null guards. Tune parameters (including higher threshold). --- .../src/EntityTreeRenderer.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 49187c3b2f..1d888bd7da 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -1111,6 +1111,9 @@ void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityTree* entityTree, const EntityItemID& id, const Collision& collision) { EntityItem* entity = entityTree->findEntityByEntityItemID(id); + if (!entity) { + return; + } QUuid simulatorID = entity->getSimulatorID(); if (simulatorID.isNull() || (simulatorID != myNodeID)) { return; // Only one injector per simulation, please. @@ -1124,20 +1127,24 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT const float linearVelocity = glm::length(collision.penetration) * COLLISION_PENTRATION_TO_VELOCITY; const float energy = mass * linearVelocity * linearVelocity / 2.0f; const glm::vec3 position = collision.contactPoint; - const float COLLISION_ENERGY_AT_FULL_VOLUME = 1.0f; + const float COLLISION_ENERGY_AT_FULL_VOLUME = 0.5f; const float COLLISION_MINIMUM_VOLUME = 0.001f; const float energyFactorOfFull = fmin(1.0f, energy / COLLISION_ENERGY_AT_FULL_VOLUME); if (energyFactorOfFull < COLLISION_MINIMUM_VOLUME) { return; } - SharedSoundPointer sound = DependencyManager::get().data()->getSound(QUrl(collisionSoundURL)); - if (!sound->isReady()) { + auto soundCache = DependencyManager::get(); + if (soundCache.isNull()) { + return; + } + SharedSoundPointer sound = soundCache.data()->getSound(QUrl(collisionSoundURL)); + if (sound.isNull() || !sound->isReady()) { return; } // This is a hack. Quiet sound aren't really heard at all, so we compress everything to the range [1-c, 1], if we play it all. - const float COLLISION_SOUND_COMPRESSION_RANGE = 0.95f; + const float COLLISION_SOUND_COMPRESSION_RANGE = 0.7f; float volume = energyFactorOfFull; volume = (volume * COLLISION_SOUND_COMPRESSION_RANGE) + (1.0f - COLLISION_SOUND_COMPRESSION_RANGE); @@ -1148,6 +1155,7 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT options.volume = volume; AudioInjector* injector = new AudioInjector(sound.data(), options); injector->setLocalAudioInterface(_localAudioInterface); + injector->triggerDeleteAfterFinish(); QThread* injectorThread = new QThread(); injectorThread->setObjectName("Audio Injector Thread"); injector->moveToThread(injectorThread); @@ -1168,7 +1176,7 @@ void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, cons } // Don't respond to small continuous contacts. It causes deadlocks when locking the entityTree. // Note that any entity script is likely to Entities.getEntityProperties(), which locks the tree. - const float COLLISION_MINUMUM_PENETRATION = 0.001; + const float COLLISION_MINUMUM_PENETRATION = 0.005; if ((collision.type != CONTACT_EVENT_TYPE_START) && (glm::length(collision.penetration) < COLLISION_MINUMUM_PENETRATION)) { return; }