From b953d880a3d57ef3d834cdcb7a3a28df06a9a7e8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 5 Sep 2014 14:41:18 -0700 Subject: [PATCH 01/45] minor optimization on CubeList and VoxelPool --- interface/src/avatar/VoxelShapeManager.h | 2 +- libraries/octree/src/Octree.cpp | 11 ++++++----- libraries/octree/src/Octree.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/VoxelShapeManager.h b/interface/src/avatar/VoxelShapeManager.h index 1b7179788d..8ca930f71b 100644 --- a/interface/src/avatar/VoxelShapeManager.h +++ b/interface/src/avatar/VoxelShapeManager.h @@ -28,7 +28,7 @@ public: AACubeShape* _shape; }; -typedef QHash VoxelPool; +typedef QHash VoxelPool; class VoxelShapeManager : public PhysicsEntity { public: diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3611605515..7852b6532f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -793,7 +793,7 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { return false; } -quint64 cubeListHashKey(const glm::vec3& point) { +uint qHash(const glm::vec3& point) { // NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits), // so each component (26 bits) uses more than its alloted 21 bits. // however we don't expect to span huge cubes so it is ok if we wrap @@ -801,9 +801,9 @@ quint64 cubeListHashKey(const glm::vec3& point) { const uint BITS_PER_COMPONENT = 21; const quint64 MAX_SCALED_COMPONENT = 2097152; // 2^21 const float RESOLUTION_PER_METER = 1024.0f; // 2^10 - return (quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT + + return qHash((quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT + (((quint64)(point.y * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << BITS_PER_COMPONENT) + - (((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT); + (((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT)); } bool findContentInCubeOp(OctreeElement* element, void* extraData) { @@ -819,8 +819,9 @@ bool findContentInCubeOp(OctreeElement* element, void* extraData) { return true; // recurse on children } if (element->hasContent()) { - // NOTE: the voxel's center is unique so we use it as the input for the key - args->cubes->insert(cubeListHashKey(cube.calcCenter()), cube); + // NOTE: the voxel's center is unique so we use it as the input for the key. + // We use the qHash(glm::vec()) as the key as an optimization for the code that uses CubeLists. + args->cubes->insert(qHash(cube.calcCenter()), cube); return true; } return false; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index f58b5f0cbd..05d061035a 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -48,7 +48,7 @@ public: // Callback function, for recuseTreeWithOperation typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData); typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; -typedef QHash CubeList; +typedef QHash CubeList; const bool NO_EXISTS_BITS = false; const bool WANT_EXISTS_BITS = true; From da740e8a88c08bdd295dab4b1d3c505e780d807d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 8 Sep 2014 15:31:53 -0700 Subject: [PATCH 02/45] First cut at radio.js --- examples/radio.js | 79 ++++++++++++++++++++++ libraries/audio/src/AudioInjectorOptions.h | 2 + 2 files changed, 81 insertions(+) create mode 100644 examples/radio.js diff --git a/examples/radio.js b/examples/radio.js new file mode 100644 index 0000000000..51233f5bb4 --- /dev/null +++ b/examples/radio.js @@ -0,0 +1,79 @@ +// +// Radio.js +// examples +// +// Created by Clément Brisset on 8/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var position = { x:1, y: 1, z: 20 }; +var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); +var scale = 1.0; + +var modelURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/entities/radio/Speakers2Finished.fbx"; +var soundURL = "https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/FamilyStereo.raw"; + +var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); +var audioOptions = new AudioInjectionOptions(); +audioOptions.volume = 0.7; +audioOptions.position = position; +//audioOptions.orientation = Quat.multiply(AudioRotationOffset, rotation); +//audioOptions.loop = true; +audioOptions.isStereo = true; +var injector = null; + +var sound = new Sound(soundURL); + +var entity = null; +var properties = null; + +function update() { + if (entity === null) { + if (sound.downloaded) { + print("Sound file downloaded"); + entity = Entities.addEntity({ + type: "Model", + position: position, + rotation: rotation, + radius: scale / 2.0, + modelURL: modelURL + }); + properties = Entities.getEntityProperties(entity); + + injector = Audio.playSound(sound, audioOptions); + } + } else { + var newProperties = Entities.getEntityProperties(entity); + if (newProperties.type === "Model") { + if (newProperties.position != properties.position) { + + } + + + + + + } else { + entity = null; + Script.update.disconnect(update); + Script.scriptEnding.connect(scriptEnding); + scriptEnding(); + } + } +} + +function scriptEnding() { + if (entity != null) { + Entities.deleteEntity(entity); + } + if (injector != null) { + injector.stop(); + } +} + +Script.update.connect(update); +Script.scriptEnding.connect(scriptEnding); + diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index e1beb3e689..66e8f1550b 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -24,9 +24,11 @@ class AudioInjectorOptions : public QObject { Q_OBJECT + Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition) Q_PROPERTY(float volume READ getVolume WRITE setVolume) Q_PROPERTY(bool loop READ getLoop WRITE setLoop) + Q_PROPERTY(bool isStereo READ isStereo WRITE setIsStereo) public: AudioInjectorOptions(QObject* parent = 0); AudioInjectorOptions(const AudioInjectorOptions& other); From 4da1ca22ba17779baeaaff546cac742f2ea5d929 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 9 Sep 2014 17:35:06 -0700 Subject: [PATCH 03/45] Add RayIntersectionInfo and use for shape-vs-ray --- interface/src/MetavoxelSystem.cpp | 6 +- interface/src/MetavoxelSystem.h | 4 +- interface/src/avatar/Avatar.cpp | 18 +- interface/src/avatar/Avatar.h | 2 +- libraries/shared/src/AACubeShape.cpp | 3 +- libraries/shared/src/AACubeShape.h | 2 +- libraries/shared/src/CapsuleShape.cpp | 10 +- libraries/shared/src/CapsuleShape.h | 2 +- libraries/shared/src/PhysicsEntity.cpp | 19 +- libraries/shared/src/PhysicsEntity.h | 3 +- libraries/shared/src/PlaneShape.cpp | 18 +- libraries/shared/src/PlaneShape.h | 2 +- libraries/shared/src/RayIntersectionInfo.h | 35 +++ libraries/shared/src/Shape.h | 4 +- libraries/shared/src/ShapeCollider.cpp | 16 +- libraries/shared/src/ShapeCollider.h | 7 +- libraries/shared/src/SphereShape.cpp | 18 +- libraries/shared/src/SphereShape.h | 2 +- tests/physics/src/ShapeColliderTests.cpp | 248 +++++++++++---------- 19 files changed, 222 insertions(+), 197 deletions(-) create mode 100644 libraries/shared/src/RayIntersectionInfo.h diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 892efe7211..970b53274e 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -2468,9 +2468,9 @@ void StaticModelRenderer::renderUnclipped(float alpha, Mode mode) { _model->render(alpha); } -bool StaticModelRenderer::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const { - return _model->findRayIntersection(origin, direction, distance); +bool StaticModelRenderer::findRayIntersection(RayIntersectionInfo& intersection, + const glm::vec3& clipMinimum, float clipSize) const { + return _model->findRayIntersection(intersection); } void StaticModelRenderer::applyTranslation(const glm::vec3& translation) { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index f3b9a02117..ab1f8b32fc 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -392,8 +392,8 @@ public: virtual void init(Spanner* spanner); virtual void simulate(float deltaTime); - virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - const glm::vec3& clipMinimum, float clipSize, float& distance) const; + virtual bool findRayIntersection(RayIntersectionInfo& intersection, + const glm::vec3& clipMinimum, float clipSize) const; protected: diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index b5b238b9d7..b33b8884e3 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -708,20 +708,10 @@ void Avatar::renderDisplayName() { glEnable(GL_LIGHTING); } -bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - float minDistance = FLT_MAX; - float modelDistance; - if (_skeletonModel.findRayIntersection(origin, direction, modelDistance)) { - minDistance = qMin(minDistance, modelDistance); - } - if (getHead()->getFaceModel().findRayIntersection(origin, direction, modelDistance)) { - minDistance = qMin(minDistance, modelDistance); - } - if (minDistance < FLT_MAX) { - distance = minDistance; - return true; - } - return false; +bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const { + bool hit = _skeletonModel.findRayIntersection(intersection); + hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit; + return hit; } bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 43b5150a48..0385456372 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -99,7 +99,7 @@ public: /// Returns the distance to use as a LOD parameter. float getLODDistance() const; - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; /// \param shapes list of shapes to collide against avatar /// \param collisions list to store collision results diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp index 70477d5682..16db9fc510 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/shared/src/AACubeShape.cpp @@ -11,6 +11,7 @@ #include "AACubeShape.h" -bool AACubeShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { +bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { + // TODO: Andrew to implement this return false; } diff --git a/libraries/shared/src/AACubeShape.h b/libraries/shared/src/AACubeShape.h index 96010926c7..4b834aa1bf 100644 --- a/libraries/shared/src/AACubeShape.h +++ b/libraries/shared/src/AACubeShape.h @@ -25,7 +25,7 @@ public: float getScale() const { return _scale; } void setScale(float scale) { _scale = scale; } - bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; float getVolume() const { return _scale * _scale * _scale; } diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 09776a233f..f6bad6d5d1 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -78,13 +78,9 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en updateBoundingRadius(); } -bool CapsuleShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { - glm::vec3 capsuleStart, capsuleEnd; - getStartPoint(capsuleStart); - getEndPoint(capsuleEnd); - // NOTE: findRayCapsuleIntersection returns 'true' with distance = 0 when rayStart is inside capsule. - // TODO: implement the raycast to return inside surface intersection for the internal rayStart. - return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, _radius, distance); +bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const { + // TODO: Andrew to implement this + return false; } // static diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 8d84e32a97..423aa850ba 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -47,7 +47,7 @@ public: /// Sets the endpoints and updates center, rotation, and halfHeight to agree. virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); - bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); } diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 6be37a7528..a01706f539 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -76,23 +76,8 @@ void PhysicsEntity::clearShapes() { _shapes.clear(); } -bool PhysicsEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - int numShapes = _shapes.size(); - float minDistance = FLT_MAX; - for (int j = 0; j < numShapes; ++j) { - const Shape* shape = _shapes[j]; - float thisDistance = FLT_MAX; - if (shape && shape->findRayIntersection(origin, direction, thisDistance)) { - if (thisDistance < minDistance) { - minDistance = thisDistance; - } - } - } - if (minDistance < FLT_MAX) { - distance = minDistance; - return true; - } - return false; +bool PhysicsEntity::findRayIntersection(RayIntersectionInfo& intersection) const { + return ShapeCollider::findRayIntersection(_shapes, intersection); } bool PhysicsEntity::findCollisions(const QVector shapes, CollisionList& collisions) { diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index 9f98cc96ca..a96754b75c 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -19,6 +19,7 @@ #include #include "CollisionInfo.h" +#include "RayIntersectionInfo.h" class Shape; class PhysicsSimulation; @@ -52,7 +53,7 @@ public: PhysicsSimulation* getSimulation() const { return _simulation; } - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; bool findCollisions(const QVector shapes, CollisionList& collisions); bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions); bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index 72704c3116..d302bcb656 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -39,17 +39,23 @@ glm::vec4 PlaneShape::getCoefficients() const { return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _translation)); } -bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { +bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { glm::vec3 n = getNormal(); - float denominator = glm::dot(n, rayDirection); + float denominator = glm::dot(n, intersection._rayDirection); if (fabsf(denominator) < EPSILON) { // line is parallel to plane - return glm::dot(_translation - rayStart, n) < EPSILON; + if (glm::dot(_translation - intersection._rayStart, n) < EPSILON) { + // ray starts on the plane + intersection._hitDistance = 0.0f; + intersection._hitNormal = n; + return true; + } } else { - float d = glm::dot(_translation - rayStart, n) / denominator; - if (d > 0.0f) { + float d = glm::dot(_translation - intersection._rayStart, n) / denominator; + if (d > 0.0f && d < intersection._rayLength && intersection._hitDistance) { // ray points toward plane - distance = d; + intersection._hitDistance = d; + intersection._hitNormal = n; return true; } } diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index b8a93324b7..a2e4a3799d 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -21,7 +21,7 @@ public: glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; - bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; }; #endif // hifi_PlaneShape_h diff --git a/libraries/shared/src/RayIntersectionInfo.h b/libraries/shared/src/RayIntersectionInfo.h new file mode 100644 index 0000000000..2d24282b5d --- /dev/null +++ b/libraries/shared/src/RayIntersectionInfo.h @@ -0,0 +1,35 @@ +// +// RayIntersectionInfo.h +// interface/src/avatar +// +// Created by Andrew Meadows 2014.09.09 +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RayIntersectionInfo_h +#define hifi_RayIntersectionInfo_h + +#include + +class Shape; + +class RayIntersectionInfo { +public: + RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX), + _hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { } + + // input + glm::vec3 _rayStart; + glm::vec3 _rayDirection; + float _rayLength; + + // output + float _hitDistance; + glm::vec3 _hitNormal; + Shape* _hitShape; +}; + +#endif // hifi_RayIntersectionInfo_h diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 345d69d8e4..4b85234eb3 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -17,6 +17,8 @@ #include #include +#include "RayIntersectionInfo.h" + class PhysicsEntity; class VerletPoint; @@ -59,7 +61,7 @@ public: virtual void setMass(float mass) { _mass = mass; } virtual float getMass() const { return _mass; } - virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; + virtual bool findRayIntersection(RayIntersectionInfo& intersection) const = 0; /// \param penetration of collision /// \param contactPoint of collision diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 259b7c9118..be3b086776 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -1087,24 +1087,18 @@ bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCe return sphereVsAACubeLegacy(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } -bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& minDistance) { - float hitDistance = FLT_MAX; +bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection) { int numShapes = shapes.size(); + bool hit = false; for (int i = 0; i < numShapes; ++i) { Shape* shape = shapes.at(i); if (shape) { - float distance; - if (shape->findRayIntersection(rayStart, rayDirection, distance)) { - if (distance < hitDistance) { - hitDistance = distance; - } + if (shape->findRayIntersection(intersection)) { + hit = true; } } } - if (hitDistance < FLT_MAX) { - minDistance = hitDistance; - } - return false; + return hit; } } // namespace ShapeCollider diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 3cfec4c8a2..618a5ba115 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -15,6 +15,7 @@ #include #include "CollisionInfo.h" +#include "RayIntersectionInfo.h" #include "SharedUtil.h" class Shape; @@ -145,11 +146,9 @@ namespace ShapeCollider { bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); /// \param shapes list of pointers to shapes (shape pointers may be NULL) - /// \param startPoint beginning of ray - /// \param direction direction of ray - /// \param minDistance[out] shortest distance to intersection of ray with a shapes + /// \param intersection[out] struct with info about Ray and hit /// \return true if ray hits any shape in shapes - bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& startPoint, const glm::vec3& direction, float& minDistance); + bool findRayIntersection(const QVector& shapes, RayIntersectionInfo& intersection); } // namespace ShapeCollider diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp index c77b0c97fb..d5db68086d 100644 --- a/libraries/shared/src/SphereShape.cpp +++ b/libraries/shared/src/SphereShape.cpp @@ -13,18 +13,19 @@ #include "SphereShape.h" -bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { +bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const { float r2 = _boundingRadius * _boundingRadius; // compute closest approach (CA) - float a = glm::dot(_translation - rayStart, rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(_translation, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + float a = glm::dot(_translation - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(_translation, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA if (b2 > r2) { // ray does not hit sphere return false; } - float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection - float d2 = glm::distance2(rayStart, _translation); // d2 = squared distance from sphere-center to ray-start + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection + float d2 = glm::distance2(intersection._rayStart, _translation); // d2 = squared distance from sphere-center to ray-start + float distance = FLT_MAX; if (a < 0.0f) { // ray points away from sphere-center if (d2 > r2) { @@ -40,5 +41,10 @@ bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3 // ray starts inside sphere distance = a + c; } - return true; + if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { + intersection._hitDistance = distance; + intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation); + return true; + } + return false; } diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index 0626927453..b5f2c50d8f 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -34,7 +34,7 @@ public: void setRadius(float radius) { _boundingRadius = radius; } - bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + bool findRayIntersection(RayIntersectionInfo& intersection) const; float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; } }; diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 88cb9fa548..c7bef547f4 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -1803,23 +1803,23 @@ void ShapeColliderTests::capsuleTouchesAACube() { void ShapeColliderTests::rayHitsSphere() { float startDistance = 3.0f; - glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); float radius = 1.0f; glm::vec3 center(0.0f); - SphereShape sphere(radius, center); // very simple ray along xAxis { - float distance = FLT_MAX; - if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); + + if (!sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } float expectedDistance = startDistance - radius; - float relativeError = fabsf(distance - expectedDistance) / startDistance; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; } @@ -1827,16 +1827,16 @@ void ShapeColliderTests::rayHitsSphere() { // ray along a diagonal axis { - rayStart = glm::vec3(startDistance, startDistance, 0.0f); - rayDirection = - glm::normalize(rayStart); + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, startDistance, 0.0f); + intersection._rayDirection = - glm::normalize(intersection._rayStart); - float distance = FLT_MAX; - if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } float expectedDistance = SQUARE_ROOT_OF_2 * startDistance - radius; - float relativeError = fabsf(distance - expectedDistance) / startDistance; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; } @@ -1854,19 +1854,19 @@ void ShapeColliderTests::rayHitsSphere() { glm::vec3 unrotatedRayDirection(-1.0f, 0.0f, 0.0f); glm::vec3 untransformedRayStart(startDistance, 0.0f, 0.0f); - rayStart = rotation * (untransformedRayStart + translation); - rayDirection = rotation * unrotatedRayDirection; + RayIntersectionInfo intersection; + intersection._rayStart = rotation * (untransformedRayStart + translation); + intersection._rayDirection = rotation * unrotatedRayDirection; sphere.setRadius(radius); sphere.setTranslation(rotation * translation); - float distance = FLT_MAX; - if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { + if (!sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; } float expectedDistance = startDistance - radius; - float relativeError = fabsf(distance - expectedDistance) / startDistance; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; @@ -1879,31 +1879,36 @@ void ShapeColliderTests::rayBarelyHitsSphere() { glm::vec3 center(0.0f); float delta = 2.0f * EPSILON; - float startDistance = 3.0f; - glm::vec3 rayStart(-startDistance, radius - delta, 0.0f); - glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); - SphereShape sphere(radius, center); + float startDistance = 3.0f; - // very simple ray along xAxis - float distance = FLT_MAX; - if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + { + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, radius - delta, 0.0f); + intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); + + // very simple ray along xAxis + if (!sphere.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } } - // translate and rotate the whole system... - glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - glm::quat rotation = glm::angleAxis(0.987654321f, axis); - glm::vec3 translation(35.7f, 2.46f, -1.97f); - - rayStart = rotation * (rayStart + translation); - rayDirection = rotation * rayDirection; - sphere.setTranslation(rotation * translation); - - // ...and test again - distance = FLT_MAX; - if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + { + // translate and rotate the whole system... + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(0.987654321f, axis); + glm::vec3 translation(1.7f, 0.46f, -1.97f); + + RayIntersectionInfo intersection; + intersection._rayStart = rotation * (intersection._rayStart + translation); + intersection._rayDirection = rotation * intersection._rayDirection; + + sphere.setTranslation(rotation * translation); + + // ...and test again + if (!sphere.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; + } } } @@ -1915,17 +1920,17 @@ void ShapeColliderTests::rayBarelyMissesSphere() { float delta = 2.0f * EPSILON; float startDistance = 3.0f; - glm::vec3 rayStart(-startDistance, radius + delta, 0.0f); - glm::vec3 rayDirection(1.0f, 0.0f, 0.0f); + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, radius + delta, 0.0f); + intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); SphereShape sphere(radius, center); // very simple ray along xAxis - float distance = FLT_MAX; - if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -1935,16 +1940,16 @@ void ShapeColliderTests::rayBarelyMissesSphere() { glm::quat rotation = glm::angleAxis(0.987654321f, axis); glm::vec3 translation(35.7f, 2.46f, -1.97f); - rayStart = rotation * (rayStart + translation); - rayDirection = rotation * rayDirection; + intersection._hitDistance = FLT_MAX; + intersection._rayStart = rotation * (intersection._rayStart + translation); + intersection._rayDirection = rotation * intersection._rayDirection; sphere.setTranslation(rotation * translation); // ...and test again - distance = FLT_MAX; - if (sphere.findRayIntersection(rayStart, rayDirection, distance)) { + if (sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -1959,26 +1964,26 @@ void ShapeColliderTests::rayHitsCapsule() { { // simple test along xAxis // toward capsule center - glm::vec3 rayStart(startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); - float distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::vec3(-1.0f, 0.0f, 0.0f); + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } float expectedDistance = startDistance - radius; - float relativeError = fabsf(distance - expectedDistance) / startDistance; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } // toward top of cylindrical wall - rayStart.y = halfHeight; - distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = halfHeight; + intersection._hitDistance = FLT_MAX; + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - relativeError = fabsf(distance - expectedDistance) / startDistance; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; @@ -1986,12 +1991,12 @@ void ShapeColliderTests::rayHitsCapsule() { // toward top cap float delta = 2.0f * EPSILON; - rayStart.y = halfHeight + delta; - distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = halfHeight + delta; + intersection._hitDistance = FLT_MAX; + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - relativeError = fabsf(distance - expectedDistance) / startDistance; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; @@ -2000,13 +2005,13 @@ void ShapeColliderTests::rayHitsCapsule() { const float EDGE_CASE_SLOP_FACTOR = 20.0f; // toward tip of top cap - rayStart.y = halfHeight + radius - delta; - distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = halfHeight + radius - delta; + intersection._hitDistance = FLT_MAX; + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(distance - expectedDistance) / startDistance; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " @@ -2014,13 +2019,13 @@ void ShapeColliderTests::rayHitsCapsule() { } // toward tip of bottom cap - rayStart.y = - halfHeight - radius + delta; - distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = - halfHeight - radius + delta; + intersection._hitDistance = FLT_MAX; + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(distance - expectedDistance) / startDistance; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " @@ -2028,14 +2033,14 @@ void ShapeColliderTests::rayHitsCapsule() { } // toward edge of capsule cylindrical face - rayStart.y = 0.0f; - rayStart.z = radius - delta; - distance = FLT_MAX; - if (!capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = 0.0f; + intersection._rayStart.z = radius - delta; + intersection._hitDistance = FLT_MAX; + if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(distance - expectedDistance) / startDistance; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " @@ -2055,40 +2060,41 @@ void ShapeColliderTests::rayMissesCapsule() { { // simple test along xAxis // toward capsule center - glm::vec3 rayStart(startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection(-1.0f, 0.0f, 0.0f); + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::vec3(-1.0f, 0.0f, 0.0f); float delta = 2.0f * EPSILON; // over top cap - rayStart.y = halfHeight + radius + delta; - float distance = FLT_MAX; - if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = halfHeight + radius + delta; + intersection._hitDistance = FLT_MAX; + if (capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } // below bottom cap - rayStart.y = - halfHeight - radius - delta; - distance = FLT_MAX; - if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = - halfHeight - radius - delta; + intersection._hitDistance = FLT_MAX; + if (capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } // past edge of capsule cylindrical face - rayStart.y = 0.0f; - rayStart.z = radius + delta; - distance = FLT_MAX; - if (capsule.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._rayStart.y = 0.0f; + intersection._rayStart.z = radius + delta; + intersection._hitDistance = FLT_MAX; + if (capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -2105,16 +2111,16 @@ void ShapeColliderTests::rayHitsPlane() { // make a simple ray float startDistance = 1.234f; - glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); - float distance = FLT_MAX; - if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { + if (!plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - float relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; @@ -2127,16 +2133,16 @@ void ShapeColliderTests::rayHitsPlane() { plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); - rayStart = rotation * rayStart; - rayDirection = rotation * rayDirection; + intersection._rayStart = rotation * intersection._rayStart; + intersection._rayDirection = rotation * intersection._rayDirection; - distance = FLT_MAX; - if (!plane.findRayIntersection(rayStart, rayDirection, distance)) { + intersection._hitDistance = FLT_MAX; + if (!plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; } expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; + relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; @@ -2152,14 +2158,14 @@ void ShapeColliderTests::rayMissesPlane() { { // parallel rays should miss float startDistance = 1.234f; - glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::normalize(glm::vec3(-1.0f, 0.0f, -1.0f)); - float distance = FLT_MAX; - if (plane.findRayIntersection(rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -2171,14 +2177,15 @@ void ShapeColliderTests::rayMissesPlane() { plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); - rayStart = rotation * rayStart; - rayDirection = rotation * rayDirection; - - distance = FLT_MAX; - if (plane.findRayIntersection(rayStart, rayDirection, distance)) { + + intersection._rayStart = rotation * intersection._rayStart; + intersection._rayDirection = rotation * intersection._rayDirection; + intersection._hitDistance = FLT_MAX; + + if (plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -2186,14 +2193,16 @@ void ShapeColliderTests::rayMissesPlane() { { // make a simple ray that points away from plane float startDistance = 1.234f; - glm::vec3 rayStart(-startDistance, 0.0f, 0.0f); - glm::vec3 rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); + + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); + intersection._rayDirection = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f)); + intersection._hitDistance = FLT_MAX; - float distance = FLT_MAX; - if (plane.findRayIntersection(rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } @@ -2205,14 +2214,15 @@ void ShapeColliderTests::rayMissesPlane() { plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); - rayStart = rotation * rayStart; - rayDirection = rotation * rayDirection; + + intersection._rayStart = rotation * intersection._rayStart; + intersection._rayDirection = rotation * intersection._rayDirection; + intersection._hitDistance = FLT_MAX; - distance = FLT_MAX; - if (plane.findRayIntersection(rayStart, rayDirection, distance)) { + if (plane.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } - if (distance != FLT_MAX) { + if (intersection._hitDistance != FLT_MAX) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } From 0494ffcf38f4ad6cae1cd078497e5fd5aed1a296 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 10 Sep 2014 17:01:51 -0700 Subject: [PATCH 04/45] implement Ray-vs-Capsule and Ray-vs-AACube --- libraries/shared/src/AACubeShape.cpp | 63 ++++++++++- libraries/shared/src/CapsuleShape.cpp | 130 ++++++++++++++++++++++- libraries/shared/src/CapsuleShape.h | 1 + libraries/shared/src/PlaneShape.cpp | 2 + libraries/shared/src/SphereShape.cpp | 1 + tests/physics/src/ShapeColliderTests.cpp | 12 +++ tests/physics/src/ShapeColliderTests.h | 2 + 7 files changed, 207 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp index 16db9fc510..1fa41bb521 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/shared/src/AACubeShape.cpp @@ -9,9 +9,68 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include + #include "AACubeShape.h" +#include "SharedUtil.h" // for SQUARE_ROOT_OF_3 + +glm::vec3 planeNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // TODO: Andrew to implement this - return false; + // A = ray point + // B = cube center + glm::vec3 BA = _translation - intersection._rayStart; + + // check for ray intersection with cube's bounding sphere + // a = distance along line to closest approach to B + float a = glm::dot(intersection._rayDirection, BA); + // b2 = squared distance from cube center to point of closest approach + float b2 = glm::length2(a * intersection._rayDirection - BA); + // r = bounding radius of cube + float halfSide = 0.5f * _scale; + const float r = SQUARE_ROOT_OF_3 * halfSide; + if (b2 > r * r) { + // line doesn't hit cube's bounding sphere + return false; + } + + // check for tuncated/short ray + const float maxBA = glm::min(intersection._rayLength, intersection._hitDistance) + halfSide; + if (maxBA * maxBA > a * a + b2) { + // ray is not long enough to reach cube's bounding sphere + // NOTE: we don't fall in here when ray's length if FLT_MAX because maxBA^2 will be NaN + // and all NaN comparisons are false + return false; + } + + // the trivial checks have been exhausted, so must trace to each face + bool hit = false; + for (int i = 0; i < 3; ++i) { + for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { + glm::vec3 planeNormal = sign * planeNormals[i]; + float rayDotPlane = glm::dot(intersection._rayDirection, planeNormal); + if (glm::abs(rayDotPlane) > EPSILON) { + float distanceToPlane = (halfSide + glm::dot(BA, planeNormal)) / rayDotPlane; + if (distanceToPlane >= 0.0f) { + glm::vec3 point = distanceToPlane * intersection._rayDirection - BA; + int j = (i + 1) % 3; + int k = (i + 2) % 3; + glm::vec3 secondNormal = planeNormals[j]; + glm::vec3 thirdNormal = planeNormals[k]; + if (glm::abs(glm::dot(point, secondNormal)) > halfSide || + glm::abs(glm::dot(point, thirdNormal)) > halfSide) { + continue; + } + if (distanceToPlane < intersection._hitDistance && distanceToPlane < intersection._rayLength) { + intersection._hitDistance = distanceToPlane; + intersection._hitNormal = planeNormal; + intersection._hitShape = const_cast(this); + hit = true; + } + } + } + } + } + return hit; } diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index f6bad6d5d1..5bb118d36e 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -78,9 +78,135 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en updateBoundingRadius(); } -bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const { - // TODO: Andrew to implement this +// helper +bool findRayIntersectionWithCap(const glm::vec3& sphereCenter, float sphereRadius, + const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) { + float r2 = sphereRadius * sphereRadius; + + // compute closest approach (CA) + float a = glm::dot(sphereCenter - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(sphereCenter, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA + if (b2 > r2) { + // ray does not hit sphere return false; + } + float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection + float d2 = glm::distance2(intersection._rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start + float distance = FLT_MAX; + if (a < 0.0f) { + // ray points away from sphere-center + if (d2 > r2) { + // ray starts outside sphere + return false; + } + // ray starts inside sphere + distance = c + a; + } else if (d2 > r2) { + // ray starts outside sphere + distance = a - c; + } else { + // ray starts inside sphere + distance = a + c; + } + if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { + glm::vec3 sphereCenterToHitPoint = intersection._rayStart + distance * intersection._rayDirection - sphereCenter; + if (glm::dot(sphereCenterToHitPoint, sphereCenter - capsuleCenter) >= 0.0f) { + intersection._hitDistance = distance; + intersection._hitNormal = glm::normalize(sphereCenterToHitPoint); + return true; + } + } + return false; +} + +bool CapsuleShape::findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const { + glm::vec3 capCenter; + getStartPoint(capCenter); + bool hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection); + getEndPoint(capCenter); + hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection) || hit; + if (hit) { + intersection._hitShape = const_cast(this); + } + return hit; +} + +bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const { + // ray is U, capsule is V + glm::vec3 axisV; + computeNormalizedAxis(axisV); + glm::vec3 centerV = getTranslation(); + + // first handle parallel case + float uDotV = glm::dot(axisV, intersection._rayDirection); + glm::vec3 UV = intersection._rayStart - centerV; + if (glm::abs(1.0f - glm::abs(uDotV)) < EPSILON) { + // line and cylinder are parallel + float distanceV = glm::dot(UV, intersection._rayDirection); + if (glm::length2(UV - distanceV * intersection._rayDirection) <= _radius * _radius) { + // ray is inside cylinder's radius and might intersect caps + return findRayIntersectionWithCaps(centerV, intersection); + } + return false; + } + + // Given a line with point 'U' and normalized direction 'u' and + // a cylinder with axial point 'V', radius 'r', and normalized direction 'v' + // the intersection of the two is on the line at distance 't' from 'U'. + // + // Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0 + // + // where: + // + // UV = U-V + // w = u-(u.v)v + // Q = UV-(UV.v)v + // + // A = w^2 + // B = 2(w.Q) + // C = Q^2 - r^2 + + glm::vec3 w = intersection._rayDirection - uDotV * axisV; + glm::vec3 Q = UV - glm::dot(UV, axisV) * axisV; + + // we save a few multiplies by storing 2*A rather than just A + float A2 = 2.0f * glm::dot(w, w); + float B = 2.0f * glm::dot(w, Q); + + // since C is only ever used once (in the determinant) we compute it inline + float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - _radius * _radius); + if (determinant < 0.0f) { + return false; + } + float hitLow = (-B - sqrtf(determinant)) / A2; + float hitHigh = -(hitLow + 2.0f * B / A2); + + if (hitLow > hitHigh) { + // re-arrange so hitLow is always the smaller value + float temp = hitHigh; + hitHigh = hitLow; + hitLow = temp; + } + if (hitLow < 0.0f) { + if (hitHigh < 0.0f) { + // capsule is completely behind rayStart + return false; + } + hitLow = hitHigh; + } + + glm::vec3 p = intersection._rayStart + hitLow * intersection._rayDirection; + float d = glm::dot(p - centerV, axisV); + if (glm::abs(d) <= getHalfHeight()) { + // we definitely hit the cylinder wall + intersection._hitDistance = hitLow; + intersection._hitNormal = glm::normalize(p - centerV - d * axisV); + intersection._hitShape = const_cast(this); + return true; + } + + // ray still might hit the caps + return findRayIntersectionWithCaps(centerV, intersection); } // static diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 423aa850ba..6e889f6566 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -52,6 +52,7 @@ public: virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); } protected: + bool findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const; virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } static glm::quat computeNewRotation(const glm::vec3& newAxis); diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index d302bcb656..b844bac299 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -48,6 +48,7 @@ bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { // ray starts on the plane intersection._hitDistance = 0.0f; intersection._hitNormal = n; + intersection._hitShape = const_cast(this); return true; } } else { @@ -56,6 +57,7 @@ bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { // ray points toward plane intersection._hitDistance = d; intersection._hitNormal = n; + intersection._hitShape = const_cast(this); return true; } } diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp index d5db68086d..4c47ae91c0 100644 --- a/libraries/shared/src/SphereShape.cpp +++ b/libraries/shared/src/SphereShape.cpp @@ -44,6 +44,7 @@ bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const { if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) { intersection._hitDistance = distance; intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation); + intersection._hitShape = const_cast(this); return true; } return false; diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index c7bef547f4..d1ba30ec36 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -2150,6 +2150,7 @@ void ShapeColliderTests::rayHitsPlane() { } void ShapeColliderTests::rayMissesPlane() { + // TODO: Andrew to test RayIntersectionInfo::_hitShape // make a simple plane float planeDistanceFromOrigin = 3.579f; glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); @@ -2229,6 +2230,14 @@ void ShapeColliderTests::rayMissesPlane() { } } +void ShapeColliderTests::rayHitsAACube() { + // TODO: Andrew to implement this +} + +void ShapeColliderTests::rayMissesAACube() { + // TODO: Andrew to implement this +} + void ShapeColliderTests::measureTimeOfCollisionDispatch() { /* KEEP for future manual testing // create two non-colliding spheres @@ -2288,4 +2297,7 @@ void ShapeColliderTests::runAllTests() { rayMissesCapsule(); rayHitsPlane(); rayMissesPlane(); + + rayHitsAACube(); + rayMissesAACube(); } diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h index a7495d32bf..fa6887f685 100644 --- a/tests/physics/src/ShapeColliderTests.h +++ b/tests/physics/src/ShapeColliderTests.h @@ -38,6 +38,8 @@ namespace ShapeColliderTests { void rayMissesCapsule(); void rayHitsPlane(); void rayMissesPlane(); + void rayHitsAACube(); + void rayMissesAACube(); void measureTimeOfCollisionDispatch(); From 08e5dbadfdacc70203064a959c67ee1fc7e00710 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 11 Sep 2014 09:44:41 -0700 Subject: [PATCH 05/45] added validation of RayIntersectionInfo::_hitShape --- tests/physics/src/ShapeColliderTests.cpp | 154 +++++++++++++---------- 1 file changed, 89 insertions(+), 65 deletions(-) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index d1ba30ec36..dc74bd42af 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -1811,8 +1811,8 @@ void ShapeColliderTests::rayHitsSphere() { // very simple ray along xAxis { RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); + intersection._rayStart = -startDistance * xAxis; + intersection._rayDirection = xAxis; if (!sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should intersect sphere" << std::endl; @@ -1851,8 +1851,8 @@ void ShapeColliderTests::rayHitsSphere() { glm::quat rotation = glm::angleAxis(0.987654321f, axis); glm::vec3 translation(35.7f, 2.46f, -1.97f); - glm::vec3 unrotatedRayDirection(-1.0f, 0.0f, 0.0f); - glm::vec3 untransformedRayStart(startDistance, 0.0f, 0.0f); + glm::vec3 unrotatedRayDirection = -xAxis; + glm::vec3 untransformedRayStart = startDistance * xAxis; RayIntersectionInfo intersection; intersection._rayStart = rotation * (untransformedRayStart + translation); @@ -1885,7 +1885,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { { RayIntersectionInfo intersection; intersection._rayStart = glm::vec3(-startDistance, radius - delta, 0.0f); - intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); + intersection._rayDirection = xAxis; // very simple ray along xAxis if (!sphere.findRayIntersection(intersection)) { @@ -1897,7 +1897,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { // translate and rotate the whole system... glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); glm::quat rotation = glm::angleAxis(0.987654321f, axis); - glm::vec3 translation(1.7f, 0.46f, -1.97f); + glm::vec3 translation(35.7f, 0.46f, -1.97f); RayIntersectionInfo intersection; intersection._rayStart = rotation * (intersection._rayStart + translation); @@ -1919,39 +1919,47 @@ void ShapeColliderTests::rayBarelyMissesSphere() { glm::vec3 center(0.0f); float delta = 2.0f * EPSILON; - float startDistance = 3.0f; - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, radius + delta, 0.0f); - intersection._rayDirection = glm::vec3(1.0f, 0.0f, 0.0f); - SphereShape sphere(radius, center); + float startDistance = 3.0f; - // very simple ray along xAxis - if (sphere.findRayIntersection(intersection)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; - } - if (intersection._hitDistance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" - << std::endl; + { + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(-startDistance, radius + delta, 0.0f); + intersection._rayDirection = xAxis; + + // very simple ray along xAxis + if (sphere.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (intersection._hitDistance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; + } } - // translate and rotate the whole system... - glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); - glm::quat rotation = glm::angleAxis(0.987654321f, axis); - glm::vec3 translation(35.7f, 2.46f, -1.97f); + { + // translate and rotate the whole system... + float angle = 0.987654321f; + glm::vec3 axis = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + glm::quat rotation = glm::angleAxis(angle, axis); + glm::vec3 translation(35.7f, 2.46f, -1.97f); - intersection._hitDistance = FLT_MAX; - intersection._rayStart = rotation * (intersection._rayStart + translation); - intersection._rayDirection = rotation * intersection._rayDirection; - sphere.setTranslation(rotation * translation); - - // ...and test again - if (sphere.findRayIntersection(intersection)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; - } - if (intersection._hitDistance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" - << std::endl; + RayIntersectionInfo intersection; + intersection._rayStart = rotation * (glm::vec3(-startDistance, radius + delta, 0.0f) + translation); + intersection._rayDirection = rotation * xAxis; + sphere.setTranslation(rotation * translation); + + // ...and test again + if (sphere.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; + } + if (intersection._hitDistance != FLT_MAX) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; + } + if (intersection._hitShape != NULL) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should be NULL" << std::endl; + } } } @@ -1962,11 +1970,11 @@ void ShapeColliderTests::rayHitsCapsule() { glm::vec3 center(0.0f); CapsuleShape capsule(radius, halfHeight); - { // simple test along xAxis - // toward capsule center + // simple tests along xAxis + { // toward capsule center RayIntersectionInfo intersection; intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::vec3(-1.0f, 0.0f, 0.0f); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } @@ -1976,71 +1984,81 @@ void ShapeColliderTests::rayHitsCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + } - // toward top of cylindrical wall - intersection._rayStart.y = halfHeight; - intersection._hitDistance = FLT_MAX; + { // toward top of cylindrical wall + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, halfHeight, 0.0f); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; + float expectedDistance = startDistance - radius; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + } - // toward top cap - float delta = 2.0f * EPSILON; - intersection._rayStart.y = halfHeight + delta; - intersection._hitDistance = FLT_MAX; + float delta = 2.0f * EPSILON; + { // toward top cap + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, halfHeight + delta, 0.0f); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; + float expectedDistance = startDistance - radius; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + } - const float EDGE_CASE_SLOP_FACTOR = 20.0f; - - // toward tip of top cap - intersection._rayStart.y = halfHeight + radius - delta; - intersection._hitDistance = FLT_MAX; + const float EDGE_CASE_SLOP_FACTOR = 20.0f; + { // toward tip of top cap + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, halfHeight + radius - delta, 0.0f); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; + float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + } - // toward tip of bottom cap - intersection._rayStart.y = - halfHeight - radius + delta; - intersection._hitDistance = FLT_MAX; + { // toward tip of bottom cap + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, - halfHeight - radius + delta, 0.0f); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; + float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + } - // toward edge of capsule cylindrical face - intersection._rayStart.y = 0.0f; - intersection._rayStart.z = radius - delta; - intersection._hitDistance = FLT_MAX; + { // toward edge of capsule cylindrical face + RayIntersectionInfo intersection; + intersection._rayStart = glm::vec3(startDistance, 0.0f, radius - delta); + intersection._rayDirection = - xAxis; if (!capsule.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit capsule" << std::endl; } - expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine - relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; + float expectedDistance = startDistance - radius * sqrtf(2.0f * delta); // using small angle approximation of cosine + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / startDistance; // for edge cases we allow a LOT of error if (relativeError > EDGE_CASE_SLOP_FACTOR * EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " @@ -2062,7 +2080,7 @@ void ShapeColliderTests::rayMissesCapsule() { // toward capsule center RayIntersectionInfo intersection; intersection._rayStart = glm::vec3(startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::vec3(-1.0f, 0.0f, 0.0f); + intersection._rayDirection = -xAxis; float delta = 2.0f * EPSILON; // over top cap @@ -2098,6 +2116,9 @@ void ShapeColliderTests::rayMissesCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } + if (intersection._hitShape != NULL) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should be NULL" << std::endl; + } } // TODO: test at steep angles near edge } @@ -2190,6 +2211,9 @@ void ShapeColliderTests::rayMissesPlane() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; } + if (intersection._hitShape != NULL) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should be NULL" << std::endl; + } } { // make a simple ray that points away from plane From 46089a06113f1fd6877a7ca55f19a9760444ae5f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 08:37:47 -0700 Subject: [PATCH 06/45] fix ray-vs-AACube logic --- libraries/shared/src/PlaneShape.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h index a2e4a3799d..8d6de326af 100644 --- a/libraries/shared/src/PlaneShape.h +++ b/libraries/shared/src/PlaneShape.h @@ -21,6 +21,9 @@ public: glm::vec3 getNormal() const; glm::vec4 getCoefficients() const; + void setNormal(const glm::vec3& normal); + void setPoint(const glm::vec3& point); + bool findRayIntersection(RayIntersectionInfo& intersection) const; }; From ff0872a5f068eae817eee1ad9cea78957b3491c0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 08:38:44 -0700 Subject: [PATCH 07/45] add RayIntersectionInfo::getIntersectionPoint() --- libraries/shared/src/RayIntersectionInfo.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/shared/src/RayIntersectionInfo.h b/libraries/shared/src/RayIntersectionInfo.h index 2d24282b5d..6c4eb3f8dd 100644 --- a/libraries/shared/src/RayIntersectionInfo.h +++ b/libraries/shared/src/RayIntersectionInfo.h @@ -21,6 +21,8 @@ public: RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX), _hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { } + glm::vec3 getIntersectionPoint() const { return _rayStart + _hitDistance * _rayDirection; } + // input glm::vec3 _rayStart; glm::vec3 _rayDirection; From a9957ac7df97b15bc6b6fb5e714431dd403f3018 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 08:40:03 -0700 Subject: [PATCH 08/45] the real fix for ray-vs-plane intersection --- libraries/shared/src/PlaneShape.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index b844bac299..845b58728a 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -11,6 +11,7 @@ #include "PlaneShape.h" #include "SharedUtil.h" +#include "GLMHelpers.h" const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f); @@ -34,6 +35,18 @@ glm::vec3 PlaneShape::getNormal() const { return _rotation * UNROTATED_NORMAL; } +void PlaneShape::setNormal(const glm::vec3& direction) { + glm::vec3 oldTranslation = _translation; + _rotation = rotationBetween(UNROTATED_NORMAL, direction); + glm::vec3 normal = getNormal(); + _translation = glm::dot(oldTranslation, normal) * normal; +} + +void PlaneShape::setPoint(const glm::vec3& point) { + glm::vec3 normal = getNormal(); + _translation = glm::dot(point, normal) * normal; +} + glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _translation)); @@ -53,7 +66,7 @@ bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const { } } else { float d = glm::dot(_translation - intersection._rayStart, n) / denominator; - if (d > 0.0f && d < intersection._rayLength && intersection._hitDistance) { + if (d > 0.0f && d < intersection._rayLength && d < intersection._hitDistance) { // ray points toward plane intersection._hitDistance = d; intersection._hitNormal = n; From 825be3e1e7e75fd2ef65f43632e8f90b60e7c2b1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 08:40:48 -0700 Subject: [PATCH 09/45] fix for ray-vs-AACubeShape intersection test --- libraries/shared/src/AACubeShape.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp index 1fa41bb521..131885864b 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/shared/src/AACubeShape.cpp @@ -36,11 +36,12 @@ bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { } // check for tuncated/short ray - const float maxBA = glm::min(intersection._rayLength, intersection._hitDistance) + halfSide; - if (maxBA * maxBA > a * a + b2) { + // maxLength = maximum possible distance between rayStart and center of cube + const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r; + float maxBA2 = maxLength * maxLength; + if (a * a + b2 > maxLength * maxLength) { // ray is not long enough to reach cube's bounding sphere - // NOTE: we don't fall in here when ray's length if FLT_MAX because maxBA^2 will be NaN - // and all NaN comparisons are false + // NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan return false; } From c475d5db36a0c49428e3cc9c9bf81f58e4517ddd Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 08:41:28 -0700 Subject: [PATCH 10/45] unit tests for ray-vs-AACubeShape and PlaneShape --- tests/physics/src/ShapeColliderTests.cpp | 289 ++++++++++++++++++++--- 1 file changed, 252 insertions(+), 37 deletions(-) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index dc74bd42af..84335901bf 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -1823,6 +1823,10 @@ void ShapeColliderTests::rayHitsSphere() { if (relativeError > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; } + if (intersection._hitShape != &sphere) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should point at sphere" + << std::endl; + } } // ray along a diagonal axis @@ -1891,6 +1895,10 @@ void ShapeColliderTests::rayBarelyHitsSphere() { if (!sphere.findRayIntersection(intersection)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely hit sphere" << std::endl; } + if (intersection._hitShape != &sphere) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should point at sphere" + << std::endl; + } } { @@ -1984,6 +1992,10 @@ void ShapeColliderTests::rayHitsCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; } + if (intersection._hitShape != &capsule) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should point at capsule" + << std::endl; + } } { // toward top of cylindrical wall @@ -2128,50 +2140,57 @@ void ShapeColliderTests::rayHitsPlane() { float planeDistanceFromOrigin = 3.579f; glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); PlaneShape plane; - plane.setTranslation(planePosition); + plane.setPoint(planePosition); + plane.setNormal(yAxis); // make a simple ray float startDistance = 1.234f; - RayIntersectionInfo intersection; - intersection._rayStart = glm::vec3(-startDistance, 0.0f, 0.0f); - intersection._rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); - - if (!plane.findRayIntersection(intersection)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + { + RayIntersectionInfo intersection; + intersection._rayStart = -startDistance * xAxis; + intersection._rayDirection = glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + + if (!plane.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " + << relativeError << std::endl; + } + if (intersection._hitShape != &plane) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should point at plane" + << std::endl; + } } - float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - float relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; - if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " - << relativeError << std::endl; - } - - // rotate the whole system and try again - float angle = 37.8f; - glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); - glm::quat rotation = glm::angleAxis(angle, axis); - - plane.setTranslation(rotation * planePosition); - plane.setRotation(rotation); - intersection._rayStart = rotation * intersection._rayStart; - intersection._rayDirection = rotation * intersection._rayDirection; - - intersection._hitDistance = FLT_MAX; - if (!plane.findRayIntersection(intersection)) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; - } - - expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; - relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; - if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " - << relativeError << std::endl; + { // rotate the whole system and try again + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + + plane.setNormal(rotation * yAxis); + plane.setPoint(rotation * planePosition); + RayIntersectionInfo intersection; + intersection._rayStart = rotation * (-startDistance * xAxis); + intersection._rayDirection = rotation * glm::normalize(glm::vec3(1.0f, 1.0f, 1.0f)); + + if (!plane.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit plane" << std::endl; + } + + float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; + float relativeError = fabsf(intersection._hitDistance - expectedDistance) / planeDistanceFromOrigin; + if (relativeError > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " + << relativeError << std::endl; + } } } void ShapeColliderTests::rayMissesPlane() { - // TODO: Andrew to test RayIntersectionInfo::_hitShape // make a simple plane float planeDistanceFromOrigin = 3.579f; glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); @@ -2255,11 +2274,207 @@ void ShapeColliderTests::rayMissesPlane() { } void ShapeColliderTests::rayHitsAACube() { - // TODO: Andrew to implement this + glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); + float cubeSide = 2.127f; + AACubeShape cube(cubeSide, cubeCenter); + + float rayOffset = 3.796f; + + glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; + int numDirections = 3; + int numRayCasts = 5; + + for (int i = 0; i < numDirections; ++i) { + for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { + glm::vec3 faceNormal = sign * faceNormals[i]; + glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; + glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; + + // pick a random point somewhere above the face + glm::vec3 rayStart = cubeCenter + + (cubeSide + rayOffset) * faceNormal + + (cubeSide * (randFloat() - 0.5f)) * secondNormal + + (cubeSide * (randFloat() - 0.5f)) * thirdNormal; + + // cast multiple rays toward the face + for (int j = 0; j < numRayCasts; ++j) { + // pick a random point on the face + glm::vec3 facePoint = cubeCenter + + 0.5f * cubeSide * faceNormal + + (cubeSide * (randFloat() - 0.5f)) * secondNormal + + (cubeSide * (randFloat() - 0.5f)) * thirdNormal; + + // construct a ray from first point through second point + RayIntersectionInfo intersection; + intersection._rayStart = rayStart; + intersection._rayDirection = glm::normalize(facePoint - rayStart); + intersection._rayLength = 1.0001f * glm::distance(rayStart, facePoint); + + // cast the ray + bool hit = cube.findRayIntersection(intersection); + + // validate + if (!hit) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should hit cube face" << std::endl; + break; + } + if (glm::abs(1.0f - glm::dot(faceNormal, intersection._hitNormal)) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: ray should hit cube face with normal " << faceNormal + << " but found different normal " << intersection._hitNormal << std::endl; + } + if (glm::distance(facePoint, intersection.getIntersectionPoint()) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: ray should hit cube face at " << facePoint + << " but actually hit at " << intersection.getIntersectionPoint() + << std::endl; + } + if (intersection._hitShape != &cube) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray intersection._hitShape should point at cube" + << std::endl; + } + } + } + } } void ShapeColliderTests::rayMissesAACube() { - // TODO: Andrew to implement this + //glm::vec3 cubeCenter(1.23f, 4.56f, 7.89f); + //float cubeSide = 2.127f; + glm::vec3 cubeCenter(0.0f); + float cubeSide = 2.f; + AACubeShape cube(cubeSide, cubeCenter); + + float rayOffset = 3.796f; + + glm::vec3 faceNormals[] = {xAxis, yAxis, zAxis}; + int numDirections = 3; + int numRayCasts = 5; + + const float SOME_SMALL_NUMBER = 0.0001f; + + { // ray misses cube for being too short + for (int i = 0; i < numDirections; ++i) { + for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { + glm::vec3 faceNormal = sign * faceNormals[i]; + glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; + glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; + + // pick a random point somewhere above the face + glm::vec3 rayStart = cubeCenter + + (cubeSide + rayOffset) * faceNormal + + (cubeSide * (randFloat() - 0.5f)) * secondNormal + + (cubeSide * (randFloat() - 0.5f)) * thirdNormal; + + // cast multiple rays toward the face + for (int j = 0; j < numRayCasts; ++j) { + // pick a random point on the face + glm::vec3 facePoint = cubeCenter + + 0.5f * cubeSide * faceNormal + + (cubeSide * (randFloat() - 0.5f)) * secondNormal + + (cubeSide * (randFloat() - 0.5f)) * thirdNormal; + + // construct a ray from first point to almost second point + RayIntersectionInfo intersection; + intersection._rayStart = rayStart; + intersection._rayDirection = glm::normalize(facePoint - rayStart); + intersection._rayLength = (1.0f - SOME_SMALL_NUMBER) * glm::distance(rayStart, facePoint); + + // cast the ray + if (cube.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should NOT hit cube face " + << faceNormal << std::endl; + } + } + } + } + } + { // long ray misses cube + for (int i = 0; i < numDirections; ++i) { + for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { + glm::vec3 faceNormal = sign * faceNormals[i]; + glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; + glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; + + // pick a random point somewhere above the face + glm::vec3 rayStart = cubeCenter + + (cubeSide + rayOffset) * faceNormal + + (cubeSide * (randFloat() - 0.5f)) * secondNormal + + (cubeSide * (randFloat() - 0.5f)) * thirdNormal; + + // cast multiple rays that miss the face + for (int j = 0; j < numRayCasts; ++j) { + // pick a random point just outside of face + float inside = (cubeSide * (randFloat() - 0.5f)); + float outside = 0.5f * cubeSide + SOME_SMALL_NUMBER * randFloat(); + if (randFloat() - 0.5f < 0.0f) { + outside *= -1.0f; + } + glm::vec3 sidePoint = cubeCenter + 0.5f * cubeSide * faceNormal; + if (randFloat() - 0.5f < 0.0f) { + sidePoint += outside * secondNormal + inside * thirdNormal; + } else { + sidePoint += inside * secondNormal + outside * thirdNormal; + } + + // construct a ray from first point through second point + RayIntersectionInfo intersection; + intersection._rayStart = rayStart; + intersection._rayDirection = glm::normalize(sidePoint - rayStart); + + // cast the ray + if (cube.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should NOT hit cube face " + << faceNormal << std::endl; + } + } + } + } + } + { // ray parallel to face barely misses cube + for (int i = 0; i < numDirections; ++i) { + for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { + glm::vec3 faceNormal = sign * faceNormals[i]; + glm::vec3 secondNormal = faceNormals[(i + 1) % numDirections]; + glm::vec3 thirdNormal = faceNormals[(i + 2) % numDirections]; + + // cast multiple rays that miss the face + for (int j = 0; j < numRayCasts; ++j) { + // rayStart is above the face + glm::vec3 rayStart = cubeCenter + (0.5f + SOME_SMALL_NUMBER) * cubeSide * faceNormal; + + // move rayStart to some random edge and choose the ray direction to point across the face + float inside = (cubeSide * (randFloat() - 0.5f)); + float outside = 0.5f * cubeSide + SOME_SMALL_NUMBER * randFloat(); + if (randFloat() - 0.5f < 0.0f) { + outside *= -1.0f; + } + glm::vec3 rayDirection = secondNormal; + if (randFloat() - 0.5f < 0.0f) { + rayStart += outside * secondNormal + inside * thirdNormal; + } else { + rayStart += inside * secondNormal + outside * thirdNormal; + rayDirection = thirdNormal; + } + if (outside > 0.0f) { + rayDirection *= -1.0f; + } + + // construct a ray from first point through second point + RayIntersectionInfo intersection; + intersection._rayStart = rayStart; + intersection._rayDirection = rayDirection; + + // cast the ray + if (cube.findRayIntersection(intersection)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should NOT hit cube face " + << faceNormal << std::endl; + } + } + } + } + } + } void ShapeColliderTests::measureTimeOfCollisionDispatch() { From 4e7a7667b1a5e8f70af4f53aa3d21591de283aa6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 09:19:03 -0700 Subject: [PATCH 11/45] remove unused variable --- libraries/shared/src/AACubeShape.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp index 131885864b..fb5c3df6e3 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/shared/src/AACubeShape.cpp @@ -38,7 +38,6 @@ bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { // check for tuncated/short ray // maxLength = maximum possible distance between rayStart and center of cube const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r; - float maxBA2 = maxLength * maxLength; if (a * a + b2 > maxLength * maxLength) { // ray is not long enough to reach cube's bounding sphere // NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan From 32b07027d32f61ce0cbbd7ecce4555e627d03e01 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 17:27:56 -0700 Subject: [PATCH 12/45] namechange planeNormal --> faceNormal --- libraries/shared/src/AACubeShape.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libraries/shared/src/AACubeShape.cpp b/libraries/shared/src/AACubeShape.cpp index fb5c3df6e3..30197d6bfd 100644 --- a/libraries/shared/src/AACubeShape.cpp +++ b/libraries/shared/src/AACubeShape.cpp @@ -15,7 +15,7 @@ #include "AACubeShape.h" #include "SharedUtil.h" // for SQUARE_ROOT_OF_3 -glm::vec3 planeNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; +glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) }; bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { // A = ray point @@ -48,23 +48,23 @@ bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const { bool hit = false; for (int i = 0; i < 3; ++i) { for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) { - glm::vec3 planeNormal = sign * planeNormals[i]; - float rayDotPlane = glm::dot(intersection._rayDirection, planeNormal); + glm::vec3 faceNormal = sign * faceNormals[i]; + float rayDotPlane = glm::dot(intersection._rayDirection, faceNormal); if (glm::abs(rayDotPlane) > EPSILON) { - float distanceToPlane = (halfSide + glm::dot(BA, planeNormal)) / rayDotPlane; - if (distanceToPlane >= 0.0f) { - glm::vec3 point = distanceToPlane * intersection._rayDirection - BA; + float distanceToFace = (halfSide + glm::dot(BA, faceNormal)) / rayDotPlane; + if (distanceToFace >= 0.0f) { + glm::vec3 point = distanceToFace * intersection._rayDirection - BA; int j = (i + 1) % 3; int k = (i + 2) % 3; - glm::vec3 secondNormal = planeNormals[j]; - glm::vec3 thirdNormal = planeNormals[k]; + glm::vec3 secondNormal = faceNormals[j]; + glm::vec3 thirdNormal = faceNormals[k]; if (glm::abs(glm::dot(point, secondNormal)) > halfSide || glm::abs(glm::dot(point, thirdNormal)) > halfSide) { continue; } - if (distanceToPlane < intersection._hitDistance && distanceToPlane < intersection._rayLength) { - intersection._hitDistance = distanceToPlane; - intersection._hitNormal = planeNormal; + if (distanceToFace < intersection._hitDistance && distanceToFace < intersection._rayLength) { + intersection._hitDistance = distanceToFace; + intersection._hitNormal = faceNormal; intersection._hitShape = const_cast(this); hit = true; } From c21491a0345179c1d9d075e05dcdd5beb74f59fe Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 17:38:20 -0700 Subject: [PATCH 13/45] add update expiry to VoxelShapeManager --- interface/src/avatar/VoxelShapeManager.cpp | 6 ++++-- interface/src/avatar/VoxelShapeManager.h | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/VoxelShapeManager.cpp b/interface/src/avatar/VoxelShapeManager.cpp index a73679a2c4..f59077eb91 100644 --- a/interface/src/avatar/VoxelShapeManager.cpp +++ b/interface/src/avatar/VoxelShapeManager.cpp @@ -17,7 +17,7 @@ #include "VoxelShapeManager.h" -VoxelShapeManager::VoxelShapeManager() : PhysicsEntity(), _lastSimulationTranslation(0.0f) { +VoxelShapeManager::VoxelShapeManager() : PhysicsEntity(), _updateExpiry(0), _lastSimulationTranslation(0.0f) { } VoxelShapeManager::~VoxelShapeManager() { @@ -57,7 +57,9 @@ void VoxelShapeManager::clearShapes() { _voxels.clear(); } -void VoxelShapeManager::updateVoxels(CubeList& cubes) { +void VoxelShapeManager::updateVoxels(const quint64& now, CubeList& cubes) { + const quint64 VOXEL_UPDATE_PERIOD = 100000; // usec + _updateExpiry = now + VOXEL_UPDATE_PERIOD; PhysicsSimulation* simulation = getSimulation(); if (!simulation) { return; diff --git a/interface/src/avatar/VoxelShapeManager.h b/interface/src/avatar/VoxelShapeManager.h index 8ca930f71b..fe0024b321 100644 --- a/interface/src/avatar/VoxelShapeManager.h +++ b/interface/src/avatar/VoxelShapeManager.h @@ -39,11 +39,14 @@ public: void buildShapes(); void clearShapes(); + bool needsUpdate(const quint64& now) const { return _updateExpiry < now; } + /// \param cubes list of AACubes representing all of the voxels that should be in this VoxelShapeManager - void updateVoxels(CubeList& cubes); + void updateVoxels(const quint64& now, CubeList& cubes); private: + quint64 _updateExpiry; glm::vec3 _lastSimulationTranslation; VoxelPool _voxels; }; From 62867391a38900c34adaef879fe4f32568f942c8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 17:38:45 -0700 Subject: [PATCH 14/45] collide against PhysSimulation instead of VoxelTree --- interface/src/Menu.cpp | 4 +- interface/src/avatar/MyAvatar.cpp | 87 ++++++++++++++-------- interface/src/avatar/MyAvatar.h | 1 + libraries/shared/src/PhysicsSimulation.cpp | 50 ++++++++++--- libraries/shared/src/PhysicsSimulation.h | 13 +++- 5 files changed, 113 insertions(+), 42 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 11c98c738c..6f66da3e96 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -286,7 +286,8 @@ Menu::Menu() : avatar, SLOT(updateMotionBehaviorsFromMenu())); QMenu* collisionsMenu = avatarMenu->addMenu("Collide With..."); - addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll); + addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideAsRagdoll, 0, false, + avatar, SLOT(onToggleRagdoll())); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithAvatars, 0, true, avatar, SLOT(updateCollisionGroups())); addCheckableActionToQMenuAndActionHash(collisionsMenu, MenuOption::CollideWithVoxels, @@ -733,6 +734,7 @@ void Menu::loadSettings(QSettings* settings) { // TODO: cache more settings in MyAvatar that are checked with very high frequency. MyAvatar* myAvatar = Application::getInstance()->getAvatar(); myAvatar->updateCollisionGroups(); + myAvatar->onToggleRagdoll(); if (lockedSettings) { Application::getInstance()->unlockSettings(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4b0e4eef5a..9212dc0ac8 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -86,11 +86,10 @@ MyAvatar::MyAvatar() : _driveKeys[i] = 0.0f; } _physicsSimulation.setEntity(&_skeletonModel); + _physicsSimulation.addEntity(&_voxelShapeManager); _skeletonModel.setEnableShapes(true); - Ragdoll* ragdoll = _skeletonModel.buildRagdoll(); - _physicsSimulation.setRagdoll(ragdoll); - _physicsSimulation.addEntity(&_voxelShapeManager); + _skeletonModel.buildRagdoll(); } MyAvatar::~MyAvatar() { @@ -213,15 +212,15 @@ void MyAvatar::simulate(float deltaTime) { } { - PerformanceTimer perfTimer("ragdoll"); + PerformanceTimer perfTimer("physics"); + const float minError = 0.00001f; + const float maxIterations = 3; + const quint64 maxUsec = 4000; + _physicsSimulation.setTranslation(_position); + _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); + Ragdoll* ragdoll = _skeletonModel.getRagdoll(); if (ragdoll && Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - const float minError = 0.00001f; - const float maxIterations = 3; - const quint64 maxUsec = 4000; - _physicsSimulation.setTranslation(_position); - _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); - // harvest any displacement of the Ragdoll that is a result of collisions glm::vec3 ragdollDisplacement = ragdoll->getAndClearAccumulatedMovement(); const float MAX_RAGDOLL_DISPLACEMENT_2 = 1.0f; @@ -1367,7 +1366,9 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { static CollisionList myCollisions(64); void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { - if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + + quint64 now = usecTimestampNow(); + if (_voxelShapeManager.needsUpdate(now)) { // We use a multiple of the avatar's boundingRadius as the size of the cube of interest. float cubeScale = 4.0f * getBoundingRadius(); glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale); @@ -1376,9 +1377,12 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { // query the VoxelTree for cubes that touch avatar's boundingCube CubeList cubes; if (Application::getInstance()->getVoxelTree()->findContentInCube(boundingCube, cubes)) { - _voxelShapeManager.updateVoxels(cubes); + _voxelShapeManager.updateVoxels(now, cubes); } - } else { + } + + // TODO: Andrew to do ground/walking detection in ragdoll mode + if (!Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { const float MAX_VOXEL_COLLISION_SPEED = 100.0f; float speed = glm::length(_velocity); if (speed > MAX_VOXEL_COLLISION_SPEED) { @@ -1388,15 +1392,18 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { } bool isTrapped = false; myCollisions.clear(); - const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); - if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) { + // copy the boundingShape and tranform into physicsSimulation frame + CapsuleShape boundingShape = _skeletonModel.getBoundingShape(); + boundingShape.setTranslation(boundingShape.getTranslation() - _position); + + if (_physicsSimulation.getShapeCollisions(&boundingShape, myCollisions)) { + // we temporarily move b const float VOXEL_ELASTICITY = 0.0f; const float VOXEL_DAMPING = 0.0f; - float capsuleRadius = boundingShape.getRadius(); - float capsuleHalfHeight = boundingShape.getHalfHeight(); + const float capsuleRadius = boundingShape.getRadius(); + const float capsuleHalfHeight = boundingShape.getHalfHeight(); const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; const float MIN_STEP_HEIGHT = 0.0f; - glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; float highestStep = 0.0f; float lowestStep = MAX_STEP_HEIGHT; glm::vec3 floorPoint; @@ -1405,37 +1412,48 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { for (int i = 0; i < myCollisions.size(); ++i) { CollisionInfo* collision = myCollisions[i]; - glm::vec3 cubeCenter = collision->_vecData; - float cubeSide = collision->_floatData; + float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection); float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection); const float MAX_TRAP_PERIOD = 0.125f; if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { isTrapped = true; if (_trapDuration > MAX_TRAP_PERIOD) { - float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection); - if (distance < 0.0f) { - distance = fabsf(distance) + 0.5f * cubeSide; + RayIntersectionInfo intersection; + // we pick a rayStart that we expect to be inside the boundingShape (aka shapeA) + intersection._rayStart = collision->_contactPoint - MAX_STEP_HEIGHT * glm::normalize(collision->_penetration); + intersection._rayDirection = -_worldUpDirection; + // cast the ray down against shapeA + if (collision->_shapeA->findRayIntersection(intersection)) { + float firstDepth = - intersection._hitDistance; + // recycle intersection and cast again in up against shapeB + intersection._rayDirection = _worldUpDirection; + intersection._hitDistance = FLT_MAX; + if (collision->_shapeB->findRayIntersection(intersection)) { + // now we know how much we need to move UP to get out + totalPenetration = addPenetrations(totalPenetration, + (firstDepth + intersection._hitDistance) * _worldUpDirection); + } } - distance += capsuleRadius + capsuleHalfHeight; - totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection); continue; } } else if (_trapDuration > MAX_TRAP_PERIOD) { - // we're trapped, ignore this collision + // we're trapped, ignore this shallow collision continue; } totalPenetration = addPenetrations(totalPenetration, collision->_penetration); + + // some logic to help us walk up steps if (glm::dot(collision->_penetration, _velocity) >= 0.0f) { - glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection; - float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase); + float stepHeight = - glm::dot(_worldUpDirection, collision->_penetration); if (stepHeight > highestStep) { highestStep = stepHeight; stepPenetration = collision->_penetration; } if (stepHeight < lowestStep) { lowestStep = stepHeight; - floorPoint = collision->_contactPoint - collision->_penetration; + // remember that collision is in _physicsSimulation frame so we must add _position + floorPoint = _position + collision->_contactPoint - collision->_penetration; } } } @@ -1856,6 +1874,17 @@ void MyAvatar::updateMotionBehaviorsFromMenu() { } } +void MyAvatar::onToggleRagdoll() { + Ragdoll* ragdoll = _skeletonModel.getRagdoll(); + if (ragdoll) { + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + _physicsSimulation.setRagdoll(ragdoll); + } else { + _physicsSimulation.setRagdoll(NULL); + } + } +} + void MyAvatar::renderAttachments(RenderMode renderMode) { if (Application::getInstance()->getCamera()->getMode() != CAMERA_MODE_FIRST_PERSON || renderMode == MIRROR_RENDER_MODE) { Avatar::renderAttachments(renderMode); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d7b955ce06..01315c7a35 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -163,6 +163,7 @@ public slots: void setThrust(glm::vec3 newThrust) { _thrust = newThrust; } void updateMotionBehaviorsFromMenu(); + void onToggleRagdoll(); glm::vec3 getLeftPalmPosition(); glm::vec3 getRightPalmPosition(); diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 93f0797c94..ee5ea9b2b8 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -207,9 +207,6 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { ++_frameCount; - if (!_ragdoll) { - return; - } quint64 now = usecTimestampNow(); quint64 startTime = now; quint64 expiry = startTime + maxUsec; @@ -219,7 +216,9 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter int numDolls = _otherRagdolls.size(); { PerformanceTimer perfTimer("enforce"); - _ragdoll->enforceConstraints(); + if (_ragdoll) { + _ragdoll->enforceConstraints(); + } for (int i = 0; i < numDolls; ++i) { _otherRagdolls[i]->enforceConstraints(); } @@ -235,7 +234,9 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter { // enforce constraints PerformanceTimer perfTimer("enforce"); - error = _ragdoll->enforceConstraints(); + if (_ragdoll) { + error = _ragdoll->enforceConstraints(); + } for (int i = 0; i < numDolls; ++i) { error = glm::max(error, _otherRagdolls[i]->enforceConstraints()); } @@ -246,9 +247,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter now = usecTimestampNow(); } while (_collisions.size() != 0 && (iterations < maxIterations) && (error > minError) && (now < expiry)); - // the collisions may have moved the main ragdoll from the simulation center - // so we remove this offset (potentially storing it as movement of the Ragdoll owner) - _ragdoll->removeRootOffset(collidedWithOtherRagdoll); + if (_ragdoll) { + // This is why _ragdoll is special and is not in the list of other ragdolls: + // The collisions may have moved the main ragdoll from the simulation center + // so we remove this offset (potentially storing it as movement of the Ragdoll owner) + _ragdoll->removeRootOffset(collidedWithOtherRagdoll); + } // also remove any offsets from the other ragdolls for (int i = 0; i < numDolls; ++i) { @@ -257,13 +261,41 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter pruneContacts(); } +bool PhysicsSimulation::findFloorRayIntersection(RayIntersectionInfo& intersection) const { + // only casts against otherEntities + bool hit = false; + int numEntities = _otherEntities.size(); + for (int i = 0; i < numEntities; ++i) { + const QVector otherShapes = _otherEntities.at(i)->getShapes(); + if (ShapeCollider::findRayIntersection(otherShapes, intersection)) { + hit = true; + } + } + return hit; +} + + +bool PhysicsSimulation::getShapeCollisions(const Shape* shape, CollisionList& collisions) const { + bool hit = false; + int numEntities = _otherEntities.size(); + for (int i = 0; i < numEntities; ++i) { + const QVector otherShapes = _otherEntities.at(i)->getShapes(); + if (ShapeCollider::collideShapeWithShapes(shape, otherShapes, 0, collisions)) { + hit = true; + } + } + return hit; +} + void PhysicsSimulation::integrate(float deltaTime) { PerformanceTimer perfTimer("integrate"); int numEntities = _otherEntities.size(); for (int i = 0; i < numEntities; ++i) { _otherEntities[i]->stepForward(deltaTime); } - _ragdoll->stepForward(deltaTime); + if (_ragdoll) { + _ragdoll->stepForward(deltaTime); + } int numDolls = _otherRagdolls.size(); for (int i = 0; i < numDolls; ++i) { _otherRagdolls[i]->stepForward(deltaTime); diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/shared/src/PhysicsSimulation.h index 955029c174..12506e23d0 100644 --- a/libraries/shared/src/PhysicsSimulation.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_PhysicsSimulation -#define hifi_PhysicsSimulation +#ifndef hifi_PhysicsSimulation_h +#define hifi_PhysicsSimulation_h #include #include @@ -18,6 +18,7 @@ #include "CollisionInfo.h" #include "ContactPoint.h" +#include "RayIntersectionInfo.h" class PhysicsEntity; class Ragdoll; @@ -54,6 +55,12 @@ public: /// \return distance of largest movement void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec); + /// \param intersection collision info about ray hit + /// \return true if ray hits any shape that doesn't belong to the main ragdoll/entity + bool findFloorRayIntersection(RayIntersectionInfo& hit) const; + + bool getShapeCollisions(const Shape* shape, CollisionList& collisions) const; + protected: void integrate(float deltaTime); @@ -80,4 +87,4 @@ private: QMap _contacts; }; -#endif // hifi_PhysicsSimulation +#endif // hifi_PhysicsSimulation_h From b94b737f268eb7c4931ebedb3f5d3117f740fa34 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 12 Sep 2014 17:56:25 -0700 Subject: [PATCH 15/45] fix build for no XMPP --- interface/src/scripting/GlobalServicesScriptingInterface.cpp | 4 +++- interface/src/scripting/GlobalServicesScriptingInterface.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/GlobalServicesScriptingInterface.cpp index 8c117a4a11..26fa7c564b 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/GlobalServicesScriptingInterface.cpp @@ -104,10 +104,12 @@ void GlobalServicesScriptingInterface::loggedOut() { emit GlobalServicesScriptingInterface::disconnected(QString("logout")); } +#ifdef HAVE_QXMPP void GlobalServicesScriptingInterface::messageReceived(const QXmppMessage& message) { if (message.type() != QXmppMessage::GroupChat) { return; } const QXmppMucRoom* publicChatRoom = XmppClient::getInstance().getPublicChatRoom(); emit GlobalServicesScriptingInterface::incomingMessage(message.from().right(message.from().count() - 1 - publicChatRoom->jid().count()), message.body()); -} \ No newline at end of file +} +#endif // HAVE_QXMPP diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.h b/interface/src/scripting/GlobalServicesScriptingInterface.h index 370ba1ade9..44b2f7c1bf 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.h +++ b/interface/src/scripting/GlobalServicesScriptingInterface.h @@ -24,7 +24,7 @@ #include #include -#endif +#endif // HAVE_QXMPP class GlobalServicesScriptingInterface : public QObject { Q_OBJECT @@ -47,7 +47,9 @@ private slots: void loggedOut(); void onConnected(); void participantsChanged(); +#ifdef HAVE_QXMPP void messageReceived(const QXmppMessage& message); +#endif // HAVE_QXMPP signals: void connected(); From 2f8ef6b4fba30e73ef2f8d7dececabb4e3692d88 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Fri, 12 Sep 2014 18:31:33 -0700 Subject: [PATCH 16/45] new head penumbra gain calculations --- assignment-client/src/audio/AudioMixer.cpp | 92 +++++++++++++--------- 1 file changed, 56 insertions(+), 36 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 044396f844..28c59a1e6c 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -315,53 +315,73 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* rotatedSourcePosition.y = 0.0f; // produce an oriented angle about the y-axis - bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), - glm::normalize(rotatedSourcePosition), - glm::vec3(0.0f, 1.0f, 0.0f)); + float bearingAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f), + glm::normalize(rotatedSourcePosition), + glm::vec3(0.0f, -1.0f, 0.0f)); + const float TWO_OVER_PI = 2.0f / PI; - // if the source is in the range (-pi/2,+pi/2) (e.g, -Z from the listener's perspective - if (bearingRelativeAngleToSource < -PI_OVER_TWO || bearingRelativeAngleToSource > PI_OVER_TWO) - { - AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter(); + const float ZERO_DB = 1.0f; + const float NEGATIVE_ONE_DB = 0.891f; + const float NEGATIVE_THREE_DB = 0.708f; - const float FULL_POWER = 1.0f; - const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f; - const float HALF_POWER = SQUARE_ROOT_OF_TWO_OVER_TWO; - - const float ONE_OVER_TWO_PI = 1.0f / TWO_PI; - const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; - - // calculate the updated gain, frequency and slope. - const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency - const float penumbraFilterSlope = SQUARE_ROOT_OF_TWO_OVER_TWO; // constant slope + const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f; + + const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency + const float penumbraFilterSlope = NEGATIVE_THREE_DB; // constant slope + + float penumbraFilterGainL; + float penumbraFilterGainR; + + // variable gain calculation broken down by quadrent + if (bearingAngleToSource < -PI_OVER_TWO && bearingAngleToSource > -PI) { + // gainL(-pi/2,0b)->(-pi,-1db) + penumbraFilterGainL = TWO_OVER_PI * + (ZERO_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + ZERO_DB; + // gainR(-pi/2,-3db)->(-pi,-1db) + penumbraFilterGainR = TWO_OVER_PI * + (NEGATIVE_THREE_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + NEGATIVE_THREE_DB; + } + else if (bearingAngleToSource <= PI && bearingAngleToSource > PI_OVER_TWO) { + // gainL(+pi,-1db)->(pi/2,-3db) + penumbraFilterGainL = TWO_OVER_PI * + (NEGATIVE_ONE_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; + // gainR(+pi,-1db)->(pi/2,0db) + penumbraFilterGainR = TWO_OVER_PI * + (NEGATIVE_ONE_DB - ZERO_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; + } + else if (bearingAngleToSource <= PI_OVER_TWO && bearingAngleToSource > 0) { + // gainL(+pi/2,-3db)->(0,0db) + penumbraFilterGainL = TWO_OVER_PI * + (NEGATIVE_THREE_DB - ZERO_DB) * (bearingAngleToSource - PI_OVER_TWO) + NEGATIVE_THREE_DB; + // gainR(+p1/2,0db)->(0,0db) + penumbraFilterGainR = ZERO_DB; + } + else { + // gainL(0,0db)->(-pi/2,0db) + penumbraFilterGainL = ZERO_DB; + // gainR(0,0db)->(-pi/2,-3db) + penumbraFilterGainR = TWO_OVER_PI * + (ZERO_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource) + ZERO_DB; + } - const float penumbraFilterGainL = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ? - ((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + FULL_POWER) : - ((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); - - const float penumbraFilterGainR = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ? - ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + HALF_POWER) : - ((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER); - #if 0 - float distanceBetween = glm::length(relativePosition); - qDebug() << "avatar=" - << listeningNodeStream + qDebug() << "avatar=" + << listeningNodeStream << "gainL=" << penumbraFilterGainL << "gainR=" << penumbraFilterGainR << "angle=" - << bearingRelativeAngleToSource - << "dist=" - << distanceBetween; + << bearingAngleToSource; #endif - // set the gain on both filter channels - penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); - penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); + + // set the gain on both filter channels + AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter(); + + penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope); + penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope); - penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); - } + penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); } return 1; From 2c7d15c01b09a23e115df75ff3025911982e5415 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Fri, 12 Sep 2014 18:32:28 -0700 Subject: [PATCH 17/45] disable input gain/mute --- interface/src/Audio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 885df74d2b..80500d392d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -511,7 +511,7 @@ void Audio::handleAudioInput() { _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, false /*copy in*/); - _inputGain.render(_inputFrameBuffer); // input/mic gain+mute + // _inputGain.render(_inputFrameBuffer); // input/mic gain+mute // Add audio source injection if enabled if (_audioSourceInjectEnabled && !_muted) { From 09370840d5154a5609029bab00936b21a0b665c8 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sat, 13 Sep 2014 00:35:02 -0700 Subject: [PATCH 18/45] coding standard --- assignment-client/src/audio/AudioMixer.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 28c59a1e6c..bfe2f7e249 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -340,30 +340,26 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // gainR(-pi/2,-3db)->(-pi,-1db) penumbraFilterGainR = TWO_OVER_PI * (NEGATIVE_THREE_DB - NEGATIVE_ONE_DB) * (bearingAngleToSource + PI_OVER_TWO) + NEGATIVE_THREE_DB; - } - else if (bearingAngleToSource <= PI && bearingAngleToSource > PI_OVER_TWO) { + } else if (bearingAngleToSource <= PI && bearingAngleToSource > PI_OVER_TWO) { // gainL(+pi,-1db)->(pi/2,-3db) penumbraFilterGainL = TWO_OVER_PI * (NEGATIVE_ONE_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; // gainR(+pi,-1db)->(pi/2,0db) penumbraFilterGainR = TWO_OVER_PI * (NEGATIVE_ONE_DB - ZERO_DB) * (bearingAngleToSource - PI) + NEGATIVE_ONE_DB; - } - else if (bearingAngleToSource <= PI_OVER_TWO && bearingAngleToSource > 0) { + } else if (bearingAngleToSource <= PI_OVER_TWO && bearingAngleToSource > 0) { // gainL(+pi/2,-3db)->(0,0db) penumbraFilterGainL = TWO_OVER_PI * (NEGATIVE_THREE_DB - ZERO_DB) * (bearingAngleToSource - PI_OVER_TWO) + NEGATIVE_THREE_DB; // gainR(+p1/2,0db)->(0,0db) penumbraFilterGainR = ZERO_DB; - } - else { + } else { // gainL(0,0db)->(-pi/2,0db) penumbraFilterGainL = ZERO_DB; // gainR(0,0db)->(-pi/2,-3db) penumbraFilterGainR = TWO_OVER_PI * (ZERO_DB - NEGATIVE_THREE_DB) * (bearingAngleToSource) + ZERO_DB; } - #if 0 qDebug() << "avatar=" << listeningNodeStream From 973479d1ac4e7f030f770a21512cdbbf6520489c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 07:57:45 -0700 Subject: [PATCH 19/45] fix warning in ByteCountCoded<> --- libraries/shared/src/ByteCountCoding.h | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h index ada7e7a4cb..a05ec2be3b 100644 --- a/libraries/shared/src/ByteCountCoding.h +++ b/libraries/shared/src/ByteCountCoding.h @@ -59,23 +59,15 @@ template inline QByteArray& operator>>(QByteArray& in, ByteCountCode template inline QByteArray ByteCountCoded::encode() const { QByteArray output; - //qDebug() << "data="; - //outputBufferBits((const unsigned char*)&data, sizeof(data)); - - T totalBits = sizeof(data) * BITS_IN_BYTE; - //qDebug() << "totalBits=" << totalBits; - T valueBits = totalBits; + int totalBits = sizeof(data) * BITS_IN_BYTE; + int valueBits = totalBits; bool firstValueFound = false; T temp = data; T lastBitMask = (T)(1) << (totalBits - 1); - //qDebug() << "lastBitMask="; - //outputBufferBits((const unsigned char*)&lastBitMask, sizeof(lastBitMask)); - // determine the number of bits that the value takes - for (int bitAt = 0; bitAt < totalBits; bitAt++) { + for (size_t bitAt = 0; bitAt < totalBits; bitAt++) { T bitValue = (temp & lastBitMask) == lastBitMask; - //qDebug() << "bitValue[" << bitAt <<"]=" << bitValue; if (!firstValueFound) { if (bitValue == 0) { valueBits--; @@ -85,17 +77,12 @@ template inline QByteArray ByteCountCoded::encode() const { } temp = temp << 1; } - //qDebug() << "valueBits=" << valueBits; // calculate the number of total bytes, including our header // BITS_IN_BYTE-1 because we need to code the number of bytes in the header // + 1 because we always take at least 1 byte, even if number of bits is less than a bytes worth - int numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1; - //qDebug() << "numberOfBytes=" << numberOfBytes; + size_t numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1; - //int numberOfBits = numberOfBytes + valueBits; - //qDebug() << "numberOfBits=" << numberOfBits; - output.fill(0, numberOfBytes); // next pack the number of header bits in, the first N-1 to be set to 1, the last to be set to 0 From c5e02c4ec5c16dfc6dcd04fe40fea1a2d87fd1d1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 07:58:49 -0700 Subject: [PATCH 20/45] removed unused lastAttemptedSubTree --- assignment-client/src/octree/OctreeSendThread.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 116a1dcc84..f905ae652f 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -394,8 +394,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus int extraPackingAttempts = 0; bool completedScene = false; - OctreeElement* lastAttemptedSubTree = NULL; - while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval && !nodeData->isShuttingDown()) { float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; float encodeElapsedUsec = OctreeServer::SKIP_TIME; @@ -408,9 +406,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus if (!nodeData->elementBag.isEmpty()) { OctreeElement* subTree = nodeData->elementBag.extract(); - // TODO: look into breaking early if the same subtree keeps repeating for inclusion... - lastAttemptedSubTree = subTree; - /* TODO: Looking for a way to prevent locking and encoding a tree that is not // going to result in any packets being sent... // @@ -516,8 +511,6 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus packetsSentThisInterval += handlePacketSend(nodeData, trueBytesSent, truePacketsSent); } - lastAttemptedSubTree = NULL; // reset this - nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); extraPackingAttempts = 0; quint64 compressAndWriteEnd = usecTimestampNow(); From d10bd6857dc43e2b04af11e9c4b25d3539faa79b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:02:38 -0700 Subject: [PATCH 21/45] add use of missingSet in release build to silence warning --- tests/networking/src/SequenceNumberStatsTests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index ded67b1ab6..204c77eeb3 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -254,6 +254,9 @@ void SequenceNumberStatsTests::pruneTest() { const QSet& missingSet = stats.getMissingSet(); assert(missingSet.size() <= 1000); + if (missingSet.size() > 1000) { + qDebug() << "FAIL: missingSet larger than 1000."; + } for (int i = 0; i < 10; i++) { assert(missingSet.contains(highestSkipped2)); From 51b66bdee30ef3bad99117016029973c5f24d1ce Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:14:31 -0700 Subject: [PATCH 22/45] fixed warnings and converted several asserts to debug messages --- libraries/entities/src/EntityTree.cpp | 57 +++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 8cec19eddd..9a6e53b17a 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -79,7 +79,10 @@ void EntityTree::addEntityItem(EntityItem* entityItem) { // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityItemID entityID = entityItem->getEntityItemID(); EntityTreeElement* containingElement = getContainingElement(entityID); - assert(containingElement == NULL); // don't call addEntityItem() on existing entity items + if (containingElement) { + qDebug() << "UNEXPECTED!!!! don't call addEntityItem() on existing entity items. entityID=" << entityID; + return; + } // Recurse the tree and store the entity in the correct tree element AddEntityOperator theOperator(this, entityItem); @@ -95,14 +98,13 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityTreeElement* containingElement = getContainingElement(entityID); if (!containingElement) { - //assert(containingElement); // don't call updateEntity() on entity items that don't exist qDebug() << "UNEXPECTED!!!! EntityTree::updateEntity() entityID doesn't exist!!! entityID=" << entityID; return false; } EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID); if (!existingEntity) { - assert(existingEntity); // don't call updateEntity() on entity items that don't exist + qDebug() << "UNEXPECTED!!!! don't call updateEntity() on entity items that don't exist. entityID=" << entityID; return false; } @@ -118,8 +120,8 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp containingElement = getContainingElement(entityID); if (!containingElement) { - qDebug() << "after updateEntity() we no longer have a containing element???"; - assert(containingElement); // don't call updateEntity() on entity items that don't exist + qDebug() << "UNEXPECTED!!!! after updateEntity() we no longer have a containing element??? entityID=" << entityID; + return false; } return true; @@ -127,19 +129,20 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItem* result = NULL; + // NOTE: This method is used in the client and the server tree. In the client, it's possible to create EntityItems // that do not yet have known IDs. In the server tree however we don't want to have entities without known IDs. if (getIsServer() && !entityID.isKnownID) { - //assert(entityID.isKnownID); qDebug() << "UNEXPECTED!!! ----- EntityTree::addEntity()... (getIsSever() && !entityID.isKnownID)"; + return result; } - EntityItem* result = NULL; // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityTreeElement* containingElement = getContainingElement(entityID); if (containingElement) { - qDebug() << "UNEXPECTED!!! ----- EntityTree::addEntity()... entityID=" << entityID << "containingElement=" << containingElement; - assert(containingElement == NULL); // don't call addEntity() on existing entity items + qDebug() << "UNEXPECTED!!! ----- don't call addEntity() on existing entity items. entityID=" << entityID + << "containingElement=" << containingElement; return result; } @@ -239,9 +242,9 @@ void EntityTree::removeEntityFromSimulationLists(const EntityItemID& entityID) { /// based to known IDs. This means we don't have to recurse the tree to mark the changed path as dirty. void EntityTree::handleAddEntityResponse(const QByteArray& packet) { - //assert(getIsClient()); // we should only call this on client trees if (!getIsClient()) { qDebug() << "UNEXPECTED!!! EntityTree::handleAddEntityResponse() with !getIsClient() ***"; + return; } const unsigned char* dataAt = reinterpret_cast(packet.data()); @@ -430,8 +433,15 @@ EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) / } EntityItemID EntityTree::assignEntityID(const EntityItemID& entityItemID) { - assert(getIsServer()); // NOTE: this only operates on an server tree. - assert(!getContainingElement(entityItemID)); // NOTE: don't call this for existing entityIDs + if (!getIsServer()) { + qDebug() << "UNEXPECTED!!! assignEntityID should only be called on a server tree. entityItemID:" << entityItemID; + return entityItemID; + } + + if (getContainingElement(entityItemID)) { + qDebug() << "UNEXPECTED!!! don't call assignEntityID() for existing entityIDs. entityItemID:" << entityItemID; + return entityItemID; + } // The EntityItemID is responsible for assigning actual IDs and keeping track of them. return entityItemID.assignActualIDForToken(); @@ -440,7 +450,10 @@ EntityItemID EntityTree::assignEntityID(const EntityItemID& entityItemID) { int EntityTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { - assert(getIsServer()); // NOTE: this only operates on an server tree. + if (!getIsServer()) { + qDebug() << "UNEXPECTED!!! processEditPacketData() should only be called on a server tree."; + return 0; + } int processedBytes = 0; // we handle these types of "edit" packets @@ -969,9 +982,21 @@ EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityIt // TODO: do we need to make this thread safe? Or is it acceptable as is void EntityTree::resetContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) { - assert(entityItemID.id != UNKNOWN_ENTITY_ID); - assert(entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN); - assert(element); + if (entityItemID.id == UNKNOWN_ENTITY_ID) { + //assert(entityItemID.id != UNKNOWN_ENTITY_ID); + qDebug() << "UNEXPECTED! resetContainingElement() called with UNKNOWN_ENTITY_ID. entityItemID:" << entityItemID; + return; + } + if (entityItemID.creatorTokenID == UNKNOWN_ENTITY_TOKEN) { + //assert(entityItemID.creatorTokenID != UNKNOWN_ENTITY_TOKEN); + qDebug() << "UNEXPECTED! resetContainingElement() called with UNKNOWN_ENTITY_TOKEN. entityItemID:" << entityItemID; + return; + } + if (!element) { + //assert(element); + qDebug() << "UNEXPECTED! resetContainingElement() called with NULL element. entityItemID:" << entityItemID; + return; + } // remove the old version with the creatorTokenID EntityItemID creatorTokenVersion; From 64479b067e501c98efa2aa8d691e03956513e230 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:21:23 -0700 Subject: [PATCH 23/45] fix more warnings, replace assert with debug message --- libraries/entities/src/EntityTypes.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index f6e78275d3..8851b04b7e 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -64,7 +64,9 @@ private: // static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties); #define REGISTER_ENTITY_TYPE_WITH_FACTORY(x,y) static bool x##Registration = \ EntityTypes::registerEntityType(EntityTypes::x, #x, y); \ - assert(x##Registration); + if (!x##Registration) { \ + qDebug() << "UNEXPECTED: REGISTER_ENTITY_TYPE_WITH_FACTORY(" #x "," #y ") FAILED.!"; \ + } #endif // hifi_EntityTypes_h From e76ffb2ecbe18673e40f4c7483541fc6ca09f319 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:44:13 -0700 Subject: [PATCH 24/45] fixed another warning --- libraries/shared/src/ByteCountCoding.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ByteCountCoding.h b/libraries/shared/src/ByteCountCoding.h index a05ec2be3b..ddaa750e86 100644 --- a/libraries/shared/src/ByteCountCoding.h +++ b/libraries/shared/src/ByteCountCoding.h @@ -66,7 +66,7 @@ template inline QByteArray ByteCountCoded::encode() const { T lastBitMask = (T)(1) << (totalBits - 1); // determine the number of bits that the value takes - for (size_t bitAt = 0; bitAt < totalBits; bitAt++) { + for (int bitAt = 0; bitAt < totalBits; bitAt++) { T bitValue = (temp & lastBitMask) == lastBitMask; if (!firstValueFound) { if (bitValue == 0) { @@ -81,7 +81,7 @@ template inline QByteArray ByteCountCoded::encode() const { // calculate the number of total bytes, including our header // BITS_IN_BYTE-1 because we need to code the number of bytes in the header // + 1 because we always take at least 1 byte, even if number of bits is less than a bytes worth - size_t numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1; + int numberOfBytes = (valueBits / (BITS_IN_BYTE - 1)) + 1; output.fill(0, numberOfBytes); From be8994f3a3bfafff5b92b7da24bb22737916353f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:47:18 -0700 Subject: [PATCH 25/45] attempt to fix warning --- libraries/audio/src/AudioSourceNoise.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioSourceNoise.h b/libraries/audio/src/AudioSourceNoise.h index 26cb21a065..2262c695a9 100644 --- a/libraries/audio/src/AudioSourceNoise.h +++ b/libraries/audio/src/AudioSourceNoise.h @@ -48,7 +48,7 @@ public: _runningSum = 0; _index = 0; - _indexMask = (1 << _randomRows) - 1; + _indexMask = ((uint16_t)1 << _randomRows) - 1; _scale = 1.0f / ((_randomRows + 1) * (1 << (_randomBits - 1))); } From 4ae3360f838cd51ba96d40df69925040e42a0462 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 08:50:52 -0700 Subject: [PATCH 26/45] fix warning --- libraries/shared/src/PropertyFlags.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/PropertyFlags.h b/libraries/shared/src/PropertyFlags.h index 846c4886b0..04fe1475f7 100644 --- a/libraries/shared/src/PropertyFlags.h +++ b/libraries/shared/src/PropertyFlags.h @@ -419,7 +419,7 @@ template inline PropertyFlags PropertyFlags::operator } template inline void PropertyFlags::shinkIfNeeded() { - bool maxFlagWas = _maxFlag; + int maxFlagWas = _maxFlag; while (_maxFlag >= 0) { if (_flags.testBit(_maxFlag)) { break; From a6178a137ff31933078f831ce2195536aea88d40 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:00:18 -0700 Subject: [PATCH 27/45] fix windows warnings --- interface/src/devices/DdeFaceTracker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index aab3e1deb4..b5cf1c3bd6 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -172,8 +172,8 @@ float DdeFaceTracker::getBlendshapeCoefficient(int index) const { return (index >= 0 && index < (int)_blendshapeCoefficients.size()) ? _blendshapeCoefficients[index] : 0.0f; } -static const float DDE_MIN_RANGE = -0.2; -static const float DDE_MAX_RANGE = 1.5; +static const float DDE_MIN_RANGE = -0.2f; +static const float DDE_MAX_RANGE = 1.5f; float rescaleCoef(float ddeCoef) { return (ddeCoef - DDE_MIN_RANGE) / (DDE_MAX_RANGE - DDE_MIN_RANGE); } From a95e5487fc9eddc04bc42bfef78aff8879b706ba Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:00:47 -0700 Subject: [PATCH 28/45] fix type signed/unsigned warning --- interface/src/devices/CaraFaceTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/CaraFaceTracker.cpp b/interface/src/devices/CaraFaceTracker.cpp index 9f056fab9b..bc2c4bb2d1 100644 --- a/interface/src/devices/CaraFaceTracker.cpp +++ b/interface/src/devices/CaraFaceTracker.cpp @@ -309,7 +309,7 @@ void CaraFaceTracker::bindTo(const QHostAddress& host, quint16 port) { } bool CaraFaceTracker::isActive() const { - static const int ACTIVE_TIMEOUT_USECS = 3000000; //3 secs + static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS); } From d6ebbd86330d42f7dbd8206bd094f76afb8d0ff8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:01:27 -0700 Subject: [PATCH 29/45] fixed use of uninitialized variable warning --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 903e443cda..2b41b1f0c9 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -1743,7 +1743,7 @@ int VoxelAugmentVisitor::visit(MetavoxelInfo& info) { for (int z = 0; z < expanded; z++) { const QRgb* colorY = colorZ; for (int y = 0; y < expanded; y++) { - int lastIndex; + int lastIndex = 0; const QRgb* colorX = colorY; for (int x = 0; x < expanded; x++) { int alpha0 = colorX[0] >> ALPHA_OFFSET; From 9c34aed36d1ff7b683c3533fc376419aabdd8192 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:02:48 -0700 Subject: [PATCH 30/45] fix type signed/unsigned warning --- interface/src/devices/DdeFaceTracker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/devices/DdeFaceTracker.cpp b/interface/src/devices/DdeFaceTracker.cpp index b5cf1c3bd6..b9f7e338ca 100644 --- a/interface/src/devices/DdeFaceTracker.cpp +++ b/interface/src/devices/DdeFaceTracker.cpp @@ -122,7 +122,7 @@ void DdeFaceTracker::bindTo(const QHostAddress& host, quint16 port) { } bool DdeFaceTracker::isActive() const { - static const int ACTIVE_TIMEOUT_USECS = 3000000; //3 secs + static const quint64 ACTIVE_TIMEOUT_USECS = 3000000; //3 secs return (usecTimestampNow() - _lastReceiveTimestamp < ACTIVE_TIMEOUT_USECS); } From 04772f6e5dc909a01e3d12470216efa8024be5b5 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:09:18 -0700 Subject: [PATCH 31/45] fix more signed/unsigned warnings --- libraries/audio/src/InboundAudioStream.cpp | 2 +- libraries/audio/src/InboundAudioStream.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/InboundAudioStream.cpp b/libraries/audio/src/InboundAudioStream.cpp index e12dbb42a9..cc474ea491 100644 --- a/libraries/audio/src/InboundAudioStream.cpp +++ b/libraries/audio/src/InboundAudioStream.cpp @@ -377,7 +377,7 @@ void InboundAudioStream::packetReceivedUpdateTimingStats() { // update our timegap stats and desired jitter buffer frames if necessary // discard the first few packets we receive since they usually have gaps that aren't represensative of normal jitter - const int NUM_INITIAL_PACKETS_DISCARD = 3; + const quint32 NUM_INITIAL_PACKETS_DISCARD = 3; quint64 now = usecTimestampNow(); if (_incomingSequenceNumberStats.getReceived() > NUM_INITIAL_PACKETS_DISCARD) { quint64 gap = now - _lastPacketReceivedTime; diff --git a/libraries/audio/src/InboundAudioStream.h b/libraries/audio/src/InboundAudioStream.h index ca9591a746..a395b1c6c8 100644 --- a/libraries/audio/src/InboundAudioStream.h +++ b/libraries/audio/src/InboundAudioStream.h @@ -33,7 +33,7 @@ const int STATS_FOR_STATS_PACKET_WINDOW_SECONDS = 30; // this controls the window size of the time-weighted avg of frames available. Every time the window fills up, // _currentJitterBufferFrames is updated with the time-weighted avg and the running time-weighted avg is reset. -const int FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND; +const quint64 FRAMES_AVAILABLE_STAT_WINDOW_USECS = 10 * USECS_PER_SECOND; // default values for members of the Settings struct const int DEFAULT_MAX_FRAMES_OVER_DESIRED = 10; From 95a63cf1314a571e3bb81f876ee9aacb6f131e84 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:14:52 -0700 Subject: [PATCH 32/45] fix windows warning and change QByteArray initializer to match documented prototype --- interface/src/ui/MetavoxelEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index cf839f6800..0f5c8dd048 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1022,7 +1022,7 @@ void ImportHeightfieldTool::apply() { QByteArray color; if (buffer->getColor().isEmpty()) { - const int WHITE_VALUE = 0xFF; + const char WHITE_VALUE = 0xFF; color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); } else { color = buffer->getUnextendedColor(); From d92d2d57a0bec3e438198371bdb1eaf961c43c01 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:28:28 -0700 Subject: [PATCH 33/45] one more shot at these warnings --- interface/src/ui/MetavoxelEditor.cpp | 2 +- libraries/audio/src/AudioSourceNoise.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MetavoxelEditor.cpp b/interface/src/ui/MetavoxelEditor.cpp index 0f5c8dd048..64cf12373d 100644 --- a/interface/src/ui/MetavoxelEditor.cpp +++ b/interface/src/ui/MetavoxelEditor.cpp @@ -1022,7 +1022,7 @@ void ImportHeightfieldTool::apply() { QByteArray color; if (buffer->getColor().isEmpty()) { - const char WHITE_VALUE = 0xFF; + const unsigned char WHITE_VALUE = 0xFF; color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE); } else { color = buffer->getUnextendedColor(); diff --git a/libraries/audio/src/AudioSourceNoise.h b/libraries/audio/src/AudioSourceNoise.h index 2262c695a9..fb83ac26aa 100644 --- a/libraries/audio/src/AudioSourceNoise.h +++ b/libraries/audio/src/AudioSourceNoise.h @@ -48,7 +48,7 @@ public: _runningSum = 0; _index = 0; - _indexMask = ((uint16_t)1 << _randomRows) - 1; + _indexMask = ((uint16_t)1 << _randomRows) - (uint16_t)1; _scale = 1.0f / ((_randomRows + 1) * (1 << (_randomBits - 1))); } From ac27c36c3da4d64010d9e34feca2263afd492243 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 09:40:44 -0700 Subject: [PATCH 34/45] one more shot at these warnings --- libraries/audio/src/AudioSourceNoise.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioSourceNoise.h b/libraries/audio/src/AudioSourceNoise.h index fb83ac26aa..3e71703893 100644 --- a/libraries/audio/src/AudioSourceNoise.h +++ b/libraries/audio/src/AudioSourceNoise.h @@ -48,7 +48,7 @@ public: _runningSum = 0; _index = 0; - _indexMask = ((uint16_t)1 << _randomRows) - (uint16_t)1; + _indexMask = (uint16_t)((1 << _randomRows) - 1); _scale = 1.0f / ((_randomRows + 1) * (1 << (_randomBits - 1))); } From c32d5db75cc72172a9064eaedecb362670c7b6e1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 13 Sep 2014 15:32:21 -0700 Subject: [PATCH 35/45] fix bug with attachments being stretched --- interface/src/renderer/Model.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 85e4e9c94f..51e1249bd9 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -894,7 +894,21 @@ void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions) { } void Model::setScaleToFit(bool scaleToFit, float largestDimension) { - setScaleToFit(scaleToFit, glm::vec3(largestDimension, largestDimension, largestDimension)); + if (_scaleToFit != scaleToFit || glm::length(_scaleToFitDimensions) != largestDimension) { + _scaleToFit = scaleToFit; + + // we only need to do this work if we're "turning on" scale to fit. + if (scaleToFit) { + Extents modelMeshExtents = getUnscaledMeshExtents(); + float maxDimension = glm::distance(modelMeshExtents.maximum, modelMeshExtents.minimum); + float maxScale = largestDimension / maxDimension; + glm::vec3 modelMeshDimensions = modelMeshExtents.maximum - modelMeshExtents.minimum; + glm::vec3 dimensions = modelMeshDimensions * maxScale; + + _scaleToFitDimensions = dimensions; + _scaledToFit = false; // force rescaling + } + } } void Model::scaleToFit() { From cb7a6a7af537efeb6589df75d511ccc138e0bf7a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 14 Sep 2014 09:43:55 -0700 Subject: [PATCH 36/45] don't blindly return all values if a type is not passed --- domain-server/src/DomainServerSettingsManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 9c741b2a3b..ae07374d95 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -22,7 +22,7 @@ #include "DomainServerSettingsManager.h" const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/web/settings/describe.json"; -const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/config.json"; +const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/settings.json"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionObject(), @@ -61,7 +61,7 @@ bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connec if (typeValue.isEmpty()) { // combine the description object and our current settings map responseObject["descriptions"] = _descriptionObject; - responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); + // responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); } else { // convert the string type value to a QJsonValue QJsonValue queryType = QJsonValue(typeValue.toInt()); From 4d80417e3c5530d2881dfb27348e241e74fe186f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 14 Sep 2014 09:45:02 -0700 Subject: [PATCH 37/45] don't rename the config file to settings so existing nodes work --- domain-server/src/DomainServerSettingsManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index ae07374d95..9c741b2a3b 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -22,7 +22,7 @@ #include "DomainServerSettingsManager.h" const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/web/settings/describe.json"; -const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/settings.json"; +const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/config.json"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionObject(), @@ -61,7 +61,7 @@ bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connec if (typeValue.isEmpty()) { // combine the description object and our current settings map responseObject["descriptions"] = _descriptionObject; - // responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); + responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); } else { // convert the string type value to a QJsonValue QJsonValue queryType = QJsonValue(typeValue.toInt()); From d6dca8469462cf600e0b1ffab434f0bfc89b3f10 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 14 Sep 2014 09:45:34 -0700 Subject: [PATCH 38/45] add back commenting out of blind values return --- domain-server/src/DomainServerSettingsManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 9c741b2a3b..00d15be648 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -61,7 +61,7 @@ bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connec if (typeValue.isEmpty()) { // combine the description object and our current settings map responseObject["descriptions"] = _descriptionObject; - responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); + // responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); } else { // convert the string type value to a QJsonValue QJsonValue queryType = QJsonValue(typeValue.toInt()); From c98b69f24fc486a8273e7f8641210b0931ec5656 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Sun, 14 Sep 2014 09:52:10 -0700 Subject: [PATCH 39/45] don't comment value return, rename domain settings file --- domain-server/src/DomainServerSettingsManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/domain-server/src/DomainServerSettingsManager.cpp b/domain-server/src/DomainServerSettingsManager.cpp index 00d15be648..6dff445fca 100644 --- a/domain-server/src/DomainServerSettingsManager.cpp +++ b/domain-server/src/DomainServerSettingsManager.cpp @@ -22,7 +22,7 @@ #include "DomainServerSettingsManager.h" const QString SETTINGS_DESCRIPTION_RELATIVE_PATH = "/resources/web/settings/describe.json"; -const QString SETTINGS_CONFIG_FILE_RELATIVE_PATH = "/resources/config.json"; +const QString SETTINGS_JSON_FILE_RELATIVE_PATH = "/resources/settings.json"; DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionObject(), @@ -35,7 +35,7 @@ DomainServerSettingsManager::DomainServerSettingsManager() : _descriptionObject = QJsonDocument::fromJson(descriptionFile.readAll()).object(); // load the existing config file to get the current values - QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_CONFIG_FILE_RELATIVE_PATH); + QFile configFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH); if (configFile.exists()) { configFile.open(QIODevice::ReadOnly); @@ -61,7 +61,7 @@ bool DomainServerSettingsManager::handlePublicHTTPRequest(HTTPConnection* connec if (typeValue.isEmpty()) { // combine the description object and our current settings map responseObject["descriptions"] = _descriptionObject; - // responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); + responseObject["values"] = QJsonDocument::fromVariant(_settingsMap).object(); } else { // convert the string type value to a QJsonValue QJsonValue queryType = QJsonValue(typeValue.toInt()); @@ -197,7 +197,7 @@ QByteArray DomainServerSettingsManager::getJSONSettingsMap() const { } void DomainServerSettingsManager::persistToFile() { - QFile settingsFile(QCoreApplication::applicationDirPath() + SETTINGS_CONFIG_FILE_RELATIVE_PATH); + QFile settingsFile(QCoreApplication::applicationDirPath() + SETTINGS_JSON_FILE_RELATIVE_PATH); if (settingsFile.open(QIODevice::WriteOnly)) { settingsFile.write(getJSONSettingsMap()); From fd9f64cb44cf4c07cea3f5d82fa032df32263820 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 15 Sep 2014 08:41:18 -0700 Subject: [PATCH 40/45] normalize all quats before packing --- libraries/shared/src/GLMHelpers.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/GLMHelpers.cpp b/libraries/shared/src/GLMHelpers.cpp index 566983679b..0c7f126893 100644 --- a/libraries/shared/src/GLMHelpers.cpp +++ b/libraries/shared/src/GLMHelpers.cpp @@ -86,12 +86,13 @@ int unpackFloatAngleFromTwoByte(const uint16_t* byteAnglePointer, float* destina } int packOrientationQuatToBytes(unsigned char* buffer, const glm::quat& quatInput) { + glm::quat quatNormalized = glm::normalize(quatInput); const float QUAT_PART_CONVERSION_RATIO = (std::numeric_limits::max() / 2.f); uint16_t quatParts[4]; - quatParts[0] = floorf((quatInput.x + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[1] = floorf((quatInput.y + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[2] = floorf((quatInput.z + 1.f) * QUAT_PART_CONVERSION_RATIO); - quatParts[3] = floorf((quatInput.w + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[0] = floorf((quatNormalized.x + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[1] = floorf((quatNormalized.y + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[2] = floorf((quatNormalized.z + 1.f) * QUAT_PART_CONVERSION_RATIO); + quatParts[3] = floorf((quatNormalized.w + 1.f) * QUAT_PART_CONVERSION_RATIO); memcpy(buffer, &quatParts, sizeof(quatParts)); return sizeof(quatParts); From 6a0b3fa202c8ba4d34f29fff527cfcbcc567a2b2 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 15 Sep 2014 13:16:17 -0700 Subject: [PATCH 41/45] Ryan's new blendshape mappings for Mixamo. --- interface/src/ModelUploader.cpp | 83 ++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index 91c1770e6f..f11d035ebe 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -196,39 +196,56 @@ bool ModelUploader::zip() { // mixamo blendshapes if (!mapping.contains(BLENDSHAPE_FIELD) && geometry.applicationName == "mixamo.com") { QVariantHash blendshapes; - blendshapes.insert("EyeBlink_L", QVariantList() << "Blink_Left" << 1.0); - blendshapes.insert("EyeBlink_R", QVariantList() << "Blink_Right" << 1.0); - blendshapes.insert("EyeSquint_L", QVariantList() << "Squint_Left" << 1.0); - blendshapes.insert("EyeSquint_R", QVariantList() << "Squint_Right" << 1.0); - blendshapes.insert("EyeOpen_L", QVariantList() << "EyesWide_Left" << 1.0); - blendshapes.insert("EyeOpen_R", QVariantList() << "EyesWide_Right" << 1.0); - blendshapes.insert("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0); - blendshapes.insert("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0); - blendshapes.insert("BrowsU_L", QVariantList() << "BrowsUp_Left" << 1.0); - blendshapes.insert("BrowsU_R", QVariantList() << "BrowsUp_Right" << 1.0); - blendshapes.insert("JawFwd", QVariantList() << "JawForeward" << 1.0); - blendshapes.insert("JawOpen", QVariantList() << "Jaw_Down" << 1.0); - blendshapes.insert("JawLeft", QVariantList() << "Jaw_Left" << 1.0); - blendshapes.insert("JawRight", QVariantList() << "Jaw_Right" << 1.0); - blendshapes.insert("JawChew", QVariantList() << "Jaw_Up" << 1.0); - blendshapes.insert("MouthLeft", QVariantList() << "Midmouth_Left" << 1.0); - blendshapes.insert("MouthRight", QVariantList() << "Midmouth_Right" << 1.0); - blendshapes.insert("MouthFrown_L", QVariantList() << "Frown_Left" << 1.0); - blendshapes.insert("MouthFrown_R", QVariantList() << "Frown_Right" << 1.0); - blendshapes.insert("MouthSmile_L", QVariantList() << "Smile_Left" << 1.0); - blendshapes.insert("MouthSmile_R", QVariantList() << "Smile_Right" << 1.0); - blendshapes.insert("LipsUpperUp", QVariantList() << "UpperLipUp_Left" << 0.5); - blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Right" << 0.5); - blendshapes.insert("Puff", QVariantList() << "CheekPuff_Left" << 0.5); - blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Right" << 0.5); - blendshapes.insert("Sneer", QVariantList() << "NoseScrunch_Left" << 0.5); - blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Right" << 0.5); - blendshapes.insert("CheekSquint_L", QVariantList() << "Squint_Left" << 1.0); - blendshapes.insert("CheekSquint_R", QVariantList() << "Squint_Right" << 1.0); - blendshapes.insert("LipsPucker", QVariantList() << "MouthNarrow_Left" << 0.5); - blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Right" << 0.5); - blendshapes.insert("LipsLowerDown", QVariantList() << "LowerLipDown_Left" << 0.5); - blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Right" << 0.5); + blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0); + blendshapes.insertMulti("BrowsD_R", QVariantList() << "BrowsDown_Right" << 1.0); + blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Left" << 1.0); + blendshapes.insertMulti("BrowsU_C", QVariantList() << "BrowsUp_Right" << 1.0); + blendshapes.insertMulti("BrowsU_L", QVariantList() << "BrowsUp_Left" << 1.0); + blendshapes.insertMulti("BrowsU_R", QVariantList() << "BrowsUp_Right" << 1.0); + blendshapes.insertMulti("ChinLowerRaise", QVariantList() << "Jaw_Up" << 1.0); + blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Left" << 0.5); + blendshapes.insertMulti("ChinUpperRaise", QVariantList() << "UpperLipUp_Right" << 0.5); + blendshapes.insertMulti("EyeBlink_L", QVariantList() << "Blink_Left" << 1.0); + blendshapes.insertMulti("EyeBlink_R", QVariantList() << "Blink_Right" << 1.0); + blendshapes.insertMulti("EyeOpen_L", QVariantList() << "EyesWide_Left" << 1.0); + blendshapes.insertMulti("EyeOpen_R", QVariantList() << "EyesWide_Right" << 1.0); + blendshapes.insertMulti("EyeSquint_L", QVariantList() << "Squint_Left" << 1.0); + blendshapes.insertMulti("EyeSquint_R", QVariantList() << "Squint_Right" << 1.0); + blendshapes.insertMulti("JawFwd", QVariantList() << "JawForeward" << 1.0); + blendshapes.insertMulti("JawLeft", QVariantList() << "JawRotateY_Left" << 0.5); + blendshapes.insertMulti("JawOpen", QVariantList() << "MouthOpen" << 0.7); + blendshapes.insertMulti("JawRight", QVariantList() << "Jaw_Right" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "JawForeward" << 0.39); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "Jaw_Down" << 0.36); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Left" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthNarrow_Right" << 1.0); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Left" << 0.5); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "MouthWhistle_NarrowAdjust_Right" << 0.5); + blendshapes.insertMulti("LipsFunnel", QVariantList() << "TongueUp" << 1.0); + blendshapes.insertMulti("LipsLowerClose", QVariantList() << "LowerLipIn" << 1.0); + blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Left" << 0.7); + blendshapes.insertMulti("LipsLowerDown", QVariantList() << "LowerLipDown_Right" << 0.7); + blendshapes.insertMulti("LipsLowerOpen", QVariantList() << "LowerLipOut" << 1.0); + blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Left" << 1.0); + blendshapes.insertMulti("LipsPucker", QVariantList() << "MouthNarrow_Right" << 1.0); + blendshapes.insertMulti("LipsUpperClose", QVariantList() << "UpperLipIn" << 1.0); + blendshapes.insertMulti("LipsUpperOpen", QVariantList() << "UpperLipOut" << 1.0); + blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Left" << 0.7); + blendshapes.insertMulti("LipsUpperUp", QVariantList() << "UpperLipUp_Right" << 0.7); + blendshapes.insertMulti("MouthDimple_L", QVariantList() << "Smile_Left" << 0.25); + blendshapes.insertMulti("MouthDimple_R", QVariantList() << "Smile_Right" << 0.25); + blendshapes.insertMulti("MouthFrown_L", QVariantList() << "Frown_Left" << 1.0); + blendshapes.insertMulti("MouthFrown_R", QVariantList() << "Frown_Right" << 1.0); + blendshapes.insertMulti("MouthLeft", QVariantList() << "Midmouth_Left" << 1.0); + blendshapes.insertMulti("MouthRight", QVariantList() << "Midmouth_Right" << 1.0); + blendshapes.insertMulti("MouthSmile_L", QVariantList() << "Smile_Left" << 1.0); + blendshapes.insertMulti("MouthSmile_R", QVariantList() << "Smile_Right" << 1.0); + blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Left" << 1.0); + blendshapes.insertMulti("Puff", QVariantList() << "CheekPuff_Right" << 1.0); + blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Left" << 0.75); + blendshapes.insertMulti("Sneer", QVariantList() << "NoseScrunch_Right" << 0.75); + blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Left" << 0.5); + blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Right" << 0.5); mapping.insert(BLENDSHAPE_FIELD, blendshapes); } From 6e9da9747e61f1614aa957b7fe9726bd627a25cc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 15 Sep 2014 13:25:50 -0700 Subject: [PATCH 42/45] add a missing update of having heard from the audio-mixer --- interface/src/DatagramProcessor.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 62f5b6453a..aed93ffa1a 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -45,16 +45,29 @@ void DatagramProcessor::processDatagrams() { _byteCount += incomingPacket.size(); if (nodeList->packetVersionAndHashMatch(incomingPacket)) { + + PacketType incomingType = packetTypeForPacket(incomingPacket); // only process this packet if we have a match on the packet version - switch (packetTypeForPacket(incomingPacket)) { + switch (incomingType) { case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: - QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, - Q_ARG(QByteArray, incomingPacket)); - break; case PacketTypeAudioStreamStats: - QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, - Q_ARG(QByteArray, incomingPacket)); + if (incomingType != PacketTypeAudioStreamStats) { + QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, + Q_ARG(QByteArray, incomingPacket)); + } else { + QMetaObject::invokeMethod(&application->_audio, "parseAudioStreamStatsPacket", Qt::QueuedConnection, + Q_ARG(QByteArray, incomingPacket)); + } + + // update having heard from the audio-mixer and record the bytes received + SharedNodePointer audioMixer = nodeList->sendingNodeForPacket(incomingPacket); + + if (audioMixer) { + audioMixer->setLastHeardMicrostamp(usecTimestampNow()); + audioMixer->recordBytesReceived(incomingPacket.size()); + } + break; case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly From 193c125d666a4bb3ed9081d90c930a338c67265b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 15 Sep 2014 13:29:13 -0700 Subject: [PATCH 43/45] fix scope for switch case --- interface/src/DatagramProcessor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index aed93ffa1a..0889007c76 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -51,7 +51,7 @@ void DatagramProcessor::processDatagrams() { switch (incomingType) { case PacketTypeMixedAudio: case PacketTypeSilentAudioFrame: - case PacketTypeAudioStreamStats: + case PacketTypeAudioStreamStats: { if (incomingType != PacketTypeAudioStreamStats) { QMetaObject::invokeMethod(&application->_audio, "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); @@ -69,6 +69,7 @@ void DatagramProcessor::processDatagrams() { } break; + } case PacketTypeParticleAddResponse: // this will keep creatorTokenIDs to IDs mapped correctly Particle::handleAddParticleResponse(incomingPacket); From b2432876ce49854aadd6431a86f0c4d8f9abfbc8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 15 Sep 2014 13:53:37 -0700 Subject: [PATCH 44/45] More reliable floor detection for avatar movement. Also tweaked max walking speed. --- interface/src/avatar/MyAvatar.cpp | 246 ++++++++++++----------------- interface/src/avatar/MyAvatar.h | 2 - libraries/avatars/src/AvatarData.h | 14 +- 3 files changed, 108 insertions(+), 154 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c53580eed2..6cad3d4296 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -49,7 +49,7 @@ const float PITCH_SPEED = 100.0f; // degrees/sec const float COLLISION_RADIUS_SCALAR = 1.2f; // pertains to avatar-to-avatar collisions const float COLLISION_RADIUS_SCALE = 0.125f; -const float MIN_KEYBOARD_CONTROL_SPEED = 2.0f; +const float MIN_KEYBOARD_CONTROL_SPEED = 1.5f; const float MAX_WALKING_SPEED = 3.0f * MIN_KEYBOARD_CONTROL_SPEED; // TODO: normalize avatar speed for standard avatar size, then scale all motion logic @@ -75,7 +75,6 @@ MyAvatar::MyAvatar() : _motorTimescale(DEFAULT_MOTOR_TIMESCALE), _maxMotorSpeed(MAX_MOTOR_SPEED), _motionBehaviors(AVATAR_MOTION_DEFAULTS), - _lastFloorContactPoint(0.0f), _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), @@ -1085,15 +1084,6 @@ bool MyAvatar::shouldRenderHead(const glm::vec3& cameraPosition, RenderMode rend (glm::length(cameraPosition - head->getEyePosition()) > RENDER_HEAD_CUTOFF_DISTANCE * _scale); } -float MyAvatar::computeDistanceToFloor(const glm::vec3& startPoint) { - glm::vec3 direction = -_worldUpDirection; - OctreeElement* elementHit; // output from findRayIntersection - float distance = FLT_MAX; // output from findRayIntersection - BoxFace face; // output from findRayIntersection - Application::getInstance()->getVoxelTree()->findRayIntersection(startPoint, direction, elementHit, distance, face); - return distance; -} - void MyAvatar::updateOrientation(float deltaTime) { // Gather rotation information from keyboard _bodyYawDelta -= _driveKeys[ROT_RIGHT] * YAW_SPEED * deltaTime; @@ -1151,86 +1141,69 @@ void MyAvatar::updateOrientation(float deltaTime) { const float NEARBY_FLOOR_THRESHOLD = 5.0f; void MyAvatar::updatePosition(float deltaTime) { - float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + - fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT]) + - fabsf(_driveKeys[UP] - _driveKeys[DOWN]); - - bool walkingOnFloor = false; - float gravityLength = glm::length(_gravity) * GRAVITY_EARTH; + // check for floor by casting a ray straight down from avatar's position + float heightAboveFloor = FLT_MAX; const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape(); - glm::vec3 startCap; - boundingShape.getStartPoint(startCap); - glm::vec3 bottom = startCap - boundingShape.getRadius() * _worldUpDirection; + RayIntersectionInfo intersection; + // NOTE: avatar is center of PhysicsSimulation, so rayStart is the origin for the purposes of the raycast + intersection._rayStart = glm::vec3(0.0f); + intersection._rayDirection = - _worldUpDirection; + intersection._rayLength = 5.0f * boundingShape.getBoundingRadius(); + if (_physicsSimulation.findFloorRayIntersection(intersection)) { + // NOTE: heightAboveFloor is the distance between the bottom of the avatar and the floor + heightAboveFloor = intersection._hitDistance - boundingShape.getBoundingRadius(); + } - // velocity is initialized to the measured _velocity but will be modified - // by friction, external thrust, etc + // velocity is initialized to the measured _velocity but will be modified by friction, external thrust, etc glm::vec3 velocity = _velocity; - // apply friction - if (gravityLength > EPSILON) { - float speedFromGravity = _scale * deltaTime * gravityLength; - float distanceToFall = glm::distance(bottom, _lastFloorContactPoint); - walkingOnFloor = (distanceToFall < 2.0f * deltaTime * speedFromGravity); - - if (walkingOnFloor) { - // BEGIN HACK: to prevent the avatar from bouncing on a floor surface - if (distanceToFall < deltaTime * speedFromGravity) { - float verticalSpeed = glm::dot(velocity, _worldUpDirection); - if (fabs(verticalSpeed) < speedFromGravity) { - // we're standing on a floor, and nearly at rest so we zero the vertical velocity component - velocity -= verticalSpeed * _worldUpDirection; - } - } else { - // fall with gravity against floor - velocity -= speedFromGravity * _worldUpDirection; - } - // END HACK + bool pushingUp = (_driveKeys[UP] - _driveKeys[DOWN] > 0.0f); + bool walkingOnFloor = false; + if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) { + const float MAX_SPEED_UNDER_GRAVITY = 2.0f * _scale * MAX_WALKING_SPEED; + if (pushingUp || glm::length2(velocity) > MAX_SPEED_UNDER_GRAVITY * MAX_SPEED_UNDER_GRAVITY) { + // we're pushing up or moving quickly, so disable gravity + setLocalGravity(glm::vec3(0.0f)); } else { - if (!_isBraking) { - // fall with gravity toward floor - velocity -= speedFromGravity * _worldUpDirection; - } - - if (_motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) { - const float MAX_VERTICAL_FLOOR_DETECTION_SPEED = _scale * MAX_WALKING_SPEED; - if (keyboardInput && glm::dot(_motorVelocity, _worldUpDirection) > 0.0f && - glm::dot(velocity, _worldUpDirection) > MAX_VERTICAL_FLOOR_DETECTION_SPEED) { - // disable local gravity when flying up - setLocalGravity(glm::vec3(0.0f)); - } else { - const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD; - if (computeDistanceToFloor(bottom) > maxFloorDistance) { - // disable local gravity when floor is too far - setLocalGravity(glm::vec3(0.0f)); - } - } - } - } - } else if ((_collisionGroups & COLLISION_GROUP_VOXELS) && - _motionBehaviors & AVATAR_MOTION_STAND_ON_NEARBY_FLOORS) { - const float MIN_FLOOR_DETECTION_SPEED = _scale * 1.0f; - if (glm::length(_velocity) < MIN_FLOOR_DETECTION_SPEED ) { - // scan for floor under avatar - const float maxFloorDistance = _scale * NEARBY_FLOOR_THRESHOLD; - if (computeDistanceToFloor(bottom) < maxFloorDistance) { - // enable local gravity + const float maxFloorDistance = boundingShape.getBoundingRadius() * NEARBY_FLOOR_THRESHOLD; + if (heightAboveFloor > maxFloorDistance) { + // disable local gravity when floor is too far away + setLocalGravity(glm::vec3(0.0f)); + } else { + // enable gravity + walkingOnFloor = true; setLocalGravity(-_worldUpDirection); } } } - float speed = glm::length(velocity); - if (keyboardInput > 0.0f || speed > 0.0f || glm::length2(_thrust) > 0.0f || ! walkingOnFloor) { - // update motor - if (_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED) { - // Increase motor velocity until its length is equal to _maxMotorSpeed. - glm::vec3 localVelocity = velocity; - if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { - glm::quat orientation = getHead()->getCameraOrientation(); - localVelocity = glm::inverse(orientation) * velocity; - } - + bool zeroDownwardVelocity = false; + bool gravityEnabled = (glm::length2(_gravity) > EPSILON); + if (gravityEnabled) { + if (heightAboveFloor < 0.0f) { + // Gravity is in effect so we assume that the avatar is colliding against the world and we need + // to lift avatar out of floor, but we don't want to do it too fast (keep it smooth). + float distanceToLift = glm::min(-heightAboveFloor, MAX_WALKING_SPEED * deltaTime); + + // We don't use applyPositionDelta() for this lift distance because we don't want the avatar + // to come flying out of the floor. Instead we update position directly, and set a boolean + // that will remind us later to zero any downward component of the velocity. + _position += (distanceToLift - EPSILON) * _worldUpDirection; + zeroDownwardVelocity = true; + } + velocity += (deltaTime * GRAVITY_EARTH) * _gravity; + } + + float motorEfficiency = glm::clamp(deltaTime / computeMotorTimescale(velocity), 0.0f, 1.0f); + + // compute targetVelocity + glm::vec3 targetVelocity(0.0f); + if (_motionBehaviors & AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED) { + float keyboardInput = fabsf(_driveKeys[FWD] - _driveKeys[BACK]) + + (fabsf(_driveKeys[RIGHT] - _driveKeys[LEFT])) + + fabsf(_driveKeys[UP] - _driveKeys[DOWN]); + if (keyboardInput) { // Compute keyboard input glm::vec3 front = (_driveKeys[FWD] - _driveKeys[BACK]) * IDENTITY_FRONT; glm::vec3 right = (_driveKeys[RIGHT] - _driveKeys[LEFT]) * IDENTITY_RIGHT; @@ -1242,76 +1215,69 @@ void MyAvatar::updatePosition(float deltaTime) { // Compute motor magnitude if (directionLength > EPSILON) { direction /= directionLength; - // the finalMotorSpeed depends on whether we are walking or not + + // Compute the target keyboard velocity (which ramps up slowly, and damps very quickly) + // the max magnitude of which depends on what we're doing: float finalMaxMotorSpeed = walkingOnFloor ? _scale * MAX_WALKING_SPEED : _scale * _maxMotorSpeed; - float motorLength = glm::length(_motorVelocity); if (motorLength < _scale * MIN_KEYBOARD_CONTROL_SPEED) { // an active keyboard motor should never be slower than this _motorVelocity = _scale * MIN_KEYBOARD_CONTROL_SPEED * direction; + motorEfficiency = 1.0f; } else { - float MOTOR_LENGTH_TIMESCALE = 1.5f; - float tau = glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f); - float INCREASE_FACTOR = 2.0f; - //_motorVelocity *= 1.0f + tau * INCREASE_FACTOR; - motorLength *= 1.0f + tau * INCREASE_FACTOR; + float MOTOR_LENGTH_TIMESCALE = 2.0f; + float INCREASE_FACTOR = 1.8f; + motorLength *= 1.0f + glm::clamp(deltaTime / MOTOR_LENGTH_TIMESCALE, 0.0f, 1.0f) * INCREASE_FACTOR; if (motorLength > finalMaxMotorSpeed) { motorLength = finalMaxMotorSpeed; } _motorVelocity = motorLength * direction; } _isPushing = true; - } else { - // motor opposes motion (wants to be at rest) - _motorVelocity = - localVelocity; - } + } + targetVelocity = _motorVelocity; + } else { + _motorVelocity = glm::vec3(0.0f); } + } + targetVelocity = getHead()->getCameraOrientation() * targetVelocity; - // apply motor - if (_motionBehaviors & AVATAR_MOTION_MOTOR_ENABLED) { - glm::vec3 targetVelocity = _motorVelocity; - if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { - // rotate targetVelocity into world frame - glm::quat rotation = getHead()->getCameraOrientation(); - targetVelocity = rotation * _motorVelocity; - } - - glm::vec3 deltaVelocity = targetVelocity - velocity; - - if (_motionBehaviors & AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY && glm::length2(_gravity) > EPSILON) { - // For now we subtract the component parallel to gravity but what we need to do is: - // TODO: subtract the component perp to the local surface normal (motor only pushes in surface plane). - glm::vec3 gravityDirection = glm::normalize(_gravity); - glm::vec3 parallelDelta = glm::dot(deltaVelocity, gravityDirection) * gravityDirection; - if (glm::dot(targetVelocity, velocity) > 0.0f) { - // remove parallel part from deltaVelocity - deltaVelocity -= parallelDelta; - } - } - - // simple critical damping - float timescale = computeMotorTimescale(velocity); - float tau = glm::clamp(deltaTime / timescale, 0.0f, 1.0f); - velocity += tau * deltaVelocity; - } + glm::vec3 deltaVelocity = targetVelocity - velocity; - // apply thrust - velocity += _thrust * deltaTime; - speed = glm::length(velocity); - if (speed > MAX_AVATAR_SPEED) { - velocity *= MAX_AVATAR_SPEED / speed; - speed = MAX_AVATAR_SPEED; - } - _thrust = glm::vec3(0.0f); + if (walkingOnFloor && !pushingUp) { + // remove vertical component of deltaVelocity + deltaVelocity -= glm::dot(deltaVelocity, _worldUpDirection) * _worldUpDirection; + } - // update position - const float MIN_AVATAR_SPEED = 0.075f; - if (speed > MIN_AVATAR_SPEED) { - applyPositionDelta(deltaTime * velocity); + // apply motor + velocity += motorEfficiency * deltaVelocity; + + // apply thrust + velocity += _thrust * deltaTime; + _thrust = glm::vec3(0.0f); + + // remove downward velocity so we don't push into floor + if (zeroDownwardVelocity) { + float verticalSpeed = glm::dot(velocity, _worldUpDirection); + if (verticalSpeed < 0.0f) { + velocity += verticalSpeed * _worldUpDirection; } } - // update moving flag based on speed + // cap avatar speed + float speed = glm::length(velocity); + if (speed > MAX_AVATAR_SPEED) { + velocity *= MAX_AVATAR_SPEED / speed; + speed = MAX_AVATAR_SPEED; + } + + // update position + const float MIN_AVATAR_SPEED = 0.075f; + if (speed > MIN_AVATAR_SPEED) { + applyPositionDelta(deltaTime * velocity); + } + + // update _moving flag based on speed const float MOVING_SPEED_THRESHOLD = 0.01f; _moving = speed > MOVING_SPEED_THRESHOLD; @@ -1330,8 +1296,8 @@ float MyAvatar::computeMotorTimescale(const glm::vec3& velocity) { // (3) inactive --> long timescale (gentle friction for low speeds) float MIN_MOTOR_TIMESCALE = 0.125f; - float MAX_MOTOR_TIMESCALE = 0.5f; - float MIN_BRAKE_SPEED = 0.4f; + float MAX_MOTOR_TIMESCALE = 0.4f; + float MIN_BRAKE_SPEED = 0.3f; float timescale = MAX_MOTOR_TIMESCALE; bool isThrust = (glm::length2(_thrust) > EPSILON); @@ -1372,7 +1338,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { quint64 now = usecTimestampNow(); if (_voxelShapeManager.needsUpdate(now)) { // We use a multiple of the avatar's boundingRadius as the size of the cube of interest. - float cubeScale = 4.0f * getBoundingRadius(); + float cubeScale = 6.0f * getBoundingRadius(); glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale); AACube boundingCube(corner, cubeScale); @@ -1459,9 +1425,6 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { } } } - if (lowestStep < MAX_STEP_HEIGHT) { - _lastFloorContactPoint = floorPoint; - } float penetrationLength = glm::length(totalPenetration); if (penetrationLength < EPSILON) { @@ -1471,12 +1434,11 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection); if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) { // we're colliding against an edge + + // rotate _motorVelocity into world frame glm::vec3 targetVelocity = _motorVelocity; - if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) { - // rotate _motorVelocity into world frame - glm::quat rotation = getHead()->getCameraOrientation(); - targetVelocity = rotation * _motorVelocity; - } + glm::quat rotation = getHead()->getCameraOrientation(); + targetVelocity = rotation * _motorVelocity; if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) { // we're puhing into the edge, so we want to lift diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c4d871b21d..d86829ea91 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -207,7 +207,6 @@ private: float _maxMotorSpeed; quint32 _motionBehaviors; - glm::vec3 _lastFloorContactPoint; QWeakPointer _lookAtTargetAvatar; glm::vec3 _targetAvatarPosition; bool _shouldRender; @@ -221,7 +220,6 @@ private: RecorderPointer _recorder; // private methods - float computeDistanceToFloor(const glm::vec3& startPoint); void updateOrientation(float deltaTime); void updatePosition(float deltaTime); float computeMotorTimescale(const glm::vec3& velocity); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d70876722b..5b0c6b97dd 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -55,20 +55,14 @@ typedef unsigned long long quint64; #include "HandData.h" // avatar motion behaviors -const quint32 AVATAR_MOTION_MOTOR_ENABLED = 1U << 0; -const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 1; -const quint32 AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME = 1U << 2; -const quint32 AVATAR_MOTION_MOTOR_COLLISION_SURFACE_ONLY = 1U << 3; +const quint32 AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED = 1U << 0; -const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 4; -const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 5; - -const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 6; +const quint32 AVATAR_MOTION_OBEY_ENVIRONMENTAL_GRAVITY = 1U << 1; +const quint32 AVATAR_MOTION_OBEY_LOCAL_GRAVITY = 1U << 2; +const quint32 AVATAR_MOTION_STAND_ON_NEARBY_FLOORS = 1U << 3; const quint32 AVATAR_MOTION_DEFAULTS = - AVATAR_MOTION_MOTOR_ENABLED | AVATAR_MOTION_MOTOR_KEYBOARD_ENABLED | - AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME | AVATAR_MOTION_STAND_ON_NEARBY_FLOORS; // these bits will be expanded as features are exposed From 9bed37c86c0f8d9f57852caa40c76e7916b679db Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 15 Sep 2014 16:05:26 -0700 Subject: [PATCH 45/45] Update position of audio --- examples/radio.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/radio.js b/examples/radio.js index 51233f5bb4..6ffc7deb84 100644 --- a/examples/radio.js +++ b/examples/radio.js @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var position = { x:1, y: 1, z: 20 }; +var position = { x:1, y: 1, z: 10 }; var rotation = Quat.fromPitchYawRollDegrees(0, 0, 0); var scale = 1.0; @@ -20,8 +20,8 @@ var AudioRotationOffset = Quat.fromPitchYawRollDegrees(0, -90, 0); var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.7; audioOptions.position = position; -//audioOptions.orientation = Quat.multiply(AudioRotationOffset, rotation); -//audioOptions.loop = true; +audioOptions.orientation = Quat.multiply(AudioRotationOffset, rotation); +audioOptions.loop = true; audioOptions.isStereo = true; var injector = null; @@ -49,13 +49,13 @@ function update() { var newProperties = Entities.getEntityProperties(entity); if (newProperties.type === "Model") { if (newProperties.position != properties.position) { - + audioOptions.position = newProperties.position; + } + if (newProperties.orientation != properties.orientation) { + audioOptions.orientation = newProperties.orientation; } - - - - + properties = newProperties; } else { entity = null; Script.update.disconnect(update);