From 080c1917271b0594e9d5395d3cf4331fda85221b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 21 Aug 2014 08:20:31 -0700 Subject: [PATCH 01/16] namechange updateConstraint() -> buildConstraint() --- interface/src/renderer/JointState.cpp | 2 +- interface/src/renderer/JointState.h | 2 +- interface/src/renderer/Model.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 94b4b37a3c..9ca1bf7492 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -73,7 +73,7 @@ void JointState::setFBXJoint(const FBXJoint* joint) { } } -void JointState::updateConstraint() { +void JointState::buildConstraint() { if (_constraint) { delete _constraint; _constraint = NULL; diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 56044125f1..f98438e34d 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -30,7 +30,7 @@ public: void setFBXJoint(const FBXJoint* joint); const FBXJoint& getFBXJoint() const { return *_fbxJoint; } - void updateConstraint(); + void buildConstraint(); void copyState(const JointState& state); void initTransform(const glm::mat4& parentTransform); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 290f9b5c6f..d70ecc4e6c 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -547,7 +547,7 @@ void Model::setJointStates(QVector states) { if (distance > radius) { radius = distance; } - _jointStates[i].updateConstraint(); + _jointStates[i].buildConstraint(); } for (int i = 0; i < _jointStates.size(); i++) { _jointStates[i].slaveVisibleTransform(); @@ -1192,7 +1192,7 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm: } // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose - // at in the process. This provides stability to the IK solution for most models. + // in the process. This provides stability to the IK solution for most models. glm::quat oldNextRotation = nextState.getRotation(); float mixFactor = 0.03f; nextState.mixRotationDelta(deltaRotation, mixFactor, priority); From 3ebd8c1969dada783945613ce7681144cc59b319 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 21 Aug 2014 16:40:25 -0700 Subject: [PATCH 02/16] use dispatchTable for shape-vs-shape collisions --- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/avatar/SkeletonModel.cpp | 18 +- libraries/fbx/src/FBXReader.cpp | 8 +- libraries/shared/src/CapsuleShape.cpp | 8 +- libraries/shared/src/PhysicsEntity.cpp | 2 + libraries/shared/src/PhysicsSimulation.cpp | 3 +- libraries/shared/src/PlaneShape.cpp | 2 +- libraries/shared/src/Shape.h | 23 +- libraries/shared/src/ShapeCollider.cpp | 232 ++++++++++++--------- libraries/shared/src/ShapeCollider.h | 48 +++-- libraries/shared/src/SphereShape.h | 6 +- tests/physics/src/ShapeColliderTests.cpp | 37 ++++ tests/physics/src/ShapeColliderTests.h | 2 + tests/physics/src/VerletShapeTests.cpp | 2 + 14 files changed, 240 insertions(+), 152 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1d271d5f40..239ca16186 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -81,6 +81,7 @@ MyAvatar::MyAvatar() : _billboardValid(false), _physicsSimulation() { + ShapeCollider::initDispatchTable(); for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index dd398f2b2b..e5e91cb886 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -618,19 +618,19 @@ void SkeletonModel::buildShapes() { Shape::Type type = joint.shapeType; int parentIndex = joint.parentIndex; if (parentIndex == -1 || radius < EPSILON) { - type = Shape::UNKNOWN_SHAPE; - } else if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { + type = UNKNOWN_SHAPE; + } else if (type == CAPSULE_SHAPE && halfHeight < EPSILON) { // this shape is forced to be a sphere - type = Shape::SPHERE_SHAPE; + type = SPHERE_SHAPE; } Shape* shape = NULL; - if (type == Shape::SPHERE_SHAPE) { + if (type == SPHERE_SHAPE) { shape = new VerletSphereShape(radius, &(points[i])); shape->setEntity(this); float mass = massScale * glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()); points[i].setMass(mass); totalMass += mass; - } else if (type == Shape::CAPSULE_SHAPE) { + } else if (type == CAPSULE_SHAPE) { assert(parentIndex != -1); shape = new VerletCapsuleShape(radius, &(points[parentIndex]), &(points[i])); shape->setEntity(this); @@ -729,7 +729,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { shapeExtents.reset(); glm::vec3 localPosition = shape->getTranslation(); int type = shape->getType(); - if (type == Shape::CAPSULE_SHAPE) { + if (type == CAPSULE_SHAPE) { // add the two furthest surface points of the capsule CapsuleShape* capsule = static_cast(shape); glm::vec3 axis; @@ -741,7 +741,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { shapeExtents.addPoint(localPosition + axis); shapeExtents.addPoint(localPosition - axis); totalExtents.addExtents(shapeExtents); - } else if (type == Shape::SPHERE_SHAPE) { + } else if (type == SPHERE_SHAPE) { float radius = shape->getBoundingRadius(); glm::vec3 axis = glm::vec3(radius); shapeExtents.addPoint(localPosition + axis); @@ -845,13 +845,13 @@ void SkeletonModel::renderJointCollisionShapes(float alpha) { glPushMatrix(); // shapes are stored in simulation-frame but we want position to be model-relative - if (shape->getType() == Shape::SPHERE_SHAPE) { + if (shape->getType() == SPHERE_SHAPE) { glm::vec3 position = shape->getTranslation() - simulationTranslation; glTranslatef(position.x, position.y, position.z); // draw a grey sphere at shape position glColor4f(0.75f, 0.75f, 0.75f, alpha); glutSolidSphere(shape->getBoundingRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); - } else if (shape->getType() == Shape::CAPSULE_SHAPE) { + } else if (shape->getType() == CAPSULE_SHAPE) { CapsuleShape* capsule = static_cast(shape); // draw a blue sphere at the capsule endpoint diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1a152dc217..d8b52fb794 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1503,7 +1503,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) joint.inverseBindRotation = joint.inverseDefaultRotation; joint.name = model.name; joint.shapePosition = glm::vec3(0.f); - joint.shapeType = Shape::UNKNOWN_SHAPE; + joint.shapeType = UNKNOWN_SHAPE; geometry.joints.append(joint); geometry.jointIndices.insert(model.name, geometry.joints.size()); @@ -1848,10 +1848,10 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (collideLikeCapsule) { joint.shapeRotation = rotationBetween(defaultCapsuleAxis, jointShapeInfo.boneBegin); joint.shapePosition = 0.5f * jointShapeInfo.boneBegin; - joint.shapeType = Shape::CAPSULE_SHAPE; + joint.shapeType = CAPSULE_SHAPE; } else { // collide the joint like a sphere - joint.shapeType = Shape::SPHERE_SHAPE; + joint.shapeType = SPHERE_SHAPE; if (jointShapeInfo.numVertices > 0) { jointShapeInfo.averageVertex /= (float)jointShapeInfo.numVertices; joint.shapePosition = jointShapeInfo.averageVertex; @@ -1872,7 +1872,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // The shape is further from both joint endpoints than the endpoints are from each other // which probably means the model has a bad transform somewhere. We disable this shape // by setting its type to UNKNOWN_SHAPE. - joint.shapeType = Shape::UNKNOWN_SHAPE; + joint.shapeType = UNKNOWN_SHAPE; } } } diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 03bc48bd94..09776a233f 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -18,20 +18,20 @@ #include "SharedUtil.h" -CapsuleShape::CapsuleShape() : Shape(Shape::CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} +CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} -CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(Shape::CAPSULE_SHAPE), +CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(halfHeight) { updateBoundingRadius(); } CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation) : - Shape(Shape::CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) { + Shape(CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) { updateBoundingRadius(); } CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) : - Shape(Shape::CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { + Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) { setEndPoints(startPoint, endPoint); } diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 09b00c201c..6be37a7528 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -12,8 +12,10 @@ #include "PhysicsEntity.h" #include "PhysicsSimulation.h" +#include "PlaneShape.h" #include "Shape.h" #include "ShapeCollider.h" +#include "SphereShape.h" PhysicsEntity::PhysicsEntity() : _translation(0.0f), diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 6c4901bcd5..b58f62dfd4 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -16,8 +16,9 @@ #include "PerfStat.h" #include "PhysicsEntity.h" #include "Ragdoll.h" -#include "SharedUtil.h" +#include "Shape.h" #include "ShapeCollider.h" +#include "SharedUtil.h" int MAX_DOLLS_PER_SIMULATION = 16; int MAX_ENTITIES_PER_SIMULATION = 64; diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index 15ea281510..72704c3116 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -15,7 +15,7 @@ const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f); PlaneShape::PlaneShape(const glm::vec4& coefficients) : - Shape(Shape::PLANE_SHAPE) { + Shape(PLANE_SHAPE) { glm::vec3 normal = glm::vec3(coefficients); _translation = -normal * coefficients.w; diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 2efa5b824f..9c30a0fdf4 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -22,17 +22,18 @@ class VerletPoint; const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) +const quint8 SPHERE_SHAPE = 0; +const quint8 CAPSULE_SHAPE = 1; +const quint8 PLANE_SHAPE = 2; +const quint8 LIST_SHAPE = 3; +const quint8 UNKNOWN_SHAPE = 4; + class Shape { public: - static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; } - enum Type{ - UNKNOWN_SHAPE = 0, - SPHERE_SHAPE, - CAPSULE_SHAPE, - PLANE_SHAPE, - LIST_SHAPE - }; + typedef quint8 Type; + + static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; } Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation(), _mass(MAX_SHAPE_MASS) { @@ -40,7 +41,7 @@ public: } virtual ~Shape() { } - int getType() const { return _type; } + Type getType() const { return _type; } quint32 getID() const { return _id; } void setEntity(PhysicsEntity* entity) { _owningEntity = entity; } @@ -95,8 +96,8 @@ protected: void setBoundingRadius(float radius) { _boundingRadius = radius; } - int _type; - unsigned int _id; + Type _type; + quint32 _id; PhysicsEntity* _owningEntity; float _boundingRadius; glm::vec3 _translation; diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 805e7f30f6..8d09c0408a 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -15,56 +15,66 @@ #include "GeometryUtil.h" #include "ShapeCollider.h" +#include "CapsuleShape.h" +#include "ListShape.h" +#include "PlaneShape.h" +#include "SphereShape.h" // NOTE: // // * Large ListShape's are inefficient keep the lists short. // * Collisions between lists of lists work in theory but are not recommended. +const Shape::Type NUM_SHAPE_TYPES = 5; +const quint8 NUM__DISPATCH_CELLS = NUM_SHAPE_TYPES * NUM_SHAPE_TYPES; + +Shape::Type getDispatchKey(Shape::Type typeA, Shape::Type typeB) { + return typeA + NUM_SHAPE_TYPES * typeB; +} + +// dummy dispatch for any non-implemented pairings +bool notImplemented(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + return false; +} + +// NOTE: hardcode the number of dispatchTable entries (NUM_SHAPE_TYPES ^2) +bool (*dispatchTable[NUM__DISPATCH_CELLS])(const Shape*, const Shape*, CollisionList&); + namespace ShapeCollider { -bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // TODO: make a fast lookup for correct method - int typeA = shapeA->getType(); - int typeB = shapeB->getType(); - if (typeA == Shape::SPHERE_SHAPE) { - const SphereShape* sphereA = static_cast(shapeA); - if (typeB == Shape::SPHERE_SHAPE) { - return sphereSphere(sphereA, static_cast(shapeB), collisions); - } else if (typeB == Shape::CAPSULE_SHAPE) { - return sphereCapsule(sphereA, static_cast(shapeB), collisions); - } else if (typeB == Shape::PLANE_SHAPE) { - return spherePlane(sphereA, static_cast(shapeB), collisions); - } - } else if (typeA == Shape::CAPSULE_SHAPE) { - const CapsuleShape* capsuleA = static_cast(shapeA); - if (typeB == Shape::SPHERE_SHAPE) { - return capsuleSphere(capsuleA, static_cast(shapeB), collisions); - } else if (typeB == Shape::CAPSULE_SHAPE) { - return capsuleCapsule(capsuleA, static_cast(shapeB), collisions); - } else if (typeB == Shape::PLANE_SHAPE) { - return capsulePlane(capsuleA, static_cast(shapeB), collisions); - } - } else if (typeA == Shape::PLANE_SHAPE) { - const PlaneShape* planeA = static_cast(shapeA); - if (typeB == Shape::SPHERE_SHAPE) { - return planeSphere(planeA, static_cast(shapeB), collisions); - } else if (typeB == Shape::CAPSULE_SHAPE) { - return planeCapsule(planeA, static_cast(shapeB), collisions); - } else if (typeB == Shape::PLANE_SHAPE) { - return planePlane(planeA, static_cast(shapeB), collisions); - } - } else if (typeA == Shape::LIST_SHAPE) { - const ListShape* listA = static_cast(shapeA); - if (typeB == Shape::SPHERE_SHAPE) { - return listSphere(listA, static_cast(shapeB), collisions); - } else if (typeB == Shape::CAPSULE_SHAPE) { - return listCapsule(listA, static_cast(shapeB), collisions); - } else if (typeB == Shape::PLANE_SHAPE) { - return listPlane(listA, static_cast(shapeB), collisions); - } +// NOTE: the dispatch table must be initialized before the ShapeCollider is used. +void initDispatchTable() { + for (Shape::Type i = 0; i < NUM__DISPATCH_CELLS; ++i) { + dispatchTable[i] = ¬Implemented; } - return false; + + // NOTE: no need to update any that are notImplemented, but we leave them + // commented out in the code so that we remember that they exist. + dispatchTable[getDispatchKey(SPHERE_SHAPE, SPHERE_SHAPE)] = &sphereSphere; + dispatchTable[getDispatchKey(SPHERE_SHAPE, CAPSULE_SHAPE)] = &sphereCapsule; + dispatchTable[getDispatchKey(SPHERE_SHAPE, PLANE_SHAPE)] = &spherePlane; + //dispatchTable[getDispatchKey(SPHERE_SHAPE, LIST_SHAPE)] = ¬Implemented; + + dispatchTable[getDispatchKey(CAPSULE_SHAPE, SPHERE_SHAPE)] = &capsuleSphere; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, CAPSULE_SHAPE)] = &capsuleCapsule; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, PLANE_SHAPE)] = &capsulePlane; + //dispatchTable[getDispatchKey(CAPSULE_SHAPE, LIST_SHAPE)] = ¬Implemented; + + dispatchTable[getDispatchKey(PLANE_SHAPE, SPHERE_SHAPE)] = &planeSphere; + dispatchTable[getDispatchKey(PLANE_SHAPE, CAPSULE_SHAPE)] = &planeCapsule; + dispatchTable[getDispatchKey(PLANE_SHAPE, PLANE_SHAPE)] = &planePlane; + //dispatchTable[getDispatchKey(PLANE_SHAPE, LIST_SHAPE)] = ¬Implemented; + + //dispatchTable[getDispatchKey(LIST_SHAPE, SPHERE_SHAPE)] = ¬Implemented; + //dispatchTable[getDispatchKey(LIST_SHAPE, CAPSULE_SHAPE)] = ¬Implemented; + //dispatchTable[getDispatchKey(LIST_SHAPE, PLANE_SHAPE)] = ¬Implemented; + //dispatchTable[getDispatchKey(LIST_SHAPE, LIST_SHAPE)] = ¬Implemented; + + // all of the UNKNOWN_SHAPE pairings point at notImplemented +} + +bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + return (*dispatchTable[shapeA->getType() + NUM_SHAPE_TYPES * shapeB->getType()])(shapeA, shapeB, collisions); } static CollisionList tempCollisions(32); @@ -133,20 +143,20 @@ bool collideShapesWithShapes(const QVector& shapesA, const QVectorgetType(); - if (typeA == Shape::SPHERE_SHAPE) { + Shape::Type typeA = shapeA->getType(); + if (typeA == SPHERE_SHAPE) { return sphereAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == Shape::CAPSULE_SHAPE) { + } else if (typeA == CAPSULE_SHAPE) { return capsuleAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); - } else if (typeA == Shape::LIST_SHAPE) { + } else if (typeA == LIST_SHAPE) { const ListShape* listA = static_cast(shapeA); bool touching = false; for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { + if (subType == SPHERE_SHAPE) { touching = sphereAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { + } else if (subType == CAPSULE_SHAPE) { touching = capsuleAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; } } @@ -155,7 +165,9 @@ bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, fl return false; } -bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions) { +bool sphereSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const SphereShape* sphereA = static_cast(shapeA); + const SphereShape* sphereB = static_cast(shapeB); glm::vec3 BA = sphereB->getTranslation() - sphereA->getTranslation(); float distanceSquared = glm::dot(BA, BA); float totalRadius = sphereA->getRadius() + sphereB->getRadius(); @@ -183,7 +195,9 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis return false; } -bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions) { +bool sphereCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const SphereShape* sphereA = static_cast(shapeA); + const CapsuleShape* capsuleB = static_cast(shapeB); // find sphereA's closest approach to axis of capsuleB glm::vec3 BA = capsuleB->getTranslation() - sphereA->getTranslation(); glm::vec3 capsuleAxis; @@ -252,7 +266,9 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col return false; } -bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions) { +bool spherePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const SphereShape* sphereA = static_cast(shapeA); + const PlaneShape* planeB = static_cast(shapeB); glm::vec3 penetration; if (findSpherePlanePenetration(sphereA->getTranslation(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { CollisionInfo* collision = collisions.getNewCollision(); @@ -268,7 +284,9 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision return false; } -bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions) { +bool capsuleSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const CapsuleShape* capsuleA = static_cast(shapeA); + const SphereShape* sphereB = static_cast(shapeB); // find sphereB's closest approach to axis of capsuleA glm::vec3 AB = capsuleA->getTranslation() - sphereB->getTranslation(); glm::vec3 capsuleAxis; @@ -409,7 +427,9 @@ bool lineCylinder(const glm::vec3& lineP, const glm::vec3& lineDir, return true; } -bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions) { +bool capsuleCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const CapsuleShape* capsuleA = static_cast(shapeA); + const CapsuleShape* capsuleB = static_cast(shapeB); glm::vec3 axisA; capsuleA->computeNormalizedAxis(axisA); glm::vec3 axisB; @@ -568,7 +588,9 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, return false; } -bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions) { +bool capsulePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const CapsuleShape* capsuleA = static_cast(shapeA); + const PlaneShape* planeB = static_cast(shapeB); glm::vec3 start, end, penetration; capsuleA->getStartPoint(start); capsuleA->getEndPoint(end); @@ -588,7 +610,9 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis return false; } -bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions) { +bool planeSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const PlaneShape* planeA = static_cast(shapeA); + const SphereShape* sphereB = static_cast(shapeB); glm::vec3 penetration; if (findSpherePlanePenetration(sphereB->getTranslation(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) { CollisionInfo* collision = collisions.getNewCollision(); @@ -605,7 +629,9 @@ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, Collision return false; } -bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions) { +bool planeCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { + const PlaneShape* planeA = static_cast(shapeA); + const CapsuleShape* capsuleB = static_cast(shapeB); glm::vec3 start, end, penetration; capsuleB->getStartPoint(start); capsuleB->getEndPoint(end); @@ -625,110 +651,118 @@ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, Collis return false; } -bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions) { +bool planePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { // technically, planes always collide unless they're parallel and not coincident; however, that's // not going to give us any useful information return false; } -bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions) { +bool sphereList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listB = static_cast(shapeB); for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { const Shape* subShape = listB->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = sphereSphere(sphereA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = sphereCapsule(sphereA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = spherePlane(sphereA, static_cast(subShape), collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = sphereSphere(shapeA, subShape, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = sphereCapsule(shapeA, subShape, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = spherePlane(shapeA, subShape, collisions) || touching; } } return touching; } -bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions) { +bool capsuleList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listB = static_cast(shapeB); for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { const Shape* subShape = listB->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = capsuleSphere(capsuleA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = capsuleCapsule(capsuleA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = capsulePlane(capsuleA, static_cast(subShape), collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = capsuleSphere(shapeA, subShape, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = capsuleCapsule(shapeA, subShape, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = capsulePlane(shapeA, subShape, collisions) || touching; } } return touching; } -bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions) { +bool planeList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listB = static_cast(shapeB); for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { const Shape* subShape = listB->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = planeSphere(planeA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = planeCapsule(planeA, static_cast(subShape), collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = planePlane(planeA, static_cast(subShape), collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = planeSphere(shapeA, subShape, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = planeCapsule(shapeA, subShape, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = planePlane(shapeA, subShape, collisions) || touching; } } return touching; } -bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions) { +bool listSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listA = static_cast(shapeA); for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = sphereSphere(static_cast(subShape), sphereB, collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = capsuleSphere(static_cast(subShape), sphereB, collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = planeSphere(static_cast(subShape), sphereB, collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = sphereSphere(subShape, shapeB, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = capsuleSphere(subShape, shapeB, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = planeSphere(subShape, shapeB, collisions) || touching; } } return touching; } -bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions) { +bool listCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listA = static_cast(shapeA); for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = sphereCapsule(static_cast(subShape), capsuleB, collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = capsuleCapsule(static_cast(subShape), capsuleB, collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = planeCapsule(static_cast(subShape), capsuleB, collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = sphereCapsule(subShape, shapeB, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = capsuleCapsule(subShape, shapeB, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = planeCapsule(subShape, shapeB, collisions) || touching; } } return touching; } -bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions) { +bool listPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listA = static_cast(shapeA); for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); int subType = subShape->getType(); - if (subType == Shape::SPHERE_SHAPE) { - touching = spherePlane(static_cast(subShape), planeB, collisions) || touching; - } else if (subType == Shape::CAPSULE_SHAPE) { - touching = capsulePlane(static_cast(subShape), planeB, collisions) || touching; - } else if (subType == Shape::PLANE_SHAPE) { - touching = planePlane(static_cast(subShape), planeB, collisions) || touching; + if (subType == SPHERE_SHAPE) { + touching = spherePlane(subShape, shapeB, collisions) || touching; + } else if (subType == CAPSULE_SHAPE) { + touching = capsulePlane(subShape, shapeB, collisions) || touching; + } else if (subType == PLANE_SHAPE) { + touching = planePlane(subShape, shapeB, collisions) || touching; } } return touching; } -bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions) { +bool listList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; + const ListShape* listA = static_cast(shapeA); + const ListShape* listB = static_cast(shapeB); for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); for (int j = 0; j < listB->size() && !collisions.isFull(); ++j) { diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index b1be75fa40..11768f0a48 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -14,20 +14,28 @@ #include -#include "CapsuleShape.h" #include "CollisionInfo.h" -#include "ListShape.h" -#include "PlaneShape.h" #include "SharedUtil.h" -#include "SphereShape.h" +//#include "CapsuleShape.h" +//#include "ListShape.h" +//#include "PlaneShape.h" +//#include "SphereShape.h" + +class Shape; +class SphereShape; +class CapsuleShape; namespace ShapeCollider { + /// MUST CALL this FIRST before using the ShapeCollider + void initDispatchTable(); + /// \param shapeA pointer to first shape (cannot be NULL) /// \param shapeB pointer to second shape (cannot be NULL) /// \param collisions[out] collision details /// \return true if shapes collide bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); + bool collideShapesOld(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); /// \param shapesA list of shapes /// \param shapeB list of shapes @@ -49,97 +57,97 @@ namespace ShapeCollider { /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions); + bool sphereSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions); /// \param sphereA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions); + bool sphereCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions); /// \param sphereA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions); + bool spherePlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions); + bool capsuleSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions); + bool capsuleCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions); + bool capsulePlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions); + bool planeSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions); + bool planeCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions); + bool planePlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions); /// \param sphereA pointer to first shape (cannot be NULL) /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions); + bool sphereList(const Shape* sphereA, const Shape* listB, CollisionList& collisions); /// \param capuleA pointer to first shape (cannot be NULL) /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions); + bool capsuleList(const Shape* capsuleA, const Shape* listB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions); + bool planeList(const Shape* planeA, const Shape* listB, CollisionList& collisions); /// \param listA pointer to first shape (cannot be NULL) /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionList& collisions); + bool listSphere(const Shape* listA, const Shape* sphereB, CollisionList& collisions); /// \param listA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions); + bool listCapsule(const Shape* listA, const Shape* capsuleB, CollisionList& collisions); /// \param listA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions); + bool listPlane(const Shape* listA, const Shape* planeB, CollisionList& collisions); /// \param listA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool listList(const ListShape* listA, const ListShape* listB, CollisionList& collisions); + bool listList(const Shape* listA, const Shape* listB, CollisionList& collisions); /// \param sphereA pointer to sphere (cannot be NULL) /// \param cubeCenter center of cube diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index d2f2a8596f..0626927453 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -18,13 +18,13 @@ class SphereShape : public Shape { public: - SphereShape() : Shape(Shape::SPHERE_SHAPE) {} + SphereShape() : Shape(SPHERE_SHAPE) {} - SphereShape(float radius) : Shape(Shape::SPHERE_SHAPE) { + SphereShape(float radius) : Shape(SPHERE_SHAPE) { _boundingRadius = radius; } - SphereShape(float radius, const glm::vec3& position) : Shape(Shape::SPHERE_SHAPE, position) { + SphereShape(float radius, const glm::vec3& position) : Shape(SPHERE_SHAPE, position) { _boundingRadius = radius; } diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index bde29ea588..db4833b20a 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -16,7 +16,9 @@ #include #include +#include #include +#include #include #include #include @@ -112,6 +114,7 @@ void ShapeColliderTests::sphereTouchesSphere() { if (!collision) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: null collision" << std::endl; + return; } // penetration points from sphereA into sphereB @@ -1298,7 +1301,41 @@ void ShapeColliderTests::rayMissesPlane() { } } +void ShapeColliderTests::measureTimeOfCollisionDispatch() { + /* KEEP for future manual testing + // create two non-colliding spheres + float radiusA = 7.0f; + float radiusB = 3.0f; + float alpha = 1.2f; + float beta = 1.3f; + glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + float offsetDistance = alpha * radiusA + beta * radiusB; + + SphereShape sphereA(radiusA, origin); + SphereShape sphereB(radiusB, offsetDistance * offsetDirection); + CollisionList collisions(16); + + //int numTests = 1; + quint64 oldTime; + quint64 newTime; + int numTests = 100000000; + { + quint64 startTime = usecTimestampNow(); + for (int i = 0; i < numTests; ++i) { + ShapeCollider::collideShapes(&sphereA, &sphereB, collisions); + } + quint64 endTime = usecTimestampNow(); + std::cout << numTests << " non-colliding collisions in " << (endTime - startTime) << " usec" << std::endl; + newTime = endTime - startTime; + } + */ +} + void ShapeColliderTests::runAllTests() { + ShapeCollider::initDispatchTable(); + + //measureTimeOfCollisionDispatch(); + sphereMissesSphere(); sphereTouchesSphere(); diff --git a/tests/physics/src/ShapeColliderTests.h b/tests/physics/src/ShapeColliderTests.h index fd9f1f9706..4a51651cb8 100644 --- a/tests/physics/src/ShapeColliderTests.h +++ b/tests/physics/src/ShapeColliderTests.h @@ -35,6 +35,8 @@ namespace ShapeColliderTests { void rayHitsPlane(); void rayMissesPlane(); + void measureTimeOfCollisionDispatch(); + void runAllTests(); } diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp index 3a3bd43278..705ddeeac3 100644 --- a/tests/physics/src/VerletShapeTests.cpp +++ b/tests/physics/src/VerletShapeTests.cpp @@ -757,6 +757,8 @@ void VerletShapeTests::capsuleTouchesCapsule() { } void VerletShapeTests::runAllTests() { + ShapeCollider::initDispatchTable(); + setSpherePosition(); sphereMissesSphere(); sphereTouchesSphere(); From c8ac5f584a6ff24387703d8047cc4553b8db1ed6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Aug 2014 09:29:08 -0700 Subject: [PATCH 03/16] Convert to vector maths --- examples/editVoxels.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index 1ed3dcc0c3..b92c6c28b0 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1299,26 +1299,19 @@ function mouseMoveEvent(event) { // Watch the drag direction to tell which way to 'extrude' this voxel if (!isExtruding) { var pickRay = Camera.computePickRay(event.x, event.y); - var lastVoxelDistance = { x: pickRay.origin.x - lastVoxelPosition.x, - y: pickRay.origin.y - lastVoxelPosition.y, - z: pickRay.origin.z - lastVoxelPosition.z }; - var distance = Vec3.length(lastVoxelDistance); - var mouseSpot = { x: pickRay.direction.x * distance, y: pickRay.direction.y * distance, z: pickRay.direction.z * distance }; - mouseSpot.x += pickRay.origin.x; - mouseSpot.y += pickRay.origin.y; - mouseSpot.z += pickRay.origin.z; - var dx = mouseSpot.x - lastVoxelPosition.x; - var dy = mouseSpot.y - lastVoxelPosition.y; - var dz = mouseSpot.z - lastVoxelPosition.z; + var distance = Vec3.length(Vec3.subtract(pickRay.origin, lastVoxelPosition)); + var mouseSpot = Vec3.sum(Vec3.multiply(pickRay.direction, distance), pickRay.origin); + var delta = Vec3.subtract(mouseSpot, lastVoxelPosition); + extrudeScale = lastVoxelScale; extrudeDirection = { x: 0, y: 0, z: 0 }; isExtruding = true; - if (dx > lastVoxelScale) extrudeDirection.x = extrudeScale; - else if (dx < -lastVoxelScale) extrudeDirection.x = -extrudeScale; - else if (dy > lastVoxelScale) extrudeDirection.y = extrudeScale; - else if (dy < -lastVoxelScale) extrudeDirection.y = -extrudeScale; - else if (dz > lastVoxelScale) extrudeDirection.z = extrudeScale; - else if (dz < -lastVoxelScale) extrudeDirection.z = -extrudeScale; + if (delta.x > lastVoxelScale) extrudeDirection.x = extrudeScale; + else if (delta.x < -lastVoxelScale) extrudeDirection.x = -extrudeScale; + else if (delta.y > lastVoxelScale) extrudeDirection.y = extrudeScale; + else if (delta.y < -lastVoxelScale) extrudeDirection.y = -extrudeScale; + else if (delta.z > lastVoxelScale) extrudeDirection.z = extrudeScale; + else if (delta.z < -lastVoxelScale) extrudeDirection.z = -extrudeScale; else isExtruding = false; } else { // We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel From 761387854d2f3baef4a5d2a36cae5ed9f0092673 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 22 Aug 2014 10:09:10 -0700 Subject: [PATCH 04/16] Extrude per mouse movement in voxel space instead of screen space --- examples/editVoxels.js | 44 +++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index b92c6c28b0..a85c04dd02 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -51,9 +51,6 @@ var lastVoxelScale = 0; var dragStart = { x: 0, y: 0 }; var wheelPixelsMoved = 0; -var mouseX = 0; -var mouseY = 0; - // Create a table of the different colors you can choose var colors = new Array(); colors[0] = { red: 120, green: 181, blue: 126 }; @@ -1041,8 +1038,6 @@ function mousePressEvent(event) { // TODO: does any of this stuff need to execute if we're panning or orbiting? trackMouseEvent(event); // used by preview support - mouseX = event.x; - mouseY = event.y; var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); audioOptions.position = Vec3.sum(pickRay.origin, pickRay.direction); @@ -1296,33 +1291,30 @@ function mouseMoveEvent(event) { } if (isAdding) { - // Watch the drag direction to tell which way to 'extrude' this voxel - if (!isExtruding) { - var pickRay = Camera.computePickRay(event.x, event.y); - var distance = Vec3.length(Vec3.subtract(pickRay.origin, lastVoxelPosition)); - var mouseSpot = Vec3.sum(Vec3.multiply(pickRay.direction, distance), pickRay.origin); - var delta = Vec3.subtract(mouseSpot, lastVoxelPosition); + var pickRay = Camera.computePickRay(event.x, event.y); + var distance = Vec3.length(Vec3.subtract(pickRay.origin, lastVoxelPosition)); + var mouseSpot = Vec3.sum(Vec3.multiply(pickRay.direction, distance), pickRay.origin); + var delta = Vec3.subtract(mouseSpot, lastVoxelPosition); + if (!isExtruding) { + // Use the drag direction to tell which way to 'extrude' this voxel extrudeScale = lastVoxelScale; extrudeDirection = { x: 0, y: 0, z: 0 }; isExtruding = true; - if (delta.x > lastVoxelScale) extrudeDirection.x = extrudeScale; - else if (delta.x < -lastVoxelScale) extrudeDirection.x = -extrudeScale; - else if (delta.y > lastVoxelScale) extrudeDirection.y = extrudeScale; - else if (delta.y < -lastVoxelScale) extrudeDirection.y = -extrudeScale; - else if (delta.z > lastVoxelScale) extrudeDirection.z = extrudeScale; - else if (delta.z < -lastVoxelScale) extrudeDirection.z = -extrudeScale; + if (delta.x > lastVoxelScale) extrudeDirection.x = 1; + else if (delta.x < -lastVoxelScale) extrudeDirection.x = -1; + else if (delta.y > lastVoxelScale) extrudeDirection.y = 1; + else if (delta.y < -lastVoxelScale) extrudeDirection.y = -1; + else if (delta.z > lastVoxelScale) extrudeDirection.z = 1; + else if (delta.z < -lastVoxelScale) extrudeDirection.z = -1; else isExtruding = false; } else { - // We have got an extrusion direction, now look for mouse move beyond threshold to add new voxel - var dx = event.x - mouseX; - var dy = event.y - mouseY; - if (Math.sqrt(dx*dx + dy*dy) > PIXELS_PER_EXTRUDE_VOXEL) { - lastVoxelPosition = Vec3.sum(lastVoxelPosition, extrudeDirection); - Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, - extrudeScale, lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); - mouseX = event.x; - mouseY = event.y; + // Extrude if mouse has moved by a voxel in the extrude direction + var distanceInDirection = Vec3.dot(delta, extrudeDirection); + if (distanceInDirection > extrudeScale) { + lastVoxelPosition = Vec3.sum(lastVoxelPosition, Vec3.multiply(extrudeDirection, extrudeScale)); + Voxels.setVoxel(lastVoxelPosition.x, lastVoxelPosition.y, lastVoxelPosition.z, extrudeScale, + lastVoxelColor.red, lastVoxelColor.green, lastVoxelColor.blue); } } } From 1be922f986e0f55a5567861562a315cb28ce4a48 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 22 Aug 2014 11:20:53 -0700 Subject: [PATCH 05/16] add shapeVsList etc to dispatch table renamed some functions for more readability --- libraries/shared/src/ShapeCollider.cpp | 187 +++++------------------ libraries/shared/src/ShapeCollider.h | 73 +++------ tests/physics/src/ShapeColliderTests.cpp | 18 +-- 3 files changed, 70 insertions(+), 208 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 8d09c0408a..536d9bdcde 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -50,27 +50,27 @@ void initDispatchTable() { // NOTE: no need to update any that are notImplemented, but we leave them // commented out in the code so that we remember that they exist. - dispatchTable[getDispatchKey(SPHERE_SHAPE, SPHERE_SHAPE)] = &sphereSphere; - dispatchTable[getDispatchKey(SPHERE_SHAPE, CAPSULE_SHAPE)] = &sphereCapsule; - dispatchTable[getDispatchKey(SPHERE_SHAPE, PLANE_SHAPE)] = &spherePlane; - //dispatchTable[getDispatchKey(SPHERE_SHAPE, LIST_SHAPE)] = ¬Implemented; + dispatchTable[getDispatchKey(SPHERE_SHAPE, SPHERE_SHAPE)] = &sphereVsSphere; + dispatchTable[getDispatchKey(SPHERE_SHAPE, CAPSULE_SHAPE)] = &sphereVsCapsule; + dispatchTable[getDispatchKey(SPHERE_SHAPE, PLANE_SHAPE)] = &sphereVsPlane; + dispatchTable[getDispatchKey(SPHERE_SHAPE, LIST_SHAPE)] = &shapeVsList; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, SPHERE_SHAPE)] = &capsuleSphere; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, CAPSULE_SHAPE)] = &capsuleCapsule; - dispatchTable[getDispatchKey(CAPSULE_SHAPE, PLANE_SHAPE)] = &capsulePlane; - //dispatchTable[getDispatchKey(CAPSULE_SHAPE, LIST_SHAPE)] = ¬Implemented; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, SPHERE_SHAPE)] = &capsuleVsSphere; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, CAPSULE_SHAPE)] = &capsuleVsCapsule; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, PLANE_SHAPE)] = &capsuleVsPlane; + dispatchTable[getDispatchKey(CAPSULE_SHAPE, LIST_SHAPE)] = &shapeVsList; - dispatchTable[getDispatchKey(PLANE_SHAPE, SPHERE_SHAPE)] = &planeSphere; - dispatchTable[getDispatchKey(PLANE_SHAPE, CAPSULE_SHAPE)] = &planeCapsule; - dispatchTable[getDispatchKey(PLANE_SHAPE, PLANE_SHAPE)] = &planePlane; - //dispatchTable[getDispatchKey(PLANE_SHAPE, LIST_SHAPE)] = ¬Implemented; + dispatchTable[getDispatchKey(PLANE_SHAPE, SPHERE_SHAPE)] = &planeVsSphere; + dispatchTable[getDispatchKey(PLANE_SHAPE, CAPSULE_SHAPE)] = &planeVsCapsule; + dispatchTable[getDispatchKey(PLANE_SHAPE, PLANE_SHAPE)] = &planeVsPlane; + dispatchTable[getDispatchKey(PLANE_SHAPE, LIST_SHAPE)] = &shapeVsList; - //dispatchTable[getDispatchKey(LIST_SHAPE, SPHERE_SHAPE)] = ¬Implemented; - //dispatchTable[getDispatchKey(LIST_SHAPE, CAPSULE_SHAPE)] = ¬Implemented; - //dispatchTable[getDispatchKey(LIST_SHAPE, PLANE_SHAPE)] = ¬Implemented; - //dispatchTable[getDispatchKey(LIST_SHAPE, LIST_SHAPE)] = ¬Implemented; + dispatchTable[getDispatchKey(LIST_SHAPE, SPHERE_SHAPE)] = &listVsShape; + dispatchTable[getDispatchKey(LIST_SHAPE, CAPSULE_SHAPE)] = &listVsShape; + dispatchTable[getDispatchKey(LIST_SHAPE, PLANE_SHAPE)] = &listVsShape; + dispatchTable[getDispatchKey(LIST_SHAPE, LIST_SHAPE)] = &listVsList; - // all of the UNKNOWN_SHAPE pairings point at notImplemented + // all of the UNKNOWN_SHAPE pairings are notImplemented } bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { @@ -79,31 +79,6 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll static CollisionList tempCollisions(32); -bool collideShapesCoarse(const QVector& shapesA, const QVector& shapesB, CollisionInfo& collision) { - tempCollisions.clear(); - foreach (const Shape* shapeA, shapesA) { - foreach (const Shape* shapeB, shapesB) { - collideShapes(shapeA, shapeB, tempCollisions); - } - } - if (tempCollisions.size() > 0) { - glm::vec3 totalPenetration(0.0f); - glm::vec3 averageContactPoint(0.0f); - for (int j = 0; j < tempCollisions.size(); ++j) { - CollisionInfo* c = tempCollisions.getCollision(j); - totalPenetration = addPenetrations(totalPenetration, c->_penetration); - averageContactPoint += c->_contactPoint; - } - collision._penetration = totalPenetration; - collision._contactPoint = averageContactPoint / (float)(tempCollisions.size()); - // there are no valid shape pointers for this collision so we set them NULL - collision._shapeA = NULL; - collision._shapeB = NULL; - return true; - } - return false; -} - bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions) { bool collided = false; if (shapeA) { @@ -145,9 +120,9 @@ bool collideShapesWithShapes(const QVector& shapesA, const QVectorgetType(); if (typeA == SPHERE_SHAPE) { - return sphereAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); + return sphereVsAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); } else if (typeA == CAPSULE_SHAPE) { - return capsuleAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); + return capsuleVsAACube(static_cast(shapeA), cubeCenter, cubeSide, collisions); } else if (typeA == LIST_SHAPE) { const ListShape* listA = static_cast(shapeA); bool touching = false; @@ -155,9 +130,9 @@ bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, fl const Shape* subShape = listA->getSubShape(i); int subType = subShape->getType(); if (subType == SPHERE_SHAPE) { - touching = sphereAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; + touching = sphereVsAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; } else if (subType == CAPSULE_SHAPE) { - touching = capsuleAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; + touching = capsuleVsAACube(static_cast(subShape), cubeCenter, cubeSide, collisions) || touching; } } return touching; @@ -165,7 +140,7 @@ bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, fl return false; } -bool sphereSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool sphereVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const SphereShape* sphereA = static_cast(shapeA); const SphereShape* sphereB = static_cast(shapeB); glm::vec3 BA = sphereB->getTranslation() - sphereA->getTranslation(); @@ -195,7 +170,7 @@ bool sphereSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& colli return false; } -bool sphereCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool sphereVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const SphereShape* sphereA = static_cast(shapeA); const CapsuleShape* capsuleB = static_cast(shapeB); // find sphereA's closest approach to axis of capsuleB @@ -266,7 +241,7 @@ bool sphereCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& coll return false; } -bool spherePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool sphereVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const SphereShape* sphereA = static_cast(shapeA); const PlaneShape* planeB = static_cast(shapeB); glm::vec3 penetration; @@ -284,7 +259,7 @@ bool spherePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collis return false; } -bool capsuleSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool capsuleVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const CapsuleShape* capsuleA = static_cast(shapeA); const SphereShape* sphereB = static_cast(shapeB); // find sphereB's closest approach to axis of capsuleA @@ -427,7 +402,7 @@ bool lineCylinder(const glm::vec3& lineP, const glm::vec3& lineDir, return true; } -bool capsuleCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool capsuleVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const CapsuleShape* capsuleA = static_cast(shapeA); const CapsuleShape* capsuleB = static_cast(shapeB); glm::vec3 axisA; @@ -588,7 +563,7 @@ bool capsuleCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& col return false; } -bool capsulePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool capsuleVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const CapsuleShape* capsuleA = static_cast(shapeA); const PlaneShape* planeB = static_cast(shapeB); glm::vec3 start, end, penetration; @@ -610,7 +585,7 @@ bool capsulePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& colli return false; } -bool planeSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool planeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const PlaneShape* planeA = static_cast(shapeA); const SphereShape* sphereB = static_cast(shapeB); glm::vec3 penetration; @@ -629,7 +604,7 @@ bool planeSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collis return false; } -bool planeCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool planeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { const PlaneShape* planeA = static_cast(shapeA); const CapsuleShape* capsuleB = static_cast(shapeB); glm::vec3 start, end, penetration; @@ -651,115 +626,33 @@ bool planeCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& colli return false; } -bool planePlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool planeVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { // technically, planes always collide unless they're parallel and not coincident; however, that's // not going to give us any useful information return false; } -bool sphereList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool shapeVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; const ListShape* listB = static_cast(shapeB); for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { const Shape* subShape = listB->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = sphereSphere(shapeA, subShape, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = sphereCapsule(shapeA, subShape, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = spherePlane(shapeA, subShape, collisions) || touching; - } + touching = collideShapes(shapeA, subShape, collisions) || touching; } return touching; } -bool capsuleList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listB->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = capsuleSphere(shapeA, subShape, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsuleCapsule(shapeA, subShape, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = capsulePlane(shapeA, subShape, collisions) || touching; - } - } - return touching; -} - -bool planeList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listB = static_cast(shapeB); - for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listB->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = planeSphere(shapeA, subShape, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = planeCapsule(shapeA, subShape, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = planePlane(shapeA, subShape, collisions) || touching; - } - } - return touching; -} - -bool listSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool listVsShape(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; const ListShape* listA = static_cast(shapeA); for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { const Shape* subShape = listA->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = sphereSphere(subShape, shapeB, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsuleSphere(subShape, shapeB, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = planeSphere(subShape, shapeB, collisions) || touching; - } + touching = collideShapes(subShape, shapeB, collisions) || touching; } return touching; } -bool listCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = sphereCapsule(subShape, shapeB, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsuleCapsule(subShape, shapeB, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = planeCapsule(subShape, shapeB, collisions) || touching; - } - } - return touching; -} - -bool listPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - bool touching = false; - const ListShape* listA = static_cast(shapeA); - for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) { - const Shape* subShape = listA->getSubShape(i); - int subType = subShape->getType(); - if (subType == SPHERE_SHAPE) { - touching = spherePlane(subShape, shapeB, collisions) || touching; - } else if (subType == CAPSULE_SHAPE) { - touching = capsulePlane(subShape, shapeB, collisions) || touching; - } else if (subType == PLANE_SHAPE) { - touching = planePlane(subShape, shapeB, collisions) || touching; - } - } - return touching; -} - -bool listList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { +bool listVsList(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { bool touching = false; const ListShape* listA = static_cast(shapeA); const ListShape* listB = static_cast(shapeB); @@ -773,7 +666,7 @@ bool listList(const Shape* shapeA, const Shape* shapeB, CollisionList& collision } // helper function -bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, +bool sphereVsAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { // sphere is A // cube is B @@ -921,11 +814,11 @@ bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, } */ -bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { - return sphereAACube(sphereA->getTranslation(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); +bool sphereVsAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { + return sphereVsAACube(sphereA->getTranslation(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); } -bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { +bool capsuleVsAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { // find nerest approach of capsule line segment to cube glm::vec3 capsuleAxis; capsuleA->computeNormalizedAxis(capsuleAxis); @@ -938,7 +831,7 @@ bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, fl } glm::vec3 nearestApproach = capsuleA->getTranslation() + offset * capsuleAxis; // collide nearest approach like a sphere at that point - return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); + return sphereVsAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } bool findRayIntersectionWithShapes(const QVector shapes, const glm::vec3& rayStart, const glm::vec3& rayDirection, float& minDistance) { diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 11768f0a48..3aa795e6fa 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -35,13 +35,6 @@ namespace ShapeCollider { /// \param collisions[out] collision details /// \return true if shapes collide bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - bool collideShapesOld(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions); - - /// \param shapesA list of shapes - /// \param shapeB list of shapes - /// \param collisions[out] average collision details - /// \return true if any shapes collide - bool collideShapesCoarse(const QVector& shapesA, const QVector& shapesB, CollisionInfo& collision); bool collideShapeWithShapes(const Shape* shapeA, const QVector& shapes, int startIndex, CollisionList& collisions); bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions); @@ -57,111 +50,87 @@ namespace ShapeCollider { /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions); + bool sphereVsSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions); /// \param sphereA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions); + bool sphereVsCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions); /// \param sphereA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool spherePlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions); + bool sphereVsPlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions); + bool capsuleVsSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions); + bool capsuleVsCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions); /// \param capsuleA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsulePlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions); + bool capsuleVsPlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param sphereB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planeSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions); + bool planeVsSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param capsuleB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planeCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions); + bool planeVsCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions); /// \param planeA pointer to first shape (cannot be NULL) /// \param planeB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool planePlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions); + bool planeVsPlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions); - /// \param sphereA pointer to first shape (cannot be NULL) + /// \param shapeA pointer to first shape (cannot be NULL) /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool sphereList(const Shape* sphereA, const Shape* listB, CollisionList& collisions); + bool shapeVsList(const Shape* shapeA, const Shape* listB, CollisionList& collisions); - /// \param capuleA pointer to first shape (cannot be NULL) + /// \param listA pointer to first shape (cannot be NULL) + /// \param shapeB pointer to second shape (cannot be NULL) + /// \param[out] collisions where to append collision details + /// \return true if shapes collide + bool listVsShape(const Shape* listA, const Shape* shapeB, CollisionList& collisions); + + /// \param listA pointer to first shape (cannot be NULL) /// \param listB pointer to second shape (cannot be NULL) /// \param[out] collisions where to append collision details /// \return true if shapes collide - bool capsuleList(const Shape* capsuleA, const Shape* listB, CollisionList& collisions); - - /// \param planeA pointer to first shape (cannot be NULL) - /// \param listB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool planeList(const Shape* planeA, const Shape* listB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param sphereB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listSphere(const Shape* listA, const Shape* sphereB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listCapsule(const Shape* listA, const Shape* capsuleB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param planeB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listPlane(const Shape* listA, const Shape* planeB, CollisionList& collisions); - - /// \param listA pointer to first shape (cannot be NULL) - /// \param capsuleB pointer to second shape (cannot be NULL) - /// \param[out] collisions where to append collision details - /// \return true if shapes collide - bool listList(const Shape* listA, const Shape* listB, CollisionList& collisions); + bool listVsList(const Shape* listA, const Shape* listB, CollisionList& collisions); /// \param sphereA pointer to sphere (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if sphereA collides with axis aligned cube - bool sphereAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); + bool sphereVsAACube(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); /// \param capsuleA pointer to capsule (cannot be NULL) /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube /// \param[out] collisions where to append collision details /// \return true if capsuleA collides with axis aligned cube - bool capsuleAACube(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions); + bool capsuleVsAACube(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 diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index db4833b20a..9473cc3452 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -713,7 +713,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { sphereCenter = cubeCenter + sphereOffset * axis; sphere.setTranslation(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (!ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; } CollisionInfo* collision = collisions[0]; @@ -746,7 +746,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { sphereCenter = cubeCenter + sphereOffset * axis; sphere.setTranslation(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (!ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube." << " axis = " << axis << std::endl; @@ -820,7 +820,7 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { sphereCenter = cubeCenter + (lengthAxis * 0.5f * cubeSide + sphereRadius - overlap) * axis; sphere.setTranslation(sphereCenter); - if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (!ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; } CollisionInfo* collision = collisions[i]; @@ -861,42 +861,42 @@ void ShapeColliderTests::sphereMissesAACube() { // top sphereCenter = cubeCenter + sphereOffset * yAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // bottom sphereCenter = cubeCenter - sphereOffset * yAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // left sphereCenter = cubeCenter + sphereOffset * xAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // right sphereCenter = cubeCenter - sphereOffset * xAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // forward sphereCenter = cubeCenter + sphereOffset * zAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // back sphereCenter = cubeCenter - sphereOffset * zAxis; sphere.setTranslation(sphereCenter); - if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ + if (ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } } From 5d39efea827d02b83788b4c84a85f34cb5914513 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 22 Aug 2014 17:26:19 -0700 Subject: [PATCH 06/16] make sure each test error has a newline at end --- tests/physics/src/ShapeColliderTests.cpp | 213 ++++++++++------------- 1 file changed, 94 insertions(+), 119 deletions(-) diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 9473cc3452..18882423fb 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -73,8 +73,7 @@ void ShapeColliderTests::sphereMissesSphere() { if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -122,7 +121,7 @@ void ShapeColliderTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -132,7 +131,7 @@ void ShapeColliderTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } @@ -152,7 +151,7 @@ void ShapeColliderTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -162,7 +161,7 @@ void ShapeColliderTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } } @@ -202,23 +201,20 @@ void ShapeColliderTests::sphereMissesCapsule() { if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should NOT touch" - << std::endl; + << " ERROR: sphere and capsule should NOT touch" << std::endl; } // capsuleB against sphereA if (ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should NOT touch" - << std::endl; + << " ERROR: sphere and capsule should NOT touch" << std::endl; } } if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -244,8 +240,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -257,7 +252,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -266,15 +261,14 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -286,7 +280,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -297,7 +291,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } { // sphereA hits end cap at axis @@ -307,8 +301,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -320,7 +313,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -329,15 +322,14 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -349,7 +341,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -360,7 +352,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } { // sphereA hits start cap at axis @@ -370,8 +362,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -383,7 +374,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -392,15 +383,14 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -412,7 +402,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -423,7 +413,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } if (collisions.size() != numCollisions) { @@ -453,14 +443,12 @@ void ShapeColliderTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } // end to end @@ -468,14 +456,12 @@ void ShapeColliderTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } // rotate B and move it to the side @@ -485,20 +471,17 @@ void ShapeColliderTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -523,16 +506,14 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -544,16 +525,14 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -567,16 +546,14 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -593,8 +570,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -605,7 +581,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * xAxis; @@ -613,15 +589,14 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB vs capsuleA if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -632,8 +607,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration - << std::endl; + << " actual = " << collision->_penetration << std::endl; } expectedContactPoint = capsuleB.getTranslation() - (radiusB + halfHeightB) * xAxis; @@ -641,8 +615,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint - << std::endl; + << " actual = " << collision->_contactPoint << std::endl; } } @@ -658,8 +631,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -670,8 +642,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration - << std::endl; + << " actual = " << collision->_penetration << std::endl; } glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * zAxis + shift * yAxis; @@ -679,8 +650,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint - << std::endl; + << " actual = " << collision->_contactPoint << std::endl; } } } @@ -714,7 +684,8 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { sphere.setTranslation(sphereCenter); if (!ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis + << std::endl; } CollisionInfo* collision = collisions[0]; if (!collision) { @@ -724,17 +695,13 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { glm::vec3 expectedPenetration = - overlap * axis; if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration - << " expected " << expectedPenetration - << " axis = " << axis - << std::endl; + << " expected " << expectedPenetration << " axis = " << axis << std::endl; } glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint - << " expected " << expectedContact - << " axis = " << axis - << std::endl; + << " expected " << expectedContact << " axis = " << axis << std::endl; } } @@ -748,30 +715,24 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { if (!ShapeCollider::sphereVsAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube." - << " axis = " << axis - << std::endl; + << " axis = " << axis << std::endl; } CollisionInfo* collision = collisions[0]; if (!collision) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: no CollisionInfo on y-axis." - << " axis = " << axis - << std::endl; + << " axis = " << axis << std::endl; } glm::vec3 expectedPenetration = - overlap * axis; if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration - << " expected " << expectedPenetration - << " axis = " << axis - << std::endl; + << " expected " << expectedPenetration << " axis = " << axis << std::endl; } glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint - << " expected " << expectedContact - << " axis = " << axis - << std::endl; + << " expected " << expectedContact << " axis = " << axis << std::endl; } } } @@ -831,17 +792,13 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { glm::vec3 expectedPenetration = - overlap * axis; if (glm::distance(expectedPenetration, collision->_penetration) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: penetration = " << collision->_penetration - << " expected " << expectedPenetration - << " axis = " << axis - << std::endl; + << " expected " << expectedPenetration << " axis = " << axis << std::endl; } glm::vec3 expectedContact = sphereCenter - sphereRadius * axis; if (glm::distance(expectedContact, collision->_contactPoint) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: contactaPoint = " << collision->_contactPoint - << " expected " << expectedContact - << " axis = " << axis - << std::endl; + << " expected " << expectedContact << " axis = " << axis << std::endl; } } } @@ -968,7 +925,8 @@ void ShapeColliderTests::rayHitsSphere() { float expectedDistance = startDistance - radius; float relativeError = fabsf(distance - expectedDistance) / startDistance; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray sphere intersection distance error = " + << relativeError << std::endl; } } } @@ -1025,7 +983,8 @@ void ShapeColliderTests::rayBarelyMissesSphere() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } // translate and rotate the whole system... @@ -1043,7 +1002,8 @@ void ShapeColliderTests::rayBarelyMissesSphere() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should just barely miss sphere" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } } @@ -1065,7 +1025,8 @@ void ShapeColliderTests::rayHitsCapsule() { float expectedDistance = startDistance - radius; float relativeError = fabsf(distance - expectedDistance) / startDistance; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } // toward top of cylindrical wall @@ -1076,7 +1037,8 @@ void ShapeColliderTests::rayHitsCapsule() { } relativeError = fabsf(distance - expectedDistance) / startDistance; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } // toward top cap @@ -1088,7 +1050,8 @@ void ShapeColliderTests::rayHitsCapsule() { } relativeError = fabsf(distance - expectedDistance) / startDistance; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } const float EDGE_CASE_SLOP_FACTOR = 20.0f; @@ -1103,7 +1066,8 @@ void ShapeColliderTests::rayHitsCapsule() { relativeError = fabsf(distance - 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; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } // toward tip of bottom cap @@ -1116,7 +1080,8 @@ void ShapeColliderTests::rayHitsCapsule() { relativeError = fabsf(distance - 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; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } // toward edge of capsule cylindrical face @@ -1130,7 +1095,8 @@ void ShapeColliderTests::rayHitsCapsule() { relativeError = fabsf(distance - 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; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray capsule intersection distance error = " + << relativeError << std::endl; } } // TODO: test at steep angles near cylinder/cap junction @@ -1157,7 +1123,8 @@ void ShapeColliderTests::rayMissesCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } // below bottom cap @@ -1167,7 +1134,8 @@ void ShapeColliderTests::rayMissesCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } // past edge of capsule cylindrical face @@ -1178,7 +1146,8 @@ void ShapeColliderTests::rayMissesCapsule() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss capsule" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } } // TODO: test at steep angles near edge @@ -1204,7 +1173,8 @@ void ShapeColliderTests::rayHitsPlane() { float expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; float relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " + << relativeError << std::endl; } // rotate the whole system and try again @@ -1225,7 +1195,8 @@ void ShapeColliderTests::rayHitsPlane() { expectedDistance = SQUARE_ROOT_OF_3 * planeDistanceFromOrigin; relativeError = fabsf(distance - expectedDistance) / planeDistanceFromOrigin; if (relativeError > EPSILON) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " << relativeError << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray plane intersection distance error = " + << relativeError << std::endl; } } @@ -1246,7 +1217,8 @@ void ShapeColliderTests::rayMissesPlane() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } // rotate the whole system and try again @@ -1264,7 +1236,8 @@ void ShapeColliderTests::rayMissesPlane() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } } @@ -1278,7 +1251,8 @@ void ShapeColliderTests::rayMissesPlane() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } // rotate the whole system and try again @@ -1296,7 +1270,8 @@ void ShapeColliderTests::rayMissesPlane() { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: ray should miss plane" << std::endl; } if (distance != FLT_MAX) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: distance should be unchanged after intersection miss" + << std::endl; } } } From de0ebf2f575a0eea8d49849673dd0005e96f89ad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 22 Aug 2014 17:33:31 -0700 Subject: [PATCH 07/16] make sure all test errors have newline at end --- tests/physics/src/VerletShapeTests.cpp | 126 ++++++++++--------------- 1 file changed, 48 insertions(+), 78 deletions(-) diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp index 705ddeeac3..55ebbdc741 100644 --- a/tests/physics/src/VerletShapeTests.cpp +++ b/tests/physics/src/VerletShapeTests.cpp @@ -102,8 +102,7 @@ void VerletShapeTests::sphereMissesSphere() { if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -159,7 +158,7 @@ void VerletShapeTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -169,7 +168,7 @@ void VerletShapeTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } @@ -189,7 +188,7 @@ void VerletShapeTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -199,7 +198,7 @@ void VerletShapeTests::sphereTouchesSphere() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } } @@ -247,23 +246,20 @@ void VerletShapeTests::sphereMissesCapsule() { if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should NOT touch" - << std::endl; + << " ERROR: sphere and capsule should NOT touch" << std::endl; } // capsuleB against sphereA if (ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should NOT touch" - << std::endl; + << " ERROR: sphere and capsule should NOT touch" << std::endl; } } if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -297,8 +293,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -310,7 +305,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -319,15 +314,14 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -339,7 +333,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -350,7 +344,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } { // sphereA hits end cap at axis @@ -360,8 +354,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -373,7 +366,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -382,15 +375,14 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -402,7 +394,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -413,7 +405,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } { // sphereA hits start cap at axis @@ -423,8 +415,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: sphere and capsule should touch" - << std::endl; + << " ERROR: sphere and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -436,7 +427,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of sphereA @@ -445,15 +436,14 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB collides with sphereA if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and sphere should touch" - << std::endl; + << " ERROR: capsule and sphere should touch" << std::endl; } else { ++numCollisions; } @@ -465,7 +455,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } // contactPoint is on surface of capsuleB @@ -476,7 +466,7 @@ void VerletShapeTests::sphereTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } } if (collisions.size() != numCollisions) { @@ -515,14 +505,12 @@ void VerletShapeTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } // end to end @@ -530,14 +518,12 @@ void VerletShapeTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } // rotate B and move it to the side @@ -547,20 +533,17 @@ void VerletShapeTests::capsuleMissesCapsule() { if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should NOT touch" - << std::endl; + << " ERROR: capsule and capsule should NOT touch" << std::endl; } if (collisions.size() > 0) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: expected empty collision list but size is " << collisions.size() - << std::endl; + << " ERROR: expected empty collision list but size is " << collisions.size() << std::endl; } } @@ -594,16 +577,14 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -615,16 +596,14 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -638,16 +617,14 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -664,8 +641,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -676,7 +652,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration; + << " actual = " << collision->_penetration << std::endl; } glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * xAxis; @@ -684,15 +660,14 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint; + << " actual = " << collision->_contactPoint << std::endl; } // capsuleB vs capsuleA if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -703,8 +678,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration - << std::endl; + << " actual = " << collision->_penetration << std::endl; } expectedContactPoint = capsuleB.getTranslation() - (radiusB + halfHeightB) * xAxis; @@ -712,8 +686,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint - << std::endl; + << " actual = " << collision->_contactPoint << std::endl; } } @@ -729,8 +702,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ - << " ERROR: capsule and capsule should touch" - << std::endl; + << " ERROR: capsule and capsule should touch" << std::endl; } else { ++numCollisions; } @@ -741,8 +713,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad penetration: expected = " << expectedPenetration - << " actual = " << collision->_penetration - << std::endl; + << " actual = " << collision->_penetration << std::endl; } glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * zAxis + shift * yAxis; @@ -750,8 +721,7 @@ void VerletShapeTests::capsuleTouchesCapsule() { if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: bad contactPoint: expected = " << expectedContactPoint - << " actual = " << collision->_contactPoint - << std::endl; + << " actual = " << collision->_contactPoint << std::endl; } } } From 1bd7734ec1b376c7603da9f8d51729fe73688c83 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 22 Aug 2014 18:07:11 -0700 Subject: [PATCH 08/16] sometimes swap order of shapes for simpler code --- libraries/shared/src/ShapeCollider.cpp | 112 +---------------------- tests/physics/src/ShapeColliderTests.cpp | 29 +++++- tests/physics/src/VerletShapeTests.cpp | 29 +++++- 3 files changed, 57 insertions(+), 113 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 536d9bdcde..ec0c88bd0f 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -260,80 +260,7 @@ bool sphereVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& coll } bool capsuleVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const CapsuleShape* capsuleA = static_cast(shapeA); - const SphereShape* sphereB = static_cast(shapeB); - // find sphereB's closest approach to axis of capsuleA - glm::vec3 AB = capsuleA->getTranslation() - sphereB->getTranslation(); - glm::vec3 capsuleAxis; - capsuleA->computeNormalizedAxis(capsuleAxis); - float axialDistance = - glm::dot(AB, capsuleAxis); - float absAxialDistance = fabsf(axialDistance); - float totalRadius = sphereB->getRadius() + capsuleA->getRadius(); - if (absAxialDistance < totalRadius + capsuleA->getHalfHeight()) { - glm::vec3 radialAxis = AB + axialDistance * capsuleAxis; // from sphereB to axis of capsuleA - float radialDistance2 = glm::length2(radialAxis); - float totalRadius2 = totalRadius * totalRadius; - if (radialDistance2 > totalRadius2) { - // sphere is too far from capsule axis - return false; - } - - // closestApproach = point on capsuleA's axis that is closest to sphereB's center - glm::vec3 closestApproach = capsuleA->getTranslation() + axialDistance * capsuleAxis; - - if (absAxialDistance > capsuleA->getHalfHeight()) { - // sphere hits capsule on a cap - // --> recompute radialAxis and closestApproach - float sign = (axialDistance > 0.0f) ? 1.0f : -1.0f; - closestApproach = capsuleA->getTranslation() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; - radialAxis = closestApproach - sphereB->getTranslation(); - radialDistance2 = glm::length2(radialAxis); - if (radialDistance2 > totalRadius2) { - return false; - } - } - if (radialDistance2 > EPSILON * EPSILON) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // normalize the radialAxis - float radialDistance = sqrtf(radialDistance2); - radialAxis /= radialDistance; - // penetration points from A into B - collision->_penetration = (radialDistance - totalRadius) * radialAxis; // points from A into B - // contactPoint is on surface of capsuleA - collision->_contactPoint = closestApproach - capsuleA->getRadius() * radialAxis; - collision->_shapeA = capsuleA; - collision->_shapeB = sphereB; - } else { - // A is on B's axis, so the penetration is undefined... - if (absAxialDistance > capsuleA->getHalfHeight()) { - // ...for the cylinder case (for now we pretend the collision doesn't exist) - return false; - } else { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - // collisions list is full - return false; - } - // ... but still defined for the cap case - if (axialDistance < 0.0f) { - // we're hitting the start cap, so we negate the capsuleAxis - capsuleAxis *= -1; - } - float sign = (axialDistance > 0.0f) ? 1.0f : -1.0f; - collision->_penetration = (sign * (totalRadius + capsuleA->getHalfHeight() - absAxialDistance)) * capsuleAxis; - // contactPoint is on surface of sphereA - collision->_contactPoint = closestApproach + (sign * capsuleA->getRadius()) * capsuleAxis; - collision->_shapeA = capsuleA; - collision->_shapeB = sphereB; - } - } - return true; - } - return false; + return sphereVsCapsule(shapeB, shapeA, collisions); } /// \param lineP point on line @@ -586,44 +513,11 @@ bool capsuleVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& col } bool planeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const PlaneShape* planeA = static_cast(shapeA); - const SphereShape* sphereB = static_cast(shapeB); - glm::vec3 penetration; - if (findSpherePlanePenetration(sphereB->getTranslation(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = -penetration; - collision->_contactPoint = sphereB->getTranslation() + - (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration; - collision->_shapeA = planeA; - collision->_shapeB = sphereB; - return true; - } - return false; + return sphereVsPlane(shapeB, shapeA, collisions); } bool planeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - const PlaneShape* planeA = static_cast(shapeA); - const CapsuleShape* capsuleB = static_cast(shapeB); - glm::vec3 start, end, penetration; - capsuleB->getStartPoint(start); - capsuleB->getEndPoint(end); - glm::vec4 plane = planeA->getCoefficients(); - if (findCapsulePlanePenetration(start, end, capsuleB->getRadius(), plane, penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (!collision) { - return false; // collision list is full - } - collision->_penetration = -penetration; - glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end; - collision->_contactPoint = deepestEnd + (capsuleB->getRadius() / glm::length(penetration) - 1.0f) * penetration; - collision->_shapeA = planeA; - collision->_shapeB = capsuleB; - return true; - } - return false; + return capsuleVsPlane(shapeB, shapeA, collisions); } bool planeVsPlane(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 18882423fb..45d3ed6508 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -276,6 +276,10 @@ void ShapeColliderTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = - (radialOffset - totalRadius) * xAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -287,6 +291,11 @@ void ShapeColliderTests::sphereTouchesCapsule() { glm::vec3 BtoA = sphereA.getTranslation() - capsuleB.getTranslation(); glm::vec3 closestApproach = capsuleB.getTranslation() + glm::dot(BtoA, yAxis) * yAxis; expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach); + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + closestApproach = sphereA.getTranslation() - glm::dot(BtoA, yAxis) * yAxis; + expectedContactPoint = closestApproach - radiusB * glm::normalize(BtoA - closestApproach); + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -296,7 +305,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits end cap at axis glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset * yAxis); + sphereA.setTranslation(axialOffset); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -337,6 +346,10 @@ void ShapeColliderTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -348,6 +361,10 @@ void ShapeColliderTests::sphereTouchesCapsule() { glm::vec3 endPoint; capsuleB.getEndPoint(endPoint); expectedContactPoint = endPoint + radiusB * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedContactPoint = axialOffset - radiusA * yAxis; + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -357,7 +374,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits start cap at axis glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset * yAxis); + sphereA.setTranslation(axialOffset); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -398,6 +415,10 @@ void ShapeColliderTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -409,6 +430,10 @@ void ShapeColliderTests::sphereTouchesCapsule() { glm::vec3 startPoint; capsuleB.getStartPoint(startPoint); expectedContactPoint = startPoint - radiusB * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedContactPoint = axialOffset + radiusA * yAxis; + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp index 55ebbdc741..df5cdc5c6b 100644 --- a/tests/physics/src/VerletShapeTests.cpp +++ b/tests/physics/src/VerletShapeTests.cpp @@ -329,6 +329,10 @@ void VerletShapeTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = - (radialOffset - totalRadius) * xAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -340,6 +344,11 @@ void VerletShapeTests::sphereTouchesCapsule() { glm::vec3 BtoA = sphereA.getTranslation() - capsuleB.getTranslation(); glm::vec3 closestApproach = capsuleB.getTranslation() + glm::dot(BtoA, yAxis) * yAxis; expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach); + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + closestApproach = sphereA.getTranslation() - glm::dot(BtoA, yAxis) * yAxis; + expectedContactPoint = closestApproach - radiusB * glm::normalize(BtoA - closestApproach); + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -349,7 +358,7 @@ void VerletShapeTests::sphereTouchesCapsule() { } { // sphereA hits end cap at axis glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset * yAxis); + sphereA.setTranslation(axialOffset); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -390,6 +399,10 @@ void VerletShapeTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -401,6 +414,10 @@ void VerletShapeTests::sphereTouchesCapsule() { glm::vec3 endPoint; capsuleB.getEndPoint(endPoint); expectedContactPoint = endPoint + radiusB * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedContactPoint = axialOffset - radiusA * yAxis; + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -410,7 +427,7 @@ void VerletShapeTests::sphereTouchesCapsule() { } { // sphereA hits start cap at axis glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setTranslation(axialOffset * yAxis); + sphereA.setTranslation(axialOffset); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -451,6 +468,10 @@ void VerletShapeTests::sphereTouchesCapsule() { // penetration points from sphereA into capsuleB collision = collisions.getCollision(numCollisions - 1); expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedPenetration *= -1.0f; + } inaccuracy = glm::length(collision->_penetration - expectedPenetration); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -462,6 +483,10 @@ void VerletShapeTests::sphereTouchesCapsule() { glm::vec3 startPoint; capsuleB.getStartPoint(startPoint); expectedContactPoint = startPoint - radiusB * yAxis; + if (collision->_shapeA == &sphereA) { + // the ShapeCollider swapped the order of the shapes + expectedContactPoint = axialOffset + radiusA * yAxis; + } inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ From 4cd1f4afef126891f36a07e2547dbf22af3d858e Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Sun, 24 Aug 2014 22:17:07 -0700 Subject: [PATCH 09/16] add support for head-shadow/penumbra filter for positional audio streams --- assignment-client/src/audio/AudioMixer.cpp | 47 +++- assignment-client/src/audio/AudioMixer.h | 5 +- .../resources/web/settings/describe.json | 8 +- examples/playSound.js | 4 +- examples/playSoundOrbit.js | 42 ++++ interface/src/Audio.h | 5 +- libraries/audio/src/AudioFilter.cpp | 26 -- libraries/audio/src/AudioFilter.h | 233 ++++++------------ libraries/audio/src/AudioFilterBank.cpp | 45 ++++ libraries/audio/src/AudioFilterBank.h | 169 +++++++++++++ libraries/audio/src/PositionalAudioStream.cpp | 3 + libraries/audio/src/PositionalAudioStream.h | 6 + 12 files changed, 405 insertions(+), 188 deletions(-) create mode 100644 examples/playSoundOrbit.js delete mode 100644 libraries/audio/src/AudioFilter.cpp create mode 100644 libraries/audio/src/AudioFilterBank.cpp create mode 100644 libraries/audio/src/AudioFilterBank.h diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 5f4c3827f2..e1d118a11d 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -75,6 +75,8 @@ int AudioMixer::_maxFramesOverDesired = 0; bool AudioMixer::_printStreamStats = false; +bool AudioMixer::_enableFilter = false; + AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), _trailingSleepRatio(1.0f), @@ -107,7 +109,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* float weakChannelAmplitudeRatio = 1.0f; bool shouldAttenuate = (streamToAdd != listeningNodeStream); - + if (shouldAttenuate) { // if the two stream pointers do not match then these are different streams @@ -267,6 +269,43 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* MIN_SAMPLE_VALUE, MAX_SAMPLE_VALUE); } } + + if ( _enableFilter && shouldAttenuate ) { + + glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream->getPosition(); + if ( relativePosition.z < 0 ) { // if the source is behind us + + AudioFilterPEQ1s& penumbraFilter = streamToAdd->getFilter(); + + // calculate penumbra angle + float headPenumbraAngle = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f), + glm::normalize(relativePosition)); + + // normalize penumbra angle + float normalizedHeadPenumbraAngle = headPenumbraAngle / PI_OVER_TWO; + + if ( normalizedHeadPenumbraAngle < EPSILON ) { + normalizedHeadPenumbraAngle = EPSILON; + } + + float penumbraFilterGain; + float penumbraFilterFrequency; + float penumbraFilterSlope; + + // calculate the updated gain + penumbraFilterGain = normalizedHeadPenumbraAngle; // Note this will be tuned - consider this only a crude-first pass at correlating gain with penumbra angle. + penumbraFilterFrequency = 2000.0f; + penumbraFilterSlope = 1.0f; // gentle slope + +// printf("gain=%f,angle=%f\n",penumbraFilterGain,headPenumbraAngle); + + // set the gain on both filter channels + penumbraFilter.setParameters(0,0,SAMPLE_RATE,penumbraFilterFrequency,penumbraFilterGain,penumbraFilterSlope); + penumbraFilter.setParameters(0,1,SAMPLE_RATE,penumbraFilterFrequency,penumbraFilterGain,penumbraFilterSlope); + + penumbraFilter.render( _clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); + } + } } void AudioMixer::prepareMixForListeningNode(Node* node) { @@ -462,6 +501,12 @@ void AudioMixer::run() { bool ok; + const QString FILTER_KEY = "E-enable-filter"; + _enableFilter = audioGroupObject[FILTER_KEY].toBool(); + if (_enableFilter) { + qDebug() << "Filter enabled"; + } + const QString DESIRED_JITTER_BUFFER_FRAMES_KEY = "B-desired-jitter-buffer-frames"; _staticDesiredJitterBufferFrames = audioGroupObject[DESIRED_JITTER_BUFFER_FRAMES_KEY].toString().toInt(&ok); if (!ok) { diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 2a4b93149c..47526adcfe 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -41,7 +41,7 @@ public slots: static bool getUseDynamicJitterBuffers() { return _useDynamicJitterBuffers; } static int getStaticDesiredJitterBufferFrames() { return _staticDesiredJitterBufferFrames; } static int getMaxFramesOverDesired() { return _maxFramesOverDesired; } - + private: /// adds one stream to the mix for a listening node void addStreamToMixForListeningNodeWithStream(PositionalAudioStream* streamToAdd, @@ -68,7 +68,8 @@ private: static int _maxFramesOverDesired; static bool _printStreamStats; - + static bool _enableFilter; + quint64 _lastSendAudioStreamStatsTime; }; diff --git a/domain-server/resources/web/settings/describe.json b/domain-server/resources/web/settings/describe.json index 788a3ad551..a9466eede9 100644 --- a/domain-server/resources/web/settings/describe.json +++ b/domain-server/resources/web/settings/describe.json @@ -32,7 +32,13 @@ "help": "Boxes for source and listener (corner x, corner y, corner z, size x, size y, size z, corner x, corner y, corner z, size x, size y, size z)", "placeholder": "no zone", "default": "" + }, + "E-enable-filter": { + "type": "checkbox", + "label": "Enable Positional Filter", + "help": "If enabled, positional audio stream uses lowpass filter", + "default": false } } } -} \ No newline at end of file +} diff --git a/examples/playSound.js b/examples/playSound.js index 189c24e86f..867768f807 100644 --- a/examples/playSound.js +++ b/examples/playSound.js @@ -12,14 +12,14 @@ var bird = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); function maybePlaySound(deltaTime) { - if (Math.random() < 0.01) { +// if (Math.random() < 0.01) { // Set the location and other info for the sound to play var options = new AudioInjectionOptions(); var position = MyAvatar.position; options.position = position; options.volume = 0.5; Audio.playSound(bird, options); - } +// } } // Connect a call back that happens every frame diff --git a/examples/playSoundOrbit.js b/examples/playSoundOrbit.js new file mode 100644 index 0000000000..92b8e0a96e --- /dev/null +++ b/examples/playSoundOrbit.js @@ -0,0 +1,42 @@ +// +// playSoundPath.js +// examples +// +// Created by Craig Hansen-Sturm on 05/27/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 soundClip = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Voxels/voxel create 3.raw"); + +var currentTime = 1.570079; // pi/2 +var deltaTime = 0.05; +var distance = 1; +var debug = 0; + +function playSound() { + var options = new AudioInjectionOptions(); + currentTime += deltaTime; + + var s = distance * Math.sin(currentTime); + var c = distance * Math.cos(currentTime); + + var soundOffset = { x:s, y:0, z:c }; + + if( debug ) { + print("t=" + currentTime + "offset=" + soundOffset.x + "," + soundOffset.y + "," + soundOffset.z); + } + + var avatarPosition = MyAvatar.position; + var soundPosition = Vec3.sum(avatarPosition,soundOffset); + + options.position = soundPosition + options.volume = 1.0; + Audio.playSound(soundClip, options); +} + +Script.setInterval(playSound, 250); + + diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 4fb54218af..c7e9e6b2f0 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -20,6 +20,7 @@ #include "RingBufferHistory.h" #include "MovingMinMaxAvg.h" #include "AudioFilter.h" +#include "AudioFilterBank.h" #include #include @@ -278,8 +279,8 @@ private: int _samplesPerScope; // Multi-band parametric EQ - bool _peqEnabled; - AudioFilterPEQ3 _peq; + bool _peqEnabled; + AudioFilterPEQ3m _peq; QMutex _guard; QByteArray* _scopeInput; diff --git a/libraries/audio/src/AudioFilter.cpp b/libraries/audio/src/AudioFilter.cpp deleted file mode 100644 index 28e7716578..0000000000 --- a/libraries/audio/src/AudioFilter.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// AudioFilter.cpp -// hifi -// -// Created by Craig Hansen-Sturm on 8/10/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 -// - -#include -#include -#include -#include "AudioRingBuffer.h" -#include "AudioFilter.h" - -template<> -AudioFilterPEQ3::FilterParameter AudioFilterPEQ3::_profiles[ AudioFilterPEQ3::_profileCount ][ AudioFilterPEQ3::_filterCount ] = { - - // Freq Gain Q Freq Gain Q Freq Gain Q - { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // flat response (default) - { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 0.1f, 1.0f } }, // treble cut - { { 300.0f, 0.1f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // bass cut - { { 300.0f, 1.5f, 0.71f }, { 1000.0f, 0.5f, 1.0f }, { 4000.0f, 1.50f, 0.71f } } // smiley curve -}; diff --git a/libraries/audio/src/AudioFilter.h b/libraries/audio/src/AudioFilter.h index 0f3ec06f64..3907e3e378 100644 --- a/libraries/audio/src/AudioFilter.h +++ b/libraries/audio/src/AudioFilter.h @@ -90,209 +90,134 @@ public: } }; -//////////////////////////////////////////////////////////////////////////////////////////// -// Implements a single-band parametric EQ using a biquad "peaking EQ" configuration -// -// gain > 1.0 boosts the center frequency -// gain < 1.0 cuts the center frequency -// -class AudioParametricEQ { +//////////////////////////////////////////////////////////////////////////////////////////// +// Implements common base class interface for all Audio Filter Objects +// +template< class T > +class AudioFilterBase { + +protected: + // - // private data + // data // AudioBiquad _kernel; float _sampleRate; float _frequency; float _gain; float _slope; - + + // // helpers + // void updateKernel() { - - /* - a0 = 1 + alpha*A - a1 = -2*cos(w0) - a2 = 1 - alpha*A - b1 = -2*cos(w0) - b2 = 1 - alpha/A - */ - - const float a = _gain; - const float omega = TWO_PI * _frequency / _sampleRate; - const float alpha = 0.5f * sinf(omega) / _slope; - const float gamma = 1.0f / ( 1.0f + (alpha/a) ); - - const float a0 = 1.0f + (alpha*a); - const float a1 = -2.0f * cosf(omega); - const float a2 = 1.0f - (alpha*a); - const float b1 = a1; - const float b2 = 1.0f - (alpha/a); - - _kernel.setParameters( a0*gamma,a1*gamma,a2*gamma,b1*gamma,b2*gamma ); + static_cast(this)->updateKernel(); } - + public: // // ctor/dtor // - AudioParametricEQ() { - + AudioFilterBase() { setParameters(0.,0.,0.,0.); - updateKernel(); } - - ~AudioParametricEQ() { + + ~AudioFilterBase() { } - + // // public interface // void setParameters( const float sampleRate, const float frequency, const float gain, const float slope ) { - + _sampleRate = std::max(sampleRate,1.0f); _frequency = std::max(frequency,2.0f); _gain = std::max(gain,0.0f); _slope = std::max(slope,0.00001f); - + updateKernel(); } - + void getParameters( float& sampleRate, float& frequency, float& gain, float& slope ) { sampleRate = _sampleRate; frequency = _frequency; gain = _gain; slope = _slope; } - + void render(const float* in, float* out, const int frames ) { _kernel.render(in,out,frames); } - + void reset() { _kernel.reset(); } }; //////////////////////////////////////////////////////////////////////////////////////////// -// Helper/convenience class that implements a bank of EQ objects +// Implements a low-shelf filter using a biquad // -template< typename T, const int N> -class AudioFilterBank { - - // - // types - // - struct FilterParameter { - float _p1; - float _p2; - float _p3; - }; - - // - // private static data - // - static const int _filterCount = N; - static const int _profileCount = 4; - - static FilterParameter _profiles[_profileCount][_filterCount]; - - // - // private data - // - T _filters[ _filterCount ]; - float* _buffer; - float _sampleRate; - uint16_t _frameCount; - +class AudioFilterLSF : +public AudioFilterBase< AudioFilterLSF > +{ public: - + // - // ctor/dtor + // helpers // - AudioFilterBank() - : _buffer(NULL) - , _sampleRate(0.) - , _frameCount(0) { + void updateKernel() { + // TBD } - - ~AudioFilterBank() { - finalize(); - } - - // - // public interface - // - void initialize( const float sampleRate, const int frameCount ) { - finalize(); - - _buffer = (float*)malloc( frameCount * sizeof(float) ); - if(!_buffer) { - return; - } - - _sampleRate = sampleRate; - _frameCount = frameCount; - - reset(); - loadProfile(0); // load default profile "flat response" into the bank (see AudioFilter.cpp) - } - - void finalize() { - if (_buffer ) { - free (_buffer); - _buffer = NULL; - } - } - - void loadProfile( int profileIndex ) { - if (profileIndex >= 0 && profileIndex < _profileCount) { - - for (int i = 0; i < _filterCount; ++i) { - FilterParameter p = _profiles[profileIndex][i]; - - _filters[i].setParameters(_sampleRate,p._p1,p._p2,p._p3); - } - } - } - - void render( const float* in, float* out, const int frameCount ) { - for (int i = 0; i < _filterCount; ++i) { - _filters[i].render( in, out, frameCount ); - } - } - - void render( const int16_t* in, int16_t* out, const int frameCount ) { - if (!_buffer || ( frameCount > _frameCount )) - return; - - const int scale = (2 << ((8*sizeof(int16_t))-1)); - - // convert int16_t to float32 (normalized to -1. ... 1.) - for (int i = 0; i < frameCount; ++i) { - _buffer[i] = ((float)(*in++)) / scale; - } - // for this filter, we share input/output buffers at each stage, but our design does not mandate this - render( _buffer, _buffer, frameCount ); - - // convert float32 to int16_t - for (int i = 0; i < frameCount; ++i) { - *out++ = (int16_t)(_buffer[i] * scale); - } - } - - void reset() { - for (int i = 0; i < _filterCount; ++i ) { - _filters[i].reset(); - } - } - }; //////////////////////////////////////////////////////////////////////////////////////////// -// Specializations of AudioFilterBank +// Implements a hi-shelf filter using a biquad // -typedef AudioFilterBank< AudioParametricEQ, 1> AudioFilterPEQ1; // bank with one band of PEQ -typedef AudioFilterBank< AudioParametricEQ, 2> AudioFilterPEQ2; // bank with two bands of PEQ -typedef AudioFilterBank< AudioParametricEQ, 3> AudioFilterPEQ3; // bank with three bands of PEQ -// etc.... +class AudioFilterHSF : +public AudioFilterBase< AudioFilterHSF > +{ +public: + + // + // helpers + // + void updateKernel() { + // TBD + } +}; +//////////////////////////////////////////////////////////////////////////////////////////// +// Implements a single-band parametric EQ using a biquad "peaking EQ" configuration +// +class AudioFilterPEQ : + public AudioFilterBase< AudioFilterPEQ > +{ +public: + + // + // helpers + // + void updateKernel() { + + const float a = _gain; + const float omega = TWO_PI * _frequency / _sampleRate; + const float alpha = 0.5f * sinf(omega) / _slope; + + /* + a0 = 1 + alpha*A + a1 = -2*cos(w0) + a2 = 1 - alpha*A + b1 = -2*cos(w0) + b2 = 1 - alpha/A + */ + const float a0 = 1.0f + (alpha*a); + const float a1 = -2.0f * cosf(omega); + const float a2 = 1.0f - (alpha*a); + const float b1 = a1; + const float b2 = 1.0f - (alpha/a); + + const float scale = 1.0f / ( 1.0f + (alpha/a) ); + + _kernel.setParameters( a0*scale,a1*scale,a2*scale,b1*scale,b2*scale ); + } +}; #endif // hifi_AudioFilter_h diff --git a/libraries/audio/src/AudioFilterBank.cpp b/libraries/audio/src/AudioFilterBank.cpp new file mode 100644 index 0000000000..e46195b5b9 --- /dev/null +++ b/libraries/audio/src/AudioFilterBank.cpp @@ -0,0 +1,45 @@ +// +// AudioFilterBank.cpp +// hifi +// +// Created by Craig Hansen-Sturm on 8/10/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 +// + +#include +#include +#include +#include "AudioRingBuffer.h" +#include "AudioFilter.h" +#include "AudioFilterBank.h" + +template<> +AudioFilterLSF1s::FilterParameter AudioFilterLSF1s::_profiles[ AudioFilterLSF1s::_profileCount ][ AudioFilterLSF1s::_filterCount ] = { + // Freq Gain Slope + { { 1000.0f, 1.0f, 1.0f } } // flat response (default) +}; + +template<> +AudioFilterHSF1s::FilterParameter AudioFilterHSF1s::_profiles[ AudioFilterHSF1s::_profileCount ][ AudioFilterHSF1s::_filterCount ] = { + // Freq Gain Slope + { { 1000.0f, 1.0f, 1.0f } } // flat response (default) +}; + +template<> +AudioFilterPEQ1s::FilterParameter AudioFilterPEQ1s::_profiles[ AudioFilterPEQ1s::_profileCount ][ AudioFilterPEQ1s::_filterCount ] = { + // Freq Gain Q + { { 1000.0f, 1.0f, 1.0f } } // flat response (default) +}; + +template<> +AudioFilterPEQ3m::FilterParameter AudioFilterPEQ3m::_profiles[ AudioFilterPEQ3m::_profileCount ][ AudioFilterPEQ3m::_filterCount ] = { + + // Freq Gain Q Freq Gain Q Freq Gain Q + { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // flat response (default) + { { 300.0f, 1.0f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 0.1f, 1.0f } }, // treble cut + { { 300.0f, 0.1f, 1.0f }, { 1000.0f, 1.0f, 1.0f }, { 4000.0f, 1.0f, 1.0f } }, // bass cut + { { 300.0f, 1.5f, 0.71f }, { 1000.0f, 0.5f, 1.0f }, { 4000.0f, 1.50f, 0.71f } } // smiley curve +}; diff --git a/libraries/audio/src/AudioFilterBank.h b/libraries/audio/src/AudioFilterBank.h new file mode 100644 index 0000000000..c995a87b63 --- /dev/null +++ b/libraries/audio/src/AudioFilterBank.h @@ -0,0 +1,169 @@ +// +// AudioFilterBank.h +// hifi +// +// Created by Craig Hansen-Sturm on 8/23/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 +// + +#ifndef hifi_AudioFilterBank_h +#define hifi_AudioFilterBank_h + +//////////////////////////////////////////////////////////////////////////////////////////// +// Helper/convenience class that implements a bank of Filter objects +// +template< typename T, const int N, const int C > +class AudioFilterBank { + + // + // types + // + struct FilterParameter { + float _p1; + float _p2; + float _p3; + }; + + // + // private static data + // + static const int _filterCount = N; + static const int _channelCount = C; + static const int _profileCount = 4; + + static FilterParameter _profiles[_profileCount][_filterCount]; + + // + // private data + // + T _filters[ _filterCount ][ _channelCount ]; + float* _buffer[ _channelCount ]; + float _sampleRate; + uint16_t _frameCount; + +public: + + // + // ctor/dtor + // + AudioFilterBank() + : _sampleRate(0.) + , _frameCount(0) { + for (int i = 0; i < _channelCount; ++i ) { + _buffer[ i ] = NULL; + } + } + + ~AudioFilterBank() { + finalize(); + } + + // + // public interface + // + void initialize( const float sampleRate, const int frameCount ) { + finalize(); + + for (int i = 0; i < _channelCount; ++i ) { + _buffer[i] = (float*)malloc( frameCount * sizeof( float ) ); + } + + _sampleRate = sampleRate; + _frameCount = frameCount; + + reset(); + loadProfile(0); // load default profile "flat response" into the bank (see AudioFilterBank.cpp) + } + + void finalize() { + for (int i = 0; i < _channelCount; ++i ) { + if (_buffer[i]) { + free (_buffer[i]); + _buffer[i] = NULL; + } + } + } + + void loadProfile( int profileIndex ) { + if (profileIndex >= 0 && profileIndex < _profileCount) { + + for (int i = 0; i < _filterCount; ++i) { + FilterParameter p = _profiles[profileIndex][i]; + + for (int j = 0; j < _channelCount; ++j) { + _filters[i][j].setParameters(_sampleRate,p._p1,p._p2,p._p3); + } + } + } + } + + void setParameters( int filterStage, int filterChannel, const float sampleRate, const float frequency, const float gain, const float slope ) { + if ( filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount ) { + _filters[filterStage][filterChannel].setParameters(sampleRate,frequency,gain,slope); + } + } + + void getParameters( int filterStage, int filterChannel, float& sampleRate, float& frequency, float& gain, float& slope ) { + if ( filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount ) { + _filters[filterStage][filterChannel].getParameters(sampleRate,frequency,gain,slope); + } + } + + void render( const int16_t* in, int16_t* out, const int frameCount ) { + if (!_buffer || ( frameCount > _frameCount )) + return; + + const int scale = (2 << ((8*sizeof(int16_t))-1)); + + // de-interleave and convert int16_t to float32 (normalized to -1. ... 1.) + for (int i = 0; i < frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + _buffer[j][i] = ((float)(*in++)) / scale; + } + } + + // now step through each filter + for (int i = 0; i < _channelCount; ++i) { + for (int j = 0; j < _filterCount; ++j ) { + _filters[j][i].render( &_buffer[i][0], &_buffer[i][0], frameCount ); + } + } + + // convert float32 to int16_t and interleave + for (int i = 0; i < frameCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { + *out++ = (int16_t)(_buffer[j][i] * scale); + } + } + } + + void reset() { + for (int i = 0; i < _filterCount; ++i ) { + for (int j = 0; j < _channelCount; ++j ) { + _filters[i][j].reset(); + } + } + } + +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Specializations of AudioFilterBank +// +typedef AudioFilterBank< AudioFilterLSF, 1, 1> AudioFilterLSF1m; // mono bank with one band of LSF +typedef AudioFilterBank< AudioFilterLSF, 1, 2> AudioFilterLSF1s; // stereo bank with one band of LSF +typedef AudioFilterBank< AudioFilterHSF, 1, 1> AudioFilterHSF1m; // mono bank with one band of HSF +typedef AudioFilterBank< AudioFilterHSF, 1, 2> AudioFilterHSF1s; // stereo bank with one band of HSF +typedef AudioFilterBank< AudioFilterPEQ, 1, 1> AudioFilterPEQ1m; // mono bank with one band of PEQ +typedef AudioFilterBank< AudioFilterPEQ, 2, 1> AudioFilterPEQ2m; // mono bank with two bands of PEQ +typedef AudioFilterBank< AudioFilterPEQ, 3, 1> AudioFilterPEQ3m; // mono bank with three bands of PEQ +typedef AudioFilterBank< AudioFilterPEQ, 1, 2> AudioFilterPEQ1s; // stereo bank with one band of PEQ +typedef AudioFilterBank< AudioFilterPEQ, 2, 2> AudioFilterPEQ2s; // stereo bank with two bands of PEQ +typedef AudioFilterBank< AudioFilterPEQ, 3, 2> AudioFilterPEQ3s; // stereo bank with three bands of PEQ +// etc.... + + +#endif // hifi_AudioFilter_h diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index 7b407ba62c..a57a78c9f2 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -33,6 +33,9 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b _lastPopOutputTrailingLoudness(0.0f), _listenerUnattenuatedZone(NULL) { + // constant defined in AudioMixer.h. However, we don't want to include this here, since we will soon find a better common home for these audio-related constants + const int SAMPLE_PHASE_DELAY_AT_90 = 20; + _filter.initialize( SAMPLE_RATE, ( NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2) ) / 2); } void PositionalAudioStream::updateLastPopOutputTrailingLoudness() { diff --git a/libraries/audio/src/PositionalAudioStream.h b/libraries/audio/src/PositionalAudioStream.h index f99dc3a464..e31cd35ad4 100644 --- a/libraries/audio/src/PositionalAudioStream.h +++ b/libraries/audio/src/PositionalAudioStream.h @@ -16,6 +16,8 @@ #include #include "InboundAudioStream.h" +#include "AudioFilter.h" +#include "AudioFilterBank.h" const int AUDIOMIXER_INBOUND_RING_BUFFER_FRAME_CAPACITY = 100; @@ -44,6 +46,8 @@ public: void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; } + AudioFilterPEQ1s& getFilter() { return _filter; } + protected: // disallow copying of PositionalAudioStream objects PositionalAudioStream(const PositionalAudioStream&); @@ -61,6 +65,8 @@ protected: float _lastPopOutputTrailingLoudness; AABox* _listenerUnattenuatedZone; + + AudioFilterPEQ1s _filter; }; #endif // hifi_PositionalAudioStream_h From fd5e3d61dc5d9a9a78ece4fc9b976fcee3dc3b42 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 25 Aug 2014 10:55:11 -0700 Subject: [PATCH 10/16] minor cleanup of playSoundOrbit.js + playSound.js --- examples/playSound.js | 4 ++-- examples/playSoundOrbit.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/playSound.js b/examples/playSound.js index 867768f807..189c24e86f 100644 --- a/examples/playSound.js +++ b/examples/playSound.js @@ -12,14 +12,14 @@ var bird = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/bushtit_1.raw"); function maybePlaySound(deltaTime) { -// if (Math.random() < 0.01) { + if (Math.random() < 0.01) { // Set the location and other info for the sound to play var options = new AudioInjectionOptions(); var position = MyAvatar.position; options.position = position; options.volume = 0.5; Audio.playSound(bird, options); -// } + } } // Connect a call back that happens every frame diff --git a/examples/playSoundOrbit.js b/examples/playSoundOrbit.js index 92b8e0a96e..4349eac888 100644 --- a/examples/playSoundOrbit.js +++ b/examples/playSoundOrbit.js @@ -19,19 +19,19 @@ var debug = 0; function playSound() { var options = new AudioInjectionOptions(); currentTime += deltaTime; - + var s = distance * Math.sin(currentTime); var c = distance * Math.cos(currentTime); var soundOffset = { x:s, y:0, z:c }; - + if( debug ) { print("t=" + currentTime + "offset=" + soundOffset.x + "," + soundOffset.y + "," + soundOffset.z); } - + var avatarPosition = MyAvatar.position; var soundPosition = Vec3.sum(avatarPosition,soundOffset); - + options.position = soundPosition options.volume = 1.0; Audio.playSound(soundClip, options); From bf6d34b6f33ad9b8885a296df0cab39f0b9fef1a Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 25 Aug 2014 11:43:49 -0700 Subject: [PATCH 11/16] windows/linux build break + addresses zappoman review comments --- assignment-client/src/audio/AudioMixer.cpp | 14 +++++++------- libraries/audio/src/AudioFilter.h | 10 +++++----- libraries/audio/src/AudioFilterBank.cpp | 1 - libraries/audio/src/AudioFilterBank.h | 4 ++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index e1d118a11d..1fe3ddbbe9 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -270,10 +270,10 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* } } - if ( _enableFilter && shouldAttenuate ) { + if (_enableFilter && shouldAttenuate) { glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream->getPosition(); - if ( relativePosition.z < 0 ) { // if the source is behind us + if (relativePosition.z < 0) { // if the source is behind us AudioFilterPEQ1s& penumbraFilter = streamToAdd->getFilter(); @@ -297,13 +297,13 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* penumbraFilterFrequency = 2000.0f; penumbraFilterSlope = 1.0f; // gentle slope -// printf("gain=%f,angle=%f\n",penumbraFilterGain,headPenumbraAngle); - + qDebug() << "penumbra gain=" << penumbraFilterGain << ", penumbraAngle=" << normalizedHeadPenumbraAngle; + // set the gain on both filter channels - penumbraFilter.setParameters(0,0,SAMPLE_RATE,penumbraFilterFrequency,penumbraFilterGain,penumbraFilterSlope); - penumbraFilter.setParameters(0,1,SAMPLE_RATE,penumbraFilterFrequency,penumbraFilterGain,penumbraFilterSlope); + penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGain, penumbraFilterSlope); + penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGain, penumbraFilterSlope); - penumbraFilter.render( _clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); + penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2); } } } diff --git a/libraries/audio/src/AudioFilter.h b/libraries/audio/src/AudioFilter.h index 3907e3e378..2be88322af 100644 --- a/libraries/audio/src/AudioFilter.h +++ b/libraries/audio/src/AudioFilter.h @@ -12,7 +12,7 @@ #ifndef hifi_AudioFilter_h #define hifi_AudioFilter_h -//////////////////////////////////////////////////////////////////////////////////////////// +// // Implements a standard biquad filter in "Direct Form 1" // Reference http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt // @@ -91,7 +91,7 @@ public: }; -//////////////////////////////////////////////////////////////////////////////////////////// +// // Implements common base class interface for all Audio Filter Objects // template< class T > @@ -152,7 +152,7 @@ public: } }; -//////////////////////////////////////////////////////////////////////////////////////////// +// // Implements a low-shelf filter using a biquad // class AudioFilterLSF : @@ -168,7 +168,7 @@ public: } }; -//////////////////////////////////////////////////////////////////////////////////////////// +// // Implements a hi-shelf filter using a biquad // class AudioFilterHSF : @@ -184,7 +184,7 @@ public: } }; -//////////////////////////////////////////////////////////////////////////////////////////// +// // Implements a single-band parametric EQ using a biquad "peaking EQ" configuration // class AudioFilterPEQ : diff --git a/libraries/audio/src/AudioFilterBank.cpp b/libraries/audio/src/AudioFilterBank.cpp index e46195b5b9..a2cf008fc9 100644 --- a/libraries/audio/src/AudioFilterBank.cpp +++ b/libraries/audio/src/AudioFilterBank.cpp @@ -10,7 +10,6 @@ // #include -#include #include #include "AudioRingBuffer.h" #include "AudioFilter.h" diff --git a/libraries/audio/src/AudioFilterBank.h b/libraries/audio/src/AudioFilterBank.h index c995a87b63..6721d077f7 100644 --- a/libraries/audio/src/AudioFilterBank.h +++ b/libraries/audio/src/AudioFilterBank.h @@ -12,7 +12,7 @@ #ifndef hifi_AudioFilterBank_h #define hifi_AudioFilterBank_h -//////////////////////////////////////////////////////////////////////////////////////////// +// // Helper/convenience class that implements a bank of Filter objects // template< typename T, const int N, const int C > @@ -150,7 +150,7 @@ public: }; -//////////////////////////////////////////////////////////////////////////////////////////// +// // Specializations of AudioFilterBank // typedef AudioFilterBank< AudioFilterLSF, 1, 1> AudioFilterLSF1m; // mono bank with one band of LSF From 4f346bfe62ae7f737813d225e1575244d2d39ac9 Mon Sep 17 00:00:00 2001 From: Craig Hansen-Sturm Date: Mon, 25 Aug 2014 13:07:52 -0700 Subject: [PATCH 12/16] addresses more coding-standard spacing issues --- assignment-client/src/audio/AudioMixer.cpp | 2 +- examples/playSoundOrbit.js | 2 +- libraries/audio/src/AudioFilter.h | 30 ++++++++-------- libraries/audio/src/AudioFilterBank.h | 35 ++++++++++--------- libraries/audio/src/PositionalAudioStream.cpp | 2 +- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 1fe3ddbbe9..85ce208548 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -284,7 +284,7 @@ void AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream* // normalize penumbra angle float normalizedHeadPenumbraAngle = headPenumbraAngle / PI_OVER_TWO; - if ( normalizedHeadPenumbraAngle < EPSILON ) { + if (normalizedHeadPenumbraAngle < EPSILON) { normalizedHeadPenumbraAngle = EPSILON; } diff --git a/examples/playSoundOrbit.js b/examples/playSoundOrbit.js index 4349eac888..da7c746e8e 100644 --- a/examples/playSoundOrbit.js +++ b/examples/playSoundOrbit.js @@ -25,7 +25,7 @@ function playSound() { var soundOffset = { x:s, y:0, z:c }; - if( debug ) { + if (debug) { print("t=" + currentTime + "offset=" + soundOffset.x + "," + soundOffset.y + "," + soundOffset.z); } diff --git a/libraries/audio/src/AudioFilter.h b/libraries/audio/src/AudioFilter.h index 2be88322af..408a713db7 100644 --- a/libraries/audio/src/AudioFilter.h +++ b/libraries/audio/src/AudioFilter.h @@ -51,15 +51,15 @@ public: // // public interface // - void setParameters( const float a0, const float a1, const float a2, const float b1, const float b2 ) { + void setParameters(const float a0, const float a1, const float a2, const float b1, const float b2) { _a0 = a0; _a1 = a1; _a2 = a2; _b1 = b1; _b2 = b2; } - void getParameters( float& a0, float& a1, float& a2, float& b1, float& b2 ) { + void getParameters(float& a0, float& a1, float& a2, float& b1, float& b2) { a0 = _a0; a1 = _a1; a2 = _a2; b1 = _b1; b2 = _b2; } - void render( const float* in, float* out, const int frames) { + void render(const float* in, float* out, const int frames) { float x; float y; @@ -129,21 +129,21 @@ public: // // public interface // - void setParameters( const float sampleRate, const float frequency, const float gain, const float slope ) { + void setParameters(const float sampleRate, const float frequency, const float gain, const float slope) { - _sampleRate = std::max(sampleRate,1.0f); - _frequency = std::max(frequency,2.0f); - _gain = std::max(gain,0.0f); - _slope = std::max(slope,0.00001f); + _sampleRate = std::max(sampleRate, 1.0f); + _frequency = std::max(frequency, 2.0f); + _gain = std::max(gain, 0.0f); + _slope = std::max(slope, 0.00001f); updateKernel(); } - void getParameters( float& sampleRate, float& frequency, float& gain, float& slope ) { + void getParameters(float& sampleRate, float& frequency, float& gain, float& slope) { sampleRate = _sampleRate; frequency = _frequency; gain = _gain; slope = _slope; } - void render(const float* in, float* out, const int frames ) { + void render(const float* in, float* out, const int frames) { _kernel.render(in,out,frames); } @@ -208,15 +208,15 @@ public: b1 = -2*cos(w0) b2 = 1 - alpha/A */ - const float a0 = 1.0f + (alpha*a); + const float a0 = 1.0f + (alpha * a); const float a1 = -2.0f * cosf(omega); - const float a2 = 1.0f - (alpha*a); + const float a2 = 1.0f - (alpha * a); const float b1 = a1; - const float b2 = 1.0f - (alpha/a); + const float b2 = 1.0f - (alpha / a); - const float scale = 1.0f / ( 1.0f + (alpha/a) ); + const float scale = 1.0f / (1.0f + (alpha / a)); - _kernel.setParameters( a0*scale,a1*scale,a2*scale,b1*scale,b2*scale ); + _kernel.setParameters(a0 * scale, a1 * scale, a2 * scale, b1 * scale, b2 * scale); } }; diff --git a/libraries/audio/src/AudioFilterBank.h b/libraries/audio/src/AudioFilterBank.h index 6721d077f7..c523736a57 100644 --- a/libraries/audio/src/AudioFilterBank.h +++ b/libraries/audio/src/AudioFilterBank.h @@ -34,7 +34,7 @@ class AudioFilterBank { static const int _channelCount = C; static const int _profileCount = 4; - static FilterParameter _profiles[_profileCount][_filterCount]; + static FilterParameter _profiles[ _profileCount ][ _filterCount ]; // // private data @@ -52,7 +52,7 @@ public: AudioFilterBank() : _sampleRate(0.) , _frameCount(0) { - for (int i = 0; i < _channelCount; ++i ) { + for (int i = 0; i < _channelCount; ++i) { _buffer[ i ] = NULL; } } @@ -64,11 +64,11 @@ public: // // public interface // - void initialize( const float sampleRate, const int frameCount ) { + void initialize(const float sampleRate, const int frameCount) { finalize(); - for (int i = 0; i < _channelCount; ++i ) { - _buffer[i] = (float*)malloc( frameCount * sizeof( float ) ); + for (int i = 0; i < _channelCount; ++i) { + _buffer[i] = (float*)malloc(frameCount * sizeof(float)); } _sampleRate = sampleRate; @@ -79,7 +79,7 @@ public: } void finalize() { - for (int i = 0; i < _channelCount; ++i ) { + for (int i = 0; i < _channelCount; ++i) { if (_buffer[i]) { free (_buffer[i]); _buffer[i] = NULL; @@ -87,7 +87,7 @@ public: } } - void loadProfile( int profileIndex ) { + void loadProfile(int profileIndex) { if (profileIndex >= 0 && profileIndex < _profileCount) { for (int i = 0; i < _filterCount; ++i) { @@ -100,23 +100,24 @@ public: } } - void setParameters( int filterStage, int filterChannel, const float sampleRate, const float frequency, const float gain, const float slope ) { - if ( filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount ) { + void setParameters(int filterStage, int filterChannel, const float sampleRate, const float frequency, const float gain, + const float slope) { + if (filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { _filters[filterStage][filterChannel].setParameters(sampleRate,frequency,gain,slope); } } - void getParameters( int filterStage, int filterChannel, float& sampleRate, float& frequency, float& gain, float& slope ) { - if ( filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount ) { + void getParameters(int filterStage, int filterChannel, float& sampleRate, float& frequency, float& gain, float& slope) { + if (filterStage >= 0 && filterStage < _filterCount && filterChannel >= 0 && filterChannel < _channelCount) { _filters[filterStage][filterChannel].getParameters(sampleRate,frequency,gain,slope); } } - void render( const int16_t* in, int16_t* out, const int frameCount ) { - if (!_buffer || ( frameCount > _frameCount )) + void render(const int16_t* in, int16_t* out, const int frameCount) { + if (!_buffer || (frameCount > _frameCount)) return; - const int scale = (2 << ((8*sizeof(int16_t))-1)); + const int scale = (2 << ((8 * sizeof(int16_t)) - 1)); // de-interleave and convert int16_t to float32 (normalized to -1. ... 1.) for (int i = 0; i < frameCount; ++i) { @@ -127,7 +128,7 @@ public: // now step through each filter for (int i = 0; i < _channelCount; ++i) { - for (int j = 0; j < _filterCount; ++j ) { + for (int j = 0; j < _filterCount; ++j) { _filters[j][i].render( &_buffer[i][0], &_buffer[i][0], frameCount ); } } @@ -141,8 +142,8 @@ public: } void reset() { - for (int i = 0; i < _filterCount; ++i ) { - for (int j = 0; j < _channelCount; ++j ) { + for (int i = 0; i < _filterCount; ++i) { + for (int j = 0; j < _channelCount; ++j) { _filters[i][j].reset(); } } diff --git a/libraries/audio/src/PositionalAudioStream.cpp b/libraries/audio/src/PositionalAudioStream.cpp index a57a78c9f2..46e83806bc 100644 --- a/libraries/audio/src/PositionalAudioStream.cpp +++ b/libraries/audio/src/PositionalAudioStream.cpp @@ -35,7 +35,7 @@ PositionalAudioStream::PositionalAudioStream(PositionalAudioStream::Type type, b { // constant defined in AudioMixer.h. However, we don't want to include this here, since we will soon find a better common home for these audio-related constants const int SAMPLE_PHASE_DELAY_AT_90 = 20; - _filter.initialize( SAMPLE_RATE, ( NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2) ) / 2); + _filter.initialize(SAMPLE_RATE, (NETWORK_BUFFER_LENGTH_SAMPLES_STEREO + (SAMPLE_PHASE_DELAY_AT_90 * 2)) / 2); } void PositionalAudioStream::updateLastPopOutputTrailingLoudness() { From 3ad9797607dc5e636ef0043a86ed7f6bdd9c2b52 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 25 Aug 2014 15:15:27 -0700 Subject: [PATCH 13/16] fix crash: divide by zero when computing avg color --- interface/src/renderer/TextureCache.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index e13f97a1d2..a2aba32594 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -404,6 +404,7 @@ void ImageReader::run() { const int EIGHT_BIT_MAXIMUM = 255; if (!image.hasAlphaChannel()) { + QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } @@ -416,8 +417,11 @@ void ImageReader::run() { blueTotal += qBlue(rgb); } } + if (imageArea > 0) { + averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); + } QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), Q_ARG(bool, false), - Q_ARG(const QColor&, QColor(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea))); + Q_ARG(const QColor&, averageColor)); return; } if (image.format() != QImage::Format_ARGB32) { From 8057071c126f8c5d9df35012089444b991cbabd1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 25 Aug 2014 15:18:57 -0700 Subject: [PATCH 14/16] put averageColor init closer to where it is used --- interface/src/renderer/TextureCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index a2aba32594..d960525817 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -404,7 +404,6 @@ void ImageReader::run() { const int EIGHT_BIT_MAXIMUM = 255; if (!image.hasAlphaChannel()) { - QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } @@ -417,6 +416,7 @@ void ImageReader::run() { blueTotal += qBlue(rgb); } } + QColor averageColor(EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM, EIGHT_BIT_MAXIMUM); if (imageArea > 0) { averageColor.setRgb(redTotal / imageArea, greenTotal / imageArea, blueTotal / imageArea); } From 7a5c4fdb16e43a6c7523b0b5540231bfe2b686d7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 25 Aug 2014 15:25:46 -0700 Subject: [PATCH 15/16] remove commented out #includes --- libraries/shared/src/ShapeCollider.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 3aa795e6fa..279cbe3810 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -16,10 +16,6 @@ #include "CollisionInfo.h" #include "SharedUtil.h" -//#include "CapsuleShape.h" -//#include "ListShape.h" -//#include "PlaneShape.h" -//#include "SphereShape.h" class Shape; class SphereShape; From 1acce5c725e1059e3ce75d6f2ab917929ad77592 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 25 Aug 2014 18:02:30 -0700 Subject: [PATCH 16/16] fix sitting points on domain switch --- examples/sit.js | 3 +-- interface/src/scripting/LocationScriptingInterface.cpp | 4 ++++ interface/src/scripting/LocationScriptingInterface.h | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/sit.js b/examples/sit.js index 072471aa30..b5664b0d10 100644 --- a/examples/sit.js +++ b/examples/sit.js @@ -269,8 +269,7 @@ function update(deltaTime){ } var locationChanged = false; - if (location.hostname != oldHost) { - print("Changed domain"); + if (location.hostname != oldHost || !location.isConnected) { for (model in models) { removeIndicators(models[model]); } diff --git a/interface/src/scripting/LocationScriptingInterface.cpp b/interface/src/scripting/LocationScriptingInterface.cpp index 44ff94aa1f..9e68778942 100644 --- a/interface/src/scripting/LocationScriptingInterface.cpp +++ b/interface/src/scripting/LocationScriptingInterface.cpp @@ -20,6 +20,10 @@ LocationScriptingInterface* LocationScriptingInterface::getInstance() { return &sharedInstance; } +bool LocationScriptingInterface::isConnected() { + return NodeList::getInstance()->getDomainHandler().isConnected(); +} + QString LocationScriptingInterface::getHref() { return getProtocol() + "//" + getHostname() + getPathname(); } diff --git a/interface/src/scripting/LocationScriptingInterface.h b/interface/src/scripting/LocationScriptingInterface.h index 36b6d97561..20f63bceed 100644 --- a/interface/src/scripting/LocationScriptingInterface.h +++ b/interface/src/scripting/LocationScriptingInterface.h @@ -22,6 +22,7 @@ class LocationScriptingInterface : public QObject { Q_OBJECT + Q_PROPERTY(bool isConnected READ isConnected) Q_PROPERTY(QString href READ getHref) Q_PROPERTY(QString protocol READ getProtocol) Q_PROPERTY(QString hostname READ getHostname) @@ -30,6 +31,7 @@ class LocationScriptingInterface : public QObject { public: static LocationScriptingInterface* getInstance(); + bool isConnected(); QString getHref(); QString getProtocol() { return CUSTOM_URL_SCHEME; }; QString getPathname();