From ca77eb9d1e927445b62c4cb96531ae702791bea9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 10:51:22 -0700 Subject: [PATCH 001/105] cleanup of avatar collisions, prep for more ragdoll collisions removal of paddle hands particles use general sphere collision path against avatars temp removal of skeleton self collision --- interface/src/avatar/Avatar.cpp | 67 +------------------ interface/src/avatar/Avatar.h | 13 +--- interface/src/avatar/Hand.cpp | 2 + libraries/avatars/src/AvatarData.h | 2 +- .../particles/src/ParticleCollisionSystem.cpp | 24 +------ libraries/shared/src/CollisionInfo.cpp | 3 +- libraries/shared/src/CollisionInfo.h | 1 - 7 files changed, 10 insertions(+), 102 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index baf46605fd..1bbe730b40 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -579,8 +579,8 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc return false; } -bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - CollisionList& collisions, int skeletonSkipIndex) { +bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) { + int skeletonSkipIndex = -1; return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, skeletonSkipIndex); // Temporarily disabling collisions against the head because most of its collision proxies are bad. //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); @@ -613,69 +613,6 @@ bool Avatar::findCollisions(const QVector& shapes, CollisionList& return collided; } -bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { - if (_collisionGroups & COLLISION_GROUP_PARTICLES) { - return false; - } - bool collided = false; - // first do the hand collisions - const HandData* handData = getHandData(); - if (handData) { - for (int i = 0; i < NUM_HANDS; i++) { - const PalmData* palm = handData->getPalm(i); - if (palm && palm->hasPaddle()) { - // create a disk collision proxy where the hand is - int jointIndex = -1; - glm::vec3 handPosition; - if (i == 0) { - _skeletonModel.getLeftHandPosition(handPosition); - jointIndex = _skeletonModel.getLeftHandJointIndex(); - } - else { - _skeletonModel.getRightHandPosition(handPosition); - jointIndex = _skeletonModel.getRightHandJointIndex(); - } - - glm::vec3 fingerAxis = palm->getFingerDirection(); - glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis; - glm::vec3 diskNormal = palm->getNormal(); - const float DISK_THICKNESS = 0.08f; - - // collide against the disk - glm::vec3 penetration; - if (findSphereDiskPenetration(particleCenter, particleRadius, - diskCenter, HAND_PADDLE_RADIUS, DISK_THICKNESS, diskNormal, - penetration)) { - CollisionInfo* collision = collisions.getNewCollision(); - if (collision) { - collision->_type = COLLISION_TYPE_PADDLE_HAND; - collision->_intData = jointIndex; - collision->_penetration = penetration; - collision->_addedVelocity = palm->getVelocity(); - collided = true; - } else { - // collisions are full, so we might as well bail now - return collided; - } - } - } - } - } - // then collide against the models - int preNumCollisions = collisions.size(); - if (_skeletonModel.findSphereCollisions(particleCenter, particleRadius, collisions)) { - // the Model doesn't have velocity info, so we have to set it for each new collision - int postNumCollisions = collisions.size(); - for (int i = preNumCollisions; i < postNumCollisions; ++i) { - CollisionInfo* collision = collisions.getCollision(i); - collision->_penetration /= (float)(TREE_SCALE); - collision->_addedVelocity = getVelocity(); - } - collided = true; - } - return collided; -} - glm::quat Avatar::getJointRotation(int index) const { if (QThread::currentThread() != thread()) { return AvatarData::getJointRotation(index); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index f928881068..b37a5205fa 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -101,14 +101,12 @@ public: /// \return true if at least one shape collided with avatar bool findCollisions(const QVector& shapes, CollisionList& collisions); - /// Checks for penetration between the described sphere and the avatar. + /// Checks for penetration between the a sphere and the avatar's models. /// \param penetratorCenter the center of the penetration test sphere /// \param penetratorRadius the radius of the penetration test sphere /// \param collisions[out] a list to which collisions get appended - /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated - bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - CollisionList& collisions, int skeletonSkipIndex = -1); + bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions); /// Checks for penetration between the described plane and the avatar. /// \param plane the penetration plane @@ -116,13 +114,6 @@ public: /// \return whether or not the plane penetrated bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - /// Checks for collision between the a spherical particle and the avatar (including paddle hands) - /// \param collisionCenter the center of particle's bounding sphere - /// \param collisionRadius the radius of particle's bounding sphere - /// \param collisions[out] a list to which collisions get appended - /// \return whether or not the particle collided - bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions); - virtual bool isMyAvatar() { return false; } virtual glm::quat getJointRotation(int index) const; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 3aff984893..ab8e06cfff 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -95,6 +95,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { } void Hand::collideAgainstOurself() { + /* TODO: Andrew to re-implement this if (!Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { return; } @@ -125,6 +126,7 @@ void Hand::collideAgainstOurself() { palm.addToPenetration(totalPenetration); } } + */ } void Hand::resolvePenetrations() { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8f658678b5..4c7136fd0a 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -223,7 +223,7 @@ public: virtual const glm::vec3& getVelocity() const { return vec3Zero; } - virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { + virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { return false; } diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 358c5a1b84..d8d5887d97 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -202,14 +202,13 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { AvatarData* avatar = avatarPointer.data(); - // use a very generous bounding radius since the arms can stretch - float totalRadius = 2.f * avatar->getBoundingRadius() + radius; + float totalRadius = avatar->getBoundingRadius() + radius; glm::vec3 relativePosition = center - avatar->getPosition(); if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) { continue; } - if (avatar->findParticleCollisions(center, radius, _collisions)) { + if (avatar->findSphereCollisions(center, radius, _collisions)) { int numCollisions = _collisions.size(); for (int i = 0; i < numCollisions; ++i) { CollisionInfo* collision = _collisions.getCollision(i); @@ -222,25 +221,6 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { // only collide when particle and collision point are moving toward each other // (doing this prevents some "collision snagging" when particle penetrates the object) - - // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them. - if (collision->_type == COLLISION_TYPE_PADDLE_HAND) { - // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle. - // TODO: make this less hacky when we have more per-collision details - float elasticity = ELASTICITY; - float attenuationFactor = glm::length(collision->_addedVelocity) / HALTING_SPEED; - float damping = DAMPING; - if (attenuationFactor < 1.f) { - collision->_addedVelocity *= attenuationFactor; - elasticity *= attenuationFactor; - // NOTE: the math below keeps the damping piecewise continuous, - // while ramping it up to 1 when attenuationFactor = 0 - damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING); - } - collision->_damping = damping; - } - // HACK END - updateCollisionSound(particle, collision->_penetration, COLLISION_FREQUENCY); collision->_penetration /= (float)(TREE_SCALE); particle->applyHardCollision(*collision); diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index 38e3a4b2db..c58f75a2f4 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -38,8 +38,7 @@ CollisionInfo* CollisionList::getLastCollision() { } void CollisionList::clear() { - // we rely on the external context to properly set or clear the data members of a collision - // whenever it is used. + // we rely on the external context to properly set or clear the data members of CollisionInfos /* for (int i = 0; i < _size; ++i) { // we only clear the important stuff diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 52d5298fde..08baabd98c 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -19,7 +19,6 @@ enum CollisionType { COLLISION_TYPE_UNKNOWN = 0, - COLLISION_TYPE_PADDLE_HAND, COLLISION_TYPE_MODEL, // _data = pointer to Model that owns joint // _intData = joint index From 0addf3bae4cc3d29bee2ef8d5a50be3896d1814d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 14:36:05 -0700 Subject: [PATCH 002/105] cleaup: remove some unused collision code remove CollisionInfo::_type remove unused Avatar collision response stuff comment out some code that works but will probably be replaced --- interface/src/avatar/Avatar.cpp | 19 ---------- interface/src/avatar/Avatar.h | 3 -- interface/src/avatar/MyAvatar.cpp | 7 ---- interface/src/renderer/Model.cpp | 51 -------------------------- interface/src/renderer/Model.h | 8 ---- libraries/shared/src/CollisionInfo.cpp | 1 - libraries/shared/src/CollisionInfo.h | 18 +-------- libraries/shared/src/ShapeCollider.cpp | 14 ------- 8 files changed, 2 insertions(+), 119 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 1bbe730b40..7a45512778 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -846,25 +846,6 @@ float Avatar::getHeadHeight() const { return DEFAULT_HEAD_HEIGHT; } -bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { - if (!collision._data || collision._type != COLLISION_TYPE_MODEL) { - return false; - } - Model* model = static_cast(collision._data); - int jointIndex = collision._intData; - - if (model == &(_skeletonModel) && jointIndex != -1) { - // collision response of skeleton is temporarily disabled - return false; - //return _skeletonModel.collisionHitsMoveableJoint(collision); - } - if (model == &(getHead()->getFaceModel())) { - // ATM we always handle COLLISION_TYPE_MODEL against the face. - return true; - } - return false; -} - float Avatar::getBoundingRadius() const { // TODO: also use head model when computing the avatar's bounding radius return _skeletonModel.getBoundingRadius(); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index b37a5205fa..8e18aecd26 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -132,9 +132,6 @@ public: static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); - /// \return true if we expect the avatar would move as a result of the collision - bool collisionWouldMoveAvatar(CollisionInfo& collision) const; - virtual void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { } /// \return bounding radius of avatar diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a14152aa04..bebe2fbec7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1461,17 +1461,10 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { } } - // collide our hands against them - // TODO: make this work when we can figure out when the other avatar won't yeild - // (for example, we're colliding against their chest or leg) - //getHand()->collideAgainstAvatar(avatar, true); - // collide their hands against us avatar->getHand()->collideAgainstAvatar(this, false); } } - // TODO: uncomment this when we handle collisions that won't affect other avatar - //getHand()->resolvePenetrations(); } class SortedAvatar { diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 105301054b..42492d64fb 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1049,7 +1049,6 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi } if (ShapeCollider::collideShapes(&sphere, _jointShapes[i], collisions)) { CollisionInfo* collision = collisions.getLastCollision(); - collision->_type = COLLISION_TYPE_MODEL; collision->_data = (void*)(this); collision->_intData = i; collided = true; @@ -1065,7 +1064,6 @@ bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collision for (int i = 0; i < _jointShapes.size(); i++) { if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) { CollisionInfo* collision = collisions.getLastCollision(); - collision->_type = COLLISION_TYPE_MODEL; collision->_data = (void*)(this); collision->_intData = i; collided = true; @@ -1446,55 +1444,6 @@ void Model::renderBoundingCollisionShapes(float alpha) { glPopMatrix(); } -bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const { - if (collision._type == COLLISION_TYPE_MODEL) { - // the joint is pokable by a collision if it exists and is free to move - const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._intData]; - if (joint.parentIndex == -1 || _jointStates.isEmpty()) { - return false; - } - // an empty freeLineage means the joint can't move - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - int jointIndex = collision._intData; - const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - return !freeLineage.isEmpty(); - } - return false; -} - -void Model::applyCollision(CollisionInfo& collision) { - if (collision._type != COLLISION_TYPE_MODEL) { - return; - } - - glm::vec3 jointPosition(0.0f); - int jointIndex = collision._intData; - if (getJointPositionInWorldFrame(jointIndex, jointPosition)) { - const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex]; - if (joint.parentIndex != -1) { - // compute the approximate distance (travel) that the joint needs to move - glm::vec3 start; - getJointPositionInWorldFrame(joint.parentIndex, start); - glm::vec3 contactPoint = collision._contactPoint - start; - glm::vec3 penetrationEnd = contactPoint + collision._penetration; - glm::vec3 axis = glm::cross(contactPoint, penetrationEnd); - float travel = glm::length(axis); - const float MIN_TRAVEL = 1.0e-8f; - if (travel > MIN_TRAVEL) { - // compute the new position of the joint - float angle = asinf(travel / (glm::length(contactPoint) * glm::length(penetrationEnd))); - axis = glm::normalize(axis); - glm::vec3 end; - getJointPositionInWorldFrame(jointIndex, end); - // transform into model-frame - glm::vec3 newEnd = glm::inverse(_rotation) * (start + glm::angleAxis(angle, axis) * (end - start) - _translation); - // try to move it - setJointPosition(jointIndex, newEnd, glm::quat(), false, -1, true); - } - } - } -} - void Model::setBlendedVertices(const QVector& vertices, const QVector& normals) { if (_blendedVertexBuffers.isEmpty()) { return; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 11e6861775..be5b892229 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -156,14 +156,6 @@ public: bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - /// \param collision details about the collisions - /// \return true if the collision is against a moveable joint - bool collisionHitsMoveableJoint(CollisionInfo& collision) const; - - /// \param collision details about the collision - /// Use the collision to affect the model - void applyCollision(CollisionInfo& collision); - float getBoundingRadius() const { return _boundingRadius; } float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index c58f75a2f4..32deb56cd1 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -43,7 +43,6 @@ void CollisionList::clear() { for (int i = 0; i < _size; ++i) { // we only clear the important stuff CollisionInfo& collision = _collisions[i]; - collision._type = COLLISION_TYPE_UNKNOWN; //collision._data = NULL; //collision._intData = 0; //collision._floatDAta = 0.0f; diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 08baabd98c..cf48701a2b 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -17,16 +17,6 @@ #include -enum CollisionType { - COLLISION_TYPE_UNKNOWN = 0, - COLLISION_TYPE_MODEL, - // _data = pointer to Model that owns joint - // _intData = joint index - COLLISION_TYPE_AACUBE, - // _floatData = cube side - // _vecData = cube center -}; - const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0; const quint32 COLLISION_GROUP_AVATARS = 1U << 1; const quint32 COLLISION_GROUP_VOXELS = 1U << 2; @@ -41,8 +31,7 @@ const quint32 VALID_COLLISION_GROUPS = 0x0f; class CollisionInfo { public: CollisionInfo() - : _type(0), - _data(NULL), + : _data(NULL), _intData(0), _damping(0.f), _elasticity(1.f), @@ -52,8 +41,7 @@ public: } CollisionInfo(qint32 type) - : _type(type), - _data(NULL), + : _data(NULL), _intData(0), _damping(0.f), _elasticity(1.f), @@ -64,8 +52,6 @@ public: ~CollisionInfo() {} - int _type; // type of Collision - // the value of the *Data fields depend on the type void* _data; int _intData; diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 7c29fbae00..e80d26d860 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -132,7 +132,6 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis // penetration points from A into B CollisionInfo* collision = collisions.getNewCollision(); if (collision) { - collision->_type = COLLISION_TYPE_UNKNOWN; collision->_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * BA; @@ -180,7 +179,6 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B // contactPoint is on surface of sphereA collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * radialAxis; - collision->_type = COLLISION_TYPE_UNKNOWN; } else { // A is on B's axis, so the penetration is undefined... if (absAxialDistance > capsuleB->getHalfHeight()) { @@ -202,7 +200,6 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; // contactPoint is on surface of sphereA collision->_contactPoint = sphereA->getPosition() + (sign * sphereA->getRadius()) * capsuleAxis; - collision->_type = COLLISION_TYPE_UNKNOWN; } return true; } @@ -218,7 +215,6 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision } collision->_penetration = penetration; collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration); - collision->_type = COLLISION_TYPE_UNKNOWN; return true; } return false; @@ -268,7 +264,6 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col collision->_penetration = (radialDistance - totalRadius) * radialAxis; // points from A into B // contactPoint is on surface of capsuleA collision->_contactPoint = closestApproach - capsuleA->getRadius() * radialAxis; - collision->_type = COLLISION_TYPE_UNKNOWN; } else { // A is on B's axis, so the penetration is undefined... if (absAxialDistance > capsuleA->getHalfHeight()) { @@ -289,7 +284,6 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col collision->_penetration = (sign * (totalRadius + capsuleA->getHalfHeight() - absAxialDistance)) * capsuleAxis; // contactPoint is on surface of sphereA collision->_contactPoint = closestApproach + (sign * capsuleA->getRadius()) * capsuleAxis; - collision->_type = COLLISION_TYPE_UNKNOWN; } } return true; @@ -361,7 +355,6 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, collision->_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA; - collision->_type = COLLISION_TYPE_UNKNOWN; return true; } } else { @@ -427,7 +420,6 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, // average the internal pair, and then do the math from centerB collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB + (capsuleA->getRadius() - distance) * BA; - collision->_type = COLLISION_TYPE_UNKNOWN; return true; } } @@ -447,7 +439,6 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis collision->_penetration = penetration; glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end; collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration); - collision->_type = COLLISION_TYPE_UNKNOWN; return true; } return false; @@ -463,7 +454,6 @@ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, Collision collision->_penetration = -penetration; collision->_contactPoint = sphereB->getPosition() + (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration; - collision->_type = COLLISION_TYPE_UNKNOWN; return true; } return false; @@ -482,7 +472,6 @@ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, Collis 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->_type = COLLISION_TYPE_UNKNOWN; return true; } return false; @@ -668,13 +657,11 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: direction /= lengthDirection; // compute collision details - collision->_type = COLLISION_TYPE_AACUBE; collision->_floatData = cubeSide; collision->_vecData = cubeCenter; collision->_penetration = (halfCubeSide * lengthDirection + sphereRadius - maxBA * glm::dot(BA, direction)) * direction; collision->_contactPoint = sphereCenter + sphereRadius * direction; } - collision->_type = COLLISION_TYPE_AACUBE; collision->_floatData = cubeSide; collision->_vecData = cubeCenter; return true; @@ -688,7 +675,6 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: // contactPoint is on surface of A collision->_contactPoint = sphereCenter + collision->_penetration; - collision->_type = COLLISION_TYPE_AACUBE; collision->_floatData = cubeSide; collision->_vecData = cubeCenter; return true; From 131a2c27b86084d4076466d4c09527b4f5ebd920 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 15:51:50 -0700 Subject: [PATCH 003/105] move RagDoll to libraries/shared --- interface/src/avatar/SkeletonModel.cpp | 25 ++++++++++- interface/src/avatar/SkeletonModel.h | 3 +- .../shared/src}/RagDoll.cpp | 45 +++++++------------ .../shared/src}/RagDoll.h | 14 +++--- 4 files changed, 47 insertions(+), 40 deletions(-) rename {interface/src/renderer => libraries/shared/src}/RagDoll.cpp (80%) rename {interface/src/renderer => libraries/shared/src}/RagDoll.h (88%) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ca40ba1d64..4db77b998f 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -25,7 +25,19 @@ void SkeletonModel::setJointStates(QVector states) { Model::setJointStates(states); if (isActive() && _owningAvatar->isMyAvatar()) { - _ragDoll.init(_jointStates); + // extract lists of parentIndex and position from _jointStates... + QVector parentIndices; + QVector points; + int numJoints = _jointStates.size(); + parentIndices.reserve(numJoints); + points.reserve(numJoints); + for (int i = 0; i < numJoints; ++i) { + JointState& state = _jointStates[i]; + parentIndices.push_back(state.getFBXJoint().parentIndex); + points.push_back(state.getPosition()); + } + // ... and feed the results to _ragDoll + _ragDoll.init(parentIndices, points); } } @@ -91,8 +103,17 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::simulateRagDoll(float deltaTime) { - _ragDoll.slaveToSkeleton(_jointStates, 0.1f); // fraction = 0.1f left intentionally low for demo purposes + // move ragdoll points toward joints + QVector& points = _ragDoll.getPoints(); + const int numStates = _jointStates.size(); + assert(numStates == points.size()); + float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes + float oneMinusFraction = 1.0f - fraction; + for (int i = 0; i < numStates; ++i) { + points[i] = oneMinusFraction * points[i] + fraction * _jointStates[i].getPosition(); + } + // enforce the constraints float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm int MAX_ITERATIONS = 4; int iterations = 0; diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 086973807d..9f034b2aa3 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -13,7 +13,8 @@ #define hifi_SkeletonModel_h #include "renderer/Model.h" -#include "renderer/RagDoll.h" + +#include class Avatar; diff --git a/interface/src/renderer/RagDoll.cpp b/libraries/shared/src/RagDoll.cpp similarity index 80% rename from interface/src/renderer/RagDoll.cpp rename to libraries/shared/src/RagDoll.cpp index e2654d737f..e6a556a4fd 100644 --- a/interface/src/renderer/RagDoll.cpp +++ b/libraries/shared/src/RagDoll.cpp @@ -9,17 +9,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -#include -#include - -#include -#include -#include -#include - #include "RagDoll.h" +#include "CapsuleShape.h" +#include "CollisionInfo.h" +#include "SharedUtil.h" +#include "SphereShape.h" + // ---------------------------------------------------------------------------- // FixedConstraint // ---------------------------------------------------------------------------- @@ -105,15 +101,17 @@ RagDoll::~RagDoll() { clear(); } -void RagDoll::init(const QVector& states) { +void RagDoll::init(const QVector& parentIndices, const QVector& points) { clear(); - const int numStates = states.size(); - _points.reserve(numStates); - for (int i = 0; i < numStates; ++i) { - const JointState& state = states[i]; - _points.push_back(state.getPosition()); - int parentIndex = state.getFBXJoint().parentIndex; - assert(parentIndex < i); + const int numPoints = points.size(); + assert(numPoints == parentIndices.size()); + _points.reserve(numPoints); + for (int i = 0; i < numPoints; ++i) { + glm::vec3 position = points[i]; + _points.push_back(position); + + int parentIndex = parentIndices[i]; + assert(parentIndex < i && parentIndex >= -1); if (parentIndex == -1) { FixedConstraint* anchor = new FixedConstraint(&(_points[i]), glm::vec3(0.0f)); _constraints.push_back(anchor); @@ -134,19 +132,6 @@ void RagDoll::clear() { _points.clear(); } -float RagDoll::slaveToSkeleton(const QVector& states, float fraction) { - const int numStates = states.size(); - assert(numStates == _points.size()); - fraction = glm::clamp(fraction, 0.0f, 1.0f); - float maxDistance = 0.0f; - for (int i = 0; i < numStates; ++i) { - glm::vec3 oldPoint = _points[i]; - _points[i] = (1.0f - fraction) * _points[i] + fraction * states[i].getPosition(); - maxDistance = glm::max(maxDistance, glm::distance(oldPoint, _points[i])); - } - return maxDistance; -} - float RagDoll::enforceConstraints() { float maxDistance = 0.0f; const int numConstraints = _constraints.size(); diff --git a/interface/src/renderer/RagDoll.h b/libraries/shared/src/RagDoll.h similarity index 88% rename from interface/src/renderer/RagDoll.h rename to libraries/shared/src/RagDoll.h index 60e242d19b..68648f7655 100644 --- a/interface/src/renderer/RagDoll.h +++ b/libraries/shared/src/RagDoll.h @@ -12,7 +12,10 @@ #ifndef hifi_RagDoll_h #define hifi_RagDoll_h -#include "renderer/Model.h" +#include +#include + +#include class Shape; @@ -66,21 +69,18 @@ public: /// Create points and constraints based on topology of collection of joints /// \param joints list of connected joint states - void init(const QVector& states); + void init(const QVector& parentIndices, const QVector& points); /// Delete all data. void clear(); - /// \param states list of joint states - /// \param fraction range from 0.0 (no movement) to 1.0 (use joint locations) - /// \return max distance of point movement - float slaveToSkeleton(const QVector& states, float fraction); - /// Enforce contraints. /// \return max distance of point movement float enforceConstraints(); + // both const and non-const getPoints() const QVector& getPoints() const { return _points; } + QVector& getPoints() { return _points; } /// \param shapes list of shapes to be updated with new positions /// \param rotation rotation into shapes' collision frame From 90c55ad06c7329b7929dfba1f226686121a51c92 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 16:09:21 -0700 Subject: [PATCH 004/105] fix some comments --- libraries/shared/src/RagDoll.cpp | 2 +- libraries/shared/src/RagDoll.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/RagDoll.cpp b/libraries/shared/src/RagDoll.cpp index e6a556a4fd..d4b78e4732 100644 --- a/libraries/shared/src/RagDoll.cpp +++ b/libraries/shared/src/RagDoll.cpp @@ -1,6 +1,6 @@ // // RagDoll.cpp -// interface/src/avatar +// libraries/shared/src // // Created by Andrew Meadows 2014.05.30 // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/shared/src/RagDoll.h b/libraries/shared/src/RagDoll.h index 68648f7655..43d9c906c0 100644 --- a/libraries/shared/src/RagDoll.h +++ b/libraries/shared/src/RagDoll.h @@ -1,6 +1,6 @@ // // RagDoll.h -// interface/src/avatar +// libraries/shared/src // // Created by Andrew Meadows 2014.05.30 // Copyright 2014 High Fidelity, Inc. From 8e536ebd9afedc7f964b4aa7159e263469851e9a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 09:35:27 -0700 Subject: [PATCH 005/105] Use NULL Shape* for non-colliding joints in Models --- interface/src/avatar/SkeletonModel.cpp | 11 +++-- interface/src/renderer/Model.cpp | 58 ++++++++++++++++---------- interface/src/renderer/Model.h | 7 ++-- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 4db77b998f..08882cbd42 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -135,18 +135,21 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) for (int i = 0; i < _jointStates.size(); i++) { const FBXJoint& joint = geometry.joints[i]; int parentIndex = joint.parentIndex; + Shape* shape = _jointShapes[i]; if (i == jointIndex) { // this shape is the hand - shapes.push_back(_jointShapes[i]); - if (parentIndex != -1) { + if (shape) { + shapes.push_back(shape); + } + if (parentIndex != -1 && _jointShapes[parentIndex]) { // also add the forearm shapes.push_back(_jointShapes[parentIndex]); } - } else { + } else if (shape) { while (parentIndex != -1) { if (parentIndex == jointIndex) { // this shape is a child of the hand - shapes.push_back(_jointShapes[i]); + shapes.push_back(shape); break; } parentIndex = geometry.joints[parentIndex].parentIndex; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 42492d64fb..03c1544aab 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -813,11 +813,8 @@ void Model::rebuildShapes() { _jointShapes.push_back(sphere); } else { // this shape type is not handled and the joint shouldn't collide, - // however we must have a shape for each joint, - // so we make a bogus sphere with zero radius. - // TODO: implement collision groups for more control over what collides with what - SphereShape* sphere = new SphereShape(0.0f, glm::vec3(0.0f)); - _jointShapes.push_back(sphere); + // however we must have a Shape* for each joint, so we push NULL + _jointShapes.push_back(NULL); } } @@ -875,10 +872,13 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { _boundingRadius = 0.0f; float uniformScale = extractUniformScale(_scale); for (int i = 0; i < _jointShapes.size(); i++) { + Shape* shape = _jointShapes[i]; + if (!shape) { + continue; + } const FBXJoint& joint = geometry.joints[i]; glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; - Shape* shape = _jointShapes[i]; shape->setPosition(localPosition); shape->setRotation(finalRotations[i] * joint.shapeRotation); float distance = glm::length(localPosition) + shape->getBoundingRadius(); @@ -890,11 +890,14 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { // compute bounding box Extents totalExtents; totalExtents.reset(); + totalExtents.addPoint(glm::vec3(0.0f)); for (int i = 0; i < _jointShapes.size(); i++) { + Shape* shape = _jointShapes[i]; + if (!shape) { + continue; + } Extents shapeExtents; shapeExtents.reset(); - - Shape* shape = _jointShapes[i]; glm::vec3 localPosition = shape->getPosition(); int type = shape->getType(); if (type == Shape::CAPSULE_SHAPE) { @@ -949,8 +952,10 @@ void Model::resetShapePositions() { // Then we move them into world frame for rendering at the Model's location. for (int i = 0; i < _jointShapes.size(); i++) { Shape* shape = _jointShapes[i]; - shape->setPosition(_translation + _rotation * shape->getPosition()); - shape->setRotation(_rotation * shape->getRotation()); + if (shape) { + shape->setPosition(_translation + _rotation * shape->getPosition()); + shape->setRotation(_rotation * shape->getRotation()); + } } _boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); @@ -969,11 +974,13 @@ void Model::updateShapePositions() { glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); Shape* shape = _jointShapes[i]; - shape->setPosition(worldPosition); - shape->setRotation(_rotation * stateRotation * joint.shapeRotation); - float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; + if (shape) { + shape->setPosition(worldPosition); + shape->setRotation(_rotation * stateRotation * joint.shapeRotation); + float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); + if (distance > _boundingRadius) { + _boundingRadius = distance; + } } if (joint.parentIndex == -1) { rootPosition = worldPosition; @@ -1018,9 +1025,12 @@ bool Model::findCollisions(const QVector shapes, CollisionList& co bool collided = false; for (int i = 0; i < shapes.size(); ++i) { const Shape* theirShape = shapes[i]; + if (!theirShape) { + continue; + } for (int j = 0; j < _jointShapes.size(); ++j) { const Shape* ourShape = _jointShapes[j]; - if (ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { + if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { collided = true; } } @@ -1034,6 +1044,10 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi SphereShape sphere(sphereRadius, sphereCenter); const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointShapes.size(); i++) { + Shape* shape = _jointShapes[i]; + if (!shape) { + continue; + } const FBXJoint& joint = geometry.joints[i]; if (joint.parentIndex != -1) { if (skipIndex != -1) { @@ -1047,7 +1061,7 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi } while (ancestorIndex != -1); } } - if (ShapeCollider::collideShapes(&sphere, _jointShapes[i], collisions)) { + if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_data = (void*)(this); collision->_intData = i; @@ -1062,7 +1076,7 @@ bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collision bool collided = false; PlaneShape planeShape(plane); for (int i = 0; i < _jointShapes.size(); i++) { - if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) { + if (_jointShapes[i] && ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_data = (void*)(this); collision->_intData = i; @@ -1369,10 +1383,12 @@ void Model::renderJointCollisionShapes(float alpha) { glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); for (int i = 0; i < _jointShapes.size(); i++) { - glPushMatrix(); - Shape* shape = _jointShapes[i]; - + if (!shape) { + continue; + } + + glPushMatrix(); if (shape->getType() == Shape::SPHERE_SHAPE) { // shapes are stored in world-frame, so we have to transform into model frame glm::vec3 position = shape->getPosition() - _translation; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index be5b892229..e507aee6aa 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -137,10 +137,11 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - void clearShapes(); - void rebuildShapes(); - void resetShapePositions(); + virtual void clearShapes(); + virtual void rebuildShapes(); + void resetShapePositions(); // DEBUG method virtual void updateShapePositions(); + void renderJointCollisionShapes(float alpha); void renderBoundingCollisionShapes(float alpha); From b70020595cab238d165a9f43656f0ae3f25431a2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 09:55:15 -0700 Subject: [PATCH 006/105] clearShapes(), rebuildShapes() need not be virtual --- interface/src/renderer/Model.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index e507aee6aa..f8d4cf628b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -137,8 +137,8 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - virtual void clearShapes(); - virtual void rebuildShapes(); + void clearShapes(); + void rebuildShapes(); void resetShapePositions(); // DEBUG method virtual void updateShapePositions(); From 5a76a9b4b1fbfb8f712dc39c66eb47b09b638935 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 9 Jun 2014 11:04:20 -0700 Subject: [PATCH 007/105] RagDoll keeps a pointer to list of shapes --- interface/src/avatar/SkeletonModel.cpp | 4 ++-- libraries/shared/src/RagDoll.cpp | 16 +++++++++++----- libraries/shared/src/RagDoll.h | 13 +++++++++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 08882cbd42..1d1294da03 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -37,7 +37,7 @@ void SkeletonModel::setJointStates(QVector states) { points.push_back(state.getPosition()); } // ... and feed the results to _ragDoll - _ragDoll.init(parentIndices, points); + _ragDoll.init(&_jointShapes, parentIndices, points); } } @@ -286,7 +286,7 @@ void SkeletonModel::updateJointState(int index) { void SkeletonModel::updateShapePositions() { if (isActive() && _owningAvatar->isMyAvatar() && Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagDoll)) { - _ragDoll.updateShapes(_jointShapes, _rotation, _translation); + _ragDoll.updateShapes(_rotation, _translation); } else { Model::updateShapePositions(); } diff --git a/libraries/shared/src/RagDoll.cpp b/libraries/shared/src/RagDoll.cpp index d4b78e4732..0895daffa0 100644 --- a/libraries/shared/src/RagDoll.cpp +++ b/libraries/shared/src/RagDoll.cpp @@ -94,15 +94,16 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio // RagDoll // ---------------------------------------------------------------------------- -RagDoll::RagDoll() { +RagDoll::RagDoll() : _shapes(NULL) { } RagDoll::~RagDoll() { clear(); } -void RagDoll::init(const QVector& parentIndices, const QVector& points) { +void RagDoll::init(QVector* shapes, const QVector& parentIndices, const QVector& points) { clear(); + _shapes = shapes; const int numPoints = points.size(); assert(numPoints == parentIndices.size()); _points.reserve(numPoints); @@ -143,10 +144,15 @@ float RagDoll::enforceConstraints() { return maxDistance; } -void RagDoll::updateShapes(const QVector& shapes, const glm::quat& rotation, const glm::vec3& translation) const { - int numShapes = shapes.size(); +void RagDoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const { + if (!_shapes) { + return; + } + int numShapes = _shapes->size(); int numConstraints = _constraints.size(); + + // NOTE: we assume a one-to-one relationship between shapes and constraints for (int i = 0; i < numShapes && i < numConstraints; ++i) { - _constraints[i]->updateProxyShape(shapes[i], rotation, translation); + _constraints[i]->updateProxyShape((*_shapes)[i], rotation, translation); } } diff --git a/libraries/shared/src/RagDoll.h b/libraries/shared/src/RagDoll.h index 43d9c906c0..bc3786d547 100644 --- a/libraries/shared/src/RagDoll.h +++ b/libraries/shared/src/RagDoll.h @@ -69,7 +69,9 @@ public: /// Create points and constraints based on topology of collection of joints /// \param joints list of connected joint states - void init(const QVector& parentIndices, const QVector& points); + void init(QVector* shapes, const QVector& parentIndices, const QVector& points); + + void setShapes(QVector* shapes) { _shapes = shapes; } /// Delete all data. void clear(); @@ -82,14 +84,17 @@ public: const QVector& getPoints() const { return _points; } QVector& getPoints() { return _points; } - /// \param shapes list of shapes to be updated with new positions /// \param rotation rotation into shapes' collision frame /// \param translation translation into shapes' collision frame - void updateShapes(const QVector& shapes, const glm::quat& rotation, const glm::vec3& translation) const; + /// Moves and modifies elements of _shapes to agree with state of _points + void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const; private: - QVector _constraints; QVector _points; + QVector _constraints; + + // the RagDoll does NOT own the data in _shapes. + QVector* _shapes; }; #endif // hifi_RagDoll_h From 3c51ce76fc7d745f4290213f0f0d6a46862d1603 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 10 Jun 2014 15:24:17 -0700 Subject: [PATCH 008/105] Add simulationID to Model Shape and CollisionInfo --- interface/src/renderer/Model.cpp | 29 ++++++++++++++++++++++++++ interface/src/renderer/Model.h | 7 +++++-- libraries/shared/src/CollisionInfo.cpp | 2 ++ libraries/shared/src/CollisionInfo.h | 7 +++++++ libraries/shared/src/Shape.h | 7 +++++-- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 03c1544aab..3003e29f54 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -37,6 +37,7 @@ Model::Model(QObject* parent) : _scaleToFit(false), _scaleToFitLargestDimension(0.0f), _scaledToFit(false), + _simulationIndex(-1), _snapModelToCenter(false), _snappedToCenter(false), _rootIndex(-1), @@ -774,6 +775,34 @@ AnimationHandlePointer Model::createAnimationHandle() { return handle; } +quint8 BITS_FOR_SHAPE_INDEX = 15; +int MAX_SIMULATION_ID = 1 << (31 - BITS_FOR_SHAPE_INDEX); + +void Model::setSimulationIndex(int index) { + _simulationIndex = index; + + if (_simulationIndex < 0 || _simulationIndex > MAX_SIMULATION_ID) { + // clear simulation ID's of all the shapes + for (int i = 0; i < _jointShapes.size(); i++) { + Shape* shape = _jointShapes[i]; + if (shape) { + shape->setSimulationID(-1); + } + } + } else { + // update the simulation ID's of the shapes + // upper bits store this Model's index... + int shiftedIndex = _simulationIndex << BITS_FOR_SHAPE_INDEX; + for (int i = 0; i < _jointShapes.size(); i++) { + Shape* shape = _jointShapes[i]; + if (shape) { + // ... lower bits are for the shape's index + shape->setSimulationID(shiftedIndex + i); + } + } + } +} + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index f8d4cf628b..aa0b34d750 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -134,9 +134,10 @@ public: QStringList getJointNames() const; AnimationHandlePointer createAnimationHandle(); - + const QList& getRunningAnimations() const { return _runningAnimations; } - + + void setSimulationIndex(int index); void clearShapes(); void rebuildShapes(); void resetShapePositions(); // DEBUG method @@ -177,6 +178,8 @@ protected: bool _scaleToFit; /// If you set scaleToFit, we will calculate scale based on MeshExtents float _scaleToFitLargestDimension; /// this is the dimension that scale to fit will use bool _scaledToFit; /// have we scaled to fit + + int _simulationIndex; // set by SimulationEngine (if any) bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snappedToCenter; /// are we currently snapped to center diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index 32deb56cd1..a1c3f0b368 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -47,6 +47,8 @@ void CollisionList::clear() { //collision._intData = 0; //collision._floatDAta = 0.0f; //collision._vecData = glm::vec3(0.0f); + //collision._shapeA = -1; + //collision._shapeB = -1; //collision._damping; //collision._elasticity; //collision._contactPoint; diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index cf48701a2b..ff228e3863 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -33,6 +33,8 @@ public: CollisionInfo() : _data(NULL), _intData(0), + _shapeA(-1), + _shapeB(-1), _damping(0.f), _elasticity(1.f), _contactPoint(0.f), @@ -43,6 +45,8 @@ public: CollisionInfo(qint32 type) : _data(NULL), _intData(0), + _shapeA(-1), + _shapeB(-1), _damping(0.f), _elasticity(1.f), _contactPoint(0.f), @@ -58,6 +62,9 @@ public: float _floatData; glm::vec3 _vecData; + int _shapeA; // ID of shapeA in its SimulationEngine + int _shapeB; // ID of shapeB in its SimulationEngine + float _damping; // range [0,1] of friction coeficient float _elasticity; // range [0,1] of energy conservation glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 87b84ea73b..a9bddc00df 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -23,11 +23,10 @@ public: SPHERE_SHAPE, CAPSULE_SHAPE, PLANE_SHAPE, - BOX_SHAPE, LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _boundingRadius(0.f), _position(0.f), _rotation() { } + Shape() : _type(UNKNOWN_SHAPE), _simulationID(-1), _boundingRadius(0.f), _position(0.f), _rotation() { } virtual ~Shape() {} int getType() const { return _type; } @@ -38,6 +37,9 @@ public: virtual void setPosition(const glm::vec3& position) { _position = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } + void setSimulationID(int id) { _simulationID = id; } + int getSimulationID() const { return _simulationID; } + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {} @@ -51,6 +53,7 @@ protected: void setBoundingRadius(float radius) { _boundingRadius = radius; } int _type; + int _simulationID; // shape's simulation ID in SimulationEngine float _boundingRadius; glm::vec3 _position; glm::quat _rotation; From 5be470bcbcbe1c34f9017d7768029257cd976d44 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Jun 2014 13:13:00 -0700 Subject: [PATCH 009/105] add PhysicalEntity class Model and RagDoll derive from PhysicalEntity Shape gets back pointer to its Entity (shape sharing not possible) CollisionInfo gets backpointers the shapes involved --- interface/src/renderer/Model.cpp | 43 +++++++------------------ interface/src/renderer/Model.h | 5 +-- libraries/shared/src/CollisionInfo.cpp | 4 +-- libraries/shared/src/CollisionInfo.h | 17 ++++++---- libraries/shared/src/PhysicalEntity.cpp | 24 ++++++++++++++ libraries/shared/src/PhysicalEntity.h | 43 +++++++++++++++++++++++++ libraries/shared/src/RagDoll.cpp | 9 +++++- libraries/shared/src/RagDoll.h | 4 +-- libraries/shared/src/Shape.h | 15 +++++---- libraries/shared/src/ShapeCollider.cpp | 33 +++++++++++++++++++ 10 files changed, 145 insertions(+), 52 deletions(-) create mode 100644 libraries/shared/src/PhysicalEntity.cpp create mode 100644 libraries/shared/src/PhysicalEntity.h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 640b34b090..a1ef53163d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -16,15 +16,15 @@ #include #include +#include #include +#include +#include +#include #include "Application.h" #include "Model.h" -#include -#include -#include - using namespace std; static int modelPointerTypeId = qRegisterMetaType >(); @@ -33,6 +33,7 @@ static int vec3VectorTypeId = qRegisterMetaType >(); Model::Model(QObject* parent) : QObject(parent), + PhysicalEntity(PhysicalEntity::ENTITY_MODEL), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), _scaleToFitLargestDimension(0.0f), @@ -775,34 +776,6 @@ AnimationHandlePointer Model::createAnimationHandle() { return handle; } -quint8 BITS_FOR_SHAPE_INDEX = 15; -int MAX_SIMULATION_ID = 1 << (31 - BITS_FOR_SHAPE_INDEX); - -void Model::setSimulationIndex(int index) { - _simulationIndex = index; - - if (_simulationIndex < 0 || _simulationIndex > MAX_SIMULATION_ID) { - // clear simulation ID's of all the shapes - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; - if (shape) { - shape->setSimulationID(-1); - } - } - } else { - // update the simulation ID's of the shapes - // upper bits store this Model's index... - int shiftedIndex = _simulationIndex << BITS_FOR_SHAPE_INDEX; - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; - if (shape) { - // ... lower bits are for the shape's index - shape->setSimulationID(shiftedIndex + i); - } - } - } -} - void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; @@ -836,10 +809,12 @@ void Model::rebuildShapes() { } if (type == Shape::CAPSULE_SHAPE) { CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); + capsule->setEntity(this); _jointShapes.push_back(capsule); } else if (type == Shape::SPHERE_SHAPE) { SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); _jointShapes.push_back(sphere); + sphere->setEntity(this); } else { // this shape type is not handled and the joint shouldn't collide, // however we must have a Shape* for each joint, so we push NULL @@ -1815,6 +1790,10 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } } +void Model::setShapeBackPointers() { + PhysicalEntity::setShapeBackPointers(_jointShapes, this); +} + void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index aa0b34d750..cafca9abdd 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -17,6 +17,7 @@ #include #include +#include #include @@ -33,7 +34,7 @@ typedef QSharedPointer AnimationHandlePointer; typedef QWeakPointer WeakAnimationHandlePointer; /// A generic 3D model displaying geometry loaded from a URL. -class Model : public QObject { +class Model : public QObject, public PhysicalEntity { Q_OBJECT public: @@ -137,7 +138,6 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - void setSimulationIndex(int index); void clearShapes(); void rebuildShapes(); void resetShapePositions(); // DEBUG method @@ -167,6 +167,7 @@ public: const CapsuleShape& getBoundingShape() const { return _boundingShape; } protected: + virtual void setShapeBackPointers(); QSharedPointer _geometry; diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index a1c3f0b368..39b49ed90e 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -47,8 +47,8 @@ void CollisionList::clear() { //collision._intData = 0; //collision._floatDAta = 0.0f; //collision._vecData = glm::vec3(0.0f); - //collision._shapeA = -1; - //collision._shapeB = -1; + //collision._shapeA = NULL; + //collision._shapeB = NULL; //collision._damping; //collision._elasticity; //collision._contactPoint; diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index ff228e3863..7f14f6b5dc 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -17,6 +17,8 @@ #include +class Shape; + const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0; const quint32 COLLISION_GROUP_AVATARS = 1U << 1; const quint32 COLLISION_GROUP_VOXELS = 1U << 2; @@ -33,8 +35,8 @@ public: CollisionInfo() : _data(NULL), _intData(0), - _shapeA(-1), - _shapeB(-1), + _shapeA(NULL), + _shapeB(NULL), _damping(0.f), _elasticity(1.f), _contactPoint(0.f), @@ -45,8 +47,8 @@ public: CollisionInfo(qint32 type) : _data(NULL), _intData(0), - _shapeA(-1), - _shapeB(-1), + _shapeA(NULL), + _shapeB(NULL), _damping(0.f), _elasticity(1.f), _contactPoint(0.f), @@ -62,8 +64,11 @@ public: float _floatData; glm::vec3 _vecData; - int _shapeA; // ID of shapeA in its SimulationEngine - int _shapeB; // ID of shapeB in its SimulationEngine + Shape* getShapeA() const { return const_cast(_shapeA); } + Shape* getShapeB() const { return const_cast(_shapeB); } + + const Shape* _shapeA; // pointer to shapeA in this collision + const Shape* _shapeB; // pointer to shapeB in this collision float _damping; // range [0,1] of friction coeficient float _elasticity; // range [0,1] of energy conservation diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp new file mode 100644 index 0000000000..43e25b2f1b --- /dev/null +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -0,0 +1,24 @@ +// +// PhysicalEntity.cpp +// libraries/shared/src +// +// Created by Andrew Meadows 2014.06.11 +// 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 "PhysicalEntity.h" +#include "Shape.h" + +// static +void PhysicalEntity::setShapeBackPointers(const QVector& shapes, PhysicalEntity* entity) { + for (int i = 0; i < shapes.size(); i++) { + Shape* shape = shapes[i]; + if (shape) { + shape->setEntity(entity); + } + } +} + diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h new file mode 100644 index 0000000000..83469d4775 --- /dev/null +++ b/libraries/shared/src/PhysicalEntity.h @@ -0,0 +1,43 @@ +// +// PhysicalEntity.h +// libraries/shared/src +// +// Created by Andrew Meadows 2014.05.30 +// 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_PhysicalEntity_h +#define hifi_PhysicalEntity_h + +#include + +class Shape; + +// PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a +// SimulationEngine. Each CollisionInfo generated by a SimulationEngine has back pointers to the +// two Shapes involved, and those Shapes may (optionally) have valid back pointers to their PhysicalEntity. + +class PhysicalEntity { + +public: + enum EntityType { + ENTITY_UNKNOWN, + ENTITY_MODEL, + ENTITY_RAGDOLL, + }; + + static void setShapeBackPointers(const QVector& shapes, PhysicalEntity* entity); + + PhysicalEntity(EntityType type = ENTITY_UNKNOWN) : _entityType(type) {} + virtual ~PhysicalEntity() {} + + EntityType getEntityType() const { return _entityType; } + +protected: + EntityType _entityType; +}; + +#endif // hifi_PhysicalEntity_h diff --git a/libraries/shared/src/RagDoll.cpp b/libraries/shared/src/RagDoll.cpp index 867011f0f2..67cb368a02 100644 --- a/libraries/shared/src/RagDoll.cpp +++ b/libraries/shared/src/RagDoll.cpp @@ -94,7 +94,7 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio // RagDoll // ---------------------------------------------------------------------------- -RagDoll::RagDoll() : _shapes(NULL) { +RagDoll::RagDoll() : PhysicalEntity(PhysicalEntity::ENTITY_RAGDOLL), _shapes(NULL) { } RagDoll::~RagDoll() { @@ -121,6 +121,9 @@ void RagDoll::init(QVector* shapes, const QVector& parentIndices, c _constraints.push_back(stick); } } + if (_shapes) { + PhysicalEntity::setShapeBackPointers(*_shapes, this); + } } /// Delete all data. @@ -131,6 +134,10 @@ void RagDoll::clear() { } _constraints.clear(); _points.clear(); + if (_shapes) { + PhysicalEntity::setShapeBackPointers(*_shapes, NULL); + } + _shapes = NULL; } float RagDoll::enforceConstraints() { diff --git a/libraries/shared/src/RagDoll.h b/libraries/shared/src/RagDoll.h index bc3786d547..5a7387102a 100644 --- a/libraries/shared/src/RagDoll.h +++ b/libraries/shared/src/RagDoll.h @@ -17,7 +17,7 @@ #include -class Shape; +#include "PhysicalEntity.h" class Constraint { public: @@ -61,7 +61,7 @@ private: glm::vec3* _points[2]; }; -class RagDoll { +class RagDoll : public PhysicalEntity { public: RagDoll(); diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index a9bddc00df..7c8a3abc76 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -15,6 +15,7 @@ #include #include +class PhysicalEntity; class Shape { public: @@ -26,7 +27,7 @@ public: LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _simulationID(-1), _boundingRadius(0.f), _position(0.f), _rotation() { } + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _position(0.f), _rotation() { } virtual ~Shape() {} int getType() const { return _type; } @@ -37,23 +38,23 @@ public: virtual void setPosition(const glm::vec3& position) { _position = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } - void setSimulationID(int id) { _simulationID = id; } - int getSimulationID() const { return _simulationID; } + void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } + PhysicalEntity* getEntity() const { return _owningEntity; } protected: // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _boundingRadius(0.f), _position(0.f), _rotation() {} + Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(0.f), _rotation() {} Shape(Type type, const glm::vec3& position) - : _type(type), _boundingRadius(0.f), _position(position), _rotation() {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(position), _rotation() {} Shape(Type type, const glm::vec3& position, const glm::quat& rotation) - : _type(type), _boundingRadius(0.f), _position(position), _rotation(rotation) {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(position), _rotation(rotation) {} void setBoundingRadius(float radius) { _boundingRadius = radius; } int _type; - int _simulationID; // shape's simulation ID in SimulationEngine + PhysicalEntity* _owningEntity; float _boundingRadius; glm::vec3 _position; glm::quat _rotation; diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index e80d26d860..5c7acbb6a6 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -87,6 +87,9 @@ bool collideShapesCoarse(const QVector& shapesA, const QVector_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * BA; + collision->_shapeA = sphereA; + collision->_shapeB = sphereB; return true; } } @@ -179,6 +184,8 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B // contactPoint is on surface of sphereA collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * radialAxis; + collision->_shapeA = sphereA; + collision->_shapeB = capsuleB; } else { // A is on B's axis, so the penetration is undefined... if (absAxialDistance > capsuleB->getHalfHeight()) { @@ -200,6 +207,8 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; // contactPoint is on surface of sphereA collision->_contactPoint = sphereA->getPosition() + (sign * sphereA->getRadius()) * capsuleAxis; + collision->_shapeA = sphereA; + collision->_shapeB = capsuleB; } return true; } @@ -215,6 +224,8 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision } collision->_penetration = penetration; collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration); + collision->_shapeA = sphereA; + collision->_shapeB = planeB; return true; } return false; @@ -264,6 +275,8 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col 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()) { @@ -284,6 +297,8 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col 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; @@ -355,6 +370,8 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, collision->_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A collision->_contactPoint = centerA + distanceA * axisA + capsuleA->getRadius() * BA; + collision->_shapeA = capsuleA; + collision->_shapeB = capsuleB; return true; } } else { @@ -420,6 +437,8 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, // average the internal pair, and then do the math from centerB collision->_contactPoint = centerB + (0.5f * (points[1] + points[2])) * axisB + (capsuleA->getRadius() - distance) * BA; + collision->_shapeA = capsuleA; + collision->_shapeB = capsuleB; return true; } } @@ -439,6 +458,8 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis collision->_penetration = penetration; glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end; collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration); + collision->_shapeA = capsuleA; + collision->_shapeB = planeB; return true; } return false; @@ -454,6 +475,8 @@ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, Collision collision->_penetration = -penetration; collision->_contactPoint = sphereB->getPosition() + (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration; + collision->_shapeA = planeA; + collision->_shapeB = sphereB; return true; } return false; @@ -472,6 +495,8 @@ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, Collis 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; @@ -664,6 +689,8 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: } collision->_floatData = cubeSide; collision->_vecData = cubeCenter; + collision->_shapeA = NULL; + collision->_shapeB = NULL; return true; } else if (sphereRadius + halfCubeSide > distance) { // NOTE: for cocentric approximation we collide sphere and cube as two spheres which means @@ -677,6 +704,8 @@ bool sphereAACube(const glm::vec3& sphereCenter, float sphereRadius, const glm:: collision->_floatData = cubeSide; collision->_vecData = cubeCenter; + collision->_shapeA = NULL; + collision->_shapeB = NULL; return true; } } @@ -712,6 +741,8 @@ bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, collision->_penetration = glm::dot(surfaceAB, direction) * direction; // contactPoint is on surface of A collision->_contactPoint = sphereCenter + sphereRadius * direction; + collision->_shapeA = NULL; + collision->_shapeB = NULL; return true; } } @@ -724,6 +755,8 @@ bool sphereAACube_StarkAngles(const glm::vec3& sphereCenter, float sphereRadius, collision->_penetration = (sphereRadius + 0.5f * cubeSide) * glm::vec3(0.0f, -1.0f, 0.0f); // contactPoint is on surface of A collision->_contactPoint = sphereCenter + collision->_penetration; + collision->_shapeA = NULL; + collision->_shapeB = NULL; return true; } } From fb7f5707c87a8ec573b4a32debf395c86fc9a1dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Jun 2014 18:15:20 -0700 Subject: [PATCH 010/105] add SimulationEngine, MyAvatar has one. --- interface/src/avatar/MyAvatar.cpp | 3 +- interface/src/avatar/MyAvatar.h | 3 + libraries/shared/src/SimulationEngine.cpp | 77 +++++++++++++++++++++++ libraries/shared/src/SimulationEngine.h | 44 +++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 libraries/shared/src/SimulationEngine.cpp create mode 100644 libraries/shared/src/SimulationEngine.h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index bebe2fbec7..842308ecbb 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -76,7 +76,8 @@ MyAvatar::MyAvatar() : _lastFloorContactPoint(0.0f), _lookAtTargetAvatar(), _shouldRender(true), - _billboardValid(false) + _billboardValid(false), + _simulationEngine() { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d99102c356..b2b6dee7fd 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -14,6 +14,8 @@ #include +#include + #include "Avatar.h" enum AvatarHandState @@ -172,6 +174,7 @@ private: float _oculusYawOffset; QList _animationHandles; + SimulationEngine _simulationEngine; // private methods float computeDistanceToFloor(const glm::vec3& startPoint); diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp new file mode 100644 index 0000000000..e21364b17c --- /dev/null +++ b/libraries/shared/src/SimulationEngine.cpp @@ -0,0 +1,77 @@ +// +// SimulationEngine.cpp +// interface/src/avatar +// +// Created by Andrew Meadows 2014.06.06 +// 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 "SimulationEngine.h" + +int MAX_DOLLS_PER_ENGINE = 32; +int MAX_COLLISIONS_PER_ENGINE = 256; + +const int NUM_SHAPE_BITS = 6; +const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; + +SimulationEngine::SimulationEngine() : _collisionList(MAX_COLLISIONS_PER_ENGINE) { +} + +SimulationEngine::~SimulationEngine() { + _dolls.clear(); +} + +bool SimulationEngine::addRagDoll(RagDoll* doll) { + int numDolls = _dolls.size(); + if (numDolls < MAX_DOLLS_PER_ENGINE) { + for (int i = 0; i < numDolls; ++i) { + if (doll == _dolls[i]) { + // already in list + return true; + } + } + _dolls.push_back(doll); + return true; + } + return false; +} + +void SimulationEngine::removeRagDoll(RagDoll* doll) { + int numDolls = _dolls.size(); + for (int i = 0; i < numDolls; ++i) { + if (doll == _dolls[i]) { + if (i == numDolls - 1) { + // remove it + _dolls.pop_back(); + } else { + // swap the last for this one + RagDoll* lastDoll = _dolls[numDolls - 1]; + _dolls.pop_back(); + _dolls[i] = lastDoll; + } + break; + } + } +} + +float SimulationEngine::enforceConstraints() { + float maxMovement = 0.0f; + int numDolls = _dolls.size(); + for (int i = 0; i < numDolls; ++i) { + maxMovement = glm::max(maxMovement, _dolls[i]->enforceConstraints()); + } + return maxMovement; +} + +int SimulationEngine::computeCollisions() { + return 0.0f; +} + +void SimulationEngine::processCollisions() { +} + diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/SimulationEngine.h new file mode 100644 index 0000000000..6b6a225542 --- /dev/null +++ b/libraries/shared/src/SimulationEngine.h @@ -0,0 +1,44 @@ +// +// SimulationEngine.h +// interface/src/avatar +// +// Created by Andrew Meadows 2014.06.06 +// 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_SimulationEngine_h +#define hifi_SimulationEngine_h + +#include + +#include "CollisionInfo.h" +#include "RagDoll.h" + +class SimulationEngine { +public: + + SimulationEngine(); + ~SimulationEngine(); + + /// \return true if doll was added to, or already in the list + bool addRagDoll(RagDoll* doll); + + void removeRagDoll(RagDoll* doll); + + /// \return distance of largest movement + float enforceConstraints(); + + /// \return number of collisions + int computeCollisions(); + + void processCollisions(); + +private: + CollisionList _collisionList; + QVector _dolls; +}; + +#endif // hifi_SimulationEngine_h From 718b98f70ace823bbd326ab43e53e12d86ac064e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 12 Jun 2014 18:15:44 -0700 Subject: [PATCH 011/105] Only build shapes for models that need them --- interface/src/avatar/SkeletonModel.cpp | 2 ++ interface/src/renderer/Model.cpp | 44 +++++++++++++++++++++++--- interface/src/renderer/Model.h | 4 +-- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index df69ceb356..dc2c1e9112 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -37,6 +37,8 @@ void SkeletonModel::setJointStates(QVector states) { points.push_back(state.getPosition()); } // ... and feed the results to _ragDoll + // It is OK that_jointShapes is probably empty at this stage: _ragDoll keeps a + // pointer to it and things will start working as soon as the list is populated. _ragDoll.init(&_jointShapes, parentIndices, points); } } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index a1ef53163d..03e0cac620 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -117,6 +117,19 @@ Model::SkinLocations Model::_skinCascadedShadowSpecularMapLocations; Model::SkinLocations Model::_skinCascadedShadowNormalSpecularMapLocations; Model::SkinLocations Model::_skinShadowLocations; +void Model::setTranslation(const glm::vec3& translation) { + if (_translation != translation) { + _shapesAreDirty = !_jointShapes.isEmpty(); + _translation = translation; + } +} +void Model::setRotation(const glm::quat& rotation) { + if (_rotation != rotation) { + _shapesAreDirty = !_jointShapes.isEmpty(); + _rotation = rotation; + } +} + void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); // if anyone sets scale manually, then we are no longer scaled to fit @@ -131,7 +144,9 @@ void Model::setScaleInternal(const glm::vec3& scale) { const float ONE_PERCENT = 0.01f; if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; - rebuildShapes(); + if (_jointShapes.size() > 0) { + rebuildShapes(); + } } } @@ -553,7 +568,9 @@ bool Model::updateGeometry() { model->setURL(attachment.url); _attachments.append(model); } - rebuildShapes(); + if (!_jointShapes.isEmpty()) { + rebuildShapes(); + } needFullUpdate = true; } return needFullUpdate; @@ -562,6 +579,18 @@ bool Model::updateGeometry() { // virtual void Model::setJointStates(QVector states) { _jointStates = states; + + // compute an approximate bounding radius for broadphase collision queries + // against SimulationEngine boundaries + int numJoints = _jointStates.size(); + float radius = 0.0f; + for (int i = 0; i < numJoints; ++i) { + float distance = glm::length(_jointStates[i].getPosition()); + if (distance > radius) { + radius = distance; + } + } + _boundingRadius = radius; } bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { @@ -940,7 +969,7 @@ void Model::resetShapePositions() { // Moves shapes to the joint default locations for debug visibility into // how the bounding shape is computed. - if (!_geometry || _rootIndex == -1) { + if (!_geometry || _rootIndex == -1 || _jointShapes.isEmpty()) { // geometry or joints have not yet been created return; } @@ -1213,7 +1242,7 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } - _shapesAreDirty = true; + _shapesAreDirty = ! _jointShapes.isEmpty(); // update the attachment transforms and simulate them const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -1348,7 +1377,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl for (int j = freeLineage.size() - 1; j >= 0; j--) { updateJointState(freeLineage.at(j)); } - _shapesAreDirty = true; + _shapesAreDirty = !_jointShapes.isEmpty(); return true; } @@ -1435,6 +1464,11 @@ void Model::renderJointCollisionShapes(float alpha) { } void Model::renderBoundingCollisionShapes(float alpha) { + if (_jointShapes.isEmpty()) { + // the bounding shape has not been propery computed + // so no need to render it + return; + } glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index cafca9abdd..64868c6751 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -42,10 +42,10 @@ public: Model(QObject* parent = NULL); virtual ~Model(); - void setTranslation(const glm::vec3& translation) { _translation = translation; } + void setTranslation(const glm::vec3& translation); const glm::vec3& getTranslation() const { return _translation; } - void setRotation(const glm::quat& rotation) { _rotation = rotation; } + void setRotation(const glm::quat& rotation); const glm::quat& getRotation() const { return _rotation; } /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension From 417a7def075187ecbc0d2c0c0bb0e6089675d549 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Jun 2014 11:10:32 -0700 Subject: [PATCH 012/105] SimulationEngine::enforceConstraints() takes args ...for limiting simulation costs --- libraries/shared/src/SimulationEngine.cpp | 28 +++++++++++++++++------ libraries/shared/src/SimulationEngine.h | 14 +++++++++++- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index e21364b17c..5cc7cbcc5a 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -11,6 +11,7 @@ #include +#include "SharedUtil.h" #include "SimulationEngine.h" int MAX_DOLLS_PER_ENGINE = 32; @@ -59,13 +60,26 @@ void SimulationEngine::removeRagDoll(RagDoll* doll) { } } -float SimulationEngine::enforceConstraints() { - float maxMovement = 0.0f; - int numDolls = _dolls.size(); - for (int i = 0; i < numDolls; ++i) { - maxMovement = glm::max(maxMovement, _dolls[i]->enforceConstraints()); - } - return maxMovement; +void SimulationEngine::enforceConstraints(float minError, int maxIterations, quint64 maxUsec) { + // enforce the constraints + int iterations = 0; + float delta = 0.0f; + quint64 now = usecTimestampNow(); + quint64 startTime = now; + quint64 expiry = now + maxUsec; + float error = 0.0f; + do { + error = 0.0f; + int numDolls = _dolls.size(); + for (int i = 0; i < numDolls; ++i) { + error = glm::max(error, _dolls[i]->enforceConstraints()); + } + ++iterations; + now = usecTimestampNow(); + } while (iterations < maxIterations && delta > minError && now < expiry); + _enforcementIterations = iterations; + _enforcementError = delta; + _enforcementTime = now - startTime; } int SimulationEngine::computeCollisions() { diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/SimulationEngine.h index 6b6a225542..d7e4468392 100644 --- a/libraries/shared/src/SimulationEngine.h +++ b/libraries/shared/src/SimulationEngine.h @@ -28,8 +28,15 @@ public: void removeRagDoll(RagDoll* doll); + /// \param minError constraint motion below this value is considered "close enough" + /// \param maxIterations max number of iterations before giving up + /// \param maxUsec max number of usec to spend enforcing constraints /// \return distance of largest movement - float enforceConstraints(); + void enforceConstraints(float minError, int maxIterations, quint64 maxUsec); + + int getEnforementIterations() const { return _enforcementIterations; } + float getEnforcementError() const { return _enforcementError; } + quint64 getEnforcementTime() const { return _enforcementTime; } /// \return number of collisions int computeCollisions(); @@ -39,6 +46,11 @@ public: private: CollisionList _collisionList; QVector _dolls; + + // some stats for performance queries + int _enforcementIterations; + float _enforcementError; + quint64 _enforcementTime; }; #endif // hifi_SimulationEngine_h From 5bd37acdb86ad31333ef52aff307e5ccdb60c799 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Jun 2014 11:55:39 -0700 Subject: [PATCH 013/105] Changed RagDoll to Ragdoll --- interface/src/Menu.cpp | 2 +- interface/src/Menu.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 10 +++++----- interface/src/avatar/SkeletonModel.h | 8 ++++---- .../shared/src/{RagDoll.cpp => Ragdoll.cpp} | 18 +++++++++--------- libraries/shared/src/{RagDoll.h => Ragdoll.h} | 16 ++++++++-------- libraries/shared/src/SimulationEngine.cpp | 6 +++--- libraries/shared/src/SimulationEngine.h | 8 ++++---- 8 files changed, 35 insertions(+), 35 deletions(-) rename libraries/shared/src/{RagDoll.cpp => Ragdoll.cpp} (94%) rename libraries/shared/src/{RagDoll.h => Ragdoll.h} (92%) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index f791d20588..d6257ef8d6 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -353,7 +353,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagDoll); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollideAsRagdoll); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 69015a938b..e8be04f3e2 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -311,7 +311,7 @@ namespace MenuOption { const QString CascadedShadows = "Cascaded"; const QString Chat = "Chat..."; const QString ChatCircling = "Chat Circling"; - const QString CollideAsRagDoll = "Collide As RagDoll"; + const QString CollideAsRagdoll = "Collide As Ragdoll"; const QString CollideWithAvatars = "Collide With Avatars"; const QString CollideWithEnvironment = "Collide With World Boundaries"; const QString CollideWithParticles = "Collide With Particles"; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index dc2c1e9112..ad8f4cfcaf 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -101,10 +101,10 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } - simulateRagDoll(deltaTime); + simulateRagdoll(deltaTime); } -void SkeletonModel::simulateRagDoll(float deltaTime) { +void SkeletonModel::simulateRagdoll(float deltaTime) { // move ragdoll points toward joints QVector& points = _ragDoll.getPoints(); const int numStates = _jointStates.size(); @@ -171,7 +171,7 @@ void SkeletonModel::renderIKConstraints() { renderJointConstraints(getRightHandJointIndex()); renderJointConstraints(getLeftHandJointIndex()); //if (isActive() && _owningAvatar->isMyAvatar()) { - // renderRagDoll(); + // renderRagdoll(); //} } @@ -272,7 +272,7 @@ void SkeletonModel::updateJointState(int index) { void SkeletonModel::updateShapePositions() { if (isActive() && _owningAvatar->isMyAvatar() && - Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagDoll)) { + Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { _ragDoll.updateShapes(_rotation, _translation); } else { Model::updateShapePositions(); @@ -498,7 +498,7 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco return false; } -void SkeletonModel::renderRagDoll() { +void SkeletonModel::renderRagdoll() { const int BALL_SUBDIVISIONS = 6; glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 9f034b2aa3..cf59adc5ce 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -14,7 +14,7 @@ #include "renderer/Model.h" -#include +#include class Avatar; @@ -29,7 +29,7 @@ public: void setJointStates(QVector states); void simulate(float deltaTime, bool fullUpdate = true); - void simulateRagDoll(float deltaTime); + void simulateRagdoll(float deltaTime); void updateShapePositions(); /// \param jointIndex index of hand joint @@ -95,7 +95,7 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - void renderRagDoll(); + void renderRagdoll(); protected: /// \param jointIndex index of joint in model @@ -121,7 +121,7 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); Avatar* _owningAvatar; - RagDoll _ragDoll; + Ragdoll _ragDoll; }; #endif // hifi_SkeletonModel_h diff --git a/libraries/shared/src/RagDoll.cpp b/libraries/shared/src/Ragdoll.cpp similarity index 94% rename from libraries/shared/src/RagDoll.cpp rename to libraries/shared/src/Ragdoll.cpp index 67cb368a02..30806db7c8 100644 --- a/libraries/shared/src/RagDoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -1,5 +1,5 @@ // -// RagDoll.cpp +// Ragdoll.cpp // libraries/shared/src // // Created by Andrew Meadows 2014.05.30 @@ -9,7 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "RagDoll.h" +#include "Ragdoll.h" #include "CapsuleShape.h" #include "CollisionInfo.h" @@ -91,17 +91,17 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio } // ---------------------------------------------------------------------------- -// RagDoll +// Ragdoll // ---------------------------------------------------------------------------- -RagDoll::RagDoll() : PhysicalEntity(PhysicalEntity::ENTITY_RAGDOLL), _shapes(NULL) { +Ragdoll::Ragdoll() : PhysicalEntity(PhysicalEntity::ENTITY_RAGDOLL), _shapes(NULL) { } -RagDoll::~RagDoll() { +Ragdoll::~Ragdoll() { clear(); } -void RagDoll::init(QVector* shapes, const QVector& parentIndices, const QVector& points) { +void Ragdoll::init(QVector* shapes, const QVector& parentIndices, const QVector& points) { clear(); _shapes = shapes; const int numPoints = points.size(); @@ -127,7 +127,7 @@ void RagDoll::init(QVector* shapes, const QVector& parentIndices, c } /// Delete all data. -void RagDoll::clear() { +void Ragdoll::clear() { int numConstraints = _constraints.size(); for (int i = 0; i < numConstraints; ++i) { delete _constraints[i]; @@ -140,7 +140,7 @@ void RagDoll::clear() { _shapes = NULL; } -float RagDoll::enforceConstraints() { +float Ragdoll::enforceConstraints() { float maxDistance = 0.0f; const int numConstraints = _constraints.size(); for (int i = 0; i < numConstraints; ++i) { @@ -151,7 +151,7 @@ float RagDoll::enforceConstraints() { return maxDistance; } -void RagDoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const { +void Ragdoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const { if (!_shapes) { return; } diff --git a/libraries/shared/src/RagDoll.h b/libraries/shared/src/Ragdoll.h similarity index 92% rename from libraries/shared/src/RagDoll.h rename to libraries/shared/src/Ragdoll.h index 5a7387102a..edc6efc649 100644 --- a/libraries/shared/src/RagDoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -1,5 +1,5 @@ // -// RagDoll.h +// Ragdoll.h // libraries/shared/src // // Created by Andrew Meadows 2014.05.30 @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_RagDoll_h -#define hifi_RagDoll_h +#ifndef hifi_Ragdoll_h +#define hifi_Ragdoll_h #include #include @@ -61,11 +61,11 @@ private: glm::vec3* _points[2]; }; -class RagDoll : public PhysicalEntity { +class Ragdoll : public PhysicalEntity { public: - RagDoll(); - virtual ~RagDoll(); + Ragdoll(); + virtual ~Ragdoll(); /// Create points and constraints based on topology of collection of joints /// \param joints list of connected joint states @@ -93,8 +93,8 @@ private: QVector _points; QVector _constraints; - // the RagDoll does NOT own the data in _shapes. + // the Ragdoll does NOT own the data in _shapes. QVector* _shapes; }; -#endif // hifi_RagDoll_h +#endif // hifi_Ragdoll_h diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index 5cc7cbcc5a..c314ba1bc0 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -27,7 +27,7 @@ SimulationEngine::~SimulationEngine() { _dolls.clear(); } -bool SimulationEngine::addRagDoll(RagDoll* doll) { +bool SimulationEngine::addRagdoll(Ragdoll* doll) { int numDolls = _dolls.size(); if (numDolls < MAX_DOLLS_PER_ENGINE) { for (int i = 0; i < numDolls; ++i) { @@ -42,7 +42,7 @@ bool SimulationEngine::addRagDoll(RagDoll* doll) { return false; } -void SimulationEngine::removeRagDoll(RagDoll* doll) { +void SimulationEngine::removeRagdoll(Ragdoll* doll) { int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { if (doll == _dolls[i]) { @@ -51,7 +51,7 @@ void SimulationEngine::removeRagDoll(RagDoll* doll) { _dolls.pop_back(); } else { // swap the last for this one - RagDoll* lastDoll = _dolls[numDolls - 1]; + Ragdoll* lastDoll = _dolls[numDolls - 1]; _dolls.pop_back(); _dolls[i] = lastDoll; } diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/SimulationEngine.h index d7e4468392..9dfad79d3f 100644 --- a/libraries/shared/src/SimulationEngine.h +++ b/libraries/shared/src/SimulationEngine.h @@ -15,7 +15,7 @@ #include #include "CollisionInfo.h" -#include "RagDoll.h" +#include "Ragdoll.h" class SimulationEngine { public: @@ -24,9 +24,9 @@ public: ~SimulationEngine(); /// \return true if doll was added to, or already in the list - bool addRagDoll(RagDoll* doll); + bool addRagdoll(Ragdoll* doll); - void removeRagDoll(RagDoll* doll); + void removeRagdoll(Ragdoll* doll); /// \param minError constraint motion below this value is considered "close enough" /// \param maxIterations max number of iterations before giving up @@ -45,7 +45,7 @@ public: private: CollisionList _collisionList; - QVector _dolls; + QVector _dolls; // some stats for performance queries int _enforcementIterations; From 158c7de76e8b540c0b2be3d20c8a14bbdd3d8559 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 13 Jun 2014 12:27:58 -0700 Subject: [PATCH 014/105] Ragdoll not an Entity. SkeletonModel is a Ragdoll --- interface/src/avatar/SkeletonModel.cpp | 26 ++++++++++++-------------- interface/src/avatar/SkeletonModel.h | 5 ++--- libraries/shared/src/PhysicalEntity.h | 1 - libraries/shared/src/Ragdoll.cpp | 10 ++-------- libraries/shared/src/Ragdoll.h | 10 ++++------ 5 files changed, 20 insertions(+), 32 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ad8f4cfcaf..16aae47cdc 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -17,7 +17,9 @@ #include "Menu.h" #include "SkeletonModel.h" -SkeletonModel::SkeletonModel(Avatar* owningAvatar) : +SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : + Model(parent), + Ragdoll(), _owningAvatar(owningAvatar) { } @@ -36,10 +38,8 @@ void SkeletonModel::setJointStates(QVector states) { parentIndices.push_back(state.getFBXJoint().parentIndex); points.push_back(state.getPosition()); } - // ... and feed the results to _ragDoll - // It is OK that_jointShapes is probably empty at this stage: _ragDoll keeps a - // pointer to it and things will start working as soon as the list is populated. - _ragDoll.init(&_jointShapes, parentIndices, points); + // ... and feed the results to Ragdoll + initShapesAndPoints(&_jointShapes, parentIndices, points); } } @@ -105,14 +105,13 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::simulateRagdoll(float deltaTime) { - // move ragdoll points toward joints - QVector& points = _ragDoll.getPoints(); + // move ragdoll _points toward joints const int numStates = _jointStates.size(); - assert(numStates == points.size()); + assert(numStates == _points.size()); float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes float oneMinusFraction = 1.0f - fraction; for (int i = 0; i < numStates; ++i) { - points[i] = oneMinusFraction * points[i] + fraction * _jointStates[i].getPosition(); + _points[i] = oneMinusFraction * _points[i] + fraction * _jointStates[i].getPosition(); } // enforce the constraints @@ -121,7 +120,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) { int iterations = 0; float delta = 0.0f; do { - delta = _ragDoll.enforceConstraints(); + delta = enforceConstraints(); ++iterations; } while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS); } @@ -273,7 +272,7 @@ void SkeletonModel::updateJointState(int index) { void SkeletonModel::updateShapePositions() { if (isActive() && _owningAvatar->isMyAvatar() && Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - _ragDoll.updateShapes(_rotation, _translation); + updateShapes(_rotation, _translation); } else { Model::updateShapePositions(); } @@ -505,15 +504,14 @@ void SkeletonModel::renderRagdoll() { glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); - QVector points = _ragDoll.getPoints(); - int numPoints = points.size(); + int numPoints = _points.size(); float alpha = 0.3f; float radius1 = 0.008f; float radius2 = 0.01f; for (int i = 0; i < numPoints; ++i) { glPushMatrix(); // draw each point as a yellow hexagon with black border - glm::vec3 position = _rotation * points[i]; + glm::vec3 position = _rotation * _points[i]; glTranslatef(position.x, position.y, position.z); glColor4f(0.0f, 0.0f, 0.0f, alpha); glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index cf59adc5ce..f0982e6dd7 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -19,12 +19,12 @@ class Avatar; /// A skeleton loaded from a model. -class SkeletonModel : public Model { +class SkeletonModel : public Model, public Ragdoll { Q_OBJECT public: - SkeletonModel(Avatar* owningAvatar); + SkeletonModel(Avatar* owningAvatar, QObject* parent = NULL); void setJointStates(QVector states); @@ -121,7 +121,6 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); Avatar* _owningAvatar; - Ragdoll _ragDoll; }; #endif // hifi_SkeletonModel_h diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h index 83469d4775..d1d11a0d6c 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicalEntity.h @@ -26,7 +26,6 @@ public: enum EntityType { ENTITY_UNKNOWN, ENTITY_MODEL, - ENTITY_RAGDOLL, }; static void setShapeBackPointers(const QVector& shapes, PhysicalEntity* entity); diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 30806db7c8..ee67f94b10 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -94,14 +94,14 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio // Ragdoll // ---------------------------------------------------------------------------- -Ragdoll::Ragdoll() : PhysicalEntity(PhysicalEntity::ENTITY_RAGDOLL), _shapes(NULL) { +Ragdoll::Ragdoll() : _shapes(NULL) { } Ragdoll::~Ragdoll() { clear(); } -void Ragdoll::init(QVector* shapes, const QVector& parentIndices, const QVector& points) { +void Ragdoll::initShapesAndPoints(QVector* shapes, const QVector& parentIndices, const QVector& points) { clear(); _shapes = shapes; const int numPoints = points.size(); @@ -121,9 +121,6 @@ void Ragdoll::init(QVector* shapes, const QVector& parentIndices, c _constraints.push_back(stick); } } - if (_shapes) { - PhysicalEntity::setShapeBackPointers(*_shapes, this); - } } /// Delete all data. @@ -134,9 +131,6 @@ void Ragdoll::clear() { } _constraints.clear(); _points.clear(); - if (_shapes) { - PhysicalEntity::setShapeBackPointers(*_shapes, NULL); - } _shapes = NULL; } diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index edc6efc649..7a5115142d 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -17,7 +17,7 @@ #include -#include "PhysicalEntity.h" +class Shape; class Constraint { public: @@ -61,7 +61,7 @@ private: glm::vec3* _points[2]; }; -class Ragdoll : public PhysicalEntity { +class Ragdoll { public: Ragdoll(); @@ -69,9 +69,7 @@ public: /// Create points and constraints based on topology of collection of joints /// \param joints list of connected joint states - void init(QVector* shapes, const QVector& parentIndices, const QVector& points); - - void setShapes(QVector* shapes) { _shapes = shapes; } + void initShapesAndPoints(QVector* shapes, const QVector& parentIndices, const QVector& points); /// Delete all data. void clear(); @@ -89,7 +87,7 @@ public: /// Moves and modifies elements of _shapes to agree with state of _points void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const; -private: +protected: QVector _points; QVector _constraints; From 6d279872264bfb21b27c73c5c3487737ae216628 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 15 Jun 2014 17:56:02 -0700 Subject: [PATCH 015/105] Add shape vs list-of-shapes collision method and list-of-shapes vs list-of-shapes --- libraries/shared/src/ShapeCollider.cpp | 40 +++++++++++++++++++++++++- libraries/shared/src/ShapeCollider.h | 5 ++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 5c7acbb6a6..f29be3d00e 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -74,7 +74,7 @@ bool collideShapesCoarse(const QVector& shapesA, const QVector 0) { @@ -95,6 +95,44 @@ bool collideShapesCoarse(const QVector& shapesA, const QVector& shapes, int startIndex, CollisionList& collisions) { + bool collided = false; + if (shapeA) { + int numShapes = shapes.size(); + for (int i = startIndex; i < numShapes; ++i) { + const Shape* shapeB = shapes.at(i); + if (!shapeB) { + continue; + } + if (collideShapes(shapeA, shapeB, collisions)) { + collided = true; + if (collisions.isFull()) { + break; + } + } + } + } + return collided; +} + +bool collideShapesWithShapes(const QVector& shapesA, const QVector& shapesB, CollisionList& collisions) { + bool collided = false; + int numShapesA = shapesA.size(); + for (int i = 0; i < numShapesA; ++i) { + Shape* shapeA = shapesA.at(i); + if (!shapeA) { + continue; + } + if (collideShapeWithShapes(shapeA, shapesB, 0, collisions)) { + collided = true; + if (collisions.isFull()) { + break; + } + } + } + return collided; +} + bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions) { int typeA = shapeA->getType(); if (typeA == Shape::SPHERE_SHAPE) { diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h index 9e83e31571..b76697e77e 100644 --- a/libraries/shared/src/ShapeCollider.h +++ b/libraries/shared/src/ShapeCollider.h @@ -12,6 +12,8 @@ #ifndef hifi_ShapeCollider_h #define hifi_ShapeCollider_h +#include + #include "CapsuleShape.h" #include "CollisionInfo.h" #include "ListShape.h" @@ -33,6 +35,9 @@ namespace ShapeCollider { /// \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); + /// \param shapeA a pointer to a shape /// \param cubeCenter center of cube /// \param cubeSide lenght of side of cube From 7530f8ec1e82f11c7819362e2951d43bf1d927ec Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 15 Jun 2014 17:57:42 -0700 Subject: [PATCH 016/105] add way to enable Model for collisions --- interface/src/renderer/Model.cpp | 14 +++++++++++++- interface/src/renderer/Model.h | 6 +++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 03e0cac620..6bd04d18cf 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -43,6 +43,7 @@ Model::Model(QObject* parent) : _snappedToCenter(false), _rootIndex(-1), _shapesAreDirty(true), + _enableCollisionShapes(false), _boundingRadius(0.0f), _boundingShape(), _boundingShapeLocalOffset(0.0f), @@ -201,6 +202,17 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { return jointStates; } +void Model::setEnableCollisionShapes(bool enable) { + if (enable != _enableCollisionShapes) { + _enableCollisionShapes = enable; + if (_enableCollisionShapes) { + rebuildShapes(); + } else { + clearShapes(); + } + } +} + void Model::init() { if (!_program.isLinked()) { _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -568,7 +580,7 @@ bool Model::updateGeometry() { model->setURL(attachment.url); _attachments.append(model); } - if (!_jointShapes.isEmpty()) { + if (_enableCollisionShapes) { rebuildShapes(); } needFullUpdate = true; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 64868c6751..04c457f265 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -68,6 +68,8 @@ public: void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } + + void setEnableCollisionShapes(bool enable); bool isActive() const { return _geometry && _geometry->isLoaded(); } @@ -186,8 +188,10 @@ protected: bool _snappedToCenter; /// are we currently snapped to center int _rootIndex; - bool _shapesAreDirty; QVector _jointStates; + + bool _shapesAreDirty; + bool _enableCollisionShapes; /// build collision shapes for joints QVector _jointShapes; float _boundingRadius; From 6f4a4ace0d1f4b4a4c608fa7b98d947877a7868b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 15 Jun 2014 17:58:27 -0700 Subject: [PATCH 017/105] add API stubs to RagDoll for SimulationEngine stepForward --- libraries/shared/src/Ragdoll.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 7a5115142d..996e65d954 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -74,6 +74,9 @@ public: /// Delete all data. void clear(); + // TODO: Andrew to implement this + void stepForward(float deltaTime) {} + /// Enforce contraints. /// \return max distance of point movement float enforceConstraints(); @@ -87,6 +90,8 @@ public: /// Moves and modifies elements of _shapes to agree with state of _points void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const; + const QVector* getShapes() const { return _shapes; } + protected: QVector _points; QVector _constraints; From d7a28e1441bf6db1e24a19093de79c1c5dacecb8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 15 Jun 2014 18:07:47 -0700 Subject: [PATCH 018/105] enable shape collisions for MyAvatar skeleton also some stubbery for main simulation loop --- interface/src/avatar/MyAvatar.cpp | 11 ++++ interface/src/avatar/SkeletonModel.cpp | 3 +- libraries/shared/src/SimulationEngine.cpp | 76 ++++++++++++++++++----- libraries/shared/src/SimulationEngine.h | 2 +- 4 files changed, 76 insertions(+), 16 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 842308ecbb..8bd3288ee7 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -82,6 +82,8 @@ MyAvatar::MyAvatar() : for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } + _skeletonModel.setEnableCollisionShapes(true); + _simulationEngine.addRagdoll(&_skeletonModel); } MyAvatar::~MyAvatar() { @@ -190,6 +192,14 @@ void MyAvatar::simulate(float deltaTime) { head->simulate(deltaTime, true); } + if (!Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); + const int minError = 0.005f; + const float maxIterations = 4; + const quint64 maxUsec = 500; + _simulationEngine.stepForward(deltaTime, minError, maxIterations, maxUsec); + } + /* TODO: Andrew to make this work again // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionGroups != 0) { PerformanceTimer perfTimer("MyAvatar::simulate/_collisionGroups"); @@ -216,6 +226,7 @@ void MyAvatar::simulate(float deltaTime) { updateCollisionWithAvatars(deltaTime); } } + */ // consider updating our billboard maybeUpdateBillboard(); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 16aae47cdc..74625cf6b2 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -272,7 +272,8 @@ void SkeletonModel::updateJointState(int index) { void SkeletonModel::updateShapePositions() { if (isActive() && _owningAvatar->isMyAvatar() && Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - updateShapes(_rotation, _translation); + // TODO: Andrew to move shape updates to SimulationEngine + //updateShapes(_rotation, _translation); } else { Model::updateShapePositions(); } diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index c314ba1bc0..cf3baebec0 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -12,6 +12,7 @@ #include #include "SharedUtil.h" +#include "ShapeCollider.h" #include "SimulationEngine.h" int MAX_DOLLS_PER_ENGINE = 32; @@ -28,18 +29,23 @@ SimulationEngine::~SimulationEngine() { } bool SimulationEngine::addRagdoll(Ragdoll* doll) { - int numDolls = _dolls.size(); - if (numDolls < MAX_DOLLS_PER_ENGINE) { - for (int i = 0; i < numDolls; ++i) { - if (doll == _dolls[i]) { - // already in list - return true; - } - } - _dolls.push_back(doll); - return true; + if (!doll) { + return false; } - return false; + int numDolls = _dolls.size(); + if (numDolls > MAX_DOLLS_PER_ENGINE) { + // list is full + return false; + } + for (int i = 0; i < numDolls; ++i) { + if (doll == _dolls[i]) { + // already in list + return true; + } + } + // add to list + _dolls.push_back(doll); + return true; } void SimulationEngine::removeRagdoll(Ragdoll* doll) { @@ -60,17 +66,59 @@ void SimulationEngine::removeRagdoll(Ragdoll* doll) { } } -void SimulationEngine::enforceConstraints(float minError, int maxIterations, quint64 maxUsec) { - // enforce the constraints +void SimulationEngine::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { int iterations = 0; float delta = 0.0f; quint64 now = usecTimestampNow(); quint64 startTime = now; quint64 expiry = now + maxUsec; + + int numDolls = _dolls.size(); + for (int i = 0; i < numDolls; ++i) { + // TODO: need to implement: + // (1) joints pull points (SpecialCapsuleShape would help solve this) + // (2) points slam shapes (SpecialCapsuleShape would help solve this) + // (3) shapes collide with pairwise collision bypass + // (4) collisions move points (SpecialCapsuleShape would help solve this) + // (5) enforce constraints + // (6) add and enforce angular contraints for joints + _dolls.at(i)->stepForward(deltaTime); + } + + + // collide + _collisionList.clear(); + // TODO: keep track of QSet collidedEntities; + for (int i = 0; i < numDolls; ++i) { + const QVector* shapesA = _dolls.at(i)->getShapes(); + if (!shapesA) { + continue; + } + int numShapesA = shapesA->size(); + // collide with self + for (int j = 0; j < numShapesA; ++j) { + const Shape* shapeA = shapesA->at(j); + if (!shapeA) { + continue; + } + // TODO: check for pairwise collision bypass here + ShapeCollider::collideShapeWithShapes(shapeA, *shapesA, j+1, _collisionList); + } + + // collide with others + for (int j = i+1; j < numDolls; ++j) { + const QVector* shapesB = _dolls.at(j)->getShapes(); + if (!shapesB) { + continue; + } + ShapeCollider::collideShapesWithShapes(*shapesA, *shapesB, _collisionList); + } + } + + // enforce constraints float error = 0.0f; do { error = 0.0f; - int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { error = glm::max(error, _dolls[i]->enforceConstraints()); } diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/SimulationEngine.h index 9dfad79d3f..f2775259cc 100644 --- a/libraries/shared/src/SimulationEngine.h +++ b/libraries/shared/src/SimulationEngine.h @@ -32,7 +32,7 @@ public: /// \param maxIterations max number of iterations before giving up /// \param maxUsec max number of usec to spend enforcing constraints /// \return distance of largest movement - void enforceConstraints(float minError, int maxIterations, quint64 maxUsec); + void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec); int getEnforementIterations() const { return _enforcementIterations; } float getEnforcementError() const { return _enforcementError; } From 92159a7ed5fe99d020317eb21f0222f91a48e8e6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jun 2014 08:43:27 -0700 Subject: [PATCH 019/105] Shape::get/setCenter() instead of get/setPosition() --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/renderer/Model.cpp | 14 ++--- libraries/octree/src/Octree.cpp | 2 +- libraries/shared/src/ListShape.cpp | 4 +- libraries/shared/src/ListShape.h | 1 + libraries/shared/src/Ragdoll.cpp | 2 +- libraries/shared/src/Shape.h | 7 ++- libraries/shared/src/ShapeCollider.cpp | 36 ++++++------ libraries/shared/src/SimulationEngine.cpp | 2 +- tests/physics/src/ShapeColliderTests.cpp | 68 +++++++++++------------ 10 files changed, 72 insertions(+), 68 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8bd3288ee7..403d0596c0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1245,7 +1245,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { float capsuleHalfHeight = boundingShape.getHalfHeight(); const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; const float MIN_STEP_HEIGHT = 0.0f; - glm::vec3 footBase = boundingShape.getPosition() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; + glm::vec3 footBase = boundingShape.getCenter() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; float highestStep = 0.0f; float lowestStep = MAX_STEP_HEIGHT; glm::vec3 floorPoint; @@ -1262,7 +1262,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { isTrapped = true; if (_trapDuration > MAX_TRAP_PERIOD) { - float distance = glm::dot(boundingShape.getPosition() - cubeCenter, _worldUpDirection); + float distance = glm::dot(boundingShape.getCenter() - cubeCenter, _worldUpDirection); if (distance < 0.0f) { distance = fabsf(distance) + 0.5f * cubeSide; } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 6bd04d18cf..c0ed002b69 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -924,7 +924,7 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { const FBXJoint& joint = geometry.joints[i]; glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; - shape->setPosition(localPosition); + shape->setCenter(localPosition); shape->setRotation(finalRotations[i] * joint.shapeRotation); float distance = glm::length(localPosition) + shape->getBoundingRadius(); if (distance > _boundingRadius) { @@ -943,7 +943,7 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { } Extents shapeExtents; shapeExtents.reset(); - glm::vec3 localPosition = shape->getPosition(); + glm::vec3 localPosition = shape->getCenter(); int type = shape->getType(); if (type == Shape::CAPSULE_SHAPE) { // add the two furthest surface points of the capsule @@ -998,11 +998,11 @@ void Model::resetShapePositions() { for (int i = 0; i < _jointShapes.size(); i++) { Shape* shape = _jointShapes[i]; if (shape) { - shape->setPosition(_translation + _rotation * shape->getPosition()); + shape->setCenter(_translation + _rotation * shape->getCenter()); shape->setRotation(_rotation * shape->getRotation()); } } - _boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset); + _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } @@ -1020,7 +1020,7 @@ void Model::updateShapePositions() { glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); Shape* shape = _jointShapes[i]; if (shape) { - shape->setPosition(worldPosition); + shape->setCenter(worldPosition); shape->setRotation(_rotation * stateRotation * joint.shapeRotation); float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); if (distance > _boundingRadius) { @@ -1032,7 +1032,7 @@ void Model::updateShapePositions() { } } _shapesAreDirty = false; - _boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset); + _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } } @@ -1436,7 +1436,7 @@ void Model::renderJointCollisionShapes(float alpha) { glPushMatrix(); if (shape->getType() == Shape::SPHERE_SHAPE) { // shapes are stored in world-frame, so we have to transform into model frame - glm::vec3 position = shape->getPosition() - _translation; + glm::vec3 position = shape->getCenter() - _translation; glTranslatef(position.x, position.y, position.z); const glm::quat& rotation = shape->getRotation(); glm::vec3 axis = glm::axis(rotation); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index cbdc4753dc..f43921e0d1 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -757,7 +757,7 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { // coarse check against bounds AACube cube = element->getAACube(); cube.scale(TREE_SCALE); - if (!cube.expandedContains(args->shape->getPosition(), args->shape->getBoundingRadius())) { + if (!cube.expandedContains(args->shape->getCenter(), args->shape->getBoundingRadius())) { return false; } if (!element->isLeaf()) { diff --git a/libraries/shared/src/ListShape.cpp b/libraries/shared/src/ListShape.cpp index dcea97826e..162ee111c7 100644 --- a/libraries/shared/src/ListShape.cpp +++ b/libraries/shared/src/ListShape.cpp @@ -14,7 +14,7 @@ // ListShapeEntry void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) { - _shape->setPosition(rootPosition + rootRotation * _localPosition); + _shape->setCenter(rootPosition + rootRotation * _localPosition); _shape->setRotation(_localRotation * rootRotation); } @@ -26,7 +26,7 @@ ListShape::~ListShape() { void ListShape::setPosition(const glm::vec3& position) { _subShapeTransformsAreDirty = true; - Shape::setPosition(position); + Shape::setCenter(position); } void ListShape::setRotation(const glm::quat& rotation) { diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h index 7ba2410a23..87464779ce 100644 --- a/libraries/shared/src/ListShape.h +++ b/libraries/shared/src/ListShape.h @@ -43,6 +43,7 @@ public: ~ListShape(); void setPosition(const glm::vec3& position); + glm::vec3 getPosition() const { return _position; } void setRotation(const glm::quat& rotation); const Shape* getSubShape(int index) const; diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index ee67f94b10..61e90fa39c 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -76,7 +76,7 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio case Shape::SPHERE_SHAPE: { // sphere collides at endPoint SphereShape* sphere = static_cast(shape); - sphere->setPosition(translation + rotation * (*_points[1])); + sphere->setCenter(translation + rotation * (*_points[1])); } break; case Shape::CAPSULE_SHAPE: { diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 7c8a3abc76..f1a36adfb6 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -32,15 +32,18 @@ public: int getType() const { return _type; } float getBoundingRadius() const { return _boundingRadius; } - const glm::vec3& getPosition() const { return _position; } +// const glm::vec3& getPosition() const { return _position; } const glm::quat& getRotation() const { return _rotation; } - virtual void setPosition(const glm::vec3& position) { _position = position; } +// virtual void setPosition(const glm::vec3& position) { _position = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } PhysicalEntity* getEntity() const { return _owningEntity; } + virtual void setCenter(const glm::vec3& center) { _position = center; } + virtual glm::vec3 getCenter() const { return _position; } + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(0.f), _rotation() {} diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index f29be3d00e..83105bb6bf 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -157,7 +157,7 @@ bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, fl } bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions) { - glm::vec3 BA = sphereB->getPosition() - sphereA->getPosition(); + glm::vec3 BA = sphereB->getCenter() - sphereA->getCenter(); float distanceSquared = glm::dot(BA, BA); float totalRadius = sphereA->getRadius() + sphereB->getRadius(); if (distanceSquared < totalRadius * totalRadius) { @@ -175,7 +175,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis if (collision) { collision->_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A - collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * BA; + collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * BA; collision->_shapeA = sphereA; collision->_shapeB = sphereB; return true; @@ -186,7 +186,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions) { // find sphereA's closest approach to axis of capsuleB - glm::vec3 BA = capsuleB->getPosition() - sphereA->getPosition(); + glm::vec3 BA = capsuleB->getCenter() - sphereA->getCenter(); glm::vec3 capsuleAxis; capsuleB->computeNormalizedAxis(capsuleAxis); float axialDistance = - glm::dot(BA, capsuleAxis); @@ -221,7 +221,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col // penetration points from A into B collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * radialAxis; + collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * radialAxis; collision->_shapeA = sphereA; collision->_shapeB = capsuleB; } else { @@ -244,7 +244,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col float sign = (axialDistance > 0.0f) ? -1.0f : 1.0f; collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getPosition() + (sign * sphereA->getRadius()) * capsuleAxis; + collision->_contactPoint = sphereA->getCenter() + (sign * sphereA->getRadius()) * capsuleAxis; collision->_shapeA = sphereA; collision->_shapeB = capsuleB; } @@ -255,13 +255,13 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions) { glm::vec3 penetration; - if (findSpherePlanePenetration(sphereA->getPosition(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { + if (findSpherePlanePenetration(sphereA->getCenter(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { CollisionInfo* collision = collisions.getNewCollision(); if (!collision) { return false; // collision list is full } collision->_penetration = penetration; - collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration); + collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * glm::normalize(penetration); collision->_shapeA = sphereA; collision->_shapeB = planeB; return true; @@ -271,7 +271,7 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions) { // find sphereB's closest approach to axis of capsuleA - glm::vec3 AB = capsuleA->getPosition() - sphereB->getPosition(); + glm::vec3 AB = capsuleA->getCenter() - sphereB->getCenter(); glm::vec3 capsuleAxis; capsuleA->computeNormalizedAxis(capsuleAxis); float axialDistance = - glm::dot(AB, capsuleAxis); @@ -287,14 +287,14 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col } // closestApproach = point on capsuleA's axis that is closest to sphereB's center - glm::vec3 closestApproach = capsuleA->getPosition() + axialDistance * capsuleAxis; + glm::vec3 closestApproach = capsuleA->getCenter() + 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->getPosition() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; - radialAxis = closestApproach - sphereB->getPosition(); + closestApproach = capsuleA->getCenter() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; + radialAxis = closestApproach - sphereB->getCenter(); radialDistance2 = glm::length2(radialAxis); if (radialDistance2 > totalRadius2) { return false; @@ -349,8 +349,8 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, capsuleA->computeNormalizedAxis(axisA); glm::vec3 axisB; capsuleB->computeNormalizedAxis(axisB); - glm::vec3 centerA = capsuleA->getPosition(); - glm::vec3 centerB = capsuleB->getPosition(); + glm::vec3 centerA = capsuleA->getCenter(); + glm::vec3 centerB = capsuleB->getCenter(); // NOTE: The formula for closest approach between two lines is: // d = [(B - A) . (a - (a.b)b)] / (1 - (a.b)^2) @@ -505,13 +505,13 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions) { glm::vec3 penetration; - if (findSpherePlanePenetration(sphereB->getPosition(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) { + if (findSpherePlanePenetration(sphereB->getCenter(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) { CollisionInfo* collision = collisions.getNewCollision(); if (!collision) { return false; // collision list is full } collision->_penetration = -penetration; - collision->_contactPoint = sphereB->getPosition() + + collision->_contactPoint = sphereB->getCenter() + (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration; collision->_shapeA = planeA; collision->_shapeB = sphereB; @@ -803,21 +803,21 @@ 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->getPosition(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); + return sphereAACube(sphereA->getCenter(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); } bool capsuleAACube(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); - float offset = glm::dot(cubeCenter - capsuleA->getPosition(), capsuleAxis); + float offset = glm::dot(cubeCenter - capsuleA->getCenter(), capsuleAxis); float halfHeight = capsuleA->getHalfHeight(); if (offset > halfHeight) { offset = halfHeight; } else if (offset < -halfHeight) { offset = -halfHeight; } - glm::vec3 nearestApproach = capsuleA->getPosition() + offset * capsuleAxis; + glm::vec3 nearestApproach = capsuleA->getCenter() + offset * capsuleAxis; // collide nearest approach like a sphere at that point return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index cf3baebec0..d1f773eee7 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -75,7 +75,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { - // TODO: need to implement: + // TODO: Andrew need to implement: // (1) joints pull points (SpecialCapsuleShape would help solve this) // (2) points slam shapes (SpecialCapsuleShape would help solve this) // (3) shapes collide with pairwise collision bypass diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 7b3d956065..fc1ad59925 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -122,8 +122,8 @@ void ShapeColliderTests::sphereTouchesSphere() { } // contactPoint is on surface of sphereA - glm::vec3 AtoB = sphereB.getPosition() - sphereA.getPosition(); - glm::vec3 expectedContactPoint = sphereA.getPosition() + radiusA * glm::normalize(AtoB); + glm::vec3 AtoB = sphereB.getCenter() - sphereA.getCenter(); + glm::vec3 expectedContactPoint = sphereA.getCenter() + radiusA * glm::normalize(AtoB); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -152,8 +152,8 @@ void ShapeColliderTests::sphereTouchesSphere() { } // contactPoint is on surface of sphereA - glm::vec3 BtoA = sphereA.getPosition() - sphereB.getPosition(); - glm::vec3 expectedContactPoint = sphereB.getPosition() + radiusB * glm::normalize(BtoA); + glm::vec3 BtoA = sphereA.getCenter() - sphereB.getCenter(); + glm::vec3 expectedContactPoint = sphereB.getCenter() + radiusB * glm::normalize(BtoA); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -181,7 +181,7 @@ void ShapeColliderTests::sphereMissesCapsule() { glm::quat rotation = glm::angleAxis(angle, axis); glm::vec3 translation(15.1f, -27.1f, -38.6f); capsuleB.setRotation(rotation); - capsuleB.setPosition(translation); + capsuleB.setCenter(translation); CollisionList collisions(16); @@ -192,7 +192,7 @@ void ShapeColliderTests::sphereMissesCapsule() { for (int i = 0; i < numberOfSteps; ++i) { // translate sphereA into world-frame glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis; - sphereA.setPosition(rotation * localPosition + translation); + sphereA.setCenter(rotation * localPosition + translation); // sphereA agains capsuleB if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) @@ -235,7 +235,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { int numCollisions = 0; { // sphereA collides with capsuleB's cylindrical wall - sphereA.setPosition(radialOffset * xAxis); + sphereA.setCenter(radialOffset * xAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -257,7 +257,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getPosition() - radiusA * xAxis; + glm::vec3 expectedContactPoint = sphereA.getCenter() - radiusA * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -286,8 +286,8 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of capsuleB - glm::vec3 BtoA = sphereA.getPosition() - capsuleB.getPosition(); - glm::vec3 closestApproach = capsuleB.getPosition() + glm::dot(BtoA, yAxis) * yAxis; + glm::vec3 BtoA = sphereA.getCenter() - capsuleB.getCenter(); + glm::vec3 closestApproach = capsuleB.getCenter() + glm::dot(BtoA, yAxis) * yAxis; expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { @@ -298,7 +298,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits end cap at axis glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setPosition(axialOffset * yAxis); + sphereA.setCenter(axialOffset * yAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -320,7 +320,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getPosition() - radiusA * yAxis; + glm::vec3 expectedContactPoint = sphereA.getCenter() - radiusA * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -361,7 +361,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits start cap at axis glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setPosition(axialOffset * yAxis); + sphereA.setCenter(axialOffset * yAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -383,7 +383,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getPosition() + radiusA * yAxis; + glm::vec3 expectedContactPoint = sphereA.getCenter() + radiusA * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -445,7 +445,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { CollisionList collisions(16); // side by side - capsuleB.setPosition((1.01f * totalRadius) * xAxis); + capsuleB.setCenter((1.01f * totalRadius) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -460,7 +460,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { } // end to end - capsuleB.setPosition((1.01f * totalHalfLength) * xAxis); + capsuleB.setCenter((1.01f * totalHalfLength) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -477,7 +477,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { // rotate B and move it to the side glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); - capsuleB.setPosition((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + capsuleB.setCenter((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -515,7 +515,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { int numCollisions = 0; { // side by side - capsuleB.setPosition((0.99f * totalRadius) * xAxis); + capsuleB.setCenter((0.99f * totalRadius) * xAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -535,7 +535,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { } { // end to end - capsuleB.setPosition((0.99f * totalHalfLength) * yAxis); + capsuleB.setCenter((0.99f * totalHalfLength) * yAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { @@ -558,7 +558,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { { // rotate B and move it to the side glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); - capsuleB.setPosition((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + capsuleB.setCenter((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { @@ -583,7 +583,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis; - capsuleB.setPosition(positionB); + capsuleB.setCenter(positionB); // capsuleA vs capsuleB if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) @@ -604,7 +604,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << " actual = " << collision->_penetration; } - glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * xAxis; + glm::vec3 expectedContactPoint = capsuleA.getCenter() + radiusA * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -632,7 +632,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << std::endl; } - expectedContactPoint = capsuleB.getPosition() - (radiusB + halfHeightB) * xAxis; + expectedContactPoint = capsuleB.getCenter() - (radiusB + halfHeightB) * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -648,7 +648,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis; - capsuleB.setPosition(positionB); + capsuleB.setCenter(positionB); // capsuleA vs capsuleB if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) @@ -670,7 +670,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << std::endl; } - glm::vec3 expectedContactPoint = capsuleA.getPosition() + radiusA * zAxis + shift * yAxis; + glm::vec3 expectedContactPoint = capsuleA.getCenter() + radiusA * zAxis + shift * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -707,7 +707,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { float overlap = 0.25f; float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; sphereCenter = cubeCenter + sphereOffset * axis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; @@ -740,7 +740,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { float overlap = 1.25f * sphereRadius; float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; sphereCenter = cubeCenter + sphereOffset * axis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube." @@ -814,7 +814,7 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { float overlap = 0.25f; sphereCenter = cubeCenter + (lengthAxis * 0.5f * cubeSide + sphereRadius - overlap) * axis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; @@ -856,42 +856,42 @@ void ShapeColliderTests::sphereMissesAACube() { // top sphereCenter = cubeCenter + sphereOffset * yAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // bottom sphereCenter = cubeCenter - sphereOffset * yAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // left sphereCenter = cubeCenter + sphereOffset * xAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // right sphereCenter = cubeCenter - sphereOffset * xAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // forward sphereCenter = cubeCenter + sphereOffset * zAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // back sphereCenter = cubeCenter - sphereOffset * zAxis; - sphere.setPosition(sphereCenter); + sphere.setCenter(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } From 3fd4ee02fffe489610f42ab413c7a16dd86ef32e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jun 2014 08:55:08 -0700 Subject: [PATCH 020/105] Shape uses "center" instead of "position" --- libraries/shared/src/CapsuleShape.cpp | 6 +++--- libraries/shared/src/ListShape.cpp | 6 +++--- libraries/shared/src/ListShape.h | 5 +++-- libraries/shared/src/PlaneShape.cpp | 4 ++-- libraries/shared/src/Shape.h | 16 ++++++++-------- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 8e887107dc..7313f3ee7e 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -38,12 +38,12 @@ CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm: /// \param[out] startPoint is the center of start cap void CapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = _position - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + startPoint = _center - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } /// \param[out] endPoint is the center of the end cap void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = _position + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + endPoint = _center + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { @@ -69,7 +69,7 @@ void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { glm::vec3 axis = endPoint - startPoint; - _position = 0.5f * (endPoint + startPoint); + _center = 0.5f * (endPoint + startPoint); float height = glm::length(axis); if (height > EPSILON) { _halfHeight = 0.5f * height; diff --git a/libraries/shared/src/ListShape.cpp b/libraries/shared/src/ListShape.cpp index 162ee111c7..4876111054 100644 --- a/libraries/shared/src/ListShape.cpp +++ b/libraries/shared/src/ListShape.cpp @@ -24,9 +24,9 @@ ListShape::~ListShape() { clear(); } -void ListShape::setPosition(const glm::vec3& position) { +void ListShape::setCenter(const glm::vec3& center) { _subShapeTransformsAreDirty = true; - Shape::setCenter(position); + Shape::setCenter(center); } void ListShape::setRotation(const glm::quat& rotation) { @@ -44,7 +44,7 @@ const Shape* ListShape::getSubShape(int index) const { void ListShape::updateSubTransforms() { if (_subShapeTransformsAreDirty) { for (int i = 0; i < _subShapeEntries.size(); ++i) { - _subShapeEntries[i].updateTransform(_position, _rotation); + _subShapeEntries[i].updateTransform(_center, _rotation); } _subShapeTransformsAreDirty = false; } diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h index 87464779ce..b819c404c9 100644 --- a/libraries/shared/src/ListShape.h +++ b/libraries/shared/src/ListShape.h @@ -42,8 +42,9 @@ public: ~ListShape(); - void setPosition(const glm::vec3& position); - glm::vec3 getPosition() const { return _position; } + void setCenter(const glm::vec3& center); +// void setPosition(const glm::vec3& position); +// glm::vec3 getPosition() const { return _position; } void setRotation(const glm::quat& rotation); const Shape* getSubShape(int index) const; diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index a8b4468c93..abe97c68b2 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -18,7 +18,7 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) : Shape(Shape::PLANE_SHAPE) { glm::vec3 normal = glm::vec3(coefficients); - _position = -normal * coefficients.w; + _center = -normal * coefficients.w; float angle = acosf(glm::dot(normal, UNROTATED_NORMAL)); if (angle > EPSILON) { @@ -32,5 +32,5 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) : glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; - return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position)); + return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _center)); } diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index f1a36adfb6..f366814620 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -27,7 +27,7 @@ public: LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _position(0.f), _rotation() { } + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _center(0.f), _rotation() { } virtual ~Shape() {} int getType() const { return _type; } @@ -35,31 +35,31 @@ public: // const glm::vec3& getPosition() const { return _position; } const glm::quat& getRotation() const { return _rotation; } -// virtual void setPosition(const glm::vec3& position) { _position = position; } +// virtual void setPosition(const glm::vec3& position) { _center = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } PhysicalEntity* getEntity() const { return _owningEntity; } - virtual void setCenter(const glm::vec3& center) { _position = center; } - virtual glm::vec3 getCenter() const { return _position; } + virtual void setCenter(const glm::vec3& center) { _center = center; } + virtual glm::vec3 getCenter() const { return _center; } protected: // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(0.f), _rotation() {} + Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(0.f), _rotation() {} Shape(Type type, const glm::vec3& position) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(position), _rotation() {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(position), _rotation() {} Shape(Type type, const glm::vec3& position, const glm::quat& rotation) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _position(position), _rotation(rotation) {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(position), _rotation(rotation) {} void setBoundingRadius(float radius) { _boundingRadius = radius; } int _type; PhysicalEntity* _owningEntity; float _boundingRadius; - glm::vec3 _position; + glm::vec3 _center; glm::quat _rotation; }; From 3a2f322ca39600be2b8e12cc63a3e4e141bde771 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jun 2014 12:23:18 -0700 Subject: [PATCH 021/105] SphereShape, CapsuleShape support derived classes --- libraries/shared/src/CapsuleShape.h | 18 +++++++++++------- libraries/shared/src/SphereShape.h | 2 ++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 756ae18911..6f7a064d96 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -23,24 +23,28 @@ public: CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation); CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint); + virtual ~CapsuleShape() {} + float getRadius() const { return _radius; } float getHalfHeight() const { return _halfHeight; } /// \param[out] startPoint is the center of start cap - void getStartPoint(glm::vec3& startPoint) const; + virtual void getStartPoint(glm::vec3& startPoint) const; /// \param[out] endPoint is the center of the end cap - void getEndPoint(glm::vec3& endPoint) const; + virtual void getEndPoint(glm::vec3& endPoint) const; - void computeNormalizedAxis(glm::vec3& axis) const; + virtual void computeNormalizedAxis(glm::vec3& axis) const; void setRadius(float radius); - void setHalfHeight(float height); - void setRadiusAndHalfHeight(float radius, float height); - void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); + virtual void setHalfHeight(float height); + virtual void setRadiusAndHalfHeight(float radius, float height); + + /// Sets the endpoints and updates center, rotation, and halfHeight to agree. + virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); protected: - void updateBoundingRadius() { _boundingRadius = _radius + _halfHeight; } + virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } float _radius; float _halfHeight; diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index 62783ab340..8feeb5e5b8 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -26,6 +26,8 @@ public: _boundingRadius = radius; } + virtual ~SphereShape() {} + float getRadius() const { return _boundingRadius; } void setRadius(float radius) { _boundingRadius = radius; } From 1496e6c8b2b0f7b8b7d826969f7e723ea32ae587 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jun 2014 12:29:41 -0700 Subject: [PATCH 022/105] add VirletSphereShape and VirletCapsuleShape --- libraries/shared/src/VerletCapsuleShape.cpp | 76 +++++++++++++++++++++ libraries/shared/src/VerletCapsuleShape.h | 55 +++++++++++++++ libraries/shared/src/VerletSphereShape.cpp | 32 +++++++++ libraries/shared/src/VerletSphereShape.h | 34 +++++++++ 4 files changed, 197 insertions(+) create mode 100644 libraries/shared/src/VerletCapsuleShape.cpp create mode 100644 libraries/shared/src/VerletCapsuleShape.h create mode 100644 libraries/shared/src/VerletSphereShape.cpp create mode 100644 libraries/shared/src/VerletSphereShape.h diff --git a/libraries/shared/src/VerletCapsuleShape.cpp b/libraries/shared/src/VerletCapsuleShape.cpp new file mode 100644 index 0000000000..9cae13ba2d --- /dev/null +++ b/libraries/shared/src/VerletCapsuleShape.cpp @@ -0,0 +1,76 @@ +// +// VerletCapsuleShape.cpp +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.16 +// 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 "VerletCapsuleShape.h" +#include "SharedUtil.h" + +VerletCapsuleShape::VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint) : + CapsuleShape(), _startPoint(startPoint), _endPoint(endPoint) { + assert(startPoint); + assert(endPoint); + _halfHeight = 0.5f * glm::distance(*_startPoint, *_endPoint); + updateBoundingRadius(); +} + +VerletCapsuleShape::VerletCapsuleShape(float radius, glm::vec3* startPoint, glm::vec3* endPoint) : + CapsuleShape(radius, 1.0f), _startPoint(startPoint), _endPoint(endPoint) { + assert(startPoint); + assert(endPoint); + _halfHeight = 0.5f * glm::distance(*_startPoint, *_endPoint); + updateBoundingRadius(); +} + +// virtual +float VerletCapsuleShape::getHalfHeight() const { + return 0.5f * glm::distance(*_startPoint, *_endPoint); +} + +// virtual +void VerletCapsuleShape::getStartPoint(glm::vec3& startPoint) const { + startPoint = *_startPoint; +} + +// virtual +void VerletCapsuleShape::getEndPoint(glm::vec3& endPoint) const { + endPoint = *_endPoint; +} + +// virtual +void VerletCapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { + glm::vec3 unormalizedAxis = *_endPoint - *_startPoint; + float fullLength = glm::length(unormalizedAxis); + if (fullLength > EPSILON) { + axis = unormalizedAxis / fullLength; + } else { + // the axis is meaningless, but we fill it with a normalized direction + // just in case the calling context assumes it really is normalized. + axis = glm::vec3(0.0f, 1.0f, 0.0f); + } +} + +// virtual +void VerletCapsuleShape::setHalfHeight(float height) { + // don't call this method because this is a verlet shape + assert(false); +} + +// virtual +void VerletCapsuleShape::setRadiusAndHalfHeight(float radius, float height) { + // don't call this method because this is a verlet shape + assert(false); +} + +// virtual +void VerletCapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { + *_startPoint = startPoint; + *_endPoint = endPoint; + updateBoundingRadius(); +} diff --git a/libraries/shared/src/VerletCapsuleShape.h b/libraries/shared/src/VerletCapsuleShape.h new file mode 100644 index 0000000000..c06b9c7758 --- /dev/null +++ b/libraries/shared/src/VerletCapsuleShape.h @@ -0,0 +1,55 @@ +// +// VerletCapsuleShape.h +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.16 +// 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_VerletCapsuleShape_h +#define hifi_VerletCapsuleShape_h + +#include "CapsuleShape.h" + + +// The VerletCapsuleShape is similar to a regular CapsuleShape, except it keeps pointers +// to its endpoints which are owned by some other data structure. This allows it to +// participate in a verlet integration engine. +// +// Although the true_halfHeight of the VerletCapsuleShape is considered a constant +class VerletCapsuleShape : public CapsuleShape { +public: + VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint); + VerletCapsuleShape(float radius, glm::vec3* startPoint, glm::vec3* endPoint); + //VerletCapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation); + //VerletCapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint); + + //float getRadius() const { return _radius; } + virtual float getHalfHeight() const; + + /// \param[out] startPoint is the center of start cap + void getStartPoint(glm::vec3& startPoint) const; + + /// \param[out] endPoint is the center of the end cap + void getEndPoint(glm::vec3& endPoint) const; + + /// \param[out] axis is a normalized vector that points from start to end + void computeNormalizedAxis(glm::vec3& axis) const; + + //void setRadius(float radius); + void setHalfHeight(float height); + void setRadiusAndHalfHeight(float radius, float height); + void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); + + //void assignEndPoints(glm::vec3* startPoint, glm::vec3* endPoint); + +protected: + // NOTE: VerletCapsuleShape does NOT own its points. + glm::vec3* _startPoint; + glm::vec3* _endPoint; +}; + +#endif // hifi_VerletCapsuleShape_h diff --git a/libraries/shared/src/VerletSphereShape.cpp b/libraries/shared/src/VerletSphereShape.cpp new file mode 100644 index 0000000000..b696f74dfb --- /dev/null +++ b/libraries/shared/src/VerletSphereShape.cpp @@ -0,0 +1,32 @@ +// +// VerletSphereShape.cpp +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.16 +// 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 "VerletSphereShape.h" + +VerletSphereShape::VerletSphereShape(glm::vec3* centerPoint) : SphereShape() { + assert(centerPoint); + _point = centerPoint; +} + +VerletSphereShape::VerletSphereShape(float radius, glm::vec3* centerPoint) : SphereShape(radius) { + assert(centerPoint); + _point = centerPoint; +} + +// virtual from Shape class +void VerletSphereShape::setCenter(const glm::vec3& center) { + *_point = center; +} + +// virtual from Shape class +glm::vec3 VerletSphereShape::getCenter() { + return *_point; +} diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/shared/src/VerletSphereShape.h new file mode 100644 index 0000000000..63a8531ffc --- /dev/null +++ b/libraries/shared/src/VerletSphereShape.h @@ -0,0 +1,34 @@ +// +// VerletSphereShape.h +// libraries/shared/src +// +// Created by Andrew Meadows on 2014.06.16 +// 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_VerletSphereShape_h +#define hifi_VerletSphereShape_h + +#include "SphereShape.h" + +// The VerletSphereShape is similar to a regular SphereShape, except it keeps a pointer +// to its center which is owned by some other data structure. This allows it to +// participate in a verlet integration engine. +class VerletSphereShape : public SphereShape { +public: + VerletSphereShape(glm::vec3* centerPoint); + + VerletSphereShape(float radius, glm::vec3* centerPoint); + + void setCenter(const glm::vec3& center); + glm::vec3 getCenter(); + +protected: + // NOTE: VerletSphereShape does NOT own its _point + glm::vec3* _point; +}; + +#endif // hifi_VerletSphereShape_h From 118717d96af51b6587132d1390df3bf69c037add Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jun 2014 14:51:58 -0700 Subject: [PATCH 023/105] Shapes moved from Model into PhysicalEntity --- interface/src/avatar/MyAvatar.cpp | 2 +- interface/src/avatar/SkeletonModel.cpp | 10 +-- interface/src/renderer/Model.cpp | 102 ++++++++---------------- interface/src/renderer/Model.h | 25 +----- libraries/shared/src/PhysicalEntity.cpp | 50 ++++++++++-- libraries/shared/src/PhysicalEntity.h | 29 ++++++- libraries/shared/src/Ragdoll.cpp | 12 +-- libraries/shared/src/Ragdoll.h | 8 +- 8 files changed, 121 insertions(+), 117 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 403d0596c0..eb8e44fe44 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -82,7 +82,7 @@ MyAvatar::MyAvatar() : for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } - _skeletonModel.setEnableCollisionShapes(true); + _skeletonModel.setEnableShapes(true); _simulationEngine.addRagdoll(&_skeletonModel); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 74625cf6b2..c98766f479 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -39,7 +39,7 @@ void SkeletonModel::setJointStates(QVector states) { points.push_back(state.getPosition()); } // ... and feed the results to Ragdoll - initShapesAndPoints(&_jointShapes, parentIndices, points); + initShapesAndPoints(&_shapes, parentIndices, points); } } @@ -126,7 +126,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) { } void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { - if (jointIndex < 0 || jointIndex >= int(_jointShapes.size())) { + if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { return; } if (jointIndex == getLeftHandJointIndex() @@ -136,15 +136,15 @@ void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) for (int i = 0; i < _jointStates.size(); i++) { const FBXJoint& joint = geometry.joints[i]; int parentIndex = joint.parentIndex; - Shape* shape = _jointShapes[i]; + Shape* shape = _shapes[i]; if (i == jointIndex) { // this shape is the hand if (shape) { shapes.push_back(shape); } - if (parentIndex != -1 && _jointShapes[parentIndex]) { + if (parentIndex != -1 && _shapes[parentIndex]) { // also add the forearm - shapes.push_back(_jointShapes[parentIndex]); + shapes.push_back(_shapes[parentIndex]); } } else if (shape) { while (parentIndex != -1) { diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c0ed002b69..5d871f2c9e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -38,13 +38,10 @@ Model::Model(QObject* parent) : _scaleToFit(false), _scaleToFitLargestDimension(0.0f), _scaledToFit(false), - _simulationIndex(-1), _snapModelToCenter(false), _snappedToCenter(false), _rootIndex(-1), - _shapesAreDirty(true), - _enableCollisionShapes(false), - _boundingRadius(0.0f), + //_enableCollisionShapes(false), _boundingShape(), _boundingShapeLocalOffset(0.0f), _lodDistance(0.0f), @@ -118,19 +115,6 @@ Model::SkinLocations Model::_skinCascadedShadowSpecularMapLocations; Model::SkinLocations Model::_skinCascadedShadowNormalSpecularMapLocations; Model::SkinLocations Model::_skinShadowLocations; -void Model::setTranslation(const glm::vec3& translation) { - if (_translation != translation) { - _shapesAreDirty = !_jointShapes.isEmpty(); - _translation = translation; - } -} -void Model::setRotation(const glm::quat& rotation) { - if (_rotation != rotation) { - _shapesAreDirty = !_jointShapes.isEmpty(); - _rotation = rotation; - } -} - void Model::setScale(const glm::vec3& scale) { setScaleInternal(scale); // if anyone sets scale manually, then we are no longer scaled to fit @@ -145,8 +129,9 @@ void Model::setScaleInternal(const glm::vec3& scale) { const float ONE_PERCENT = 0.01f; if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) { _scale = scale; - if (_jointShapes.size() > 0) { - rebuildShapes(); + if (_shapes.size() > 0) { + clearShapes(); + buildShapes(); } } } @@ -202,17 +187,6 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { return jointStates; } -void Model::setEnableCollisionShapes(bool enable) { - if (enable != _enableCollisionShapes) { - _enableCollisionShapes = enable; - if (_enableCollisionShapes) { - rebuildShapes(); - } else { - clearShapes(); - } - } -} - void Model::init() { if (!_program.isLinked()) { _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model.vert"); @@ -580,8 +554,8 @@ bool Model::updateGeometry() { model->setURL(attachment.url); _attachments.append(model); } - if (_enableCollisionShapes) { - rebuildShapes(); + if (_enableShapes) { + buildShapes(); } needFullUpdate = true; } @@ -817,16 +791,8 @@ AnimationHandlePointer Model::createAnimationHandle() { return handle; } -void Model::clearShapes() { - for (int i = 0; i < _jointShapes.size(); ++i) { - delete _jointShapes[i]; - } - _jointShapes.clear(); -} - -void Model::rebuildShapes() { - clearShapes(); - +// virtual override from PhysicalEntity +void Model::buildShapes() { if (!_geometry || _rootIndex == -1) { return; } @@ -851,15 +817,15 @@ void Model::rebuildShapes() { if (type == Shape::CAPSULE_SHAPE) { CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); capsule->setEntity(this); - _jointShapes.push_back(capsule); + _shapes.push_back(capsule); } else if (type == Shape::SPHERE_SHAPE) { SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); - _jointShapes.push_back(sphere); + _shapes.push_back(sphere); sphere->setEntity(this); } else { // this shape type is not handled and the joint shouldn't collide, // however we must have a Shape* for each joint, so we push NULL - _jointShapes.push_back(NULL); + _shapes.push_back(NULL); } } @@ -916,8 +882,8 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { // sync shapes to joints _boundingRadius = 0.0f; float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (!shape) { continue; } @@ -936,8 +902,8 @@ void Model::computeBoundingShape(const FBXGeometry& geometry) { Extents totalExtents; totalExtents.reset(); totalExtents.addPoint(glm::vec3(0.0f)); - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (!shape) { continue; } @@ -981,13 +947,13 @@ void Model::resetShapePositions() { // Moves shapes to the joint default locations for debug visibility into // how the bounding shape is computed. - if (!_geometry || _rootIndex == -1 || _jointShapes.isEmpty()) { + if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { // geometry or joints have not yet been created return; } const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty() || _jointShapes.size() != geometry.joints.size()) { + if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) { return; } @@ -995,8 +961,8 @@ void Model::resetShapePositions() { computeBoundingShape(geometry); // Then we move them into world frame for rendering at the Model's location. - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (shape) { shape->setCenter(_translation + _rotation * shape->getCenter()); shape->setRotation(_rotation * shape->getRotation()); @@ -1007,7 +973,7 @@ void Model::resetShapePositions() { } void Model::updateShapePositions() { - if (_shapesAreDirty && _jointShapes.size() == _jointStates.size()) { + if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { glm::vec3 rootPosition(0.0f); _boundingRadius = 0.0f; float uniformScale = extractUniformScale(_scale); @@ -1018,7 +984,7 @@ void Model::updateShapePositions() { glm::quat stateRotation = state.getRotation(); glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); - Shape* shape = _jointShapes[i]; + Shape* shape = _shapes[i]; if (shape) { shape->setCenter(worldPosition); shape->setRotation(_rotation * stateRotation * joint.shapeRotation); @@ -1073,8 +1039,8 @@ bool Model::findCollisions(const QVector shapes, CollisionList& co if (!theirShape) { continue; } - for (int j = 0; j < _jointShapes.size(); ++j) { - const Shape* ourShape = _jointShapes[j]; + for (int j = 0; j < _shapes.size(); ++j) { + const Shape* ourShape = _shapes[j]; if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { collided = true; } @@ -1088,8 +1054,8 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi bool collided = false; SphereShape sphere(sphereRadius, sphereCenter); const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (!shape) { continue; } @@ -1120,8 +1086,8 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { bool collided = false; PlaneShape planeShape(plane); - for (int i = 0; i < _jointShapes.size(); i++) { - if (_jointShapes[i] && ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) { + for (int i = 0; i < _shapes.size(); i++) { + if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_data = (void*)(this); collision->_intData = i; @@ -1254,7 +1220,7 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } - _shapesAreDirty = ! _jointShapes.isEmpty(); + _shapesAreDirty = ! _shapes.isEmpty(); // update the attachment transforms and simulate them const FBXGeometry& geometry = _geometry->getFBXGeometry(); @@ -1389,7 +1355,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl for (int j = freeLineage.size() - 1; j >= 0; j--) { updateJointState(freeLineage.at(j)); } - _shapesAreDirty = !_jointShapes.isEmpty(); + _shapesAreDirty = !_shapes.isEmpty(); return true; } @@ -1427,8 +1393,8 @@ const int BALL_SUBDIVISIONS = 10; void Model::renderJointCollisionShapes(float alpha) { glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); - for (int i = 0; i < _jointShapes.size(); i++) { - Shape* shape = _jointShapes[i]; + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (!shape) { continue; } @@ -1476,7 +1442,7 @@ void Model::renderJointCollisionShapes(float alpha) { } void Model::renderBoundingCollisionShapes(float alpha) { - if (_jointShapes.isEmpty()) { + if (_shapes.isEmpty()) { // the bounding shape has not been propery computed // so no need to render it return; @@ -1836,10 +1802,6 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool re } } -void Model::setShapeBackPointers() { - PhysicalEntity::setShapeBackPointers(_jointShapes, this); -} - void AnimationHandle::setURL(const QUrl& url) { if (_url != url) { _animation = Application::getInstance()->getAnimationCache()->getAnimation(_url = url); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 04c457f265..20612a0e21 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -42,12 +42,6 @@ public: Model(QObject* parent = NULL); virtual ~Model(); - void setTranslation(const glm::vec3& translation); - const glm::vec3& getTranslation() const { return _translation; } - - void setRotation(const glm::quat& rotation); - const glm::quat& getRotation() const { return _rotation; } - /// enables/disables scale to fit behavior, the model will be automatically scaled to the specified largest dimension void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f); bool getScaleToFit() const { return _scaleToFit; } /// is scale to fit enabled @@ -69,8 +63,6 @@ public: void setBlendshapeCoefficients(const QVector& coefficients) { _blendshapeCoefficients = coefficients; } const QVector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } - void setEnableCollisionShapes(bool enable); - bool isActive() const { return _geometry && _geometry->isLoaded(); } bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); } @@ -140,8 +132,9 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - void clearShapes(); - void rebuildShapes(); + // virtual override from PhysicalEntity + virtual void buildShapes(); + void resetShapePositions(); // DEBUG method virtual void updateShapePositions(); @@ -160,7 +153,6 @@ public: bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - float getBoundingRadius() const { return _boundingRadius; } float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } /// Sets blended vertices computed in a separate thread. @@ -169,12 +161,8 @@ public: const CapsuleShape& getBoundingShape() const { return _boundingShape; } protected: - virtual void setShapeBackPointers(); - QSharedPointer _geometry; - glm::vec3 _translation; - glm::quat _rotation; glm::vec3 _scale; glm::vec3 _offset; @@ -182,19 +170,12 @@ protected: float _scaleToFitLargestDimension; /// this is the dimension that scale to fit will use bool _scaledToFit; /// have we scaled to fit - int _simulationIndex; // set by SimulationEngine (if any) - bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snappedToCenter; /// are we currently snapped to center int _rootIndex; QVector _jointStates; - bool _shapesAreDirty; - bool _enableCollisionShapes; /// build collision shapes for joints - QVector _jointShapes; - - float _boundingRadius; CapsuleShape _boundingShape; glm::vec3 _boundingShapeLocalOffset; diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp index 43e25b2f1b..3084c31529 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -12,13 +12,53 @@ #include "PhysicalEntity.h" #include "Shape.h" -// static -void PhysicalEntity::setShapeBackPointers(const QVector& shapes, PhysicalEntity* entity) { - for (int i = 0; i < shapes.size(); i++) { - Shape* shape = shapes[i]; +PhysicalEntity::PhysicalEntity(EntityType type) : + _entityType(type), + _translation(0.0f), + _rotation(), + _boundingRadius(0.0f), + _shapesAreDirty(true), + _enableShapes(false) { +} + +void PhysicalEntity::setTranslation(const glm::vec3& translation) { + if (_translation != translation) { + _shapesAreDirty = !_shapes.isEmpty(); + _translation = translation; + } +} + +void PhysicalEntity::setRotation(const glm::quat& rotation) { + if (_rotation != rotation) { + _shapesAreDirty = !_shapes.isEmpty(); + _rotation = rotation; + } +} + +void PhysicalEntity::setShapeBackPointers() { + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; if (shape) { - shape->setEntity(entity); + shape->setEntity(this); } } } +void PhysicalEntity::setEnableShapes(bool enable) { + if (enable != _enableShapes) { + clearShapes(); + _enableShapes = enable; + if (_enableShapes) { + buildShapes(); + } + } +} + +void PhysicalEntity::clearShapes() { + for (int i = 0; i < _shapes.size(); ++i) { + delete _shapes[i]; + } + _shapes.clear(); +} + + diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h index d1d11a0d6c..22f2bff67b 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicalEntity.h @@ -14,6 +14,9 @@ #include +#include +#include + class Shape; // PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a @@ -28,15 +31,33 @@ public: ENTITY_MODEL, }; - static void setShapeBackPointers(const QVector& shapes, PhysicalEntity* entity); - - PhysicalEntity(EntityType type = ENTITY_UNKNOWN) : _entityType(type) {} + PhysicalEntity(EntityType type = ENTITY_UNKNOWN); virtual ~PhysicalEntity() {} + void setTranslation(const glm::vec3& translation); + void setRotation(const glm::quat& rotation); + + const glm::vec3& getTranslation() const { return _translation; } + const glm::quat& getRotation() const { return _rotation; } + float getBoundingRadius() const { return _boundingRadius; } + EntityType getEntityType() const { return _entityType; } - + + void setShapeBackPointers(); + + void setEnableShapes(bool enable); + + virtual void buildShapes() = 0; + virtual void clearShapes(); + protected: EntityType _entityType; + glm::vec3 _translation; + glm::quat _rotation; + float _boundingRadius; + bool _shapesAreDirty; + bool _enableShapes; + QVector _shapes; }; #endif // hifi_PhysicalEntity_h diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 61e90fa39c..9aff3d5626 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -94,7 +94,7 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio // Ragdoll // ---------------------------------------------------------------------------- -Ragdoll::Ragdoll() : _shapes(NULL) { +Ragdoll::Ragdoll() : _verletShapes(NULL) { } Ragdoll::~Ragdoll() { @@ -103,7 +103,7 @@ Ragdoll::~Ragdoll() { void Ragdoll::initShapesAndPoints(QVector* shapes, const QVector& parentIndices, const QVector& points) { clear(); - _shapes = shapes; + _verletShapes = shapes; const int numPoints = points.size(); assert(numPoints == parentIndices.size()); _points.reserve(numPoints); @@ -131,7 +131,7 @@ void Ragdoll::clear() { } _constraints.clear(); _points.clear(); - _shapes = NULL; + _verletShapes = NULL; } float Ragdoll::enforceConstraints() { @@ -146,14 +146,14 @@ float Ragdoll::enforceConstraints() { } void Ragdoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const { - if (!_shapes) { + if (!_verletShapes) { return; } - int numShapes = _shapes->size(); + int numShapes = _verletShapes->size(); int numConstraints = _constraints.size(); // NOTE: we assume a one-to-one relationship between shapes and constraints for (int i = 0; i < numShapes && i < numConstraints; ++i) { - _constraints[i]->updateProxyShape((*_shapes)[i], rotation, translation); + _constraints[i]->updateProxyShape((*_verletShapes)[i], rotation, translation); } } diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 996e65d954..37652507a9 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -87,17 +87,17 @@ public: /// \param rotation rotation into shapes' collision frame /// \param translation translation into shapes' collision frame - /// Moves and modifies elements of _shapes to agree with state of _points + /// Moves and modifies elements of _verletShapes to agree with state of _points void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const; - const QVector* getShapes() const { return _shapes; } + const QVector* getShapes() const { return _verletShapes; } protected: QVector _points; QVector _constraints; - // the Ragdoll does NOT own the data in _shapes. - QVector* _shapes; + // the Ragdoll does NOT own the data in _verletShapes. + QVector* _verletShapes; }; #endif // hifi_Ragdoll_h From bdbb12f4dcb71f2bbe93b9c5050e176842a6feec Mon Sep 17 00:00:00 2001 From: wangyix Date: Tue, 17 Jun 2014 14:35:40 -0700 Subject: [PATCH 024/105] switched edit nacks to non-verified --- assignment-client/src/octree/OctreeInboundPacketProcessor.cpp | 2 +- libraries/networking/src/PacketHeaders.h | 2 +- libraries/octree/src/OctreeEditPacketSender.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 44c9576943..78239bdf1f 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -256,7 +256,7 @@ int OctreeInboundPacketProcessor::sendNackPackets() { numSequenceNumbersAvailable -= numSequenceNumbers; // send it - qint64 bytesWritten = NodeList::getInstance()->writeDatagram(packet, dataAt - packet, destinationNode); + qint64 bytesWritten = NodeList::getInstance()->writeUnverifiedDatagram(packet, dataAt - packet, destinationNode); printf("\t\t wrote %lld bytes\n\n", bytesWritten); packetsSent++; diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 5ed4110627..0f87b0e607 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -79,7 +79,7 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainOAuthRequest << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeVoxelQuery << PacketTypeParticleQuery << PacketTypeModelQuery - << PacketTypeOctreeDataNack; + << PacketTypeOctreeDataNack << PacketTypeVoxelEditNack << PacketTypeParticleEditNack << PacketTypeModelEditNack; const int NUM_BYTES_MD5_HASH = 16; const int NUM_STATIC_HEADER_BYTES = sizeof(PacketVersion) + NUM_BYTES_RFC4122_UUID; diff --git a/libraries/octree/src/OctreeEditPacketSender.cpp b/libraries/octree/src/OctreeEditPacketSender.cpp index 01d2ecf464..5281617e72 100644 --- a/libraries/octree/src/OctreeEditPacketSender.cpp +++ b/libraries/octree/src/OctreeEditPacketSender.cpp @@ -379,7 +379,7 @@ void OctreeEditPacketSender::processNackPacket(const QByteArray& packet) { // read number of sequence numbers uint16_t numSequenceNumbers = (*(uint16_t*)dataAt); dataAt += sizeof(uint16_t); - + // read sequence numbers and queue packets for resend for (int i = 0; i < numSequenceNumbers; i++) { unsigned short int sequenceNumber = (*(unsigned short int*)dataAt); From ecbf5043d74eb9f35412a38f0f8cda2afc453c3b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 16:22:18 -0700 Subject: [PATCH 025/105] code out of Model into base and derived classes PhysicalEntity (base class) gets some shape management stuff SkeletonModel (derived class) gets some boundary shape and joint-shape stuff --- interface/src/avatar/Avatar.cpp | 1 - interface/src/avatar/SkeletonModel.cpp | 255 +++++++++++++++++- interface/src/avatar/SkeletonModel.h | 16 +- interface/src/renderer/Model.cpp | 339 +----------------------- interface/src/renderer/Model.h | 27 +- libraries/shared/src/PhysicalEntity.cpp | 93 ++++++- libraries/shared/src/PhysicalEntity.h | 17 +- 7 files changed, 371 insertions(+), 377 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 7a45512778..f940a106d7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -230,7 +230,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } if (renderBounding && shouldRenderHead(cameraPosition, renderMode)) { - getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f); _skeletonModel.renderBoundingCollisionShapes(0.7f); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index c98766f479..364044a803 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -11,6 +11,9 @@ #include +#include +#include + #include "Application.h" #include "Avatar.h" #include "Hand.h" @@ -20,7 +23,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : Model(parent), Ragdoll(), - _owningAvatar(owningAvatar) { + _owningAvatar(owningAvatar), + _boundingShape(), + _boundingShapeLocalOffset(0.0f) { } void SkeletonModel::setJointStates(QVector states) { @@ -524,3 +529,251 @@ void SkeletonModel::renderRagdoll() { glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); } + +// virtual +void SkeletonModel::buildShapes() { + if (!_geometry || _rootIndex == -1) { + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (geometry.joints.isEmpty()) { + return; + } + // We create the shapes with proper dimensions, but we set their transforms later. + float uniformScale = extractUniformScale(_scale); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + + float radius = uniformScale * joint.boneRadius; + float halfHeight = 0.5f * uniformScale * joint.distanceToParent; + Shape::Type type = joint.shapeType; + if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { + // this capsule is effectively a sphere + type = Shape::SPHERE_SHAPE; + } + if (type == Shape::CAPSULE_SHAPE) { + CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); + capsule->setEntity(this); + _shapes.push_back(capsule); + } else if (type == Shape::SPHERE_SHAPE) { + SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); + _shapes.push_back(sphere); + sphere->setEntity(this); + } else { + // this shape type is not handled and the joint shouldn't collide, + // however we must have a Shape* for each joint, so we push NULL + _shapes.push_back(NULL); + } + } + + // This method moves the shapes to their default positions in Model frame + // which is where we compute the bounding shape's parameters. + computeBoundingShape(geometry); + + // finally sync shapes to joint positions + _shapesAreDirty = true; + updateShapePositions(); +} + +void SkeletonModel::updateShapePositionsLegacy() { + if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { + glm::vec3 rootPosition(0.0f); + _boundingRadius = 0.0f; + float uniformScale = extractUniformScale(_scale); + for (int i = 0; i < _jointStates.size(); i++) { + const JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); + // shape position and rotation need to be in world-frame + glm::quat stateRotation = state.getRotation(); + glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); + glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); + Shape* shape = _shapes[i]; + if (shape) { + shape->setCenter(worldPosition); + shape->setRotation(_rotation * stateRotation * joint.shapeRotation); + float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); + if (distance > _boundingRadius) { + _boundingRadius = distance; + } + } + if (joint.parentIndex == -1) { + rootPosition = worldPosition; + } + } + _shapesAreDirty = false; + _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); + } +} + +void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { + // compute default joint transforms and rotations + // (in local frame, ignoring Model translation and rotation) + int numJoints = geometry.joints.size(); + QVector transforms; + transforms.fill(glm::mat4(), numJoints); + QVector finalRotations; + finalRotations.fill(glm::quat(), numJoints); + + QVector shapeIsSet; + shapeIsSet.fill(false, numJoints); + int numShapesSet = 0; + int lastNumShapesSet = -1; + while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) { + lastNumShapesSet = numShapesSet; + for (int i = 0; i < numJoints; i++) { + const FBXJoint& joint = geometry.joints.at(i); + int parentIndex = joint.parentIndex; + + if (parentIndex == -1) { + glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset); + glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; + glm::mat4 rootTransform = baseTransform * geometry.offset * glm::translate(joint.translation) + * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; + // remove the tranlsation part before we save the root transform + transforms[i] = glm::translate(- extractTranslation(rootTransform)) * rootTransform; + + finalRotations[i] = combinedRotation; + ++numShapesSet; + shapeIsSet[i] = true; + } else if (shapeIsSet[parentIndex]) { + glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; + transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) + * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; + finalRotations[i] = finalRotations[parentIndex] * combinedRotation; + ++numShapesSet; + shapeIsSet[i] = true; + } + } + } + + // sync shapes to joints + _boundingRadius = 0.0f; + float uniformScale = extractUniformScale(_scale); + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (!shape) { + continue; + } + const FBXJoint& joint = geometry.joints[i]; + glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); + glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; + shape->setCenter(localPosition); + shape->setRotation(finalRotations[i] * joint.shapeRotation); + float distance = glm::length(localPosition) + shape->getBoundingRadius(); + if (distance > _boundingRadius) { + _boundingRadius = distance; + } + } + + // compute bounding box + Extents totalExtents; + totalExtents.reset(); + totalExtents.addPoint(glm::vec3(0.0f)); + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (!shape) { + continue; + } + Extents shapeExtents; + shapeExtents.reset(); + glm::vec3 localPosition = shape->getCenter(); + int type = shape->getType(); + if (type == Shape::CAPSULE_SHAPE) { + // add the two furthest surface points of the capsule + CapsuleShape* capsule = static_cast(shape); + glm::vec3 axis; + capsule->computeNormalizedAxis(axis); + float radius = capsule->getRadius(); + float halfHeight = capsule->getHalfHeight(); + axis = halfHeight * axis + glm::vec3(radius); + + shapeExtents.addPoint(localPosition + axis); + shapeExtents.addPoint(localPosition - axis); + totalExtents.addExtents(shapeExtents); + } else if (type == Shape::SPHERE_SHAPE) { + float radius = shape->getBoundingRadius(); + glm::vec3 axis = glm::vec3(radius); + shapeExtents.addPoint(localPosition + axis); + shapeExtents.addPoint(localPosition - axis); + totalExtents.addExtents(shapeExtents); + } + } + + // compute bounding shape parameters + // NOTE: we assume that the longest side of totalExtents is the yAxis... + glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; + // ... and assume the radius is half the RMS of the X and Z sides: + float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); + _boundingShape.setRadius(capsuleRadius); + _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); + _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); +} + +void SkeletonModel::resetShapePositions() { + // DEBUG method. + // Moves shapes to the joint default locations for debug visibility into + // how the bounding shape is computed. + + if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { + // geometry or joints have not yet been created + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) { + return; + } + + // The shapes are moved to their default positions in computeBoundingShape(). + computeBoundingShape(geometry); + + // Then we move them into world frame for rendering at the Model's location. + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (shape) { + shape->setCenter(_translation + _rotation * shape->getCenter()); + shape->setRotation(_rotation * shape->getRotation()); + } + } + _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); +} + +void SkeletonModel::renderBoundingCollisionShapes(float alpha) { + const int BALL_SUBDIVISIONS = 10; + if (_shapes.isEmpty()) { + // the bounding shape has not been propery computed + // so no need to render it + return; + } + glPushMatrix(); + + Application::getInstance()->loadTranslatedViewMatrix(_translation); + + // draw a blue sphere at the capsule endpoint + glm::vec3 endPoint; + _boundingShape.getEndPoint(endPoint); + endPoint = endPoint - _translation; + glTranslatef(endPoint.x, endPoint.y, endPoint.z); + glColor4f(0.6f, 0.6f, 0.8f, alpha); + glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); + + // draw a yellow sphere at the capsule startpoint + glm::vec3 startPoint; + _boundingShape.getStartPoint(startPoint); + startPoint = startPoint - _translation; + glm::vec3 axis = endPoint - startPoint; + glTranslatef(-axis.x, -axis.y, -axis.z); + glColor4f(0.8f, 0.8f, 0.6f, alpha); + glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); + + // draw a green cylinder between the two points + glm::vec3 origin(0.0f); + glColor4f(0.6f, 0.8f, 0.6f, alpha); + Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius()); + + glPopMatrix(); +} + diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index f0982e6dd7..8d37697aef 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -14,6 +14,7 @@ #include "renderer/Model.h" +#include #include class Avatar; @@ -30,7 +31,6 @@ public: void simulate(float deltaTime, bool fullUpdate = true); void simulateRagdoll(float deltaTime); - void updateShapePositions(); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes @@ -95,6 +95,17 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + void buildShapes(); + void updateShapePositions(); + void updateShapePositionsLegacy(); + + void computeBoundingShape(const FBXGeometry& geometry); + void renderBoundingCollisionShapes(float alpha); + float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } + const CapsuleShape& getBoundingShape() const { return _boundingShape; } + + void resetShapePositions(); // DEBUG method + void renderRagdoll(); protected: @@ -121,6 +132,9 @@ private: void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation); Avatar* _owningAvatar; + + CapsuleShape _boundingShape; + glm::vec3 _boundingShapeLocalOffset; }; #endif // hifi_SkeletonModel_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5d871f2c9e..2057f3248b 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -33,7 +33,6 @@ static int vec3VectorTypeId = qRegisterMetaType >(); Model::Model(QObject* parent) : QObject(parent), - PhysicalEntity(PhysicalEntity::ENTITY_MODEL), _scale(1.0f, 1.0f, 1.0f), _scaleToFit(false), _scaleToFitLargestDimension(0.0f), @@ -42,8 +41,6 @@ Model::Model(QObject* parent) : _snappedToCenter(false), _rootIndex(-1), //_enableCollisionShapes(false), - _boundingShape(), - _boundingShapeLocalOffset(0.0f), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com") { @@ -793,308 +790,11 @@ AnimationHandlePointer Model::createAnimationHandle() { // virtual override from PhysicalEntity void Model::buildShapes() { - if (!_geometry || _rootIndex == -1) { - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty()) { - return; - } - - // We create the shapes with proper dimensions, but we set their transforms later. - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - - float radius = uniformScale * joint.boneRadius; - float halfHeight = 0.5f * uniformScale * joint.distanceToParent; - Shape::Type type = joint.shapeType; - if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { - // this capsule is effectively a sphere - type = Shape::SPHERE_SHAPE; - } - if (type == Shape::CAPSULE_SHAPE) { - CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); - capsule->setEntity(this); - _shapes.push_back(capsule); - } else if (type == Shape::SPHERE_SHAPE) { - SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); - _shapes.push_back(sphere); - sphere->setEntity(this); - } else { - // this shape type is not handled and the joint shouldn't collide, - // however we must have a Shape* for each joint, so we push NULL - _shapes.push_back(NULL); - } - } - - // This method moves the shapes to their default positions in Model frame - // which is where we compute the bounding shape's parameters. - computeBoundingShape(geometry); - - // finally sync shapes to joint positions - _shapesAreDirty = true; - updateShapePositions(); -} - -void Model::computeBoundingShape(const FBXGeometry& geometry) { - // compute default joint transforms and rotations - // (in local frame, ignoring Model translation and rotation) - int numJoints = geometry.joints.size(); - QVector transforms; - transforms.fill(glm::mat4(), numJoints); - QVector finalRotations; - finalRotations.fill(glm::quat(), numJoints); - - QVector shapeIsSet; - shapeIsSet.fill(false, numJoints); - int numShapesSet = 0; - int lastNumShapesSet = -1; - while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) { - lastNumShapesSet = numShapesSet; - for (int i = 0; i < numJoints; i++) { - const FBXJoint& joint = geometry.joints.at(i); - int parentIndex = joint.parentIndex; - - if (parentIndex == -1) { - glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset); - glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - glm::mat4 rootTransform = baseTransform * geometry.offset * glm::translate(joint.translation) - * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; - // remove the tranlsation part before we save the root transform - transforms[i] = glm::translate(- extractTranslation(rootTransform)) * rootTransform; - - finalRotations[i] = combinedRotation; - ++numShapesSet; - shapeIsSet[i] = true; - } else if (shapeIsSet[parentIndex]) { - glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) - * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; - finalRotations[i] = finalRotations[parentIndex] * combinedRotation; - ++numShapesSet; - shapeIsSet[i] = true; - } - } - } - - // sync shapes to joints - _boundingRadius = 0.0f; - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - const FBXJoint& joint = geometry.joints[i]; - glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); - glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; - shape->setCenter(localPosition); - shape->setRotation(finalRotations[i] * joint.shapeRotation); - float distance = glm::length(localPosition) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } - } - - // compute bounding box - Extents totalExtents; - totalExtents.reset(); - totalExtents.addPoint(glm::vec3(0.0f)); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - Extents shapeExtents; - shapeExtents.reset(); - glm::vec3 localPosition = shape->getCenter(); - int type = shape->getType(); - if (type == Shape::CAPSULE_SHAPE) { - // add the two furthest surface points of the capsule - CapsuleShape* capsule = static_cast(shape); - glm::vec3 axis; - capsule->computeNormalizedAxis(axis); - float radius = capsule->getRadius(); - float halfHeight = capsule->getHalfHeight(); - axis = halfHeight * axis + glm::vec3(radius); - - shapeExtents.addPoint(localPosition + axis); - shapeExtents.addPoint(localPosition - axis); - totalExtents.addExtents(shapeExtents); - } else if (type == Shape::SPHERE_SHAPE) { - float radius = shape->getBoundingRadius(); - glm::vec3 axis = glm::vec3(radius); - shapeExtents.addPoint(localPosition + axis); - shapeExtents.addPoint(localPosition - axis); - totalExtents.addExtents(shapeExtents); - } - } - - // compute bounding shape parameters - // NOTE: we assume that the longest side of totalExtents is the yAxis... - glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum; - // ... and assume the radius is half the RMS of the X and Z sides: - float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z)); - _boundingShape.setRadius(capsuleRadius); - _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); - _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); -} - -void Model::resetShapePositions() { - // DEBUG method. - // Moves shapes to the joint default locations for debug visibility into - // how the bounding shape is computed. - - if (!_geometry || _rootIndex == -1 || _shapes.isEmpty()) { - // geometry or joints have not yet been created - return; - } - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (geometry.joints.isEmpty() || _shapes.size() != geometry.joints.size()) { - return; - } - - // The shapes are moved to their default positions in computeBoundingShape(). - computeBoundingShape(geometry); - - // Then we move them into world frame for rendering at the Model's location. - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (shape) { - shape->setCenter(_translation + _rotation * shape->getCenter()); - shape->setRotation(_rotation * shape->getRotation()); - } - } - _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); + // TODO: figure out how to load/build collision shapes for general models } void Model::updateShapePositions() { - if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { - glm::vec3 rootPosition(0.0f); - _boundingRadius = 0.0f; - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - // shape position and rotation need to be in world-frame - glm::quat stateRotation = state.getRotation(); - glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); - glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); - Shape* shape = _shapes[i]; - if (shape) { - shape->setCenter(worldPosition); - shape->setRotation(_rotation * stateRotation * joint.shapeRotation); - float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } - } - if (joint.parentIndex == -1) { - rootPosition = worldPosition; - } - } - _shapesAreDirty = false; - _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); - } -} - -bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - const glm::vec3 relativeOrigin = origin - _translation; - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - float minDistance = FLT_MAX; - float radiusScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; - glm::vec3 end = _translation + _rotation * _jointStates[i].getPosition(); - float endRadius = joint.boneRadius * radiusScale; - glm::vec3 start = end; - float startRadius = joint.boneRadius * radiusScale; - if (joint.parentIndex != -1) { - start = _translation + _rotation * _jointStates[joint.parentIndex].getPosition(); - startRadius = geometry.joints[joint.parentIndex].boneRadius * radiusScale; - } - // for now, use average of start and end radii - float capsuleDistance; - if (findRayCapsuleIntersection(relativeOrigin, direction, start, end, - (startRadius + endRadius) / 2.0f, capsuleDistance)) { - minDistance = qMin(minDistance, capsuleDistance); - } - } - if (minDistance < FLT_MAX) { - distance = minDistance; - return true; - } - return false; -} - -bool Model::findCollisions(const QVector shapes, CollisionList& collisions) { - bool collided = false; - for (int i = 0; i < shapes.size(); ++i) { - const Shape* theirShape = shapes[i]; - if (!theirShape) { - continue; - } - for (int j = 0; j < _shapes.size(); ++j) { - const Shape* ourShape = _shapes[j]; - if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { - collided = true; - } - } - } - return collided; -} - -bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, - CollisionList& collisions, int skipIndex) { - bool collided = false; - SphereShape sphere(sphereRadius, sphereCenter); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - const FBXJoint& joint = geometry.joints[i]; - if (joint.parentIndex != -1) { - if (skipIndex != -1) { - int ancestorIndex = joint.parentIndex; - do { - if (ancestorIndex == skipIndex) { - goto outerContinue; - } - ancestorIndex = geometry.joints[ancestorIndex].parentIndex; - - } while (ancestorIndex != -1); - } - } - if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - outerContinue: ; - } - return collided; -} - -bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - bool collided = false; - PlaneShape planeShape(plane); - for (int i = 0; i < _shapes.size(); i++) { - if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { - CollisionInfo* collision = collisions.getLastCollision(); - collision->_data = (void*)(this); - collision->_intData = i; - collided = true; - } - } - return collided; + // TODO: implement this when we know how to build shapes for regular Models } class Blender : public QRunnable { @@ -1441,41 +1141,6 @@ void Model::renderJointCollisionShapes(float alpha) { glPopMatrix(); } -void Model::renderBoundingCollisionShapes(float alpha) { - if (_shapes.isEmpty()) { - // the bounding shape has not been propery computed - // so no need to render it - return; - } - glPushMatrix(); - - Application::getInstance()->loadTranslatedViewMatrix(_translation); - - // draw a blue sphere at the capsule endpoint - glm::vec3 endPoint; - _boundingShape.getEndPoint(endPoint); - endPoint = endPoint - _translation; - glTranslatef(endPoint.x, endPoint.y, endPoint.z); - glColor4f(0.6f, 0.6f, 0.8f, alpha); - glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); - - // draw a yellow sphere at the capsule startpoint - glm::vec3 startPoint; - _boundingShape.getStartPoint(startPoint); - startPoint = startPoint - _translation; - glm::vec3 axis = endPoint - startPoint; - glTranslatef(-axis.x, -axis.y, -axis.z); - glColor4f(0.8f, 0.8f, 0.6f, alpha); - glutSolidSphere(_boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); - - // draw a green cylinder between the two points - glm::vec3 origin(0.0f); - glColor4f(0.6f, 0.8f, 0.6f, alpha); - Avatar::renderJointConnectingCone( origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius()); - - glPopMatrix(); -} - void Model::setBlendedVertices(const QVector& vertices, const QVector& normals) { if (_blendedVertexBuffers.isEmpty()) { return; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 20612a0e21..8851f42eb1 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -16,7 +16,6 @@ #include #include -#include #include #include @@ -132,34 +131,15 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - // virtual override from PhysicalEntity + // virtual overrides from PhysicalEntity virtual void buildShapes(); - - void resetShapePositions(); // DEBUG method virtual void updateShapePositions(); void renderJointCollisionShapes(float alpha); - void renderBoundingCollisionShapes(float alpha); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - - /// \param shapes list of pointers shapes to test against Model - /// \param collisions list to store collision results - /// \return true if at least one shape collided agains Model - bool findCollisions(const QVector shapes, CollisionList& collisions); - - bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - CollisionList& collisions, int skipIndex = -1); - - bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); - - float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } - /// Sets blended vertices computed in a separate thread. void setBlendedVertices(const QVector& vertices, const QVector& normals); - const CapsuleShape& getBoundingShape() const { return _boundingShape; } - protected: QSharedPointer _geometry; @@ -176,9 +156,6 @@ protected: QVector _jointStates; - CapsuleShape _boundingShape; - glm::vec3 _boundingShapeLocalOffset; - class MeshState { public: QVector clusterMatrices; @@ -222,8 +199,6 @@ protected: /// first free ancestor. float getLimbLength(int jointIndex) const; - void computeBoundingShape(const FBXGeometry& geometry); - private: friend class AnimationHandle; diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp index 3084c31529..602708d4ad 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -11,9 +11,9 @@ #include "PhysicalEntity.h" #include "Shape.h" +#include "ShapeCollider.h" -PhysicalEntity::PhysicalEntity(EntityType type) : - _entityType(type), +PhysicalEntity::PhysicalEntity() : _translation(0.0f), _rotation(), _boundingRadius(0.0f), @@ -61,4 +61,93 @@ void PhysicalEntity::clearShapes() { _shapes.clear(); } +bool PhysicalEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { + /* TODO: Andrew to make this work + int numShapes = _shapes.size(); + float minDistance = FLT_MAX; + for (int j = 0; j < numShapes; ++j) { + const Shape* shape = _shapes[j]; + float thisDistance = FLT_MAX; + if (shape && ShapeCollider::findRayIntersection(ourShape, origin, direction, thisDistance)) { + if (thisDistance < minDistance) { + minDistance = thisDistance; + } + } + } + if (minDistance < FLT_MAX) { + distance = minDistance; + return true; + } + */ + return false; +} +bool PhysicalEntity::findCollisions(const QVector shapes, CollisionList& collisions) { + bool collided = false; + int numTheirShapes = shapes.size(); + for (int i = 0; i < numTheirShapes; ++i) { + const Shape* theirShape = shapes[i]; + if (!theirShape) { + continue; + } + int numOurShapes = _shapes.size(); + for (int j = 0; j < numOurShapes; ++j) { + const Shape* ourShape = _shapes[j]; + if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { + collided = true; + } + } + } + return collided; +} + +bool PhysicalEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, + CollisionList& collisions, int skipIndex) { + bool collided = false; + // TODO: Andrew to implement this or make it unecessary + /* + SphereShape sphere(sphereRadius, sphereCenter); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _shapes.size(); i++) { + Shape* shape = _shapes[i]; + if (!shape) { + continue; + } + const FBXJoint& joint = geometry.joints[i]; + if (joint.parentIndex != -1) { + if (skipIndex != -1) { + int ancestorIndex = joint.parentIndex; + do { + if (ancestorIndex == skipIndex) { + goto outerContinue; + } + ancestorIndex = geometry.joints[ancestorIndex].parentIndex; + + } while (ancestorIndex != -1); + } + } + if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { + CollisionInfo* collision = collisions.getLastCollision(); + collision->_data = (void*)(this); + collision->_intData = i; + collided = true; + } + outerContinue: ; + } + */ + return collided; +} + +bool PhysicalEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { + bool collided = false; + PlaneShape planeShape(plane); + for (int i = 0; i < _shapes.size(); i++) { + if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { + CollisionInfo* collision = collisions.getLastCollision(); + collision->_data = (void*)(this); + collision->_intData = i; + collided = true; + } + } + return collided; +} diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h index 22f2bff67b..9e95449812 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicalEntity.h @@ -17,6 +17,8 @@ #include #include +#include "CollisionInfo.h" + class Shape; // PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a @@ -26,12 +28,7 @@ class Shape; class PhysicalEntity { public: - enum EntityType { - ENTITY_UNKNOWN, - ENTITY_MODEL, - }; - - PhysicalEntity(EntityType type = ENTITY_UNKNOWN); + PhysicalEntity(); virtual ~PhysicalEntity() {} void setTranslation(const glm::vec3& translation); @@ -41,8 +38,6 @@ public: const glm::quat& getRotation() const { return _rotation; } float getBoundingRadius() const { return _boundingRadius; } - EntityType getEntityType() const { return _entityType; } - void setShapeBackPointers(); void setEnableShapes(bool enable); @@ -50,8 +45,12 @@ public: virtual void buildShapes() = 0; virtual void clearShapes(); + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; + bool findCollisions(const QVector shapes, CollisionList& collisions); + bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex); + bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); + protected: - EntityType _entityType; glm::vec3 _translation; glm::quat _rotation; float _boundingRadius; From 9e839f098083c2b451095db95e8083b2b0efd378 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 08:55:48 -0700 Subject: [PATCH 026/105] Shape::getCenter() -> Shape::getTranslation() --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 12 ++-- interface/src/renderer/Model.cpp | 2 +- libraries/octree/src/Octree.cpp | 2 +- libraries/shared/src/CapsuleShape.cpp | 6 +- libraries/shared/src/ListShape.cpp | 8 +-- libraries/shared/src/ListShape.h | 4 +- libraries/shared/src/PlaneShape.cpp | 8 +-- libraries/shared/src/Ragdoll.cpp | 2 +- libraries/shared/src/Shape.h | 16 ++--- libraries/shared/src/ShapeCollider.cpp | 36 +++++----- libraries/shared/src/SphereShape.cpp | 6 +- libraries/shared/src/VerletSphereShape.cpp | 6 +- libraries/shared/src/VerletSphereShape.h | 4 +- tests/physics/src/ShapeColliderTests.cpp | 84 +++++++++++----------- 15 files changed, 99 insertions(+), 101 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 7e82263ddc..9e89ba4f56 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1245,7 +1245,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { float capsuleHalfHeight = boundingShape.getHalfHeight(); const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight; const float MIN_STEP_HEIGHT = 0.0f; - glm::vec3 footBase = boundingShape.getCenter() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; + glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection; float highestStep = 0.0f; float lowestStep = MAX_STEP_HEIGHT; glm::vec3 floorPoint; @@ -1262,7 +1262,7 @@ void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) { isTrapped = true; if (_trapDuration > MAX_TRAP_PERIOD) { - float distance = glm::dot(boundingShape.getCenter() - cubeCenter, _worldUpDirection); + float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection); if (distance < 0.0f) { distance = fabsf(distance) + 0.5f * cubeSide; } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 2bb4b9d5e3..0b00398df8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -596,7 +596,7 @@ void SkeletonModel::updateShapePositionsLegacy() { glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); Shape* shape = _shapes[i]; if (shape) { - shape->setCenter(worldPosition); + shape->setTranslation(worldPosition); shape->setRotation(_rotation * stateRotation * joint.shapeRotation); float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); if (distance > _boundingRadius) { @@ -608,7 +608,7 @@ void SkeletonModel::updateShapePositionsLegacy() { } } _shapesAreDirty = false; - _boundingShape.setCenter(rootPosition + _rotation * _boundingShapeLocalOffset); + _boundingShape.setTranslation(rootPosition + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } } @@ -665,7 +665,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { const FBXJoint& joint = geometry.joints[i]; glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; - shape->setCenter(localPosition); + shape->setTranslation(localPosition); shape->setRotation(finalRotations[i] * joint.shapeRotation); float distance = glm::length(localPosition) + shape->getBoundingRadius(); if (distance > _boundingRadius) { @@ -684,7 +684,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { } Extents shapeExtents; shapeExtents.reset(); - glm::vec3 localPosition = shape->getCenter(); + glm::vec3 localPosition = shape->getTranslation(); int type = shape->getType(); if (type == Shape::CAPSULE_SHAPE) { // add the two furthest surface points of the capsule @@ -739,11 +739,11 @@ void SkeletonModel::resetShapePositions() { for (int i = 0; i < _shapes.size(); i++) { Shape* shape = _shapes[i]; if (shape) { - shape->setCenter(_translation + _rotation * shape->getCenter()); + shape->setTranslation(_translation + _rotation * shape->getTranslation()); shape->setRotation(_rotation * shape->getRotation()); } } - _boundingShape.setCenter(_translation + _rotation * _boundingShapeLocalOffset); + _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 2057f3248b..c3a3387e0f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1102,7 +1102,7 @@ void Model::renderJointCollisionShapes(float alpha) { glPushMatrix(); if (shape->getType() == Shape::SPHERE_SHAPE) { // shapes are stored in world-frame, so we have to transform into model frame - glm::vec3 position = shape->getCenter() - _translation; + glm::vec3 position = shape->getTranslation() - _translation; glTranslatef(position.x, position.y, position.z); const glm::quat& rotation = shape->getRotation(); glm::vec3 axis = glm::axis(rotation); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index f43921e0d1..2af86663f7 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -757,7 +757,7 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) { // coarse check against bounds AACube cube = element->getAACube(); cube.scale(TREE_SCALE); - if (!cube.expandedContains(args->shape->getCenter(), args->shape->getBoundingRadius())) { + if (!cube.expandedContains(args->shape->getTranslation(), args->shape->getBoundingRadius())) { return false; } if (!element->isLeaf()) { diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index 83276a9cc2..ab8cbdae3d 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -40,12 +40,12 @@ CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm: /// \param[out] startPoint is the center of start cap void CapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = _center - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + startPoint = _translation - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } /// \param[out] endPoint is the center of the end cap void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = _center + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); + endPoint = _translation + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f); } void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { @@ -71,7 +71,7 @@ void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { glm::vec3 axis = endPoint - startPoint; - _center = 0.5f * (endPoint + startPoint); + _translation = 0.5f * (endPoint + startPoint); float height = glm::length(axis); if (height > EPSILON) { _halfHeight = 0.5f * height; diff --git a/libraries/shared/src/ListShape.cpp b/libraries/shared/src/ListShape.cpp index 4876111054..67ec32d4b1 100644 --- a/libraries/shared/src/ListShape.cpp +++ b/libraries/shared/src/ListShape.cpp @@ -14,7 +14,7 @@ // ListShapeEntry void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) { - _shape->setCenter(rootPosition + rootRotation * _localPosition); + _shape->setTranslation(rootPosition + rootRotation * _localPosition); _shape->setRotation(_localRotation * rootRotation); } @@ -24,9 +24,9 @@ ListShape::~ListShape() { clear(); } -void ListShape::setCenter(const glm::vec3& center) { +void ListShape::setTranslation(const glm::vec3& position) { _subShapeTransformsAreDirty = true; - Shape::setCenter(center); + Shape::setTranslation(position); } void ListShape::setRotation(const glm::quat& rotation) { @@ -44,7 +44,7 @@ const Shape* ListShape::getSubShape(int index) const { void ListShape::updateSubTransforms() { if (_subShapeTransformsAreDirty) { for (int i = 0; i < _subShapeEntries.size(); ++i) { - _subShapeEntries[i].updateTransform(_center, _rotation); + _subShapeEntries[i].updateTransform(_translation, _rotation); } _subShapeTransformsAreDirty = false; } diff --git a/libraries/shared/src/ListShape.h b/libraries/shared/src/ListShape.h index 295aef8fdd..bd150c8246 100644 --- a/libraries/shared/src/ListShape.h +++ b/libraries/shared/src/ListShape.h @@ -42,9 +42,7 @@ public: ~ListShape(); - void setCenter(const glm::vec3& center); -// void setPosition(const glm::vec3& position); -// glm::vec3 getPosition() const { return _position; } + void setTranslation(const glm::vec3& position); void setRotation(const glm::quat& rotation); const Shape* getSubShape(int index) const; diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp index 76a960134b..15ea281510 100644 --- a/libraries/shared/src/PlaneShape.cpp +++ b/libraries/shared/src/PlaneShape.cpp @@ -18,7 +18,7 @@ PlaneShape::PlaneShape(const glm::vec4& coefficients) : Shape(Shape::PLANE_SHAPE) { glm::vec3 normal = glm::vec3(coefficients); - _center = -normal * coefficients.w; + _translation = -normal * coefficients.w; float angle = acosf(glm::dot(normal, UNROTATED_NORMAL)); if (angle > EPSILON) { @@ -36,7 +36,7 @@ glm::vec3 PlaneShape::getNormal() const { glm::vec4 PlaneShape::getCoefficients() const { glm::vec3 normal = _rotation * UNROTATED_NORMAL; - return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _center)); + return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _translation)); } bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const { @@ -44,9 +44,9 @@ bool PlaneShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3& float denominator = glm::dot(n, rayDirection); if (fabsf(denominator) < EPSILON) { // line is parallel to plane - return glm::dot(_center - rayStart, n) < EPSILON; + return glm::dot(_translation - rayStart, n) < EPSILON; } else { - float d = glm::dot(_center - rayStart, n) / denominator; + float d = glm::dot(_translation - rayStart, n) / denominator; if (d > 0.0f) { // ray points toward plane distance = d; diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 9aff3d5626..6a8515d3b4 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -76,7 +76,7 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio case Shape::SPHERE_SHAPE: { // sphere collides at endPoint SphereShape* sphere = static_cast(shape); - sphere->setCenter(translation + rotation * (*_points[1])); + sphere->setTranslation(translation + rotation * (*_points[1])); } break; case Shape::CAPSULE_SHAPE: { diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index e6b2bf3180..dae1114992 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -27,7 +27,7 @@ public: LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _center(0.f), _rotation() { } + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() { } virtual ~Shape() {} int getType() const { return _type; } @@ -35,33 +35,33 @@ public: // const glm::vec3& getPosition() const { return _position; } const glm::quat& getRotation() const { return _rotation; } -// virtual void setPosition(const glm::vec3& position) { _center = position; } +// virtual void setPosition(const glm::vec3& position) { _translation = position; } virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } PhysicalEntity* getEntity() const { return _owningEntity; } - virtual void setCenter(const glm::vec3& center) { _center = center; } - virtual glm::vec3 getCenter() const { return _center; } + virtual void setTranslation(const glm::vec3& center) { _translation = center; } + virtual glm::vec3 getTranslation() const { return _translation; } virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; protected: // these ctors are protected (used by derived classes only) - Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(0.f), _rotation() {} + Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() {} Shape(Type type, const glm::vec3& position) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(position), _rotation() {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation() {} Shape(Type type, const glm::vec3& position, const glm::quat& rotation) - : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _center(position), _rotation(rotation) {} + : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation(rotation) {} void setBoundingRadius(float radius) { _boundingRadius = radius; } int _type; PhysicalEntity* _owningEntity; float _boundingRadius; - glm::vec3 _center; + glm::vec3 _translation; glm::quat _rotation; }; diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 8c32a3f8da..2b672f764d 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -157,7 +157,7 @@ bool collideShapeWithAACube(const Shape* shapeA, const glm::vec3& cubeCenter, fl } bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, CollisionList& collisions) { - glm::vec3 BA = sphereB->getCenter() - sphereA->getCenter(); + glm::vec3 BA = sphereB->getTranslation() - sphereA->getTranslation(); float distanceSquared = glm::dot(BA, BA); float totalRadius = sphereA->getRadius() + sphereB->getRadius(); if (distanceSquared < totalRadius * totalRadius) { @@ -175,7 +175,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis if (collision) { collision->_penetration = BA * (totalRadius - distance); // contactPoint is on surface of A - collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * BA; + collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * BA; collision->_shapeA = sphereA; collision->_shapeB = sphereB; return true; @@ -186,7 +186,7 @@ bool sphereSphere(const SphereShape* sphereA, const SphereShape* sphereB, Collis bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions) { // find sphereA's closest approach to axis of capsuleB - glm::vec3 BA = capsuleB->getCenter() - sphereA->getCenter(); + glm::vec3 BA = capsuleB->getTranslation() - sphereA->getTranslation(); glm::vec3 capsuleAxis; capsuleB->computeNormalizedAxis(capsuleAxis); float axialDistance = - glm::dot(BA, capsuleAxis); @@ -221,7 +221,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col // penetration points from A into B collision->_penetration = (totalRadius - radialDistance) * radialAxis; // points from A into B // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * radialAxis; + collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * radialAxis; collision->_shapeA = sphereA; collision->_shapeB = capsuleB; } else { @@ -244,7 +244,7 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col float sign = (axialDistance > 0.0f) ? -1.0f : 1.0f; collision->_penetration = (sign * (totalRadius + capsuleB->getHalfHeight() - absAxialDistance)) * capsuleAxis; // contactPoint is on surface of sphereA - collision->_contactPoint = sphereA->getCenter() + (sign * sphereA->getRadius()) * capsuleAxis; + collision->_contactPoint = sphereA->getTranslation() + (sign * sphereA->getRadius()) * capsuleAxis; collision->_shapeA = sphereA; collision->_shapeB = capsuleB; } @@ -255,13 +255,13 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions) { glm::vec3 penetration; - if (findSpherePlanePenetration(sphereA->getCenter(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { + if (findSpherePlanePenetration(sphereA->getTranslation(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) { CollisionInfo* collision = collisions.getNewCollision(); if (!collision) { return false; // collision list is full } collision->_penetration = penetration; - collision->_contactPoint = sphereA->getCenter() + sphereA->getRadius() * glm::normalize(penetration); + collision->_contactPoint = sphereA->getTranslation() + sphereA->getRadius() * glm::normalize(penetration); collision->_shapeA = sphereA; collision->_shapeB = planeB; return true; @@ -271,7 +271,7 @@ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, Collision bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions) { // find sphereB's closest approach to axis of capsuleA - glm::vec3 AB = capsuleA->getCenter() - sphereB->getCenter(); + glm::vec3 AB = capsuleA->getTranslation() - sphereB->getTranslation(); glm::vec3 capsuleAxis; capsuleA->computeNormalizedAxis(capsuleAxis); float axialDistance = - glm::dot(AB, capsuleAxis); @@ -287,14 +287,14 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col } // closestApproach = point on capsuleA's axis that is closest to sphereB's center - glm::vec3 closestApproach = capsuleA->getCenter() + axialDistance * capsuleAxis; + 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->getCenter() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; - radialAxis = closestApproach - sphereB->getCenter(); + closestApproach = capsuleA->getTranslation() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; + radialAxis = closestApproach - sphereB->getTranslation(); radialDistance2 = glm::length2(radialAxis); if (radialDistance2 > totalRadius2) { return false; @@ -349,8 +349,8 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, capsuleA->computeNormalizedAxis(axisA); glm::vec3 axisB; capsuleB->computeNormalizedAxis(axisB); - glm::vec3 centerA = capsuleA->getCenter(); - glm::vec3 centerB = capsuleB->getCenter(); + glm::vec3 centerA = capsuleA->getTranslation(); + glm::vec3 centerB = capsuleB->getTranslation(); // NOTE: The formula for closest approach between two lines is: // d = [(B - A) . (a - (a.b)b)] / (1 - (a.b)^2) @@ -505,13 +505,13 @@ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, Collis bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions) { glm::vec3 penetration; - if (findSpherePlanePenetration(sphereB->getCenter(), sphereB->getRadius(), planeA->getCoefficients(), 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->getCenter() + + collision->_contactPoint = sphereB->getTranslation() + (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration; collision->_shapeA = planeA; collision->_shapeB = sphereB; @@ -803,21 +803,21 @@ 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->getCenter(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); + return sphereAACube(sphereA->getTranslation(), sphereA->getRadius(), cubeCenter, cubeSide, collisions); } bool capsuleAACube(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); - float offset = glm::dot(cubeCenter - capsuleA->getCenter(), capsuleAxis); + float offset = glm::dot(cubeCenter - capsuleA->getTranslation(), capsuleAxis); float halfHeight = capsuleA->getHalfHeight(); if (offset > halfHeight) { offset = halfHeight; } else if (offset < -halfHeight) { offset = -halfHeight; } - glm::vec3 nearestApproach = capsuleA->getCenter() + offset * capsuleAxis; + glm::vec3 nearestApproach = capsuleA->getTranslation() + offset * capsuleAxis; // collide nearest approach like a sphere at that point return sphereAACube(nearestApproach, capsuleA->getRadius(), cubeCenter, cubeSide, collisions); } diff --git a/libraries/shared/src/SphereShape.cpp b/libraries/shared/src/SphereShape.cpp index ecb97bb86d..c77b0c97fb 100644 --- a/libraries/shared/src/SphereShape.cpp +++ b/libraries/shared/src/SphereShape.cpp @@ -17,14 +17,14 @@ bool SphereShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec3 float r2 = _boundingRadius * _boundingRadius; // compute closest approach (CA) - float a = glm::dot(_center - rayStart, rayDirection); // a = distance from ray-start to CA - float b2 = glm::distance2(_center, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA + float a = glm::dot(_translation - rayStart, rayDirection); // a = distance from ray-start to CA + float b2 = glm::distance2(_translation, rayStart + a * rayDirection); // b2 = squared distance from sphere-center to CA if (b2 > r2) { // ray does not hit sphere return false; } float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along rayDirection - float d2 = glm::distance2(rayStart, _center); // d2 = squared distance from sphere-center to ray-start + float d2 = glm::distance2(rayStart, _translation); // d2 = squared distance from sphere-center to ray-start if (a < 0.0f) { // ray points away from sphere-center if (d2 > r2) { diff --git a/libraries/shared/src/VerletSphereShape.cpp b/libraries/shared/src/VerletSphereShape.cpp index b696f74dfb..f17184f434 100644 --- a/libraries/shared/src/VerletSphereShape.cpp +++ b/libraries/shared/src/VerletSphereShape.cpp @@ -22,11 +22,11 @@ VerletSphereShape::VerletSphereShape(float radius, glm::vec3* centerPoint) : Sph } // virtual from Shape class -void VerletSphereShape::setCenter(const glm::vec3& center) { - *_point = center; +void VerletSphereShape::setTranslation(const glm::vec3& position) { + *_point = position; } // virtual from Shape class -glm::vec3 VerletSphereShape::getCenter() { +glm::vec3 VerletSphereShape::getTranslation() { return *_point; } diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/shared/src/VerletSphereShape.h index 63a8531ffc..e6a70e948e 100644 --- a/libraries/shared/src/VerletSphereShape.h +++ b/libraries/shared/src/VerletSphereShape.h @@ -23,8 +23,8 @@ public: VerletSphereShape(float radius, glm::vec3* centerPoint); - void setCenter(const glm::vec3& center); - glm::vec3 getCenter(); + void setTranslation(const glm::vec3& position); + glm::vec3 getTranslation(); protected: // NOTE: VerletSphereShape does NOT own its _point diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index f9775b02ee..4ec823f738 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -123,8 +123,8 @@ void ShapeColliderTests::sphereTouchesSphere() { } // contactPoint is on surface of sphereA - glm::vec3 AtoB = sphereB.getCenter() - sphereA.getCenter(); - glm::vec3 expectedContactPoint = sphereA.getCenter() + radiusA * glm::normalize(AtoB); + glm::vec3 AtoB = sphereB.getTranslation() - sphereA.getTranslation(); + glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * glm::normalize(AtoB); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -153,8 +153,8 @@ void ShapeColliderTests::sphereTouchesSphere() { } // contactPoint is on surface of sphereA - glm::vec3 BtoA = sphereA.getCenter() - sphereB.getCenter(); - glm::vec3 expectedContactPoint = sphereB.getCenter() + radiusB * glm::normalize(BtoA); + glm::vec3 BtoA = sphereA.getTranslation() - sphereB.getTranslation(); + glm::vec3 expectedContactPoint = sphereB.getTranslation() + radiusB * glm::normalize(BtoA); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -182,7 +182,7 @@ void ShapeColliderTests::sphereMissesCapsule() { glm::quat rotation = glm::angleAxis(angle, axis); glm::vec3 translation(15.1f, -27.1f, -38.6f); capsuleB.setRotation(rotation); - capsuleB.setCenter(translation); + capsuleB.setTranslation(translation); CollisionList collisions(16); @@ -193,7 +193,7 @@ void ShapeColliderTests::sphereMissesCapsule() { for (int i = 0; i < numberOfSteps; ++i) { // translate sphereA into world-frame glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis; - sphereA.setCenter(rotation * localPosition + translation); + sphereA.setTranslation(rotation * localPosition + translation); // sphereA agains capsuleB if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) @@ -236,7 +236,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { int numCollisions = 0; { // sphereA collides with capsuleB's cylindrical wall - sphereA.setCenter(radialOffset * xAxis); + sphereA.setTranslation(radialOffset * xAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -258,7 +258,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getCenter() - radiusA * xAxis; + glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -287,8 +287,8 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of capsuleB - glm::vec3 BtoA = sphereA.getCenter() - capsuleB.getCenter(); - glm::vec3 closestApproach = capsuleB.getCenter() + glm::dot(BtoA, yAxis) * yAxis; + glm::vec3 BtoA = sphereA.getTranslation() - capsuleB.getTranslation(); + glm::vec3 closestApproach = capsuleB.getTranslation() + glm::dot(BtoA, yAxis) * yAxis; expectedContactPoint = closestApproach + radiusB * glm::normalize(BtoA - closestApproach); inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { @@ -299,7 +299,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits end cap at axis glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setCenter(axialOffset * yAxis); + sphereA.setTranslation(axialOffset * yAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -321,7 +321,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getCenter() - radiusA * yAxis; + glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -362,7 +362,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } { // sphereA hits start cap at axis glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; - sphereA.setCenter(axialOffset * yAxis); + sphereA.setTranslation(axialOffset * yAxis); if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) { @@ -384,7 +384,7 @@ void ShapeColliderTests::sphereTouchesCapsule() { } // contactPoint is on surface of sphereA - glm::vec3 expectedContactPoint = sphereA.getCenter() + radiusA * yAxis; + glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -446,7 +446,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { CollisionList collisions(16); // side by side - capsuleB.setCenter((1.01f * totalRadius) * xAxis); + capsuleB.setTranslation((1.01f * totalRadius) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -461,7 +461,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { } // end to end - capsuleB.setCenter((1.01f * totalHalfLength) * xAxis); + capsuleB.setTranslation((1.01f * totalHalfLength) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -478,7 +478,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { // rotate B and move it to the side glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); - capsuleB.setCenter((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + capsuleB.setTranslation((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -516,7 +516,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { int numCollisions = 0; { // side by side - capsuleB.setCenter((0.99f * totalRadius) * xAxis); + capsuleB.setTranslation((0.99f * totalRadius) * xAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { std::cout << __FILE__ << ":" << __LINE__ @@ -536,7 +536,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { } { // end to end - capsuleB.setCenter((0.99f * totalHalfLength) * yAxis); + capsuleB.setTranslation((0.99f * totalHalfLength) * yAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { @@ -559,7 +559,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { { // rotate B and move it to the side glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); - capsuleB.setCenter((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + capsuleB.setTranslation((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) { @@ -584,7 +584,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis; - capsuleB.setCenter(positionB); + capsuleB.setTranslation(positionB); // capsuleA vs capsuleB if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) @@ -605,7 +605,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << " actual = " << collision->_penetration; } - glm::vec3 expectedContactPoint = capsuleA.getCenter() + radiusA * xAxis; + glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -633,7 +633,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << std::endl; } - expectedContactPoint = capsuleB.getCenter() - (radiusB + halfHeightB) * xAxis; + expectedContactPoint = capsuleB.getTranslation() - (radiusB + halfHeightB) * xAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -649,7 +649,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); capsuleB.setRotation(rotation); glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis; - capsuleB.setCenter(positionB); + capsuleB.setTranslation(positionB); // capsuleA vs capsuleB if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) @@ -671,7 +671,7 @@ void ShapeColliderTests::capsuleTouchesCapsule() { << std::endl; } - glm::vec3 expectedContactPoint = capsuleA.getCenter() + radiusA * zAxis + shift * yAxis; + glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * zAxis + shift * yAxis; inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); if (fabs(inaccuracy) > EPSILON) { std::cout << __FILE__ << ":" << __LINE__ @@ -708,7 +708,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { float overlap = 0.25f; float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; sphereCenter = cubeCenter + sphereOffset * axis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; @@ -741,7 +741,7 @@ void ShapeColliderTests::sphereTouchesAACubeFaces() { float overlap = 1.25f * sphereRadius; float sphereOffset = 0.5f * cubeSide + sphereRadius - overlap; sphereCenter = cubeCenter + sphereOffset * axis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube." @@ -815,7 +815,7 @@ void ShapeColliderTests::sphereTouchesAACubeEdges() { float overlap = 0.25f; sphereCenter = cubeCenter + (lengthAxis * 0.5f * cubeSide + sphereRadius - overlap) * axis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (!ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should collide with cube. axis = " << axis << std::endl; @@ -857,42 +857,42 @@ void ShapeColliderTests::sphereMissesAACube() { // top sphereCenter = cubeCenter + sphereOffset * yAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // bottom sphereCenter = cubeCenter - sphereOffset * yAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // left sphereCenter = cubeCenter + sphereOffset * xAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // right sphereCenter = cubeCenter - sphereOffset * xAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // forward sphereCenter = cubeCenter + sphereOffset * zAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } // back sphereCenter = cubeCenter - sphereOffset * zAxis; - sphere.setCenter(sphereCenter); + sphere.setTranslation(sphereCenter); if (ShapeCollider::sphereAACube(&sphere, cubeCenter, cubeSide, collisions)){ std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should NOT collide with cube" << std::endl; } @@ -955,7 +955,7 @@ void ShapeColliderTests::rayHitsSphere() { rayDirection = rotation * unrotatedRayDirection; sphere.setRadius(radius); - sphere.setCenter(rotation * translation); + sphere.setTranslation(rotation * translation); float distance = FLT_MAX; if (!sphere.findRayIntersection(rayStart, rayDirection, distance)) { @@ -994,7 +994,7 @@ void ShapeColliderTests::rayBarelyHitsSphere() { rayStart = rotation * (rayStart + translation); rayDirection = rotation * rayDirection; - sphere.setCenter(rotation * translation); + sphere.setTranslation(rotation * translation); // ...and test again distance = FLT_MAX; @@ -1032,7 +1032,7 @@ void ShapeColliderTests::rayBarelyMissesSphere() { rayStart = rotation * (rayStart + translation); rayDirection = rotation * rayDirection; - sphere.setCenter(rotation * translation); + sphere.setTranslation(rotation * translation); // ...and test again distance = FLT_MAX; @@ -1186,7 +1186,7 @@ void ShapeColliderTests::rayHitsPlane() { float planeDistanceFromOrigin = 3.579; glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); PlaneShape plane; - plane.setCenter(planePosition); + plane.setTranslation(planePosition); // make a simple ray float startDistance = 1.234f; @@ -1209,7 +1209,7 @@ void ShapeColliderTests::rayHitsPlane() { glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); glm::quat rotation = glm::angleAxis(angle, axis); - plane.setCenter(rotation * planePosition); + plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); rayStart = rotation * rayStart; rayDirection = rotation * rayDirection; @@ -1231,7 +1231,7 @@ void ShapeColliderTests::rayMissesPlane() { float planeDistanceFromOrigin = 3.579; glm::vec3 planePosition(0.0f, planeDistanceFromOrigin, 0.0f); PlaneShape plane; - plane.setCenter(planePosition); + plane.setTranslation(planePosition); { // parallel rays should miss float startDistance = 1.234f; @@ -1251,7 +1251,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); glm::quat rotation = glm::angleAxis(angle, axis); - plane.setCenter(rotation * planePosition); + plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); rayStart = rotation * rayStart; rayDirection = rotation * rayDirection; @@ -1283,7 +1283,7 @@ void ShapeColliderTests::rayMissesPlane() { glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); glm::quat rotation = glm::angleAxis(angle, axis); - plane.setCenter(rotation * planePosition); + plane.setTranslation(rotation * planePosition); plane.setRotation(rotation); rayStart = rotation * rayStart; rayDirection = rotation * rayDirection; From 96eebec0fcc4f803a313dd626811e90ef9e89f47 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 14:06:00 -0700 Subject: [PATCH 027/105] unit tests for VerletShapes --- libraries/shared/src/CapsuleShape.cpp | 22 ++++---- libraries/shared/src/CapsuleShape.h | 5 +- libraries/shared/src/Shape.h | 15 +++--- libraries/shared/src/ShapeCollider.cpp | 1 - libraries/shared/src/VerletCapsuleShape.cpp | 56 ++++++++++++++++++--- libraries/shared/src/VerletCapsuleShape.h | 14 ++++-- libraries/shared/src/VerletSphereShape.cpp | 2 +- libraries/shared/src/VerletSphereShape.h | 9 ++-- tests/physics/src/ShapeColliderTests.cpp | 2 +- tests/physics/src/main.cpp | 2 + 10 files changed, 92 insertions(+), 36 deletions(-) diff --git a/libraries/shared/src/CapsuleShape.cpp b/libraries/shared/src/CapsuleShape.cpp index ab8cbdae3d..12ab6ba479 100644 --- a/libraries/shared/src/CapsuleShape.cpp +++ b/libraries/shared/src/CapsuleShape.cpp @@ -18,9 +18,6 @@ #include "SharedUtil.h" -// default axis of CapsuleShape is Y-axis -const glm::vec3 localAxis(0.0f, 1.0f, 0.0f); - CapsuleShape::CapsuleShape() : Shape(Shape::CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {} CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(Shape::CAPSULE_SHAPE), @@ -50,7 +47,7 @@ void CapsuleShape::getEndPoint(glm::vec3& endPoint) const { void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { // default axis of a capsule is along the yAxis - axis = _rotation * glm::vec3(0.0f, 1.0f, 0.0f); + axis = _rotation * DEFAULT_CAPSULE_AXIS; } void CapsuleShape::setRadius(float radius) { @@ -76,12 +73,7 @@ void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& en if (height > EPSILON) { _halfHeight = 0.5f * height; axis /= height; - glm::vec3 yAxis(0.0f, 1.0f, 0.0f); - float angle = glm::angle(axis, yAxis); - if (angle > EPSILON) { - axis = glm::normalize(glm::cross(yAxis, axis)); - _rotation = glm::angleAxis(angle, axis); - } + computeNewRotation(axis); } updateBoundingRadius(); } @@ -94,3 +86,13 @@ bool CapsuleShape::findRayIntersection(const glm::vec3& rayStart, const glm::vec // TODO: implement the raycast to return inside surface intersection for the internal rayStart. return findRayCapsuleIntersection(rayStart, rayDirection, capsuleStart, capsuleEnd, _radius, distance); } + +// static +glm::quat CapsuleShape::computeNewRotation(const glm::vec3& newAxis) { + float angle = glm::angle(newAxis, DEFAULT_CAPSULE_AXIS); + if (angle > EPSILON) { + glm::vec3 rotationAxis = glm::normalize(glm::cross(DEFAULT_CAPSULE_AXIS, newAxis)); + return glm::angleAxis(angle, rotationAxis); + } + return glm::quat(); +} diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index 41474e3e38..e97a6a4917 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -15,6 +15,8 @@ #include "Shape.h" // default axis of CapsuleShape is Y-axis +const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f); + class CapsuleShape : public Shape { public: @@ -26,7 +28,7 @@ public: virtual ~CapsuleShape() {} float getRadius() const { return _radius; } - float getHalfHeight() const { return _halfHeight; } + virtual float getHalfHeight() const { return _halfHeight; } /// \param[out] startPoint is the center of start cap virtual void getStartPoint(glm::vec3& startPoint) const; @@ -47,6 +49,7 @@ public: protected: virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } + static glm::quat computeNewRotation(const glm::vec3& newAxis); float _radius; float _halfHeight; diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index dae1114992..0a03ae18e3 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -31,18 +31,17 @@ public: virtual ~Shape() {} int getType() const { return _type; } - float getBoundingRadius() const { return _boundingRadius; } -// const glm::vec3& getPosition() const { return _position; } - const glm::quat& getRotation() const { return _rotation; } - -// virtual void setPosition(const glm::vec3& position) { _translation = position; } - virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } PhysicalEntity* getEntity() const { return _owningEntity; } - virtual void setTranslation(const glm::vec3& center) { _translation = center; } - virtual glm::vec3 getTranslation() const { return _translation; } + float getBoundingRadius() const { return _boundingRadius; } + + virtual const glm::quat& getRotation() const { return _rotation; } + virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; } + + virtual void setTranslation(const glm::vec3& translation) { _translation = translation; } + virtual const glm::vec3& getTranslation() const { return _translation; } virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index 2b672f764d..ffb51660e2 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -24,7 +24,6 @@ namespace ShapeCollider { bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions) { - // ATM we only have two shape types so we just check every case. // TODO: make a fast lookup for correct method int typeA = shapeA->getType(); int typeB = shapeB->getType(); diff --git a/libraries/shared/src/VerletCapsuleShape.cpp b/libraries/shared/src/VerletCapsuleShape.cpp index 9cae13ba2d..b4117e40f7 100644 --- a/libraries/shared/src/VerletCapsuleShape.cpp +++ b/libraries/shared/src/VerletCapsuleShape.cpp @@ -28,6 +28,45 @@ VerletCapsuleShape::VerletCapsuleShape(float radius, glm::vec3* startPoint, glm: updateBoundingRadius(); } +const glm::quat& VerletCapsuleShape::getRotation() const { + // NOTE: The "rotation" of this shape must be computed on the fly, + // which makes this method MUCH more more expensive than you might expect. + glm::vec3 axis; + computeNormalizedAxis(axis); + VerletCapsuleShape* thisCapsule = const_cast(this); + thisCapsule->_rotation = computeNewRotation(axis); + return _rotation; +} + +void VerletCapsuleShape::setRotation(const glm::quat& rotation) { + // NOTE: this method will update the verlet points, which is probably not + // what you want to do. Only call this method if you know what you're doing. + + // update points such that they have the same center but a different axis + glm::vec3 center = getTranslation(); + float halfHeight = getHalfHeight(); + glm::vec3 axis = rotation * DEFAULT_CAPSULE_AXIS; + *_startPoint = center - halfHeight * axis; + *_endPoint = center + halfHeight * axis; +} + +void VerletCapsuleShape::setTranslation(const glm::vec3& position) { + // NOTE: this method will update the verlet points, which is probably not + // what you want to do. Only call this method if you know what you're doing. + + // update the points such that their center is at position + glm::vec3 movement = position - getTranslation(); + *_startPoint += movement; + *_endPoint += movement; +} + +const glm::vec3& VerletCapsuleShape::getTranslation() const { + // the "translation" of this shape must be computed on the fly + VerletCapsuleShape* thisCapsule = const_cast(this); + thisCapsule->_translation = 0.5f * ((*_startPoint) + (*_endPoint)); + return _translation; +} + // virtual float VerletCapsuleShape::getHalfHeight() const { return 0.5f * glm::distance(*_startPoint, *_endPoint); @@ -57,15 +96,20 @@ void VerletCapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { } // virtual -void VerletCapsuleShape::setHalfHeight(float height) { - // don't call this method because this is a verlet shape - assert(false); +void VerletCapsuleShape::setHalfHeight(float halfHeight) { + // push points along axis so they are 2*halfHeight apart + glm::vec3 center = getTranslation(); + glm::vec3 axis; + computeNormalizedAxis(axis); + *_startPoint = center - halfHeight * axis; + *_endPoint = center + halfHeight * axis; + _boundingRadius = _radius + halfHeight; } // virtual -void VerletCapsuleShape::setRadiusAndHalfHeight(float radius, float height) { - // don't call this method because this is a verlet shape - assert(false); +void VerletCapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) { + _radius = radius; + setHalfHeight(halfHeight); } // virtual diff --git a/libraries/shared/src/VerletCapsuleShape.h b/libraries/shared/src/VerletCapsuleShape.h index c06b9c7758..b2a7f8be30 100644 --- a/libraries/shared/src/VerletCapsuleShape.h +++ b/libraries/shared/src/VerletCapsuleShape.h @@ -24,8 +24,12 @@ class VerletCapsuleShape : public CapsuleShape { public: VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint); VerletCapsuleShape(float radius, glm::vec3* startPoint, glm::vec3* endPoint); - //VerletCapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation); - //VerletCapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint); + + // virtual overrides from Shape + const glm::quat& getRotation() const; + void setRotation(const glm::quat& rotation); + void setTranslation(const glm::vec3& position); + const glm::vec3& getTranslation() const; //float getRadius() const { return _radius; } virtual float getHalfHeight() const; @@ -40,14 +44,14 @@ public: void computeNormalizedAxis(glm::vec3& axis) const; //void setRadius(float radius); - void setHalfHeight(float height); - void setRadiusAndHalfHeight(float radius, float height); + void setHalfHeight(float halfHeight); + void setRadiusAndHalfHeight(float radius, float halfHeight); void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint); //void assignEndPoints(glm::vec3* startPoint, glm::vec3* endPoint); protected: - // NOTE: VerletCapsuleShape does NOT own its points. + // NOTE: VerletCapsuleShape does NOT own the data in its points. glm::vec3* _startPoint; glm::vec3* _endPoint; }; diff --git a/libraries/shared/src/VerletSphereShape.cpp b/libraries/shared/src/VerletSphereShape.cpp index f17184f434..2c659b8159 100644 --- a/libraries/shared/src/VerletSphereShape.cpp +++ b/libraries/shared/src/VerletSphereShape.cpp @@ -27,6 +27,6 @@ void VerletSphereShape::setTranslation(const glm::vec3& position) { } // virtual from Shape class -glm::vec3 VerletSphereShape::getTranslation() { +const glm::vec3& VerletSphereShape::getTranslation() const { return *_point; } diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/shared/src/VerletSphereShape.h index e6a70e948e..747d02ff7c 100644 --- a/libraries/shared/src/VerletSphereShape.h +++ b/libraries/shared/src/VerletSphereShape.h @@ -15,16 +15,19 @@ #include "SphereShape.h" // The VerletSphereShape is similar to a regular SphereShape, except it keeps a pointer -// to its center which is owned by some other data structure. This allows it to -// participate in a verlet integration engine. +// to its center which is owned by some other data structure (a verlet simulation system). +// This makes it easier for the points to be moved around by constraints in the system +// as well as collisions with the shape. +// class VerletSphereShape : public SphereShape { public: VerletSphereShape(glm::vec3* centerPoint); VerletSphereShape(float radius, glm::vec3* centerPoint); + // virtual overrides from Shape void setTranslation(const glm::vec3& position); - glm::vec3 getTranslation(); + const glm::vec3& getTranslation() const; protected: // NOTE: VerletSphereShape does NOT own its _point diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 4ec823f738..4d3c90b905 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -441,7 +441,7 @@ void ShapeColliderTests::capsuleMissesCapsule() { float totalHalfLength = totalRadius + halfHeightA + halfHeightB; CapsuleShape capsuleA(radiusA, halfHeightA); - CapsuleShape capsuleB(radiusA, halfHeightA); + CapsuleShape capsuleB(radiusB, halfHeightB); CollisionList collisions(16); diff --git a/tests/physics/src/main.cpp b/tests/physics/src/main.cpp index ca98f4d546..086bff4dcd 100644 --- a/tests/physics/src/main.cpp +++ b/tests/physics/src/main.cpp @@ -9,8 +9,10 @@ // #include "ShapeColliderTests.h" +#include "VerletShapeTests.h" int main(int argc, char** argv) { ShapeColliderTests::runAllTests(); + VerletShapeTests::runAllTests(); return 0; } From 3791b4712b86d78b66b47b260ce76d0a31c4a6b8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 14:17:12 -0700 Subject: [PATCH 028/105] adding some documentation about the VerletShapes --- libraries/shared/src/VerletCapsuleShape.h | 20 ++++++++++++++++---- libraries/shared/src/VerletSphereShape.h | 7 ++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/VerletCapsuleShape.h b/libraries/shared/src/VerletCapsuleShape.h index b2a7f8be30..72cc8b7b0e 100644 --- a/libraries/shared/src/VerletCapsuleShape.h +++ b/libraries/shared/src/VerletCapsuleShape.h @@ -15,11 +15,23 @@ #include "CapsuleShape.h" -// The VerletCapsuleShape is similar to a regular CapsuleShape, except it keeps pointers -// to its endpoints which are owned by some other data structure. This allows it to -// participate in a verlet integration engine. +// The VerletCapsuleShape is similar to a regular CapsuleShape, except it keeps a pointer +// to its endpoints which are owned by some other data structure (a verlet simulation system). +// This makes it easier for the points to be moved around by constraints in the system +// as well as collisions with the shape, however it has some drawbacks: // -// Although the true_halfHeight of the VerletCapsuleShape is considered a constant +// (1) The Shape::_translation and ::_rotation data members are not used (wasted) +// +// (2) A VerletShape doesn't own the points that it uses, so you must be careful not to +// leave dangling pointers around. +// +// (3) Some const methods of VerletCapsuleShape are much more expensive than you might think. +// For example getHalfHeight() and setHalfHeight() methods must do extra computation. In +// particular setRotation() is significantly more expensive than for the CapsuleShape. +// Not too expensive to use when setting up shapes, but you woudln't want to use it deep +// down in a hot simulation loop, such as when processing collision results. Best to +// just let the verlet simulation do its thing and not try to constantly force a rotation. + class VerletCapsuleShape : public CapsuleShape { public: VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint); diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/shared/src/VerletSphereShape.h index 747d02ff7c..395f5901e6 100644 --- a/libraries/shared/src/VerletSphereShape.h +++ b/libraries/shared/src/VerletSphereShape.h @@ -17,8 +17,13 @@ // The VerletSphereShape is similar to a regular SphereShape, except it keeps a pointer // to its center which is owned by some other data structure (a verlet simulation system). // This makes it easier for the points to be moved around by constraints in the system -// as well as collisions with the shape. +// as well as collisions with the shape, however it has some drawbacks: // +// (1) The Shape::_translation data member is not used (wasted) +// +// (2) A VerletShape doesn't own the points that it uses, so you must be careful not to +// leave dangling pointers around. + class VerletSphereShape : public SphereShape { public: VerletSphereShape(glm::vec3* centerPoint); From 715cc3467d91a5813cf6606752931cde7e3e15dc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 14:47:39 -0700 Subject: [PATCH 029/105] forgot to add the test files --- tests/physics/src/VerletShapeTests.cpp | 768 +++++++++++++++++++++++++ tests/physics/src/VerletShapeTests.h | 30 + 2 files changed, 798 insertions(+) create mode 100644 tests/physics/src/VerletShapeTests.cpp create mode 100644 tests/physics/src/VerletShapeTests.h diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp new file mode 100644 index 0000000000..6ae839392e --- /dev/null +++ b/tests/physics/src/VerletShapeTests.cpp @@ -0,0 +1,768 @@ +// +// VerletShapeTests.cpp +// tests/physics/src +// +// Created by Andrew Meadows on 02/21/2014. +// 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 +#include + +#include +#include +#include +#include +#include +#include + +#include "VerletShapeTests.h" + +const glm::vec3 origin(0.0f); +static const glm::vec3 xAxis(1.0f, 0.0f, 0.0f); +static const glm::vec3 yAxis(0.0f, 1.0f, 0.0f); +static const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); + +void VerletShapeTests::setSpherePosition() { + float radius = 1.0f; + glm::vec3 offset(1.23f, 4.56f, 7.89f); + glm::vec3 point; + VerletSphereShape sphere(radius, &point); + + point = glm::vec3(0.f); + float d = glm::distance(glm::vec3(0.0f), sphere.getTranslation()); + if (d != 0.0f) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should be at origin" << std::endl; + } + + point = offset; + d = glm::distance(glm::vec3(0.0f), sphere.getTranslation()); + if (d != glm::length(offset)) { + std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should be at offset" << std::endl; + } +} + +void VerletShapeTests::sphereMissesSphere() { + // non-overlapping spheres of unequal size + + 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; + + // create points for the sphere centers + glm::vec3 points[2]; + + // give pointers to the spheres + VerletSphereShape sphereA(radiusA, (points + 0)); + VerletSphereShape sphereB(radiusB, (points + 1)); + + // set the positions of the spheres by slamming the points directly + points[0] = origin; + points[1] = offsetDistance * offsetDirection; + + CollisionList collisions(16); + + // collide A to B... + { + bool touching = ShapeCollider::collideShapes(&sphereA, &sphereB, collisions); + if (touching) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphereA and sphereB should NOT touch" << std::endl; + } + } + + // collide B to A... + { + bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions); + if (touching) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphereA and sphereB should NOT touch" << std::endl; + } + } + + // also test shapeShape + { + bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions); + if (touching) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphereA and sphereB 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; + } +} + +void VerletShapeTests::sphereTouchesSphere() { + // overlapping spheres of unequal size + float radiusA = 7.0f; + float radiusB = 3.0f; + float alpha = 0.2f; + float beta = 0.3f; + glm::vec3 offsetDirection = glm::normalize(glm::vec3(1.0f, 2.0f, 3.0f)); + float offsetDistance = alpha * radiusA + beta * radiusB; + float expectedPenetrationDistance = (1.0f - alpha) * radiusA + (1.0f - beta) * radiusB; + glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection; + + // create two points for the sphere centers + glm::vec3 points[2]; + + // give pointers to the spheres + VerletSphereShape sphereA(radiusA, points+0); + VerletSphereShape sphereB(radiusB, points+1); + + // set the positions of the spheres by slamming the points directly + points[0] = origin; + points[1] = offsetDistance * offsetDirection; + + CollisionList collisions(16); + int numCollisions = 0; + + // collide A to B... + { + bool touching = ShapeCollider::collideShapes(&sphereA, &sphereB, collisions); + if (!touching) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphereA and sphereB should touch" << std::endl; + } else { + ++numCollisions; + } + + // verify state of collisions + if (numCollisions != collisions.size()) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: expected collisions size of " << numCollisions << " but actual size is " << collisions.size() + << std::endl; + } + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + if (!collision) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: null collision" << std::endl; + } + + // penetration points from sphereA into sphereB + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of sphereA + glm::vec3 AtoB = sphereB.getTranslation() - sphereA.getTranslation(); + glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * glm::normalize(AtoB); + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + } + + // collide B to A... + { + bool touching = ShapeCollider::collideShapes(&sphereB, &sphereA, collisions); + if (!touching) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphereA and sphereB should touch" << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into sphereB + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + float inaccuracy = glm::length(collision->_penetration + expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of sphereA + glm::vec3 BtoA = sphereA.getTranslation() - sphereB.getTranslation(); + glm::vec3 expectedContactPoint = sphereB.getTranslation() + radiusB * glm::normalize(BtoA); + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + } +} + +void VerletShapeTests::sphereMissesCapsule() { + // non-overlapping sphere and capsule + float radiusA = 1.5f; + float radiusB = 2.3f; + float totalRadius = radiusA + radiusB; + float halfHeightB = 1.7f; + float axialOffset = totalRadius + 1.1f * halfHeightB; + float radialOffset = 1.2f * radiusA + 1.3f * radiusB; + + // create points for the sphere + capsule + glm::vec3 points[3]; + for (int i = 0; i < 3; ++i) { + points[i] = glm::vec3(0.0f); + } + + // give the points to the shapes + VerletSphereShape sphereA(radiusA, points); + VerletCapsuleShape capsuleB(radiusB, points+1, points+2); + capsuleB.setHalfHeight(halfHeightB); + + // give the capsule some arbitrary transform + float angle = 37.8f; + glm::vec3 axis = glm::normalize( glm::vec3(-7.0f, 2.8f, 9.3f) ); + glm::quat rotation = glm::angleAxis(angle, axis); + glm::vec3 translation(15.1f, -27.1f, -38.6f); + capsuleB.setRotation(rotation); + capsuleB.setTranslation(translation); + + CollisionList collisions(16); + + // walk sphereA along the local yAxis next to, but not touching, capsuleB + glm::vec3 localStartPosition(radialOffset, axialOffset, 0.0f); + int numberOfSteps = 10; + float delta = 1.3f * (totalRadius + halfHeightB) / (numberOfSteps - 1); + for (int i = 0; i < numberOfSteps; ++i) { + // translate sphereA into world-frame + glm::vec3 localPosition = localStartPosition + ((float)i * delta) * yAxis; + sphereA.setTranslation(rotation * localPosition + translation); + + // sphereA agains capsuleB + if (ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } + } + + if (collisions.size() > 0) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: expected empty collision list but size is " << collisions.size() + << std::endl; + } +} + +void VerletShapeTests::sphereTouchesCapsule() { + // overlapping sphere and capsule + float radiusA = 2.0f; + float radiusB = 1.0f; + float totalRadius = radiusA + radiusB; + float halfHeightB = 2.0f; + float alpha = 0.5f; + float beta = 0.5f; + float radialOffset = alpha * radiusA + beta * radiusB; + + // create points for the sphere + capsule + glm::vec3 points[3]; + for (int i = 0; i < 3; ++i) { + points[i] = glm::vec3(0.0f); + } + + // give the points to the shapes + VerletSphereShape sphereA(radiusA, points); + VerletCapsuleShape capsuleB(radiusB, points+1, points+2); + capsuleB.setHalfHeight(halfHeightB); + + CollisionList collisions(16); + int numCollisions = 0; + + { // sphereA collides with capsuleB's cylindrical wall + sphereA.setTranslation(radialOffset * xAxis); + + if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphere and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + glm::vec3 expectedPenetration = (radialOffset - totalRadius) * xAxis; + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of sphereA + glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * xAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + + // capsuleB collides with sphereA + if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and sphere should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + collision = collisions.getCollision(numCollisions - 1); + expectedPenetration = - (radialOffset - totalRadius) * xAxis; + inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of capsuleB + glm::vec3 BtoA = sphereA.getTranslation() - capsuleB.getTranslation(); + glm::vec3 closestApproach = capsuleB.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__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + } + { // sphereA hits end cap at axis + glm::vec3 axialOffset = (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; + sphereA.setTranslation(axialOffset * yAxis); + + if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphere and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + glm::vec3 expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of sphereA + glm::vec3 expectedContactPoint = sphereA.getTranslation() - radiusA * yAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + + // capsuleB collides with sphereA + if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and sphere should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + collision = collisions.getCollision(numCollisions - 1); + expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of capsuleB + glm::vec3 endPoint; + capsuleB.getEndPoint(endPoint); + expectedContactPoint = endPoint + radiusB * yAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + } + { // sphereA hits start cap at axis + glm::vec3 axialOffset = - (halfHeightB + alpha * radiusA + beta * radiusB) * yAxis; + sphereA.setTranslation(axialOffset * yAxis); + + if (!ShapeCollider::collideShapes(&sphereA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: sphere and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + glm::vec3 expectedPenetration = ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of sphereA + glm::vec3 expectedContactPoint = sphereA.getTranslation() + radiusA * yAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + + // capsuleB collides with sphereA + if (!ShapeCollider::collideShapes(&capsuleB, &sphereA, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and sphere should touch" + << std::endl; + } else { + ++numCollisions; + } + + // penetration points from sphereA into capsuleB + collision = collisions.getCollision(numCollisions - 1); + expectedPenetration = - ((1.0f - alpha) * radiusA + (1.0f - beta) * radiusB) * yAxis; + inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + // contactPoint is on surface of capsuleB + glm::vec3 startPoint; + capsuleB.getStartPoint(startPoint); + expectedContactPoint = startPoint - radiusB * yAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + } + if (collisions.size() != numCollisions) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: expected " << numCollisions << " collisions but actual number is " << collisions.size() + << std::endl; + } +} + +void VerletShapeTests::capsuleMissesCapsule() { + // non-overlapping capsules + float radiusA = 2.0f; + float halfHeightA = 3.0f; + float radiusB = 3.0f; + float halfHeightB = 4.0f; + + float totalRadius = radiusA + radiusB; + float totalHalfLength = totalRadius + halfHeightA + halfHeightB; + + // create points for the shapes + glm::vec3 points[4]; + for (int i = 0; i < 4; ++i) { + points[i] = glm::vec3(0.0f); + } + + // give the points to the shapes + VerletCapsuleShape capsuleA(radiusA, points+0, points+1); + VerletCapsuleShape capsuleB(radiusB, points+2, points+3); + capsuleA.setHalfHeight(halfHeightA); + capsuleA.setHalfHeight(halfHeightB); + + CollisionList collisions(16); + + // side by side + capsuleB.setTranslation((1.01f * totalRadius) * xAxis); + if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } + + // end to end + capsuleB.setTranslation((1.01f * totalHalfLength) * xAxis); + if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } + + // rotate B and move it to the side + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); + capsuleB.setRotation(rotation); + capsuleB.setTranslation((1.01f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + if (ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } + + if (collisions.size() > 0) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: expected empty collision list but size is " << collisions.size() + << std::endl; + } +} + +void VerletShapeTests::capsuleTouchesCapsule() { + // overlapping capsules + float radiusA = 2.0f; + float halfHeightA = 3.0f; + float radiusB = 3.0f; + float halfHeightB = 4.0f; + + float totalRadius = radiusA + radiusB; + float totalHalfLength = totalRadius + halfHeightA + halfHeightB; + + // create points for the shapes + glm::vec3 points[4]; + for (int i = 0; i < 4; ++i) { + points[i] = glm::vec3(0.0f); + } + + // give the points to the shapes + VerletCapsuleShape capsuleA(radiusA, points+0, points+1); + VerletCapsuleShape capsuleB(radiusB, points+2, points+3); + capsuleA.setHalfHeight(halfHeightA); + capsuleB.setHalfHeight(halfHeightB); + + CollisionList collisions(16); + int numCollisions = 0; + + { // side by side + capsuleB.setTranslation((0.99f * totalRadius) * xAxis); + if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } else { + ++numCollisions; + } + } + + { // end to end + capsuleB.setTranslation((0.99f * totalHalfLength) * yAxis); + + if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } else { + ++numCollisions; + } + } + + { // rotate B and move it to the side + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); + capsuleB.setRotation(rotation); + capsuleB.setTranslation((0.99f * (totalRadius + capsuleB.getHalfHeight())) * xAxis); + + if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " 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; + } else { + ++numCollisions; + } + } + + { // again, but this time check collision details + float overlap = 0.1f; + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); + capsuleB.setRotation(rotation); + glm::vec3 positionB = ((totalRadius + capsuleB.getHalfHeight()) - overlap) * xAxis; + capsuleB.setTranslation(positionB); + + // capsuleA vs capsuleB + if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + glm::vec3 expectedPenetration = overlap * xAxis; + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration; + } + + glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * xAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint; + } + + // capsuleB vs capsuleA + if (!ShapeCollider::collideShapes(&capsuleB, &capsuleA, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + collision = collisions.getCollision(numCollisions - 1); + expectedPenetration = - overlap * xAxis; + inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration + << std::endl; + } + + expectedContactPoint = capsuleB.getTranslation() - (radiusB + halfHeightB) * xAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint + << std::endl; + } + } + + { // collide cylinder wall against cylinder wall + float overlap = 0.137f; + float shift = 0.317f * halfHeightA; + glm::quat rotation = glm::angleAxis(PI_OVER_TWO, zAxis); + capsuleB.setRotation(rotation); + glm::vec3 positionB = (totalRadius - overlap) * zAxis + shift * yAxis; + capsuleB.setTranslation(positionB); + + // capsuleA vs capsuleB + if (!ShapeCollider::collideShapes(&capsuleA, &capsuleB, collisions)) + { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: capsule and capsule should touch" + << std::endl; + } else { + ++numCollisions; + } + + CollisionInfo* collision = collisions.getCollision(numCollisions - 1); + glm::vec3 expectedPenetration = overlap * zAxis; + float inaccuracy = glm::length(collision->_penetration - expectedPenetration); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad penetration: expected = " << expectedPenetration + << " actual = " << collision->_penetration + << std::endl; + } + + glm::vec3 expectedContactPoint = capsuleA.getTranslation() + radiusA * zAxis + shift * yAxis; + inaccuracy = glm::length(collision->_contactPoint - expectedContactPoint); + if (fabs(inaccuracy) > EPSILON) { + std::cout << __FILE__ << ":" << __LINE__ + << " ERROR: bad contactPoint: expected = " << expectedContactPoint + << " actual = " << collision->_contactPoint + << std::endl; + } + } +} + +void VerletShapeTests::runAllTests() { + setSpherePosition(); + sphereMissesSphere(); + sphereTouchesSphere(); + + sphereMissesCapsule(); + sphereTouchesCapsule(); + + capsuleMissesCapsule(); + capsuleTouchesCapsule(); +} diff --git a/tests/physics/src/VerletShapeTests.h b/tests/physics/src/VerletShapeTests.h new file mode 100644 index 0000000000..36e2fe0cbd --- /dev/null +++ b/tests/physics/src/VerletShapeTests.h @@ -0,0 +1,30 @@ +// +// VerletShapeTests.h +// tests/physics/src +// +// Created by Andrew Meadows on 2014.06.18 +// 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_VerletShapeTests_h +#define hifi_VerletShapeTests_h + +namespace VerletShapeTests { + void setSpherePosition(); + + void sphereMissesSphere(); + void sphereTouchesSphere(); + + void sphereMissesCapsule(); + void sphereTouchesCapsule(); + + void capsuleMissesCapsule(); + void capsuleTouchesCapsule(); + + void runAllTests(); +} + +#endif // hifi_VerletShapeTests_h From d4b5550cdadb4e6a67f17095041fdece7503d443 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 17:22:39 -0700 Subject: [PATCH 030/105] Skeleton now creates VerletShape --- interface/src/avatar/Avatar.cpp | 15 -- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/MyAvatar.cpp | 2 - interface/src/avatar/SkeletonModel.cpp | 189 +++++++++------------- interface/src/avatar/SkeletonModel.h | 7 +- interface/src/renderer/Model.cpp | 3 - libraries/shared/src/PhysicalEntity.cpp | 4 +- libraries/shared/src/Ragdoll.cpp | 51 +++--- libraries/shared/src/Ragdoll.h | 30 +--- libraries/shared/src/SimulationEngine.cpp | 4 +- 10 files changed, 117 insertions(+), 189 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f940a106d7..fcb1d876a1 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -218,9 +218,6 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes); bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes); bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes); - if (renderSkeleton || renderHead || renderBounding) { - updateShapePositions(); - } if (renderSkeleton) { _skeletonModel.renderJointCollisionShapes(0.7f); @@ -590,18 +587,6 @@ bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisio getHead()->getFaceModel().findPlaneCollisions(plane, collisions); } -void Avatar::updateShapePositions() { - _skeletonModel.updateShapePositions(); - Model& headModel = getHead()->getFaceModel(); - headModel.updateShapePositions(); - /* KEEP FOR DEBUG: use this in rather than code above to see shapes - * in their default positions where the bounding shape is computed. - _skeletonModel.resetShapePositions(); - Model& headModel = getHead()->getFaceModel(); - headModel.resetShapePositions(); - */ -} - bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { // TODO: Andrew to fix: also collide against _skeleton //bool collided = _skeletonModel.findCollisions(shapes, collisions); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 8e18aecd26..a0e41d7122 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -136,7 +136,6 @@ public: /// \return bounding radius of avatar virtual float getBoundingRadius() const; - void updateShapePositions(); quint32 getCollisionGroups() const { return _collisionGroups; } virtual void setCollisionGroups(quint32 collisionGroups) { _collisionGroups = (collisionGroups & VALID_COLLISION_GROUPS); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9e89ba4f56..4a4e20fabe 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -210,7 +210,6 @@ void MyAvatar::simulate(float deltaTime) { radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.0f)); radius *= COLLISION_RADIUS_SCALAR; } - updateShapePositions(); if (_collisionGroups & COLLISION_GROUP_ENVIRONMENT) { PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithEnvironment"); updateCollisionWithEnvironment(deltaTime, radius); @@ -1447,7 +1446,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // don't collide with ourselves continue; } - avatar->updateShapePositions(); float distance = glm::length(_position - avatar->getPosition()); if (_distanceToNearestAvatar > distance) { _distanceToNearestAvatar = distance; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0b00398df8..26396d1fee 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -11,8 +11,8 @@ #include -#include -#include +#include +#include #include "Application.h" #include "Avatar.h" @@ -31,20 +31,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : void SkeletonModel::setJointStates(QVector states) { Model::setJointStates(states); - if (isActive() && _owningAvatar->isMyAvatar()) { - // extract lists of parentIndex and position from _jointStates... - QVector parentIndices; - QVector points; - int numJoints = _jointStates.size(); - parentIndices.reserve(numJoints); - points.reserve(numJoints); - for (int i = 0; i < numJoints; ++i) { - JointState& state = _jointStates[i]; - parentIndices.push_back(state.getFBXJoint().parentIndex); - points.push_back(state.getPosition()); - } - // ... and feed the results to Ragdoll - initShapesAndPoints(&_shapes, parentIndices, points); + clearShapes(); + clearRagdollConstraintsAndPoints(); + if (_enableShapes) { + buildShapes(); } } @@ -110,14 +100,11 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::simulateRagdoll(float deltaTime) { - // move ragdoll _points toward joints + // move _ragdollPoints toward joints const int numStates = _jointStates.size(); - assert(numStates == _points.size()); + assert(numStates == _ragdollPoints.size()); float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes - float oneMinusFraction = 1.0f - fraction; - for (int i = 0; i < numStates; ++i) { - _points[i] = oneMinusFraction * _points[i] + fraction * _jointStates[i].getPosition(); - } + moveShapesTowardJoints(fraction); // enforce the constraints float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm @@ -125,7 +112,7 @@ void SkeletonModel::simulateRagdoll(float deltaTime) { int iterations = 0; float delta = 0.0f; do { - delta = enforceConstraints(); + delta = enforceRagdollConstraints(); ++iterations; } while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS); } @@ -274,16 +261,6 @@ void SkeletonModel::updateJointState(int index) { } } -void SkeletonModel::updateShapePositions() { - if (isActive() && _owningAvatar->isMyAvatar() && - Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - // TODO: Andrew to move shape updates to SimulationEngine - //updateShapes(_rotation, _translation); - } else { - Model::updateShapePositions(); - } -} - void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { return; @@ -516,14 +493,14 @@ void SkeletonModel::renderRagdoll() { glPushMatrix(); Application::getInstance()->loadTranslatedViewMatrix(_translation); - int numPoints = _points.size(); + int numPoints = _ragdollPoints.size(); float alpha = 0.3f; float radius1 = 0.008f; float radius2 = 0.01f; for (int i = 0; i < numPoints; ++i) { glPushMatrix(); // draw each point as a yellow hexagon with black border - glm::vec3 position = _rotation * _points[i]; + glm::vec3 position = _rotation * _ragdollPoints[i]; glTranslatef(position.x, position.y, position.z); glColor4f(0.0f, 0.0f, 0.0f, alpha); glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); @@ -536,6 +513,22 @@ void SkeletonModel::renderRagdoll() { glEnable(GL_LIGHTING); } +// virtual +void SkeletonModel::initRagdollPoints() { + assert(_ragdollPoints.size() == 0); + // one point for each joint + int numJoints = _jointStates.size(); + _ragdollPoints.reserve(numJoints); + for (int i = 0; i < numJoints; ++i) { + const JointState& state = _jointStates.at(i); + _ragdollPoints.push_back(state.getPosition()); + } +} + +// virtual +void SkeletonModel::stepRagdollForward(float deltaTime) { +} + // virtual void SkeletonModel::buildShapes() { if (!_geometry || _rootIndex == -1) { @@ -546,42 +539,53 @@ void SkeletonModel::buildShapes() { if (geometry.joints.isEmpty()) { return; } - // We create the shapes with proper dimensions, but we set their transforms later. - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const FBXJoint& joint = geometry.joints[i]; + initRagdollPoints(); + + float uniformScale = extractUniformScale(_scale); + const int numStates = _jointStates.size(); + for (int i = 0; i < numStates; i++) { + const FBXJoint& joint = geometry.joints[i]; float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; Shape::Type type = joint.shapeType; - if (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON) { - // this capsule is effectively a sphere + if (i == 0 || (type == Shape::CAPSULE_SHAPE && halfHeight < EPSILON)) { + // this shape is forced to be a sphere type = Shape::SPHERE_SHAPE; } - if (type == Shape::CAPSULE_SHAPE) { - CapsuleShape* capsule = new CapsuleShape(radius, halfHeight); - capsule->setEntity(this); - _shapes.push_back(capsule); - } else if (type == Shape::SPHERE_SHAPE) { - SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f)); - _shapes.push_back(sphere); - sphere->setEntity(this); - } else { - // this shape type is not handled and the joint shouldn't collide, - // however we must have a Shape* for each joint, so we push NULL - _shapes.push_back(NULL); - } + Shape* shape = NULL; + if (type == Shape::SPHERE_SHAPE) { + shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); + shape->setEntity(this); + } else if (type == Shape::CAPSULE_SHAPE) { + int parentIndex = joint.parentIndex; + assert(parentIndex != -1); + shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); + shape->setEntity(this); + } + _shapes.push_back(shape); } // This method moves the shapes to their default positions in Model frame // which is where we compute the bounding shape's parameters. computeBoundingShape(geometry); - // finally sync shapes to joint positions - _shapesAreDirty = true; - updateShapePositions(); + // move shapes to joint positions + moveShapesTowardJoints(1.0f); } +void SkeletonModel::moveShapesTowardJoints(float fraction) { + assert(fraction >= 0.0f && fraction <= 1.0f); + if (_ragdollPoints.size() == _jointStates.size()) { + float oneMinusFraction = 1.0f - fraction; + int numJoints = _jointStates.size(); + for (int i = 0; i < numJoints; ++i) { + _ragdollPoints[i] = oneMinusFraction * _ragdollPoints[i] + fraction * _jointStates.at(i).getPosition(); + } + } +} + +/* TODO: Andrew to remove this when done with ragdoll work. void SkeletonModel::updateShapePositionsLegacy() { if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { glm::vec3 rootPosition(0.0f); @@ -612,65 +616,28 @@ void SkeletonModel::updateShapePositionsLegacy() { _boundingShape.setRotation(_rotation); } } +*/ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { - // compute default joint transforms and rotations - // (in local frame, ignoring Model translation and rotation) + // compute default joint transforms int numJoints = geometry.joints.size(); + if (numJoints != _ragdollPoints.size()) { + return; + } QVector transforms; transforms.fill(glm::mat4(), numJoints); - QVector finalRotations; - finalRotations.fill(glm::quat(), numJoints); - QVector shapeIsSet; - shapeIsSet.fill(false, numJoints); - int numShapesSet = 0; - int lastNumShapesSet = -1; - while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) { - lastNumShapesSet = numShapesSet; - for (int i = 0; i < numJoints; i++) { - const FBXJoint& joint = geometry.joints.at(i); - int parentIndex = joint.parentIndex; - - if (parentIndex == -1) { - glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset); - glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - glm::mat4 rootTransform = baseTransform * geometry.offset * glm::translate(joint.translation) - * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; - // remove the tranlsation part before we save the root transform - transforms[i] = glm::translate(- extractTranslation(rootTransform)) * rootTransform; - - finalRotations[i] = combinedRotation; - ++numShapesSet; - shapeIsSet[i] = true; - } else if (shapeIsSet[parentIndex]) { - glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) - * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; - finalRotations[i] = finalRotations[parentIndex] * combinedRotation; - ++numShapesSet; - shapeIsSet[i] = true; - } - } - } - - // sync shapes to joints - _boundingRadius = 0.0f; - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _shapes.size(); i++) { - Shape* shape = _shapes[i]; - if (!shape) { - continue; - } - const FBXJoint& joint = geometry.joints[i]; - glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition); - glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset; - shape->setTranslation(localPosition); - shape->setRotation(finalRotations[i] * joint.shapeRotation); - float distance = glm::length(localPosition) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } + transforms[0] = glm::scale(_scale); + for (int i = 1; i < numJoints; i++) { + const FBXJoint& joint = geometry.joints.at(i); + int parentIndex = joint.parentIndex; + assert(parentIndex != -1); + + glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; + transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) + * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; + // setting the ragdollPoints here slams the VerletShapes into their default positions + _ragdollPoints[i] = extractTranslation(transforms[i]); } // compute bounding box @@ -682,6 +649,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { if (!shape) { continue; } + // TODO: skip hand and arm joints when computing bounding dimensions Extents shapeExtents; shapeExtents.reset(); glm::vec3 localPosition = shape->getTranslation(); @@ -715,6 +683,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingShape.setRadius(capsuleRadius); _boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius); _boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum); + _boundingRadius = 0.5f * glm::length(diagonal); } void SkeletonModel::resetShapePositions() { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 2bbc0d844a..06e6061352 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -95,9 +95,12 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + virtual void initRagdollPoints(); + virtual void stepRagdollForward(float deltaTime); + void buildShapes(); - void updateShapePositions(); - void updateShapePositionsLegacy(); + void moveShapesTowardJoints(float fraction); + //void updateShapePositionsLegacy(); // TODO: Andrew to remove this when done with ragdoll work void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(float alpha); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c3a3387e0f..89a57c14af 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -551,9 +551,6 @@ bool Model::updateGeometry() { model->setURL(attachment.url); _attachments.append(model); } - if (_enableShapes) { - buildShapes(); - } needFullUpdate = true; } return needFullUpdate; diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp index 602708d4ad..5b1c7a7fdf 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -92,7 +92,7 @@ bool PhysicalEntity::findCollisions(const QVector shapes, Collisio } int numOurShapes = _shapes.size(); for (int j = 0; j < numOurShapes; ++j) { - const Shape* ourShape = _shapes[j]; + const Shape* ourShape = _shapes.at(j); if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) { collided = true; } @@ -142,7 +142,7 @@ bool PhysicalEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& bool collided = false; PlaneShape planeShape(plane); for (int i = 0; i < _shapes.size(); i++) { - if (_shapes[i] && ShapeCollider::collideShapes(&planeShape, _shapes[i], collisions)) { + if (_shapes.at(i) && ShapeCollider::collideShapes(&planeShape, _shapes.at(i), collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_data = (void*)(this); collision->_intData = i; diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 6a8515d3b4..ad23daf609 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -94,66 +94,55 @@ void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotatio // Ragdoll // ---------------------------------------------------------------------------- -Ragdoll::Ragdoll() : _verletShapes(NULL) { +Ragdoll::Ragdoll() { } Ragdoll::~Ragdoll() { - clear(); + clearRagdollConstraintsAndPoints(); } -void Ragdoll::initShapesAndPoints(QVector* shapes, const QVector& parentIndices, const QVector& points) { +/* +void Ragdoll::useShapesAndCopyPoints(QVector* shapes, const QVector& parentIndices, const QVector& points) { clear(); _verletShapes = shapes; const int numPoints = points.size(); assert(numPoints == parentIndices.size()); - _points.reserve(numPoints); + _ragdollPoints.reserve(numPoints); for (int i = 0; i < numPoints; ++i) { glm::vec3 position = points[i]; - _points.push_back(position); + _ragdollPoints.push_back(position); int parentIndex = parentIndices[i]; assert(parentIndex < i && parentIndex >= -1); if (parentIndex == -1) { - FixedConstraint* anchor = new FixedConstraint(&(_points[i]), glm::vec3(0.0f)); - _constraints.push_back(anchor); + FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f)); + _ragdollConstraints.push_back(anchor); } else { - DistanceConstraint* stick = new DistanceConstraint(&(_points[i]), &(_points[parentIndex])); - _constraints.push_back(stick); + DistanceConstraint* stick = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); + _ragdollConstraints.push_back(stick); } } } +*/ /// Delete all data. -void Ragdoll::clear() { - int numConstraints = _constraints.size(); +void Ragdoll::clearRagdollConstraintsAndPoints() { + int numConstraints = _ragdollConstraints.size(); for (int i = 0; i < numConstraints; ++i) { - delete _constraints[i]; + delete _ragdollConstraints[i]; } - _constraints.clear(); - _points.clear(); - _verletShapes = NULL; + _ragdollConstraints.clear(); + _ragdollPoints.clear(); } -float Ragdoll::enforceConstraints() { +float Ragdoll::enforceRagdollConstraints() { float maxDistance = 0.0f; - const int numConstraints = _constraints.size(); + const int numConstraints = _ragdollConstraints.size(); for (int i = 0; i < numConstraints; ++i) { - DistanceConstraint* c = static_cast(_constraints[i]); - //maxDistance = glm::max(maxDistance, _constraints[i]->enforce()); + DistanceConstraint* c = static_cast(_ragdollConstraints[i]); + //maxDistance = glm::max(maxDistance, _ragdollConstraints[i]->enforce()); maxDistance = glm::max(maxDistance, c->enforce()); } return maxDistance; } -void Ragdoll::updateShapes(const glm::quat& rotation, const glm::vec3& translation) const { - if (!_verletShapes) { - return; - } - int numShapes = _verletShapes->size(); - int numConstraints = _constraints.size(); - - // NOTE: we assume a one-to-one relationship between shapes and constraints - for (int i = 0; i < numShapes && i < numConstraints; ++i) { - _constraints[i]->updateProxyShape((*_verletShapes)[i], rotation, translation); - } -} diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 37652507a9..e38541f717 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -66,38 +66,24 @@ public: Ragdoll(); virtual ~Ragdoll(); - - /// Create points and constraints based on topology of collection of joints - /// \param joints list of connected joint states - void initShapesAndPoints(QVector* shapes, const QVector& parentIndices, const QVector& points); /// Delete all data. - void clear(); + void clearRagdollConstraintsAndPoints(); - // TODO: Andrew to implement this - void stepForward(float deltaTime) {} + virtual void initRagdollPoints() = 0; + virtual void stepRagdollForward(float deltaTime) = 0; /// Enforce contraints. /// \return max distance of point movement - float enforceConstraints(); + float enforceRagdollConstraints(); // both const and non-const getPoints() - const QVector& getPoints() const { return _points; } - QVector& getPoints() { return _points; } - - /// \param rotation rotation into shapes' collision frame - /// \param translation translation into shapes' collision frame - /// Moves and modifies elements of _verletShapes to agree with state of _points - void updateShapes(const glm::quat& rotation, const glm::vec3& translation) const; - - const QVector* getShapes() const { return _verletShapes; } + const QVector& getRagdollPoints() const { return _ragdollPoints; } + QVector& getRagdollPoints() { return _ragdollPoints; } protected: - QVector _points; - QVector _constraints; - - // the Ragdoll does NOT own the data in _verletShapes. - QVector* _verletShapes; + QVector _ragdollPoints; + QVector _ragdollConstraints; }; #endif // hifi_Ragdoll_h diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index d1f773eee7..305e94af46 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -67,6 +67,7 @@ void SimulationEngine::removeRagdoll(Ragdoll* doll) { } void SimulationEngine::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { + /* TODO: Andrew to make this work int iterations = 0; float delta = 0.0f; quint64 now = usecTimestampNow(); @@ -82,7 +83,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera // (4) collisions move points (SpecialCapsuleShape would help solve this) // (5) enforce constraints // (6) add and enforce angular contraints for joints - _dolls.at(i)->stepForward(deltaTime); + //_dolls.at(i)->stepForward(deltaTime); } @@ -128,6 +129,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera _enforcementIterations = iterations; _enforcementError = delta; _enforcementTime = now - startTime; + */ } int SimulationEngine::computeCollisions() { From 531c32fdd35bf7038cd6763a077c9c03874e02ba Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 18 Jun 2014 18:07:50 -0700 Subject: [PATCH 031/105] More comments, stubbing out incremental streaming. --- libraries/metavoxels/src/Bitstream.h | 6 +++--- libraries/metavoxels/src/MetavoxelData.cpp | 8 ++++++++ libraries/metavoxels/src/MetavoxelData.h | 11 ++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index 0d9e516640..b116bd4b49 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -1104,7 +1104,7 @@ private: Q_DECLARE_METATYPE(const QMetaObject*) /// Macro for registering streamable meta-objects. Typically, one would use this macro at the top level of the source file -/// associated with the class. +/// associated with the class. The class should have a no-argument constructor flagged with Q_INVOKABLE. #define REGISTER_META_OBJECT(x) static int x##Registration = Bitstream::registerMetaObject(#x, &x::staticMetaObject); /// Contains a value along with a pointer to its streamer. This is stored in QVariants when using fallback generics and @@ -1563,8 +1563,8 @@ public: Bitstream::registerTypeStreamer(qMetaTypeId(), new CollectionTypeStreamer()); /// Declares the metatype and the streaming operators. Typically, one would use this immediately after the definition of a -/// type flagged as STREAMABLE in its header file. The last lines ensure that the generated file will be included in the link -/// phase. +/// type flagged as STREAMABLE in its header file. The type should have a no-argument constructor. The last lines of this +/// macro ensure that the generated file will be included in the link phase. #ifdef _WIN32 #define DECLARE_STREAMABLE_METATYPE(X) Q_DECLARE_METATYPE(X) \ Bitstream& operator<<(Bitstream& out, const X& obj); \ diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 0d52fc5ed6..503dcce8d2 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -602,6 +602,14 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO } } +void MetavoxelData::readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& in, const MetavoxelLOD& lod) { +} + +void MetavoxelData::writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& out, const MetavoxelLOD& lod) const { +} + MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { MetavoxelNode*& root = _roots[attribute]; if (root) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 2e6f6c4437..199b6a8aff 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -54,7 +54,8 @@ public: DECLARE_STREAMABLE_METATYPE(MetavoxelLOD) -/// The base metavoxel representation shared between server and client. +/// The base metavoxel representation shared between server and client. Contains a size (for all dimensions) and a set of +/// octrees for different attributes. class MetavoxelData { public: @@ -64,11 +65,14 @@ public: MetavoxelData& operator=(const MetavoxelData& other); + /// Sets the size in all dimensions. void setSize(float size) { _size = size; } float getSize() const { return _size; } + /// Returns the minimum extent of the octrees (which are centered about the origin). glm::vec3 getMinimum() const { return glm::vec3(_size, _size, _size) * -0.5f; } + /// Returns the bounds of the octrees. Box getBounds() const; /// Applies the specified visitor to the contained voxels. @@ -107,6 +111,11 @@ public: void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const; + void readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& in, const MetavoxelLOD& lod); + void writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, + Bitstream& out, const MetavoxelLOD& lod) const; + MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } MetavoxelNode* createRoot(const AttributePointer& attribute); From 7cd1f752827cf4622c3553d1c7198e8efc2c8070 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 20:47:57 -0700 Subject: [PATCH 032/105] PhysicalEntity gets backpointer to SimulationEngine --- libraries/shared/src/PhysicalEntity.cpp | 8 ++- libraries/shared/src/PhysicalEntity.h | 9 +++- libraries/shared/src/SimulationEngine.cpp | 60 +++++++++++++++++++++-- libraries/shared/src/SimulationEngine.h | 12 ++++- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicalEntity.cpp index 5b1c7a7fdf..87d8e982c6 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicalEntity.cpp @@ -18,7 +18,13 @@ PhysicalEntity::PhysicalEntity() : _rotation(), _boundingRadius(0.0f), _shapesAreDirty(true), - _enableShapes(false) { + _enableShapes(false), + _simulation(NULL) { +} + +PhysicalEntity::~PhysicalEntity() { + // entity should be removed from the simulation before it is deleted + assert(_simulation == NULL); } void PhysicalEntity::setTranslation(const glm::vec3& translation) { diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicalEntity.h index 9e95449812..959550dbb2 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicalEntity.h @@ -20,6 +20,7 @@ #include "CollisionInfo.h" class Shape; +class SimulationEngine; // PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a // SimulationEngine. Each CollisionInfo generated by a SimulationEngine has back pointers to the @@ -29,7 +30,7 @@ class PhysicalEntity { public: PhysicalEntity(); - virtual ~PhysicalEntity() {} + virtual ~PhysicalEntity(); void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); @@ -45,18 +46,24 @@ public: virtual void buildShapes() = 0; virtual void clearShapes(); + SimulationEngine* getSimulation() const { return _simulation; } + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; bool findCollisions(const QVector shapes, CollisionList& collisions); bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex); bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); protected: + // SimulationEngine is a friend so that it can set the protected _simulation backpointer + friend SimulationEngine; + glm::vec3 _translation; glm::quat _rotation; float _boundingRadius; bool _shapesAreDirty; bool _enableShapes; QVector _shapes; + SimulationEngine* _simulation; }; #endif // hifi_PhysicalEntity_h diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/SimulationEngine.cpp index 305e94af46..bba33592e9 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/SimulationEngine.cpp @@ -11,13 +11,18 @@ #include -#include "SharedUtil.h" -#include "ShapeCollider.h" #include "SimulationEngine.h" -int MAX_DOLLS_PER_ENGINE = 32; +#include "PhysicalEntity.h" +#include "Ragdoll.h" +#include "SharedUtil.h" +#include "ShapeCollider.h" + +int MAX_DOLLS_PER_ENGINE = 16; +int MAX_ENTITIES_PER_ENGINE = 64; int MAX_COLLISIONS_PER_ENGINE = 256; + const int NUM_SHAPE_BITS = 6; const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; @@ -28,6 +33,55 @@ SimulationEngine::~SimulationEngine() { _dolls.clear(); } +bool SimulationEngine::addEntity(PhysicalEntity* entity) { + if (!entity) { + return false; + } + if (entity->_simulation == this) { + int numEntities = _entities.size(); + for (int i = 0; i < numEntities; ++i) { + if (entity == _entities.at(i)) { + // already in list + assert(entity->_simulation == this); + return true; + } + } + // belongs to some other simulation + return false; + } + int numEntities = _entities.size(); + if (numEntities > MAX_ENTITIES_PER_ENGINE) { + // list is full + return false; + } + // add to list + entity->_simulation = this; + _entities.push_back(entity); + return true; +} + +void SimulationEngine::removeEntity(PhysicalEntity* entity) { + if (!entity || !entity->_simulation || !(entity->_simulation == this)) { + return; + } + int numEntities = _entities.size(); + for (int i = 0; i < numEntities; ++i) { + if (entity == _entities.at(i)) { + if (i == numEntities - 1) { + // remove it + _entities.pop_back(); + } else { + // swap the last for this one + PhysicalEntity* lastEntity = _entities[numEntities - 1]; + _entities.pop_back(); + _entities[i] = lastEntity; + } + entity->_simulation = NULL; + break; + } + } +} + bool SimulationEngine::addRagdoll(Ragdoll* doll) { if (!doll) { return false; diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/SimulationEngine.h index f2775259cc..f064760b6e 100644 --- a/libraries/shared/src/SimulationEngine.h +++ b/libraries/shared/src/SimulationEngine.h @@ -15,7 +15,9 @@ #include #include "CollisionInfo.h" -#include "Ragdoll.h" + +class PhysicalEntity; +class Ragdoll; class SimulationEngine { public: @@ -23,7 +25,12 @@ public: SimulationEngine(); ~SimulationEngine(); - /// \return true if doll was added to, or already in the list + /// \return true if entity was added to or is already in the list + bool addEntity(PhysicalEntity* entity); + + void removeEntity(PhysicalEntity* entity); + + /// \return true if doll was added to or is already in the list bool addRagdoll(Ragdoll* doll); void removeRagdoll(Ragdoll* doll); @@ -45,6 +52,7 @@ public: private: CollisionList _collisionList; + QVector _entities; QVector _dolls; // some stats for performance queries From a8c2003fe64dbcea61c929558f1540fadea6e906 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 08:34:12 -0700 Subject: [PATCH 033/105] namechange SimulationEngine --> PhysicsSimulation --- interface/src/avatar/MyAvatar.h | 4 +- interface/src/renderer/Model.cpp | 6 +-- interface/src/renderer/Model.h | 6 +-- .../{PhysicalEntity.cpp => PhysicsEntity.cpp} | 26 ++++++------- .../src/{PhysicalEntity.h => PhysicsEntity.h} | 30 +++++++-------- ...lationEngine.cpp => PhysicsSimulation.cpp} | 38 +++++++++---------- ...SimulationEngine.h => PhysicsSimulation.h} | 22 +++++------ libraries/shared/src/Shape.h | 8 ++-- 8 files changed, 70 insertions(+), 70 deletions(-) rename libraries/shared/src/{PhysicalEntity.cpp => PhysicsEntity.cpp} (83%) rename libraries/shared/src/{PhysicalEntity.h => PhysicsEntity.h} (68%) rename libraries/shared/src/{SimulationEngine.cpp => PhysicsSimulation.cpp} (82%) rename libraries/shared/src/{SimulationEngine.h => PhysicsSimulation.h} (80%) diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 5ebf72b215..b48e61718c 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -14,7 +14,7 @@ #include -#include +#include #include "Avatar.h" @@ -175,7 +175,7 @@ private: float _oculusYawOffset; QList _animationHandles; - SimulationEngine _simulationEngine; + PhysicsSimulation _simulationEngine; // private methods float computeDistanceToFloor(const glm::vec3& startPoint); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 89a57c14af..515dcd4f27 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include @@ -561,7 +561,7 @@ void Model::setJointStates(QVector states) { _jointStates = states; // compute an approximate bounding radius for broadphase collision queries - // against SimulationEngine boundaries + // against PhysicsSimulation boundaries int numJoints = _jointStates.size(); float radius = 0.0f; for (int i = 0; i < numJoints; ++i) { @@ -785,7 +785,7 @@ AnimationHandlePointer Model::createAnimationHandle() { return handle; } -// virtual override from PhysicalEntity +// virtual override from PhysicsEntity void Model::buildShapes() { // TODO: figure out how to load/build collision shapes for general models } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 8851f42eb1..2045a0c9b5 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -16,7 +16,7 @@ #include #include -#include +#include #include @@ -33,7 +33,7 @@ typedef QSharedPointer AnimationHandlePointer; typedef QWeakPointer WeakAnimationHandlePointer; /// A generic 3D model displaying geometry loaded from a URL. -class Model : public QObject, public PhysicalEntity { +class Model : public QObject, public PhysicsEntity { Q_OBJECT public: @@ -131,7 +131,7 @@ public: const QList& getRunningAnimations() const { return _runningAnimations; } - // virtual overrides from PhysicalEntity + // virtual overrides from PhysicsEntity virtual void buildShapes(); virtual void updateShapePositions(); diff --git a/libraries/shared/src/PhysicalEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp similarity index 83% rename from libraries/shared/src/PhysicalEntity.cpp rename to libraries/shared/src/PhysicsEntity.cpp index 87d8e982c6..10c08a3cea 100644 --- a/libraries/shared/src/PhysicalEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -1,5 +1,5 @@ // -// PhysicalEntity.cpp +// PhysicsEntity.cpp // libraries/shared/src // // Created by Andrew Meadows 2014.06.11 @@ -9,11 +9,11 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "PhysicalEntity.h" +#include "PhysicsEntity.h" #include "Shape.h" #include "ShapeCollider.h" -PhysicalEntity::PhysicalEntity() : +PhysicsEntity::PhysicsEntity() : _translation(0.0f), _rotation(), _boundingRadius(0.0f), @@ -22,26 +22,26 @@ PhysicalEntity::PhysicalEntity() : _simulation(NULL) { } -PhysicalEntity::~PhysicalEntity() { +PhysicsEntity::~PhysicsEntity() { // entity should be removed from the simulation before it is deleted assert(_simulation == NULL); } -void PhysicalEntity::setTranslation(const glm::vec3& translation) { +void PhysicsEntity::setTranslation(const glm::vec3& translation) { if (_translation != translation) { _shapesAreDirty = !_shapes.isEmpty(); _translation = translation; } } -void PhysicalEntity::setRotation(const glm::quat& rotation) { +void PhysicsEntity::setRotation(const glm::quat& rotation) { if (_rotation != rotation) { _shapesAreDirty = !_shapes.isEmpty(); _rotation = rotation; } } -void PhysicalEntity::setShapeBackPointers() { +void PhysicsEntity::setShapeBackPointers() { for (int i = 0; i < _shapes.size(); i++) { Shape* shape = _shapes[i]; if (shape) { @@ -50,7 +50,7 @@ void PhysicalEntity::setShapeBackPointers() { } } -void PhysicalEntity::setEnableShapes(bool enable) { +void PhysicsEntity::setEnableShapes(bool enable) { if (enable != _enableShapes) { clearShapes(); _enableShapes = enable; @@ -60,14 +60,14 @@ void PhysicalEntity::setEnableShapes(bool enable) { } } -void PhysicalEntity::clearShapes() { +void PhysicsEntity::clearShapes() { for (int i = 0; i < _shapes.size(); ++i) { delete _shapes[i]; } _shapes.clear(); } -bool PhysicalEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { +bool PhysicsEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { /* TODO: Andrew to make this work int numShapes = _shapes.size(); float minDistance = FLT_MAX; @@ -88,7 +88,7 @@ bool PhysicalEntity::findRayIntersection(const glm::vec3& origin, const glm::vec return false; } -bool PhysicalEntity::findCollisions(const QVector shapes, CollisionList& collisions) { +bool PhysicsEntity::findCollisions(const QVector shapes, CollisionList& collisions) { bool collided = false; int numTheirShapes = shapes.size(); for (int i = 0; i < numTheirShapes; ++i) { @@ -107,7 +107,7 @@ bool PhysicalEntity::findCollisions(const QVector shapes, Collisio return collided; } -bool PhysicalEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, +bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex) { bool collided = false; // TODO: Andrew to implement this or make it unecessary @@ -144,7 +144,7 @@ bool PhysicalEntity::findSphereCollisions(const glm::vec3& sphereCenter, float s return collided; } -bool PhysicalEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { +bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { bool collided = false; PlaneShape planeShape(plane); for (int i = 0; i < _shapes.size(); i++) { diff --git a/libraries/shared/src/PhysicalEntity.h b/libraries/shared/src/PhysicsEntity.h similarity index 68% rename from libraries/shared/src/PhysicalEntity.h rename to libraries/shared/src/PhysicsEntity.h index 959550dbb2..3e20115729 100644 --- a/libraries/shared/src/PhysicalEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -1,5 +1,5 @@ // -// PhysicalEntity.h +// PhysicsEntity.h // libraries/shared/src // // Created by Andrew Meadows 2014.05.30 @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_PhysicalEntity_h -#define hifi_PhysicalEntity_h +#ifndef hifi_PhysicsEntity_h +#define hifi_PhysicsEntity_h #include @@ -20,17 +20,17 @@ #include "CollisionInfo.h" class Shape; -class SimulationEngine; +class PhysicsSimulation; -// PhysicalEntity is the base class for anything that owns one or more Shapes that collide in a -// SimulationEngine. Each CollisionInfo generated by a SimulationEngine has back pointers to the -// two Shapes involved, and those Shapes may (optionally) have valid back pointers to their PhysicalEntity. +// PhysicsEntity is the base class for anything that owns one or more Shapes that collide in a +// PhysicsSimulation. Each CollisionInfo generated by a PhysicsSimulation has back pointers to the +// two Shapes involved, and those Shapes may (optionally) have valid back pointers to their PhysicsEntity. -class PhysicalEntity { +class PhysicsEntity { public: - PhysicalEntity(); - virtual ~PhysicalEntity(); + PhysicsEntity(); + virtual ~PhysicsEntity(); void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); @@ -46,7 +46,7 @@ public: virtual void buildShapes() = 0; virtual void clearShapes(); - SimulationEngine* getSimulation() const { return _simulation; } + PhysicsSimulation* getSimulation() const { return _simulation; } bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; bool findCollisions(const QVector shapes, CollisionList& collisions); @@ -54,8 +54,8 @@ public: bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); protected: - // SimulationEngine is a friend so that it can set the protected _simulation backpointer - friend SimulationEngine; + // PhysicsSimulation is a friend so that it can set the protected _simulation backpointer + friend PhysicsSimulation; glm::vec3 _translation; glm::quat _rotation; @@ -63,7 +63,7 @@ protected: bool _shapesAreDirty; bool _enableShapes; QVector _shapes; - SimulationEngine* _simulation; + PhysicsSimulation* _simulation; }; -#endif // hifi_PhysicalEntity_h +#endif // hifi_PhysicsEntity_h diff --git a/libraries/shared/src/SimulationEngine.cpp b/libraries/shared/src/PhysicsSimulation.cpp similarity index 82% rename from libraries/shared/src/SimulationEngine.cpp rename to libraries/shared/src/PhysicsSimulation.cpp index bba33592e9..5fb6fa4a04 100644 --- a/libraries/shared/src/SimulationEngine.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -1,5 +1,5 @@ // -// SimulationEngine.cpp +// PhysicsSimulation.cpp // interface/src/avatar // // Created by Andrew Meadows 2014.06.06 @@ -11,29 +11,29 @@ #include -#include "SimulationEngine.h" +#include "PhysicsSimulation.h" -#include "PhysicalEntity.h" +#include "PhysicsEntity.h" #include "Ragdoll.h" #include "SharedUtil.h" #include "ShapeCollider.h" -int MAX_DOLLS_PER_ENGINE = 16; -int MAX_ENTITIES_PER_ENGINE = 64; -int MAX_COLLISIONS_PER_ENGINE = 256; +int MAX_DOLLS_PER_SIMULATION = 16; +int MAX_ENTITIES_PER_SIMULATION = 64; +int MAX_COLLISIONS_PER_SIMULATION = 256; const int NUM_SHAPE_BITS = 6; const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; -SimulationEngine::SimulationEngine() : _collisionList(MAX_COLLISIONS_PER_ENGINE) { +PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION) { } -SimulationEngine::~SimulationEngine() { +PhysicsSimulation::~PhysicsSimulation() { _dolls.clear(); } -bool SimulationEngine::addEntity(PhysicalEntity* entity) { +bool PhysicsSimulation::addEntity(PhysicsEntity* entity) { if (!entity) { return false; } @@ -50,7 +50,7 @@ bool SimulationEngine::addEntity(PhysicalEntity* entity) { return false; } int numEntities = _entities.size(); - if (numEntities > MAX_ENTITIES_PER_ENGINE) { + if (numEntities > MAX_ENTITIES_PER_SIMULATION) { // list is full return false; } @@ -60,7 +60,7 @@ bool SimulationEngine::addEntity(PhysicalEntity* entity) { return true; } -void SimulationEngine::removeEntity(PhysicalEntity* entity) { +void PhysicsSimulation::removeEntity(PhysicsEntity* entity) { if (!entity || !entity->_simulation || !(entity->_simulation == this)) { return; } @@ -72,7 +72,7 @@ void SimulationEngine::removeEntity(PhysicalEntity* entity) { _entities.pop_back(); } else { // swap the last for this one - PhysicalEntity* lastEntity = _entities[numEntities - 1]; + PhysicsEntity* lastEntity = _entities[numEntities - 1]; _entities.pop_back(); _entities[i] = lastEntity; } @@ -82,12 +82,12 @@ void SimulationEngine::removeEntity(PhysicalEntity* entity) { } } -bool SimulationEngine::addRagdoll(Ragdoll* doll) { +bool PhysicsSimulation::addRagdoll(Ragdoll* doll) { if (!doll) { return false; } int numDolls = _dolls.size(); - if (numDolls > MAX_DOLLS_PER_ENGINE) { + if (numDolls > MAX_DOLLS_PER_SIMULATION) { // list is full return false; } @@ -102,7 +102,7 @@ bool SimulationEngine::addRagdoll(Ragdoll* doll) { return true; } -void SimulationEngine::removeRagdoll(Ragdoll* doll) { +void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { if (doll == _dolls[i]) { @@ -120,7 +120,7 @@ void SimulationEngine::removeRagdoll(Ragdoll* doll) { } } -void SimulationEngine::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { +void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { /* TODO: Andrew to make this work int iterations = 0; float delta = 0.0f; @@ -143,7 +143,7 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera // collide _collisionList.clear(); - // TODO: keep track of QSet collidedEntities; + // TODO: keep track of QSet collidedEntities; for (int i = 0; i < numDolls; ++i) { const QVector* shapesA = _dolls.at(i)->getShapes(); if (!shapesA) { @@ -186,10 +186,10 @@ void SimulationEngine::stepForward(float deltaTime, float minError, int maxItera */ } -int SimulationEngine::computeCollisions() { +int PhysicsSimulation::computeCollisions() { return 0.0f; } -void SimulationEngine::processCollisions() { +void PhysicsSimulation::processCollisions() { } diff --git a/libraries/shared/src/SimulationEngine.h b/libraries/shared/src/PhysicsSimulation.h similarity index 80% rename from libraries/shared/src/SimulationEngine.h rename to libraries/shared/src/PhysicsSimulation.h index f064760b6e..b5667db351 100644 --- a/libraries/shared/src/SimulationEngine.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -1,5 +1,5 @@ // -// SimulationEngine.h +// PhysicsSimulation.h // interface/src/avatar // // Created by Andrew Meadows 2014.06.06 @@ -9,26 +9,26 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_SimulationEngine_h -#define hifi_SimulationEngine_h +#ifndef hifi_PhysicsSimulation +#define hifi_PhysicsSimulation #include #include "CollisionInfo.h" -class PhysicalEntity; +class PhysicsEntity; class Ragdoll; -class SimulationEngine { +class PhysicsSimulation { public: - SimulationEngine(); - ~SimulationEngine(); + PhysicsSimulation(); + ~PhysicsSimulation(); /// \return true if entity was added to or is already in the list - bool addEntity(PhysicalEntity* entity); + bool addEntity(PhysicsEntity* entity); - void removeEntity(PhysicalEntity* entity); + void removeEntity(PhysicsEntity* entity); /// \return true if doll was added to or is already in the list bool addRagdoll(Ragdoll* doll); @@ -52,7 +52,7 @@ public: private: CollisionList _collisionList; - QVector _entities; + QVector _entities; QVector _dolls; // some stats for performance queries @@ -61,4 +61,4 @@ private: quint64 _enforcementTime; }; -#endif // hifi_SimulationEngine_h +#endif // hifi_PhysicsSimulation diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 0a03ae18e3..4926f598ba 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -15,7 +15,7 @@ #include #include -class PhysicalEntity; +class PhysicsEntity; class Shape { public: @@ -32,8 +32,8 @@ public: int getType() const { return _type; } - void setEntity(PhysicalEntity* entity) { _owningEntity = entity; } - PhysicalEntity* getEntity() const { return _owningEntity; } + void setEntity(PhysicsEntity* entity) { _owningEntity = entity; } + PhysicsEntity* getEntity() const { return _owningEntity; } float getBoundingRadius() const { return _boundingRadius; } @@ -58,7 +58,7 @@ protected: void setBoundingRadius(float radius) { _boundingRadius = radius; } int _type; - PhysicalEntity* _owningEntity; + PhysicsEntity* _owningEntity; float _boundingRadius; glm::vec3 _translation; glm::quat _rotation; From d3a78c9fc2b793e047de2199ae613a9146d12466 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 09:14:36 -0700 Subject: [PATCH 034/105] simulation step stubbed out --- libraries/shared/src/PhysicsEntity.h | 8 ++- libraries/shared/src/PhysicsSimulation.cpp | 71 +++++++++++----------- libraries/shared/src/PhysicsSimulation.h | 13 ++-- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index 3e20115729..e52ea1da23 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -45,6 +45,7 @@ public: virtual void buildShapes() = 0; virtual void clearShapes(); + const QVector getShapes() const { return _shapes; } PhysicsSimulation* getSimulation() const { return _simulation; } @@ -54,15 +55,16 @@ public: bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); protected: - // PhysicsSimulation is a friend so that it can set the protected _simulation backpointer - friend PhysicsSimulation; - glm::vec3 _translation; glm::quat _rotation; float _boundingRadius; bool _shapesAreDirty; bool _enableShapes; QVector _shapes; + +private: + // PhysicsSimulation is a friend so that it can set the protected _simulation backpointer + friend PhysicsSimulation; PhysicsSimulation* _simulation; }; diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 5fb6fa4a04..0a91babe08 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -26,7 +26,8 @@ int MAX_COLLISIONS_PER_SIMULATION = 256; const int NUM_SHAPE_BITS = 6; const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; -PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION) { +PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION), + _numIterations(0), _numCollisions(0), _constraintError(0.0f), _stepTime(0) { } PhysicsSimulation::~PhysicsSimulation() { @@ -120,70 +121,72 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { } } +// TODO: Andrew need to implement: +// DONE (1) joints pull points (SpecialCapsuleShape would help solve this) +// DONE (2) points slam shapes (SpecialCapsuleShape would help solve this) +// DONE (3) detect collisions +// DONE (4) collisions move points (SpecialCapsuleShape would help solve this) +// DONE (5) enforce constraints +// (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support +// (7) support for pairwise collision bypass +// (8) process collisions +// (9) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { - /* TODO: Andrew to make this work int iterations = 0; - float delta = 0.0f; quint64 now = usecTimestampNow(); quint64 startTime = now; quint64 expiry = now + maxUsec; + // move dolls int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { - // TODO: Andrew need to implement: - // (1) joints pull points (SpecialCapsuleShape would help solve this) - // (2) points slam shapes (SpecialCapsuleShape would help solve this) - // (3) shapes collide with pairwise collision bypass - // (4) collisions move points (SpecialCapsuleShape would help solve this) - // (5) enforce constraints - // (6) add and enforce angular contraints for joints - //_dolls.at(i)->stepForward(deltaTime); + _dolls.at(i)->stepRagdollForward(deltaTime); } - // collide _collisionList.clear(); // TODO: keep track of QSet collidedEntities; - for (int i = 0; i < numDolls; ++i) { - const QVector* shapesA = _dolls.at(i)->getShapes(); - if (!shapesA) { - continue; - } - int numShapesA = shapesA->size(); + int numEntities = _entities.size(); + for (int i = 0; i < numEntities; ++i) { + const QVector shapes = _entities.at(i)->getShapes(); + int numShapes = shapes.size(); // collide with self - for (int j = 0; j < numShapesA; ++j) { - const Shape* shapeA = shapesA->at(j); - if (!shapeA) { + for (int j = 0; j < numShapes; ++j) { + const Shape* shape = shapes.at(j); + if (!shape) { continue; } // TODO: check for pairwise collision bypass here - ShapeCollider::collideShapeWithShapes(shapeA, *shapesA, j+1, _collisionList); + for (int k = j+1; k < numShapes; ++k) { + const Shape* otherShape = shapes.at(k); + ShapeCollider::collideShapes(shape, otherShape, _collisionList); + } } // collide with others - for (int j = i+1; j < numDolls; ++j) { - const QVector* shapesB = _dolls.at(j)->getShapes(); - if (!shapesB) { - continue; - } - ShapeCollider::collideShapesWithShapes(*shapesA, *shapesB, _collisionList); + for (int j = i+1; j < numEntities; ++j) { + const QVector otherShapes = _entities.at(j)->getShapes(); + ShapeCollider::collideShapesWithShapes(shapes, otherShapes, _collisionList); } } + // TODO: process collisions + _numCollisions = _collisionList.size(); + // enforce constraints float error = 0.0f; do { error = 0.0f; for (int i = 0; i < numDolls; ++i) { - error = glm::max(error, _dolls[i]->enforceConstraints()); + error = glm::max(error, _dolls[i]->enforceRagdollConstraints()); } ++iterations; now = usecTimestampNow(); - } while (iterations < maxIterations && delta > minError && now < expiry); - _enforcementIterations = iterations; - _enforcementError = delta; - _enforcementTime = now - startTime; - */ + } while (iterations < maxIterations && error > minError && now < expiry); + + _numIterations = iterations; + _constraintError = error; + _stepTime = now - startTime; } int PhysicsSimulation::computeCollisions() { diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/shared/src/PhysicsSimulation.h index b5667db351..957309ddd7 100644 --- a/libraries/shared/src/PhysicsSimulation.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -41,10 +41,6 @@ public: /// \return distance of largest movement void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec); - int getEnforementIterations() const { return _enforcementIterations; } - float getEnforcementError() const { return _enforcementError; } - quint64 getEnforcementTime() const { return _enforcementTime; } - /// \return number of collisions int computeCollisions(); @@ -55,10 +51,11 @@ private: QVector _entities; QVector _dolls; - // some stats for performance queries - int _enforcementIterations; - float _enforcementError; - quint64 _enforcementTime; + // some stats + int _numIterations; + int _numCollisions; + float _constraintError; + quint64 _stepTime; }; #endif // hifi_PhysicsSimulation From 5e74ee8c05e4e9a0e908b634a87efff722f3e825 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 09:54:32 -0700 Subject: [PATCH 035/105] VerletPoint has position, lastPosition, and mass --- interface/src/avatar/SkeletonModel.cpp | 23 +++++++++------- libraries/shared/src/Ragdoll.cpp | 37 ++++++-------------------- libraries/shared/src/Ragdoll.h | 25 ++++++++--------- 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 3927c3811d..93064c1afa 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -100,9 +100,9 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } void SkeletonModel::simulateRagdoll(float deltaTime) { - // move _ragdollPoints toward joints const int numStates = _jointStates.size(); assert(numStates == _ragdollPoints.size()); + float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes moveShapesTowardJoints(fraction); @@ -500,7 +500,7 @@ void SkeletonModel::renderRagdoll() { for (int i = 0; i < numPoints; ++i) { glPushMatrix(); // draw each point as a yellow hexagon with black border - glm::vec3 position = _rotation * _ragdollPoints[i]; + glm::vec3 position = _rotation * _ragdollPoints[i]._position; glTranslatef(position.x, position.y, position.z); glColor4f(0.0f, 0.0f, 0.0f, alpha); glutSolidSphere(radius2, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); @@ -516,12 +516,15 @@ void SkeletonModel::renderRagdoll() { // virtual void SkeletonModel::initRagdollPoints() { assert(_ragdollPoints.size() == 0); - // one point for each joint int numJoints = _jointStates.size(); - _ragdollPoints.reserve(numJoints); + + // one point for each joint + _ragdollPoints.fill(VerletPoint()); for (int i = 0; i < numJoints; ++i) { const JointState& state = _jointStates.at(i); - _ragdollPoints.push_back(state.getPosition()); + glm::vec3 position = state.getPosition(); + _ragdollPoints[i]._position = position; + _ragdollPoints[i]._lastPosition = position; } } @@ -555,12 +558,12 @@ void SkeletonModel::buildShapes() { } Shape* shape = NULL; if (type == Shape::SPHERE_SHAPE) { - shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); + shape = new VerletSphereShape(radius, &(_ragdollPoints[i]._position)); shape->setEntity(this); } else if (type == Shape::CAPSULE_SHAPE) { int parentIndex = joint.parentIndex; assert(parentIndex != -1); - shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); + shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]._position), &(_ragdollPoints[i]._position)); shape->setEntity(this); } _shapes.push_back(shape); @@ -580,7 +583,8 @@ void SkeletonModel::moveShapesTowardJoints(float fraction) { float oneMinusFraction = 1.0f - fraction; int numJoints = _jointStates.size(); for (int i = 0; i < numJoints; ++i) { - _ragdollPoints[i] = oneMinusFraction * _ragdollPoints[i] + fraction * _jointStates.at(i).getPosition(); + _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + _ragdollPoints[i]._position = oneMinusFraction * _ragdollPoints[i]._position + fraction * _jointStates.at(i).getPosition(); } } } @@ -637,7 +641,8 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(modifiedRotation) * joint.postTransform; // setting the ragdollPoints here slams the VerletShapes into their default positions - _ragdollPoints[i] = extractTranslation(transforms[i]); + _ragdollPoints[i]._position = extractTranslation(transforms[i]); + _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; } // compute bounding box diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index ad23daf609..aa6fb4b8e2 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -40,10 +40,10 @@ void FixedConstraint::setAnchor(const glm::vec3& anchor) { // ---------------------------------------------------------------------------- // DistanceConstraint // ---------------------------------------------------------------------------- -DistanceConstraint::DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint) : _distance(-1.0f) { +DistanceConstraint::DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint) : _distance(-1.0f) { _points[0] = startPoint; _points[1] = endPoint; - _distance = glm::distance(*(_points[0]), *(_points[1])); + _distance = glm::distance(_points[0]->_position, _points[1]->_position); } DistanceConstraint::DistanceConstraint(const DistanceConstraint& other) { @@ -57,39 +57,18 @@ void DistanceConstraint::setDistance(float distance) { } float DistanceConstraint::enforce() { - float newDistance = glm::distance(*(_points[0]), *(_points[1])); + // TODO: use a fast distance approximation + float newDistance = glm::distance(_points[0]->_position, _points[1]->_position); glm::vec3 direction(0.0f, 1.0f, 0.0f); if (newDistance > EPSILON) { - direction = (*(_points[0]) - *(_points[1])) / newDistance; + direction = (_points[0]->_position - _points[1]->_position) / newDistance; } - glm::vec3 center = 0.5f * (*(_points[0]) + *(_points[1])); - *(_points[0]) = center + (0.5f * _distance) * direction; - *(_points[1]) = center - (0.5f * _distance) * direction; + glm::vec3 center = 0.5f * (_points[0]->_position + _points[1]->_position); + _points[0]->_position = center + (0.5f * _distance) * direction; + _points[1]->_position = center - (0.5f * _distance) * direction; return glm::abs(newDistance - _distance); } -void DistanceConstraint::updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const { - if (!shape) { - return; - } - switch (shape->getType()) { - case Shape::SPHERE_SHAPE: { - // sphere collides at endPoint - SphereShape* sphere = static_cast(shape); - sphere->setTranslation(translation + rotation * (*_points[1])); - } - break; - case Shape::CAPSULE_SHAPE: { - // capsule collides from startPoint to endPoint - CapsuleShape* capsule = static_cast(shape); - capsule->setEndPoints(translation + rotation * (*_points[0]), translation + rotation * (*_points[1])); - } - break; - default: - break; - } -} - // ---------------------------------------------------------------------------- // Ragdoll // ---------------------------------------------------------------------------- diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index e38541f717..e9c272b1ee 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -19,6 +19,14 @@ class Shape; +class VerletPoint { +public: + VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(0.0f) {} + glm::vec3 _position; + glm::vec3 _lastPosition; + float _mass; +}; + class Constraint { public: Constraint() {} @@ -28,12 +36,6 @@ public: /// \return max distance of point movement virtual float enforce() = 0; - /// \param shape pointer to shape that will be this Constraint's collision proxy - /// \param rotation rotation into shape's collision frame - /// \param translation translation into shape's collision frame - /// Moves the shape such that it will collide at this constraint's position - virtual void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const {} - protected: int _type; }; @@ -51,14 +53,13 @@ private: class DistanceConstraint : public Constraint { public: - DistanceConstraint(glm::vec3* startPoint, glm::vec3* endPoint); + DistanceConstraint(VerletPoint* startPoint, VerletPoint* endPoint); DistanceConstraint(const DistanceConstraint& other); float enforce(); void setDistance(float distance); - void updateProxyShape(Shape* shape, const glm::quat& rotation, const glm::vec3& translation) const; private: float _distance; - glm::vec3* _points[2]; + VerletPoint* _points[2]; }; class Ragdoll { @@ -78,11 +79,11 @@ public: float enforceRagdollConstraints(); // both const and non-const getPoints() - const QVector& getRagdollPoints() const { return _ragdollPoints; } - QVector& getRagdollPoints() { return _ragdollPoints; } + const QVector& getRagdollPoints() const { return _ragdollPoints; } + QVector& getRagdollPoints() { return _ragdollPoints; } protected: - QVector _ragdollPoints; + QVector _ragdollPoints; QVector _ragdollConstraints; }; From 2453e9c36a6151a278ac5610fbe34e818cfbc6b2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 10:31:55 -0700 Subject: [PATCH 036/105] build ragdoll constraints again --- interface/src/avatar/SkeletonModel.cpp | 40 ++++++++++++++++++++++---- interface/src/avatar/SkeletonModel.h | 9 ++++-- libraries/shared/src/Ragdoll.cpp | 25 ---------------- libraries/shared/src/Ragdoll.h | 9 +++--- 4 files changed, 45 insertions(+), 38 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 93064c1afa..f7eb7e614e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -516,10 +516,11 @@ void SkeletonModel::renderRagdoll() { // virtual void SkeletonModel::initRagdollPoints() { assert(_ragdollPoints.size() == 0); - int numJoints = _jointStates.size(); + assert(_ragdollConstraints.size() == 0); // one point for each joint - _ragdollPoints.fill(VerletPoint()); + int numJoints = _jointStates.size(); + _ragdollPoints.fill(VerletPoint(), numJoints); for (int i = 0; i < numJoints; ++i) { const JointState& state = _jointStates.at(i); glm::vec3 position = state.getPosition(); @@ -528,6 +529,27 @@ void SkeletonModel::initRagdollPoints() { } } +void SkeletonModel::buildRagdollConstraints() { + // NOTE: the length of DistanceConstraints is computed and locked in at this time + // so make sure the ragdoll positions are in a normal configuration before here. + const int numPoints = _ragdollPoints.size(); + const int numJoints = _jointStates.size(); + assert(numPoints == numJoints); + + for (int i = 0; i < numPoints; ++i) { + const JointState& state = _jointStates.at(i); + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]._position), glm::vec3(0.0f)); + _ragdollConstraints.push_back(anchor); + } else { + DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); + _ragdollConstraints.push_back(bone); + } + } +} + // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { } @@ -570,10 +592,12 @@ void SkeletonModel::buildShapes() { } // This method moves the shapes to their default positions in Model frame - // which is where we compute the bounding shape's parameters. computeBoundingShape(geometry); - // move shapes to joint positions + // while the shapes are in their default position we create the constraints + buildRagdollConstraints(); + + // move shapes back to current joint positions moveShapesTowardJoints(1.0f); } @@ -631,7 +655,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { QVector transforms; transforms.fill(glm::mat4(), numJoints); + // compute the default transforms and slam the ragdoll positions accordingly + // (which puts the shapes where we want them) transforms[0] = glm::scale(_scale); + _ragdollPoints[0]._position = glm::vec3(0.0f); + _ragdollPoints[0]._lastPosition = glm::vec3(0.0f); for (int i = 1; i < numJoints; i++) { const FBXJoint& joint = geometry.joints.at(i); int parentIndex = joint.parentIndex; @@ -645,7 +673,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; } - // compute bounding box + // compute bounding box that encloses all shapes Extents totalExtents; totalExtents.reset(); totalExtents.addPoint(glm::vec3(0.0f)); @@ -654,7 +682,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { if (!shape) { continue; } - // TODO: skip hand and arm joints when computing bounding dimensions + // TODO: skip hand and arm shapes for bounding box calculation Extents shapeExtents; shapeExtents.reset(); glm::vec3 localPosition = shape->getTranslation(); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 06e6061352..66ede8ee19 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -95,10 +95,9 @@ public: /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; - virtual void initRagdollPoints(); + // virtual overrride from Ragdoll virtual void stepRagdollForward(float deltaTime); - void buildShapes(); void moveShapesTowardJoints(float fraction); //void updateShapePositionsLegacy(); // TODO: Andrew to remove this when done with ragdoll work @@ -112,6 +111,12 @@ public: void renderRagdoll(); protected: + // virtual overrrides from Ragdoll + void initRagdollPoints(); + void buildRagdollConstraints(); + + void buildShapes(); + /// \param jointIndex index of joint in model /// \param position position of joint in model-frame void applyHandPosition(int jointIndex, const glm::vec3& position); diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index aa6fb4b8e2..76d8ce4d98 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -80,31 +80,6 @@ Ragdoll::~Ragdoll() { clearRagdollConstraintsAndPoints(); } -/* -void Ragdoll::useShapesAndCopyPoints(QVector* shapes, const QVector& parentIndices, const QVector& points) { - clear(); - _verletShapes = shapes; - const int numPoints = points.size(); - assert(numPoints == parentIndices.size()); - _ragdollPoints.reserve(numPoints); - for (int i = 0; i < numPoints; ++i) { - glm::vec3 position = points[i]; - _ragdollPoints.push_back(position); - - int parentIndex = parentIndices[i]; - assert(parentIndex < i && parentIndex >= -1); - if (parentIndex == -1) { - FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f)); - _ragdollConstraints.push_back(anchor); - } else { - DistanceConstraint* stick = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); - _ragdollConstraints.push_back(stick); - } - } -} -*/ - -/// Delete all data. void Ragdoll::clearRagdollConstraintsAndPoints() { int numConstraints = _ragdollConstraints.size(); for (int i = 0; i < numConstraints; ++i) { diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index e9c272b1ee..18cf62df49 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -68,13 +68,8 @@ public: Ragdoll(); virtual ~Ragdoll(); - /// Delete all data. - void clearRagdollConstraintsAndPoints(); - - virtual void initRagdollPoints() = 0; virtual void stepRagdollForward(float deltaTime) = 0; - /// Enforce contraints. /// \return max distance of point movement float enforceRagdollConstraints(); @@ -83,6 +78,10 @@ public: QVector& getRagdollPoints() { return _ragdollPoints; } protected: + void clearRagdollConstraintsAndPoints(); + virtual void initRagdollPoints() = 0; + virtual void buildRagdollConstraints() = 0; + QVector _ragdollPoints; QVector _ragdollConstraints; }; From 641b9e8253fdc474ad9f077358c3c3be64702915 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 19 Jun 2014 10:43:25 -0700 Subject: [PATCH 037/105] Set minimum angular size too model selection --- examples/editModels.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index eebcd075fa..d2670f59bf 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -34,6 +34,7 @@ var LASER_COLOR = { red: 255, green: 0, blue: 0 }; var LASER_LENGTH_FACTOR = 500 ; +var MIN_ANGULAR_SIZE = 2; var MAX_ANGULAR_SIZE = 45; var LEFT = 0; @@ -828,8 +829,9 @@ function mousePressEvent(event) { var X = Vec3.sum(A, Vec3.multiply(B, x)); var d = Vec3.length(Vec3.subtract(P, X)); - if (0 < x && x < LASER_LENGTH_FACTOR) { - if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 < MAX_ANGULAR_SIZE) { + var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; + if (0 < x && angularSize > MIN_ANGULAR_SIZE) { + if (angularSize < MAX_ANGULAR_SIZE) { modelSelected = true; selectedModelID = foundModel; selectedModelProperties = properties; From 676141024a8a63a33664b3cd461709eaf44af17d Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 19 Jun 2014 10:46:06 -0700 Subject: [PATCH 038/105] Added angular size limit to hydras too. --- examples/editModels.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index d2670f59bf..8a73c79532 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -278,8 +278,9 @@ function controller(wichSide) { var X = Vec3.sum(A, Vec3.multiply(B, x)); var d = Vec3.length(Vec3.subtract(P, X)); - if (0 < x && x < LASER_LENGTH_FACTOR) { - if (2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14 > MAX_ANGULAR_SIZE) { + var angularSize = 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14; + if (0 < x && angularSize > MIN_ANGULAR_SIZE) { + if (angularSize > MAX_ANGULAR_SIZE) { print("Angular size too big: " + 2 * Math.atan(properties.radius / Vec3.distance(Camera.getPosition(), properties.position)) * 180 / 3.14); return { valid: false }; } From 2dbf8d7daf6a45a18991f06dd6ec1ef08dfe8f15 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 19 Jun 2014 11:08:21 -0700 Subject: [PATCH 039/105] Fix fullscreen toggle shortcut Remove the "Meta" modifier so that the shortcut works on Windows. --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5c8c2e97aa..9df3e25bf8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -251,7 +251,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, - Qt::CTRL | Qt::META | Qt::Key_F, + Qt::CTRL | Qt::Key_F, false, appInstance, SLOT(setFullscreen(bool))); From 4a4efb0bfaae0137a5af37905f6fa6288d577503 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 19 Jun 2014 11:11:08 -0700 Subject: [PATCH 040/105] Don't glow models if you can't grab it --- examples/editModels.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 8a73c79532..55e4d56a21 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -328,7 +328,8 @@ function controller(wichSide) { origin: this.palmPosition, direction: this.front }); - if (intersection.accurate && intersection.modelID.isKnownID) { + var angularSize = 2 * Math.atan(intersection.modelProperties.radius / Vec3.distance(Camera.getPosition(), intersection.modelProperties.position)) * 180 / 3.14; + if (intersection.accurate && intersection.modelID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) { this.glowedIntersectingModel = intersection.modelID; Models.editModel(this.glowedIntersectingModel, { glowLevel: 0.25 }); } @@ -887,7 +888,8 @@ function mouseMoveEvent(event) { glowedModelID.isKnownID = false; } - if (modelIntersection.modelID.isKnownID) { + var angularSize = 2 * Math.atan(modelIntersection.modelProperties.radius / Vec3.distance(Camera.getPosition(), modelIntersection.modelProperties.position)) * 180 / 3.14; + if (modelIntersection.modelID.isKnownID && angularSize > MIN_ANGULAR_SIZE && angularSize < MAX_ANGULAR_SIZE) { Models.editModel(modelIntersection.modelID, { glowLevel: 0.25 }); glowedModelID = modelIntersection.modelID; } From fbafcb6fd8bf6bd2ec1c3a0c70c602dac584c5de Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 11:46:09 -0700 Subject: [PATCH 041/105] Fix distance constraint lengths --- interface/src/avatar/SkeletonModel.cpp | 6 +++--- interface/src/renderer/Model.cpp | 1 + libraries/shared/src/Ragdoll.h | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index f7eb7e614e..bcd35a1ce2 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -657,9 +657,9 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute the default transforms and slam the ragdoll positions accordingly // (which puts the shapes where we want them) - transforms[0] = glm::scale(_scale); - _ragdollPoints[0]._position = glm::vec3(0.0f); - _ragdollPoints[0]._lastPosition = glm::vec3(0.0f); + transforms[0] = _jointStates[0].getTransform(); + _ragdollPoints[0]._position = extractTranslation(transforms[0]); + _ragdollPoints[0]._lastPosition = _ragdollPoints[0]._position; for (int i = 1; i < numJoints; i++) { const FBXJoint& joint = geometry.joints.at(i); int parentIndex = joint.parentIndex; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 515dcd4f27..5dfdd3c801 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -174,6 +174,7 @@ QVector Model::createJointStates(const FBXGeometry& geometry) { int parentIndex = joint.parentIndex; if (parentIndex == -1) { _rootIndex = i; + // NOTE: in practice geometry.offset has a non-unity scale (rather than a translation) glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; state.computeTransform(parentTransform); } else { diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 18cf62df49..788a46d4d1 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -57,6 +57,7 @@ public: DistanceConstraint(const DistanceConstraint& other); float enforce(); void setDistance(float distance); + float getDistance() const { return _distance; } private: float _distance; VerletPoint* _points[2]; From 9950c5b209ea977b215b6f28bbe1468a8f70daa4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 11:52:44 -0700 Subject: [PATCH 042/105] remove entities in PhysicsSimulation dtor --- libraries/shared/src/PhysicsSimulation.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 0a91babe08..c7c3f5e161 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -31,6 +31,11 @@ PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMUL } PhysicsSimulation::~PhysicsSimulation() { + int numEntities = _entities.size(); + for (int i = 0; i < numEntities; ++i) { + _entities[i]->_simulation = NULL; + } + _entities.clear(); _dolls.clear(); } From 828c365b25066db260f0c5a55b50bd9681b27e5e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 11:57:32 -0700 Subject: [PATCH 043/105] more dtor cleanup of back pointers --- libraries/shared/src/PhysicsEntity.cpp | 8 ++++++-- libraries/shared/src/PhysicsSimulation.cpp | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 10c08a3cea..03d36c1c99 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -10,6 +10,8 @@ // #include "PhysicsEntity.h" + +#include "PhysicsSimulation.h" #include "Shape.h" #include "ShapeCollider.h" @@ -23,8 +25,10 @@ PhysicsEntity::PhysicsEntity() : } PhysicsEntity::~PhysicsEntity() { - // entity should be removed from the simulation before it is deleted - assert(_simulation == NULL); + if (_simulation) { + _simulation->removeEntity(this); + _simulation = NULL; + } } void PhysicsEntity::setTranslation(const glm::vec3& translation) { diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index c7c3f5e161..5c0c2c9883 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -31,11 +31,14 @@ PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMUL } PhysicsSimulation::~PhysicsSimulation() { + // entities have a backpointer to this simulator that must be cleaned up int numEntities = _entities.size(); for (int i = 0; i < numEntities; ++i) { _entities[i]->_simulation = NULL; } _entities.clear(); + + // but Ragdolls do not _dolls.clear(); } From 716ae3e47916785296158f13737cd2bc7835bdc6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 12:32:17 -0700 Subject: [PATCH 044/105] add MyAvatar's skeleton to its PhysicsSimulation --- interface/src/avatar/MyAvatar.cpp | 13 +++++++++---- interface/src/avatar/MyAvatar.h | 2 +- libraries/shared/src/PhysicsSimulation.cpp | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 4a4e20fabe..c77b0a2924 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -77,16 +77,20 @@ MyAvatar::MyAvatar() : _lookAtTargetAvatar(), _shouldRender(true), _billboardValid(false), - _simulationEngine() + _physicsSimulation() { for (int i = 0; i < MAX_DRIVE_KEYS; i++) { _driveKeys[i] = 0.0f; } _skeletonModel.setEnableShapes(true); - _simulationEngine.addRagdoll(&_skeletonModel); + // The skeleton is both a PhysicsEntity and Ragdoll, so we add it to the simulation once for each type. + _physicsSimulation.addEntity(&_skeletonModel); + _physicsSimulation.addRagdoll(&_skeletonModel); } MyAvatar::~MyAvatar() { + _physicsSimulation.removeEntity(&_skeletonModel); + _physicsSimulation.removeRagdoll(&_skeletonModel); _lookAtTargetAvatar.clear(); } @@ -192,13 +196,14 @@ void MyAvatar::simulate(float deltaTime) { head->simulate(deltaTime, true); } - if (!Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); const int minError = 0.005f; const float maxIterations = 4; const quint64 maxUsec = 500; - _simulationEngine.stepForward(deltaTime, minError, maxIterations, maxUsec); + _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); } + /* TODO: Andrew to make this work again // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionGroups != 0) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b48e61718c..e7023b45a1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -175,7 +175,7 @@ private: float _oculusYawOffset; QList _animationHandles; - PhysicsSimulation _simulationEngine; + PhysicsSimulation _physicsSimulation; // private methods float computeDistanceToFloor(const glm::vec3& startPoint); diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 5c0c2c9883..4406190c02 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -167,7 +167,9 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter // TODO: check for pairwise collision bypass here for (int k = j+1; k < numShapes; ++k) { const Shape* otherShape = shapes.at(k); - ShapeCollider::collideShapes(shape, otherShape, _collisionList); + if (otherShape) { + ShapeCollider::collideShapes(shape, otherShape, _collisionList); + } } } From aa3602f0fb5fa0def615377e25dd52e2f33e5a3f Mon Sep 17 00:00:00 2001 From: wangyix Date: Thu, 19 Jun 2014 14:34:55 -0700 Subject: [PATCH 045/105] added InterframeTimeGapHistory and frameReceived() calls --- .../src/audio/AvatarAudioRingBuffer.cpp | 2 + libraries/audio/src/AudioRingBuffer.cpp | 57 +++++++++++++++++++ libraries/audio/src/AudioRingBuffer.h | 28 +++++++++ .../audio/src/InjectedAudioRingBuffer.cpp | 2 + 4 files changed, 89 insertions(+) diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 5613a64cc4..47a0ecbea8 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -19,6 +19,8 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : } int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { + _timeGapHistory.frameReceived(); + _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); return PositionalAudioRingBuffer::parseData(packet); } diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 2101fcb9cd..ea15e27ef5 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "SharedUtil.h" #include @@ -19,6 +20,62 @@ #include "AudioRingBuffer.h" +InterframeTimeGapHistory::InterframeTimeGapHistory() + : _lastFrameReceivedTime(0), + _numSamplesInCurrentInterval(0), + _currentIntervalMaxGap(0), + _newestIntervalMaxGapAt(0), + _windowMaxGap(0), + _newWindowMaxGapAvailable(false) +{ + memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW*sizeof(quint64)); +} + +void InterframeTimeGapHistory::frameReceived() { + quint64 now = usecTimestampNow(); + + // make sure this isn't the first time frameReceived() is called, meaning there's actually a gap to calculate. + if (_lastFrameReceivedTime != 0) { + quint64 gap = now - _lastFrameReceivedTime; + + // update the current interval max + if (gap > _currentIntervalMaxGap) { + _currentIntervalMaxGap = gap; + } + _numSamplesInCurrentInterval++; + + // if the current interval of samples is now full, record it in our interval maxes + if (_numSamplesInCurrentInterval == TIME_GAP_NUM_SAMPLES_IN_INTERVAL) { + + // find location to insert this interval's max (increment index cyclically) + _newestIntervalMaxGapAt = _newestIntervalMaxGapAt == TIME_GAP_NUM_INTERVALS_IN_WINDOW - 1 ? 0 : _newestIntervalMaxGapAt + 1; + + // record the current interval's max gap as the newest + _intervalMaxGaps[_newestIntervalMaxGapAt] = _currentIntervalMaxGap; + + // update the window max gap, which is the max out of all the past intervals' max gaps + _windowMaxGap = 0; + for (int i = 0; i < TIME_GAP_NUM_INTERVALS_IN_WINDOW; i++) { + if (_intervalMaxGaps[i] > _windowMaxGap) { + _windowMaxGap = _intervalMaxGaps[i]; + } + } + _newWindowMaxGapAvailable = true; + + // reset the current interval + _numSamplesInCurrentInterval = 0; + _currentIntervalMaxGap = 0; + } + } + _lastFrameReceivedTime = now; +} + +quint64 InterframeTimeGapHistory::getPastWindowMaxGap() { + _newWindowMaxGapAvailable = false; + return _windowMaxGap; +} + + AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) : NodeData(), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 04cc67c8ac..105e3761ae 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -21,6 +21,30 @@ #include "NodeData.h" +// this means that every 500 samples, the max for the past 10*500 samples will be calculated +const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; +const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; + +class InterframeTimeGapHistory { +public: + InterframeTimeGapHistory(); + + void frameReceived(); + bool isNewWindowMaxGapAvailable() const { return _newWindowMaxGapAvailable; } + quint64 getPastWindowMaxGap(); + +private: + quint64 _lastFrameReceivedTime; + + int _numSamplesInCurrentInterval; + quint64 _currentIntervalMaxGap; + quint64 _intervalMaxGaps[TIME_GAP_NUM_INTERVALS_IN_WINDOW]; + int _newestIntervalMaxGapAt; + quint64 _windowMaxGap; + bool _newWindowMaxGapAvailable; +}; + + const int SAMPLE_RATE = 24000; const int NETWORK_BUFFER_LENGTH_BYTES_STEREO = 1024; @@ -74,6 +98,8 @@ public: bool hasStarted() const { return _hasStarted; } void addSilentFrame(int numSilentSamples); + + InterframeTimeGapHistory& getInterframeTimeGapHistory() { return _timeGapHistory; } protected: // disallow copying of AudioRingBuffer objects AudioRingBuffer(const AudioRingBuffer&); @@ -89,6 +115,8 @@ protected: bool _isStarved; bool _hasStarted; bool _randomAccessMode; /// will this ringbuffer be used for random access? if so, do some special processing + + InterframeTimeGapHistory _timeGapHistory; }; #endif // hifi_AudioRingBuffer_h diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 2658b4c336..103d0d8d2e 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -31,6 +31,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) const uchar MAX_INJECTOR_VOLUME = 255; int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { + _timeGapHistory.frameReceived(); + // setup a data stream to read from this packet QDataStream packetStream(packet); packetStream.skipRawData(numBytesForPacketHeader(packet)); From 7a9c0f0e6a504038bf7d19ef616ab4c5d3054945 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 19 Jun 2014 14:50:19 -0700 Subject: [PATCH 046/105] Working on tests for metavoxel streaming. --- libraries/metavoxels/src/MetavoxelData.cpp | 69 +++++++++ libraries/metavoxels/src/MetavoxelData.h | 13 +- tests/metavoxels/src/MetavoxelTests.cpp | 164 +++++++++++++++++---- tests/metavoxels/src/MetavoxelTests.h | 16 +- 4 files changed, 229 insertions(+), 33 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 503dcce8d2..cf645e6f5e 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -604,10 +604,79 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO void MetavoxelData::readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& in, const MetavoxelLOD& lod) { + + // shallow copy the reference + *this = reference; + + // if nothing changed, return + bool changed; + in >> changed; + if (!changed) { + return; + } + + // expand if the size has changed + bool sizeChanged; + in >> sizeChanged; + if (sizeChanged) { + float size; + in >> size; + while (_size < size) { + expand(); + } + } + + // read the nodes removed + forever { + AttributePointer attribute; + in >> attribute; + if (!attribute) { + break; + } + _roots.take(attribute)->decrementReferenceCount(attribute); + } } void MetavoxelData::writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const { + + // first things first: there might be no change whatsoever + glm::vec3 minimum = getMinimum(); + bool becameSubdivided = lod.becameSubdivided(minimum, _size, referenceLOD); + if (_size == reference._size && _roots == reference._roots && !becameSubdivided) { + out << false; + return; + } + out << true; + + // compare the size; if changed (rare), we must compare to the expanded reference + const MetavoxelData* expandedReference = &reference; + if (_size == reference._size) { + out << false; + } else { + out << true; + out << _size; + + MetavoxelData* expanded = new MetavoxelData(reference); + while (expanded->_size < _size) { + expanded->expand(); + } + expandedReference = expanded; + } + + // write the nodes removed + for (QHash::const_iterator it = expandedReference->_roots.constBegin(); + it != expandedReference->_roots.constEnd(); it++) { + if (!_roots.contains(it.key())) { + out << it.key(); + } + } + out << AttributePointer(); + + // delete the expanded reference if we had to expand + if (expandedReference != &reference) { + delete expandedReference; + } } MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 199b6a8aff..6b262d6892 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -34,7 +34,8 @@ class NetworkValue; class Spanner; class SpannerRenderer; -/// Determines whether to subdivide each node when traversing. +/// Determines whether to subdivide each node when traversing. Contains the position (presumed to be of the viewer) and a +/// threshold value, where lower thresholds cause smaller/more distant voxels to be subdivided. class MetavoxelLOD { STREAMABLE @@ -46,6 +47,7 @@ public: bool isValid() const { return threshold > 0.0f; } + /// Checks whether, according to this LOD, we should subdivide the described voxel. bool shouldSubdivide(const glm::vec3& minimum, float size, float multiplier = 1.0f) const; /// Checks whether the node or any of the nodes underneath it have had subdivision enabled as compared to the reference. @@ -78,20 +80,25 @@ public: /// Applies the specified visitor to the contained voxels. void guide(MetavoxelVisitor& visitor); + /// Inserts a spanner into the specified attribute layer. void insert(const AttributePointer& attribute, const SharedObjectPointer& object); void insert(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + /// Removes a spanner from the specified attribute layer. void remove(const AttributePointer& attribute, const SharedObjectPointer& object); void remove(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + /// Toggles the existence of a spanner in the specified attribute layer (removes if present, adds if not). void toggle(const AttributePointer& attribute, const SharedObjectPointer& object); void toggle(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& object); + /// Replaces a spanner in the specified attribute layer. void replace(const AttributePointer& attribute, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); void replace(const AttributePointer& attribute, const Box& bounds, float granularity, const SharedObjectPointer& oldObject, const SharedObjectPointer& newObject); - + + /// Clears all data in the specified attribute layer. void clear(const AttributePointer& attribute); /// Convenience function that finds the first spanner intersecting the provided ray. @@ -101,7 +108,7 @@ public: /// Sets part of the data. void set(const glm::vec3& minimum, const MetavoxelData& data, bool blend = false); - /// Expands the tree, increasing its capacity in all dimensions. + /// Expands the tree, doubling its size in all dimensions (that is, increasing its volume eightfold). void expand(); void read(Bitstream& in, const MetavoxelLOD& lod = MetavoxelLOD()); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index c9bce27cc3..daee1165f6 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -321,44 +321,76 @@ static bool testSerialization(Bitstream::MetadataType metadataType) { } bool MetavoxelTests::run() { - - qDebug() << "Running transmission tests..."; - qDebug(); - // seed the random number generator so that our tests are reproducible srand(0xBAAAAABE); - // create two endpoints with the same header + // check for an optional command line argument specifying a single test + QStringList arguments = this->arguments(); + int test = (arguments.size() > 1) ? arguments.at(1).toInt() : 0; + QByteArray datagramHeader("testheader"); - Endpoint alice(datagramHeader), bob(datagramHeader); - - alice.setOther(&bob); - bob.setOther(&alice); - - // perform a large number of simulation iterations const int SIMULATION_ITERATIONS = 10000; - for (int i = 0; i < SIMULATION_ITERATIONS; i++) { - if (alice.simulate(i) || bob.simulate(i)) { + if (test == 0 || test == 1) { + qDebug() << "Running transmission tests..."; + qDebug(); + + // create two endpoints with the same header + Endpoint alice(datagramHeader), bob(datagramHeader); + + alice.setOther(&bob); + bob.setOther(&alice); + + // perform a large number of simulation iterations + for (int i = 0; i < SIMULATION_ITERATIONS; i++) { + if (alice.simulate(i) || bob.simulate(i)) { + return true; + } + } + + qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived; + qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived; + qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived; + qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; + qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << + datagramsReceived << "with" << bytesReceived << "bytes"; + qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; + qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; + qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; + qDebug(); + } + + if (test == 0 || test == 2) { + qDebug() << "Running serialization tests..."; + qDebug(); + + if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) { return true; } } - qDebug() << "Sent" << highPriorityMessagesSent << "high priority messages, received" << highPriorityMessagesReceived; - qDebug() << "Sent" << unreliableMessagesSent << "unreliable messages, received" << unreliableMessagesReceived; - qDebug() << "Sent" << reliableMessagesSent << "reliable messages, received" << reliableMessagesReceived; - qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; - qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << - datagramsReceived << "with" << bytesReceived << "bytes"; - qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; - qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; - qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; - qDebug(); + if (test == 0 || test == 3) { + qDebug() << "Running metavoxel data tests..."; + qDebug(); - qDebug() << "Running serialization tests..."; - qDebug(); + // clear the stats + datagramsSent = bytesSent = datagramsReceived = bytesReceived = 0; - if (testSerialization(Bitstream::HASH_METADATA) || testSerialization(Bitstream::FULL_METADATA)) { - return true; + // create client and server endpoints + Endpoint client(datagramHeader, Endpoint::METAVOXEL_CLIENT_MODE); + Endpoint server(datagramHeader, Endpoint::METAVOXEL_SERVER_MODE); + + client.setOther(&server); + server.setOther(&client); + + // simulate + for (int i = 0; i < SIMULATION_ITERATIONS; i++) { + if (client.simulate(i) || server.simulate(i)) { + return true; + } + } + + qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << + datagramsReceived << "with" << bytesReceived << "bytes"; } qDebug() << "All tests passed!"; @@ -375,7 +407,8 @@ static SharedObjectPointer createRandomSharedObject() { } } -Endpoint::Endpoint(const QByteArray& datagramHeader) : +Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : + _mode(mode), _sequencer(new DatagramSequencer(datagramHeader, this)), _highPriorityMessagesToSend(0.0f), _reliableMessagesToSend(0.0f) { @@ -396,6 +429,13 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) : ReceiveRecord receiveRecord = { 0 }; _receiveRecords.append(receiveRecord); + if (mode == METAVOXEL_CLIENT_MODE) { + _lod = MetavoxelLOD(glm::vec3(), 0.01f); + return; + } + if (mode == METAVOXEL_SERVER_MODE) { + return; + } // create the object that represents out delta-encoded state _localState = new TestSharedObjectA(); @@ -415,7 +455,7 @@ Endpoint::Endpoint(const QByteArray& datagramHeader) : QByteArray bytes = createRandomBytes(MIN_STREAM_BYTES, MAX_STREAM_BYTES); _dataStreamed.append(bytes); output->getBuffer().write(bytes); - streamedBytesSent += bytes.size(); + streamedBytesSent += bytes.size(); } static QVariant createRandomMessage() { @@ -525,6 +565,34 @@ bool Endpoint::simulate(int iterationNumber) { } } + if (_mode == METAVOXEL_CLIENT_MODE) { + Bitstream& out = _sequencer->startPacket(); + + ClientStateMessage state = { _lod }; + out << QVariant::fromValue(state); + _sequencer->endPacket(); + + // record the send + SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), MetavoxelData(), _lod }; + _sendRecords.append(record); + return false; + } + if (_mode == METAVOXEL_SERVER_MODE) { + // wait until we have a valid lod + if (!_lod.isValid()) { + return false; + } + Bitstream& out = _sequencer->startPacket(); + out << QVariant::fromValue(MetavoxelDeltaMessage()); + _data.writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod); + _sequencer->endPacket(); + + // record the send + SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), _data, _lod }; + _sendRecords.append(record); + return false; + } + // enqueue some number of high priority messages const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f; @@ -619,6 +687,28 @@ void Endpoint::handleHighPriorityMessage(const QVariant& message) { } void Endpoint::readMessage(Bitstream& in) { + if (_mode == METAVOXEL_CLIENT_MODE) { + QVariant message; + in >> message; + handleMessage(message, in); + + // record the receipt + ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), SharedObjectPointer(), + _data, _sendRecords.first().lod }; + _receiveRecords.append(record); + return; + } + if (_mode == METAVOXEL_SERVER_MODE) { + QVariant message; + in >> message; + handleMessage(message, in); + + // record the receipt + ReceiveRecord record = { _sequencer->getIncomingPacketNumber() }; + _receiveRecords.append(record); + return; + } + SequencedTestMessage message; in >> message; @@ -682,6 +772,22 @@ void Endpoint::clearReceiveRecordsBefore(int index) { _receiveRecords.erase(_receiveRecords.begin(), _receiveRecords.begin() + index + 1); } +void Endpoint::handleMessage(const QVariant& message, Bitstream& in) { + int userType = message.userType(); + if (userType == ClientStateMessage::Type) { + ClientStateMessage state = message.value(); + _lod = state.lod; + + } else if (userType == MetavoxelDeltaMessage::Type) { + _data.readDelta(_receiveRecords.first().data, _receiveRecords.first().lod, in, _sendRecords.first().lod); + + } else if (userType == QMetaType::QVariantList) { + foreach (const QVariant& element, message.toList()) { + handleMessage(element, in); + } + } +} + TestSharedObjectA::TestSharedObjectA(float foo, TestEnum baz, TestFlags bong) : _foo(foo), _baz(baz), diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index ac9eda2659..1de355661d 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -16,6 +16,7 @@ #include #include +#include #include class SequencedTestMessage; @@ -39,7 +40,9 @@ class Endpoint : public QObject { public: - Endpoint(const QByteArray& datagramHeader); + enum Mode { BASIC_PEER_MODE, METAVOXEL_SERVER_MODE, METAVOXEL_CLIENT_MODE }; + + Endpoint(const QByteArray& datagramHeader, Mode mode = BASIC_PEER_MODE); void setOther(Endpoint* other) { _other = other; } @@ -60,18 +63,26 @@ private slots: private: + void handleMessage(const QVariant& message, Bitstream& in); + class SendRecord { public: int packetNumber; SharedObjectPointer localState; + MetavoxelData data; + MetavoxelLOD lod; }; class ReceiveRecord { public: int packetNumber; SharedObjectPointer remoteState; + MetavoxelData data; + MetavoxelLOD lod; }; + Mode _mode; + DatagramSequencer* _sequencer; QList _sendRecords; QList _receiveRecords; @@ -79,6 +90,9 @@ private: SharedObjectPointer _localState; SharedObjectPointer _remoteState; + MetavoxelData _data; + MetavoxelLOD _lod; + Endpoint* _other; QList > _delayedDatagrams; float _highPriorityMessagesToSend; From 8176aa129289dc99eeaa85a9727dceac6fc27cbc Mon Sep 17 00:00:00 2001 From: wangyix Date: Thu, 19 Jun 2014 14:54:22 -0700 Subject: [PATCH 047/105] added debug code for testing time gap history --- libraries/audio/src/AudioRingBuffer.cpp | 6 ++++++ libraries/audio/src/AudioRingBuffer.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index ea15e27ef5..f42ba49380 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -38,6 +38,8 @@ void InterframeTimeGapHistory::frameReceived() { if (_lastFrameReceivedTime != 0) { quint64 gap = now - _lastFrameReceivedTime; +printf("new gap: %llu\n", gap); + // update the current interval max if (gap > _currentIntervalMaxGap) { _currentIntervalMaxGap = gap; @@ -47,6 +49,8 @@ void InterframeTimeGapHistory::frameReceived() { // if the current interval of samples is now full, record it in our interval maxes if (_numSamplesInCurrentInterval == TIME_GAP_NUM_SAMPLES_IN_INTERVAL) { +printf("\t interval full: max interval gap: %llu\n", _currentIntervalMaxGap); + // find location to insert this interval's max (increment index cyclically) _newestIntervalMaxGapAt = _newestIntervalMaxGapAt == TIME_GAP_NUM_INTERVALS_IN_WINDOW - 1 ? 0 : _newestIntervalMaxGapAt + 1; @@ -62,6 +66,8 @@ void InterframeTimeGapHistory::frameReceived() { } _newWindowMaxGapAvailable = true; +printf("\t\t new window max gap: %llu\n", _windowMaxGap); + // reset the current interval _numSamplesInCurrentInterval = 0; _currentIntervalMaxGap = 0; diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 105e3761ae..b702abde7b 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -22,8 +22,8 @@ #include "NodeData.h" // this means that every 500 samples, the max for the past 10*500 samples will be calculated -const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; -const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; +const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 6; +const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 4; class InterframeTimeGapHistory { public: From 77c377711a28d735405141c305c7d72e7460b620 Mon Sep 17 00:00:00 2001 From: wangyix Date: Thu, 19 Jun 2014 15:39:54 -0700 Subject: [PATCH 048/105] removed debug code --- libraries/audio/src/AudioRingBuffer.cpp | 6 ------ libraries/audio/src/AudioRingBuffer.h | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index f42ba49380..ea15e27ef5 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -38,8 +38,6 @@ void InterframeTimeGapHistory::frameReceived() { if (_lastFrameReceivedTime != 0) { quint64 gap = now - _lastFrameReceivedTime; -printf("new gap: %llu\n", gap); - // update the current interval max if (gap > _currentIntervalMaxGap) { _currentIntervalMaxGap = gap; @@ -49,8 +47,6 @@ printf("new gap: %llu\n", gap); // if the current interval of samples is now full, record it in our interval maxes if (_numSamplesInCurrentInterval == TIME_GAP_NUM_SAMPLES_IN_INTERVAL) { -printf("\t interval full: max interval gap: %llu\n", _currentIntervalMaxGap); - // find location to insert this interval's max (increment index cyclically) _newestIntervalMaxGapAt = _newestIntervalMaxGapAt == TIME_GAP_NUM_INTERVALS_IN_WINDOW - 1 ? 0 : _newestIntervalMaxGapAt + 1; @@ -66,8 +62,6 @@ printf("\t interval full: max interval gap: %llu\n", _currentIntervalMaxGap); } _newWindowMaxGapAvailable = true; -printf("\t\t new window max gap: %llu\n", _windowMaxGap); - // reset the current interval _numSamplesInCurrentInterval = 0; _currentIntervalMaxGap = 0; diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index b702abde7b..105e3761ae 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -22,8 +22,8 @@ #include "NodeData.h" // this means that every 500 samples, the max for the past 10*500 samples will be calculated -const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 6; -const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 4; +const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; +const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; class InterframeTimeGapHistory { public: From f0c2417206cc222bfe842a3b09326e2b9ea794ab Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 15:50:16 -0700 Subject: [PATCH 049/105] Split PhysicsSimulation::stepForward() into parts --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/shared/src/PhysicsSimulation.cpp | 46 ++++++++++++---------- libraries/shared/src/PhysicsSimulation.h | 6 +-- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c77b0a2924..1f7116a254 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -200,7 +200,7 @@ void MyAvatar::simulate(float deltaTime) { PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); const int minError = 0.005f; const float maxIterations = 4; - const quint64 maxUsec = 500; + const quint64 maxUsec = 1000; _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); } diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 4406190c02..984d0b8fea 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -128,30 +128,36 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { } } } - // TODO: Andrew need to implement: // DONE (1) joints pull points (SpecialCapsuleShape would help solve this) // DONE (2) points slam shapes (SpecialCapsuleShape would help solve this) // DONE (3) detect collisions // DONE (4) collisions move points (SpecialCapsuleShape would help solve this) // DONE (5) enforce constraints -// (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support +// DONE (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support // (7) support for pairwise collision bypass // (8) process collisions // (9) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { - int iterations = 0; - quint64 now = usecTimestampNow(); - quint64 startTime = now; - quint64 expiry = now + maxUsec; + quint64 startTime = usecTimestampNow(); + quint64 expiry = startTime + maxUsec; - // move dolls + moveRagdolls(deltaTime); + computeCollisions(); + processCollisions(); + enforceConstraints(minError, maxIterations, expiry - usecTimestampNow()); + + _stepTime = usecTimestampNow()- startTime; +} + +void PhysicsSimulation::moveRagdolls(float deltaTime) { int numDolls = _dolls.size(); for (int i = 0; i < numDolls; ++i) { _dolls.at(i)->stepRagdollForward(deltaTime); } - - // collide +} + +void PhysicsSimulation::computeCollisions() { _collisionList.clear(); // TODO: keep track of QSet collidedEntities; int numEntities = _entities.size(); @@ -179,12 +185,19 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter ShapeCollider::collideShapesWithShapes(shapes, otherShapes, _collisionList); } } - - // TODO: process collisions _numCollisions = _collisionList.size(); +} - // enforce constraints +void PhysicsSimulation::processCollisions() { + // TODO: Andrew to implement this +} + +void PhysicsSimulation::enforceConstraints(float minError, int maxIterations, quint64 maxUsec) { + quint64 now = usecTimestampNow(); + quint64 expiry = now + maxUsec; + int iterations = 0; float error = 0.0f; + int numDolls = _dolls.size(); do { error = 0.0f; for (int i = 0; i < numDolls; ++i) { @@ -193,16 +206,7 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter ++iterations; now = usecTimestampNow(); } while (iterations < maxIterations && error > minError && now < expiry); - _numIterations = iterations; _constraintError = error; - _stepTime = now - startTime; -} - -int PhysicsSimulation::computeCollisions() { - return 0.0f; -} - -void PhysicsSimulation::processCollisions() { } diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/shared/src/PhysicsSimulation.h index 957309ddd7..9f8ffa4486 100644 --- a/libraries/shared/src/PhysicsSimulation.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -41,10 +41,10 @@ public: /// \return distance of largest movement void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec); - /// \return number of collisions - int computeCollisions(); - + void moveRagdolls(float deltaTime); + void computeCollisions(); void processCollisions(); + void enforceConstraints(float minError, int maxIterations, quint64 maxUsec); private: CollisionList _collisionList; From 100bc022ec55a0851a6d5ffb6bff1d16f06bd03c Mon Sep 17 00:00:00 2001 From: wangyix Date: Thu, 19 Jun 2014 16:42:51 -0700 Subject: [PATCH 050/105] fixed a for loop that erased elements while iterating --- assignment-client/src/audio/AudioMixerClientData.cpp | 9 ++++++--- libraries/audio/src/AudioRingBuffer.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 2f78a4ac78..2329946e49 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -120,9 +120,10 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSam } void AudioMixerClientData::pushBuffersAfterFrameSend() { - for (int i = 0; i < _ringBuffers.size(); i++) { + QList::iterator i = _ringBuffers.begin(); + while (i != _ringBuffers.end()) { // this was a used buffer, push the output pointer forwards - PositionalAudioRingBuffer* audioBuffer = _ringBuffers[i]; + PositionalAudioRingBuffer* audioBuffer = *i; if (audioBuffer->willBeAddedToMix()) { audioBuffer->shiftReadPosition(audioBuffer->isStereo() @@ -133,7 +134,9 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { && audioBuffer->hasStarted() && audioBuffer->isStarved()) { // this is an empty audio buffer that has starved, safe to delete delete audioBuffer; - _ringBuffers.erase(_ringBuffers.begin() + i); + i = _ringBuffers.erase(i); + continue; } + i++; } } diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 105e3761ae..46bc333e4b 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -25,6 +25,7 @@ const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; +// class used to track time between incoming frames for the purpose of varying the jitter buffer length class InterframeTimeGapHistory { public: InterframeTimeGapHistory(); From 4a0ce7a9aebe777c5d6d8b83f11a2add638bdc60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 17:39:50 -0700 Subject: [PATCH 051/105] support disabling collisions btw adjacent joints --- interface/src/avatar/SkeletonModel.cpp | 38 +------------ libraries/shared/src/PhysicsEntity.cpp | 66 ++++++++++++++++++++++ libraries/shared/src/PhysicsEntity.h | 7 +++ libraries/shared/src/PhysicsSimulation.cpp | 8 +-- 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index bcd35a1ce2..b28dd08663 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -594,10 +594,11 @@ void SkeletonModel::buildShapes() { // This method moves the shapes to their default positions in Model frame computeBoundingShape(geometry); - // while the shapes are in their default position we create the constraints + // while the shapes are in their default position... + disableSelfCollisions(); buildRagdollConstraints(); - // move shapes back to current joint positions + // ... then move shapes back to current joint positions moveShapesTowardJoints(1.0f); } @@ -613,39 +614,6 @@ void SkeletonModel::moveShapesTowardJoints(float fraction) { } } -/* TODO: Andrew to remove this when done with ragdoll work. -void SkeletonModel::updateShapePositionsLegacy() { - if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { - glm::vec3 rootPosition(0.0f); - _boundingRadius = 0.0f; - float uniformScale = extractUniformScale(_scale); - for (int i = 0; i < _jointStates.size(); i++) { - const JointState& state = _jointStates[i]; - const FBXJoint& joint = state.getFBXJoint(); - // shape position and rotation need to be in world-frame - glm::quat stateRotation = state.getRotation(); - glm::vec3 shapeOffset = uniformScale * (stateRotation * joint.shapePosition); - glm::vec3 worldPosition = _translation + _rotation * (state.getPosition() + shapeOffset); - Shape* shape = _shapes[i]; - if (shape) { - shape->setTranslation(worldPosition); - shape->setRotation(_rotation * stateRotation * joint.shapeRotation); - float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius(); - if (distance > _boundingRadius) { - _boundingRadius = distance; - } - } - if (joint.parentIndex == -1) { - rootPosition = worldPosition; - } - } - _shapesAreDirty = false; - _boundingShape.setTranslation(rootPosition + _rotation * _boundingShapeLocalOffset); - _boundingShape.setRotation(_rotation); - } -} -*/ - void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute default joint transforms int numJoints = geometry.joints.size(); diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 03d36c1c99..2955192fbf 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -161,3 +161,69 @@ bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& c } return collided; } + +// ----------------------------------------------------------- +// TODO: enforce this maximum when shapes are built. The gotcha here is that +// the Model class (derived from PhysicsEntity) expects numShapes == numJoints, +// so we have to modify that code to be safe. +const int MAX_SHAPES_PER_ENTITY = 256; + +// the first 256 prime numbers +const int primes[256] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619 }; + +void PhysicsEntity::disableCollisions(int shapeIndexA, int shapeIndexB) { + if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { + _disabledCollisions.insert(primes[shapeIndexA] * primes[shapeIndexB]); + } +} + +bool PhysicsEntity::collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const { + if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) { + return !_disabledCollisions.contains(primes[shapeIndexA] * primes[shapeIndexB]); + } + return false; +} + +void PhysicsEntity::disableSelfCollisions() { + CollisionList collisions(10); + int numShapes = _shapes.size(); + for (int i = 0; i < numShapes; ++i) { + const Shape* shape = _shapes.at(i); + if (!shape) { + continue; + } + for (int j = i+1; j < numShapes; ++j) { + const Shape* otherShape = _shapes.at(j); + if (otherShape && ShapeCollider::collideShapes(shape, otherShape, collisions)) { + disableCollisions(i, j); + collisions.clear(); + } + } + } +} diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index e52ea1da23..4d2e0ae889 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -13,6 +13,7 @@ #define hifi_PhysicsEntity_h #include +#include #include #include @@ -54,6 +55,11 @@ public: bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex); bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); + void disableCollisions(int shapeIndexA, int shapeIndexB); + bool collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const; + + void disableSelfCollisions(); + protected: glm::vec3 _translation; glm::quat _rotation; @@ -61,6 +67,7 @@ protected: bool _shapesAreDirty; bool _enableShapes; QVector _shapes; + QSet _disabledCollisions; private: // PhysicsSimulation is a friend so that it can set the protected _simulation backpointer diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 984d0b8fea..09e59b7b84 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -135,7 +135,7 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { // DONE (4) collisions move points (SpecialCapsuleShape would help solve this) // DONE (5) enforce constraints // DONE (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support -// (7) support for pairwise collision bypass +// DONE (7) support for pairwise collision bypass // (8) process collisions // (9) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { @@ -162,7 +162,8 @@ void PhysicsSimulation::computeCollisions() { // TODO: keep track of QSet collidedEntities; int numEntities = _entities.size(); for (int i = 0; i < numEntities; ++i) { - const QVector shapes = _entities.at(i)->getShapes(); + PhysicsEntity* entity = _entities.at(i); + const QVector shapes = entity->getShapes(); int numShapes = shapes.size(); // collide with self for (int j = 0; j < numShapes; ++j) { @@ -170,10 +171,9 @@ void PhysicsSimulation::computeCollisions() { if (!shape) { continue; } - // TODO: check for pairwise collision bypass here for (int k = j+1; k < numShapes; ++k) { const Shape* otherShape = shapes.at(k); - if (otherShape) { + if (otherShape && entity->collisionsAreEnabled(j, k)) { ShapeCollider::collideShapes(shape, otherShape, _collisionList); } } From 435b5094a03437ab9f942a1ab1753961bc63b8ad Mon Sep 17 00:00:00 2001 From: wangyix Date: Thu, 19 Jun 2014 17:58:38 -0700 Subject: [PATCH 052/105] moved time gap history to PositionalAudioRingBuffer, untested removed hard-coded jitter buffer length. --- assignment-client/src/audio/AudioMixer.cpp | 6 +- .../src/audio/AudioMixerClientData.cpp | 5 +- .../src/audio/AudioMixerClientData.h | 3 +- .../src/audio/AvatarAudioRingBuffer.cpp | 3 +- libraries/audio/src/AudioRingBuffer.cpp | 57 ------------- libraries/audio/src/AudioRingBuffer.h | 32 +------- .../audio/src/InjectedAudioRingBuffer.cpp | 3 +- .../audio/src/PositionalAudioRingBuffer.cpp | 79 ++++++++++++++++++- .../audio/src/PositionalAudioRingBuffer.h | 34 +++++++- 9 files changed, 118 insertions(+), 104 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 2dc51b44a0..42be1aea5a 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -54,9 +54,6 @@ #include "AudioMixer.h" -const short JITTER_BUFFER_MSECS = 12; -const short JITTER_BUFFER_SAMPLES = JITTER_BUFFER_MSECS * (SAMPLE_RATE / 1000.0); - const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f; const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer"; @@ -487,8 +484,7 @@ void AudioMixer::run() { foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { if (node->getLinkedData()) { - ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(JITTER_BUFFER_SAMPLES, - _sourceUnattenuatedZone, + ((AudioMixerClientData*) node->getLinkedData())->checkBuffersBeforeFrameSend(_sourceUnattenuatedZone, _listenerUnattenuatedZone); } } diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 2329946e49..3ee571fc46 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -98,10 +98,9 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { return 0; } -void AudioMixerClientData::checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, - AABox* checkSourceZone, AABox* listenerZone) { +void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, AABox* listenerZone) { for (int i = 0; i < _ringBuffers.size(); i++) { - if (_ringBuffers[i]->shouldBeAddedToMix(jitterBufferLengthSamples)) { + if (_ringBuffers[i]->shouldBeAddedToMix()) { // this is a ring buffer that is ready to go // set its flag so we know to push its buffer when all is said and done _ringBuffers[i]->setWillBeAddedToMix(true); diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index e52b09e134..3c4ddd3459 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -27,8 +27,7 @@ public: AvatarAudioRingBuffer* getAvatarAudioRingBuffer() const; int parseData(const QByteArray& packet); - void checkBuffersBeforeFrameSend(int jitterBufferLengthSamples, - AABox* checkSourceZone = NULL, AABox* listenerZone = NULL); + void checkBuffersBeforeFrameSend(AABox* checkSourceZone = NULL, AABox* listenerZone = NULL); void pushBuffersAfterFrameSend(); private: QList _ringBuffers; diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 47a0ecbea8..6658e446cf 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -19,7 +19,8 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : } int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { - _timeGapHistory.frameReceived(); + _interframeTimeGapHistory.frameReceived(); + updateDesiredJitterBufferNumSamples(); _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); return PositionalAudioRingBuffer::parseData(packet); diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index ea15e27ef5..2101fcb9cd 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -12,7 +12,6 @@ #include #include #include -#include "SharedUtil.h" #include @@ -20,62 +19,6 @@ #include "AudioRingBuffer.h" -InterframeTimeGapHistory::InterframeTimeGapHistory() - : _lastFrameReceivedTime(0), - _numSamplesInCurrentInterval(0), - _currentIntervalMaxGap(0), - _newestIntervalMaxGapAt(0), - _windowMaxGap(0), - _newWindowMaxGapAvailable(false) -{ - memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW*sizeof(quint64)); -} - -void InterframeTimeGapHistory::frameReceived() { - quint64 now = usecTimestampNow(); - - // make sure this isn't the first time frameReceived() is called, meaning there's actually a gap to calculate. - if (_lastFrameReceivedTime != 0) { - quint64 gap = now - _lastFrameReceivedTime; - - // update the current interval max - if (gap > _currentIntervalMaxGap) { - _currentIntervalMaxGap = gap; - } - _numSamplesInCurrentInterval++; - - // if the current interval of samples is now full, record it in our interval maxes - if (_numSamplesInCurrentInterval == TIME_GAP_NUM_SAMPLES_IN_INTERVAL) { - - // find location to insert this interval's max (increment index cyclically) - _newestIntervalMaxGapAt = _newestIntervalMaxGapAt == TIME_GAP_NUM_INTERVALS_IN_WINDOW - 1 ? 0 : _newestIntervalMaxGapAt + 1; - - // record the current interval's max gap as the newest - _intervalMaxGaps[_newestIntervalMaxGapAt] = _currentIntervalMaxGap; - - // update the window max gap, which is the max out of all the past intervals' max gaps - _windowMaxGap = 0; - for (int i = 0; i < TIME_GAP_NUM_INTERVALS_IN_WINDOW; i++) { - if (_intervalMaxGaps[i] > _windowMaxGap) { - _windowMaxGap = _intervalMaxGaps[i]; - } - } - _newWindowMaxGapAvailable = true; - - // reset the current interval - _numSamplesInCurrentInterval = 0; - _currentIntervalMaxGap = 0; - } - } - _lastFrameReceivedTime = now; -} - -quint64 InterframeTimeGapHistory::getPastWindowMaxGap() { - _newWindowMaxGapAvailable = false; - return _windowMaxGap; -} - - AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) : NodeData(), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 46bc333e4b..3d2864f373 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -21,31 +21,6 @@ #include "NodeData.h" -// this means that every 500 samples, the max for the past 10*500 samples will be calculated -const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; -const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; - -// class used to track time between incoming frames for the purpose of varying the jitter buffer length -class InterframeTimeGapHistory { -public: - InterframeTimeGapHistory(); - - void frameReceived(); - bool isNewWindowMaxGapAvailable() const { return _newWindowMaxGapAvailable; } - quint64 getPastWindowMaxGap(); - -private: - quint64 _lastFrameReceivedTime; - - int _numSamplesInCurrentInterval; - quint64 _currentIntervalMaxGap; - quint64 _intervalMaxGaps[TIME_GAP_NUM_INTERVALS_IN_WINDOW]; - int _newestIntervalMaxGapAt; - quint64 _windowMaxGap; - bool _newWindowMaxGapAvailable; -}; - - const int SAMPLE_RATE = 24000; const int NETWORK_BUFFER_LENGTH_BYTES_STEREO = 1024; @@ -99,15 +74,14 @@ public: bool hasStarted() const { return _hasStarted; } void addSilentFrame(int numSilentSamples); - - InterframeTimeGapHistory& getInterframeTimeGapHistory() { return _timeGapHistory; } + protected: // disallow copying of AudioRingBuffer objects AudioRingBuffer(const AudioRingBuffer&); AudioRingBuffer& operator= (const AudioRingBuffer&); int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const; - + int _sampleCapacity; int _numFrameSamples; int16_t* _nextOutput; @@ -116,8 +90,6 @@ protected: bool _isStarved; bool _hasStarted; bool _randomAccessMode; /// will this ringbuffer be used for random access? if so, do some special processing - - InterframeTimeGapHistory _timeGapHistory; }; #endif // hifi_AudioRingBuffer_h diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 103d0d8d2e..ffe5876bfd 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -31,7 +31,8 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) const uchar MAX_INJECTOR_VOLUME = 255; int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { - _timeGapHistory.frameReceived(); + _interframeTimeGapHistory.frameReceived(); + updateDesiredJitterBufferNumSamples(); // setup a data stream to read from this packet QDataStream packetStream(packet); diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 1cc4147175..b537e5c6d6 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -19,6 +19,63 @@ #include #include "PositionalAudioRingBuffer.h" +#include "SharedUtil.h" + +InterframeTimeGapHistory::InterframeTimeGapHistory() + : _lastFrameReceivedTime(0), + _numSamplesInCurrentInterval(0), + _currentIntervalMaxGap(0), + _newestIntervalMaxGapAt(0), + _windowMaxGap(0), + _newWindowMaxGapAvailable(false) +{ + memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW*sizeof(quint64)); +} + +void InterframeTimeGapHistory::frameReceived() { + quint64 now = usecTimestampNow(); + + // make sure this isn't the first time frameReceived() is called, meaning there's actually a gap to calculate. + if (_lastFrameReceivedTime != 0) { + quint64 gap = now - _lastFrameReceivedTime; + + // update the current interval max + if (gap > _currentIntervalMaxGap) { + _currentIntervalMaxGap = gap; + } + _numSamplesInCurrentInterval++; + + // if the current interval of samples is now full, record it in our interval maxes + if (_numSamplesInCurrentInterval == TIME_GAP_NUM_SAMPLES_IN_INTERVAL) { + + // find location to insert this interval's max (increment index cyclically) + _newestIntervalMaxGapAt = _newestIntervalMaxGapAt == TIME_GAP_NUM_INTERVALS_IN_WINDOW - 1 ? 0 : _newestIntervalMaxGapAt + 1; + + // record the current interval's max gap as the newest + _intervalMaxGaps[_newestIntervalMaxGapAt] = _currentIntervalMaxGap; + + // update the window max gap, which is the max out of all the past intervals' max gaps + _windowMaxGap = 0; + for (int i = 0; i < TIME_GAP_NUM_INTERVALS_IN_WINDOW; i++) { + if (_intervalMaxGaps[i] > _windowMaxGap) { + _windowMaxGap = _intervalMaxGaps[i]; + } + } + _newWindowMaxGapAvailable = true; + + // reset the current interval + _numSamplesInCurrentInterval = 0; + _currentIntervalMaxGap = 0; + } + } + _lastFrameReceivedTime = now; +} + +quint64 InterframeTimeGapHistory::getWindowMaxGap() { + _newWindowMaxGapAvailable = false; + return _windowMaxGap; +} + PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo) : AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL), @@ -29,7 +86,8 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: _shouldLoopbackForNode(false), _shouldOutputStarveDebug(true), _isStereo(isStereo), - _listenerUnattenuatedZone(NULL) + _listenerUnattenuatedZone(NULL), + _desiredJitterBufferNumSamples(getNumSamplesPerFrame()) { } @@ -106,14 +164,17 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { } } -bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { - if (!isNotStarvedOrHasMinimumSamples(NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL + numJitterBufferSamples)) { +bool PositionalAudioRingBuffer::shouldBeAddedToMix() { + + int samplesPerFrame = getNumSamplesPerFrame(); + + if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + _desiredJitterBufferNumSamples)) { if (_shouldOutputStarveDebug) { _shouldOutputStarveDebug = false; } return false; - } else if (samplesAvailable() < NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL) { + } else if (samplesAvailable() < samplesPerFrame) { _isStarved = true; // reset our _shouldOutputStarveDebug to true so the next is printed @@ -132,3 +193,13 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix(int numJitterBufferSamples) { return false; } + +void PositionalAudioRingBuffer::updateDesiredJitterBufferNumSamples() { + + const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; + + if (_interframeTimeGapHistory.hasNewWindowMaxGapAvailable()) { + int desiredJitterBufferNumFrames = (int)((float)_interframeTimeGapHistory.getWindowMaxGap() / USECS_PER_FRAME + 0.5f); + _desiredJitterBufferNumSamples = desiredJitterBufferNumFrames * getNumSamplesPerFrame(); + } +} \ No newline at end of file diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 00362c245a..de731c6136 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -18,6 +18,31 @@ #include "AudioRingBuffer.h" +// this means that every 500 samples, the max for the past 10*500 samples will be calculated +const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; +const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; + +// class used to track time between incoming frames for the purpose of varying the jitter buffer length +class InterframeTimeGapHistory { +public: + InterframeTimeGapHistory(); + + void frameReceived(); + bool hasNewWindowMaxGapAvailable() const { return _newWindowMaxGapAvailable; } + quint64 getWindowMaxGap(); + +private: + quint64 _lastFrameReceivedTime; + + int _numSamplesInCurrentInterval; + quint64 _currentIntervalMaxGap; + quint64 _intervalMaxGaps[TIME_GAP_NUM_INTERVALS_IN_WINDOW]; + int _newestIntervalMaxGapAt; + quint64 _windowMaxGap; + bool _newWindowMaxGapAvailable; +}; + + class PositionalAudioRingBuffer : public AudioRingBuffer { public: enum Type { @@ -34,7 +59,7 @@ public: void updateNextOutputTrailingLoudness(); float getNextOutputTrailingLoudness() const { return _nextOutputTrailingLoudness; } - bool shouldBeAddedToMix(int numJitterBufferSamples); + bool shouldBeAddedToMix(); bool willBeAddedToMix() const { return _willBeAddedToMix; } void setWillBeAddedToMix(bool willBeAddedToMix) { _willBeAddedToMix = willBeAddedToMix; } @@ -50,10 +75,14 @@ public: AABox* getListenerUnattenuatedZone() const { return _listenerUnattenuatedZone; } void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; } + int getNumSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; } + protected: // disallow copying of PositionalAudioRingBuffer objects PositionalAudioRingBuffer(const PositionalAudioRingBuffer&); PositionalAudioRingBuffer& operator= (const PositionalAudioRingBuffer&); + + void updateDesiredJitterBufferNumSamples(); PositionalAudioRingBuffer::Type _type; glm::vec3 _position; @@ -65,6 +94,9 @@ protected: float _nextOutputTrailingLoudness; AABox* _listenerUnattenuatedZone; + + InterframeTimeGapHistory _interframeTimeGapHistory; + int _desiredJitterBufferNumSamples; }; #endif // hifi_PositionalAudioRingBuffer_h From 266dc6be3ed229b7298d3b598dc850b91fd5c622 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 19 Jun 2014 18:09:17 -0700 Subject: [PATCH 053/105] fixed tooltip not disapearing when deleting model --- examples/editModels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editModels.js b/examples/editModels.js index 55e4d56a21..f2b76a28c3 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -1119,8 +1119,8 @@ function handeMenuEvent(menuItem){ Models.editModel(editModelID, properties); } } - tooltip.show(false); } + tooltip.show(false); } Menu.menuItemEvent.connect(handeMenuEvent); From 4561a6e3269693b5654f559fca7ada62b4698e10 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 19 Jun 2014 18:10:06 -0700 Subject: [PATCH 054/105] Fixed voxel being deleted even though voxel tool is not selected --- examples/editVoxels.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/editVoxels.js b/examples/editVoxels.js index cff0d65743..412612fdad 100644 --- a/examples/editVoxels.js +++ b/examples/editVoxels.js @@ -1196,7 +1196,7 @@ function menuItemEvent(menuItem) { print("deleting..."); if (isImporting) { cancelImport(); - } else { + } else if (voxelToolSelected) { Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s); } } From 24f535e02d263502680f0b04ca5628de77023251 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 19 Jun 2014 18:15:17 -0700 Subject: [PATCH 055/105] Working on metavoxel streaming tests. --- libraries/metavoxels/src/MetavoxelData.cpp | 46 +++++ libraries/metavoxels/src/MetavoxelData.h | 11 ++ tests/metavoxels/src/MetavoxelTests.cpp | 193 +++++++++++++++------ 3 files changed, 196 insertions(+), 54 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index cf645e6f5e..d7528d1959 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -687,6 +687,23 @@ MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { return root = new MetavoxelNode(attribute); } +bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod) const { + if (_size != other._size) { + return false; + } + if (_roots.size() != other._roots.size()) { + return false; + } + glm::vec3 minimum = getMinimum(); + for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { + MetavoxelNode* otherNode = other._roots.value(it.key()); + if (!(otherNode && it.value()->deepEquals(it.key(), *otherNode, minimum, _size, lod))) { + return false; + } + } + return true; +} + bool MetavoxelData::operator==(const MetavoxelData& other) const { return _size == other._size && _roots == other._roots; } @@ -1083,6 +1100,31 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { } } +bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const { + if (!attribute->equal(_attributeValue, other._attributeValue)) { + return false; + } + if (!lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { + return true; + } + bool leaf = isLeaf(), otherLeaf = other.isLeaf(); + if (leaf && otherLeaf) { + return true; + } + if (leaf || otherLeaf) { + return false; + } + float nextSize = size * 0.5f; + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i); + if (!_children[i]->deepEquals(attribute, *(other._children[i]), nextMinimum, nextSize, lod)) { + return false; + } + } + return true; +} + int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth) { return first | (second << 3) | (third << 6) | (fourth << 9) | @@ -1111,6 +1153,10 @@ int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) { indexDistances.at(6).index, indexDistances.at(7).index); } +int MetavoxelVisitor::encodeRandomOrder() { + return DEFAULT_ORDER; +} + const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); const int MetavoxelVisitor::STOP_RECURSION = 0; const int MetavoxelVisitor::SHORT_CIRCUIT = -1; diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 6b262d6892..0df97a7a4c 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -126,6 +126,10 @@ public: MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } MetavoxelNode* createRoot(const AttributePointer& attribute); + /// Performs a deep comparison between this data and the specified other (as opposed to the == operator, which does a + /// shallow comparison). + bool deepEquals(const MetavoxelData& other, const MetavoxelLOD& lod = MetavoxelLOD()) const; + bool operator==(const MetavoxelData& other) const; bool operator!=(const MetavoxelData& other) const; @@ -214,6 +218,10 @@ public: void clearChildren(const AttributePointer& attribute); + /// Performs a deep comparison between this and the specified other node. + bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const; + private: Q_DISABLE_COPY(MetavoxelNode) @@ -250,6 +258,9 @@ public: /// Encodes a visitation order sequence that visits each child as sorted along the specified direction. static int encodeOrder(const glm::vec3& direction); + /// Returns a random visitation order sequence. + static int encodeRandomOrder(); + /// The default visitation order. static const int DEFAULT_ORDER; diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index daee1165f6..d261d9d926 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -32,6 +32,8 @@ static int datagramsSent = 0; static int datagramsReceived = 0; static int bytesSent = 0; static int bytesReceived = 0; +static int maxDatagramsPerPacket = 0; +static int maxBytesPerPacket = 0; static int highPriorityMessagesSent = 0; static int highPriorityMessagesReceived = 0; static int unreliableMessagesSent = 0; @@ -353,6 +355,7 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << streamedBytesSent << "streamed bytes, received" << streamedBytesReceived; qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << datagramsReceived << "with" << bytesReceived << "bytes"; + qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; qDebug() << "Created" << sharedObjectsCreated << "shared objects, destroyed" << sharedObjectsDestroyed; qDebug() << "Performed" << objectMutationsPerformed << "object mutations"; qDebug() << "Created" << scriptObjectsCreated << "script objects, mutated" << scriptMutationsPerformed; @@ -373,7 +376,7 @@ bool MetavoxelTests::run() { qDebug(); // clear the stats - datagramsSent = bytesSent = datagramsReceived = bytesReceived = 0; + datagramsSent = bytesSent = datagramsReceived = bytesReceived = maxDatagramsPerPacket = maxBytesPerPacket = 0; // create client and server endpoints Endpoint client(datagramHeader, Endpoint::METAVOXEL_CLIENT_MODE); @@ -391,6 +394,7 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << datagramsReceived << "with" << bytesReceived << "bytes"; + qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; } qDebug() << "All tests passed!"; @@ -407,6 +411,34 @@ static SharedObjectPointer createRandomSharedObject() { } } +class RandomVisitor : public MetavoxelVisitor { +public: + + int leafCount; + + RandomVisitor(); + virtual int visit(MetavoxelInfo& info); +}; + +RandomVisitor::RandomVisitor() : + MetavoxelVisitor(QVector(), + QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + leafCount(0) { +} + +const float MAXIMUM_LEAF_SIZE = 0.5f; +const float MINIMUM_LEAF_SIZE = 0.25f; + +int RandomVisitor::visit(MetavoxelInfo& info) { + if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { + return DEFAULT_ORDER; + } + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randIntInRange(0, 255), + randIntInRange(0, 255), randIntInRange(0, 255)))); + leafCount++; + return STOP_RECURSION; +} + Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : _mode(mode), _sequencer(new DatagramSequencer(datagramHeader, this)), @@ -434,6 +466,12 @@ Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : return; } if (mode == METAVOXEL_SERVER_MODE) { + _data.expand(); + _data.expand(); + + RandomVisitor visitor; + _data.guide(visitor); + qDebug() << "Created" << visitor.leafCount << "base leaves"; return; } // create the object that represents out delta-encoded state @@ -552,6 +590,36 @@ static bool messagesEqual(const QVariant& firstMessage, const QVariant& secondMe } } +class MutateVisitor : public MetavoxelVisitor { +public: + + MutateVisitor(); + virtual int visit(MetavoxelInfo& info); + +private: + + bool _finished; +}; + +MutateVisitor::MutateVisitor() : + MetavoxelVisitor(QVector(), + QVector() << AttributeRegistry::getInstance()->getColorAttribute()), + _finished(false) { +} + +int MutateVisitor::visit(MetavoxelInfo& info) { + if (_finished) { + return STOP_RECURSION; + } + if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { + return encodeRandomOrder(); + } + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randIntInRange(0, 255), + randIntInRange(0, 255), randIntInRange(0, 255)))); + _finished = true; + return STOP_RECURSION; +} + bool Endpoint::simulate(int iterationNumber) { // update/send our delayed datagrams for (QList >::iterator it = _delayedDatagrams.begin(); it != _delayedDatagrams.end(); ) { @@ -565,6 +633,8 @@ bool Endpoint::simulate(int iterationNumber) { } } + int oldDatagramsSent = datagramsSent; + int oldBytesSent = bytesSent; if (_mode == METAVOXEL_CLIENT_MODE) { Bitstream& out = _sequencer->startPacket(); @@ -575,69 +645,74 @@ bool Endpoint::simulate(int iterationNumber) { // record the send SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), MetavoxelData(), _lod }; _sendRecords.append(record); - return false; - } - if (_mode == METAVOXEL_SERVER_MODE) { - // wait until we have a valid lod + + } else if (_mode == METAVOXEL_SERVER_MODE) { + // make a random change + MutateVisitor visitor; + _data.guide(visitor); + + // wait until we have a valid lod before sending if (!_lod.isValid()) { return false; } Bitstream& out = _sequencer->startPacket(); out << QVariant::fromValue(MetavoxelDeltaMessage()); _data.writeDelta(_sendRecords.first().data, _sendRecords.first().lod, out, _lod); - _sequencer->endPacket(); // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber(), SharedObjectPointer(), _data, _lod }; + SendRecord record = { _sequencer->getOutgoingPacketNumber() + 1, SharedObjectPointer(), _data, _lod }; _sendRecords.append(record); - return false; - } - - // enqueue some number of high priority messages - const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; - const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f; - _highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES); - while (_highPriorityMessagesToSend >= 1.0f) { - QVariant message = createRandomMessage(); - _highPriorityMessagesSent.append(message); - _sequencer->sendHighPriorityMessage(message); - highPriorityMessagesSent++; - _highPriorityMessagesToSend -= 1.0f; - } - - // and some number of reliable messages - const float MIN_RELIABLE_MESSAGES = 0.0f; - const float MAX_RELIABLE_MESSAGES = 4.0f; - _reliableMessagesToSend += randFloatInRange(MIN_RELIABLE_MESSAGES, MAX_RELIABLE_MESSAGES); - while (_reliableMessagesToSend >= 1.0f) { - QVariant message = createRandomMessage(); - _reliableMessagesSent.append(message); - _sequencer->getReliableOutputChannel()->sendMessage(message); - reliableMessagesSent++; - _reliableMessagesToSend -= 1.0f; - } - - // tweak the local state - _localState = mutate(_localState); - - // send a packet - try { - Bitstream& out = _sequencer->startPacket(); - SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState }; - _unreliableMessagesSent.append(message); - unreliableMessagesSent++; - out << message; + _sequencer->endPacket(); + + } else { + // enqueue some number of high priority messages + const float MIN_HIGH_PRIORITY_MESSAGES = 0.0f; + const float MAX_HIGH_PRIORITY_MESSAGES = 2.0f; + _highPriorityMessagesToSend += randFloatInRange(MIN_HIGH_PRIORITY_MESSAGES, MAX_HIGH_PRIORITY_MESSAGES); + while (_highPriorityMessagesToSend >= 1.0f) { + QVariant message = createRandomMessage(); + _highPriorityMessagesSent.append(message); + _sequencer->sendHighPriorityMessage(message); + highPriorityMessagesSent++; + _highPriorityMessagesToSend -= 1.0f; + } + + // and some number of reliable messages + const float MIN_RELIABLE_MESSAGES = 0.0f; + const float MAX_RELIABLE_MESSAGES = 4.0f; + _reliableMessagesToSend += randFloatInRange(MIN_RELIABLE_MESSAGES, MAX_RELIABLE_MESSAGES); + while (_reliableMessagesToSend >= 1.0f) { + QVariant message = createRandomMessage(); + _reliableMessagesSent.append(message); + _sequencer->getReliableOutputChannel()->sendMessage(message); + reliableMessagesSent++; + _reliableMessagesToSend -= 1.0f; + } + + // tweak the local state + _localState = mutate(_localState); + + // send a packet + try { + Bitstream& out = _sequencer->startPacket(); + SequencedTestMessage message = { iterationNumber, createRandomMessage(), _localState }; + _unreliableMessagesSent.append(message); + unreliableMessagesSent++; + out << message; + _sequencer->endPacket(); + + } catch (const QString& message) { + qDebug() << message; + return true; + } - } catch (const QString& message) { - qDebug() << message; - return true; + // record the send + SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState }; + _sendRecords.append(record); } - - // record the send - SendRecord record = { _sequencer->getOutgoingPacketNumber(), _localState }; - _sendRecords.append(record); - + maxDatagramsPerPacket = qMax(maxDatagramsPerPacket, datagramsSent - oldDatagramsSent); + maxBytesPerPacket = qMax(maxBytesPerPacket, bytesSent - oldBytesSent); return false; } @@ -692,9 +767,19 @@ void Endpoint::readMessage(Bitstream& in) { in >> message; handleMessage(message, in); + // deep-compare data to sent version + int packetNumber = _sequencer->getIncomingPacketNumber(); + foreach (const SendRecord& sendRecord, _other->_sendRecords) { + if (sendRecord.packetNumber == packetNumber) { + if (!sendRecord.data.deepEquals(_data, _sendRecords.first().lod)) { + throw QString("Sent/received metavoxel data mismatch."); + } + break; + } + } + // record the receipt - ReceiveRecord record = { _sequencer->getIncomingPacketNumber(), SharedObjectPointer(), - _data, _sendRecords.first().lod }; + ReceiveRecord record = { packetNumber, SharedObjectPointer(), _data, _sendRecords.first().lod }; _receiveRecords.append(record); return; } From 0c87f0aabc677e27188c887d229390fdeac65ab5 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 19 Jun 2014 19:45:43 -0700 Subject: [PATCH 056/105] Update chat window behavior Lower opacity when inactive Move focus to main window when pressing escape, instead of hiding --- interface/src/Menu.cpp | 1 + interface/src/ui/ChatWindow.cpp | 14 +++++++++++++- interface/src/ui/ChatWindow.h | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5c8c2e97aa..334a2e441b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1293,6 +1293,7 @@ void Menu::showChat() { if (_chatWindow->isHidden()) { _chatWindow->show(); } + _chatWindow->activateWindow(); } else { Application::getInstance()->getTrayIcon()->showMessage("Interface", "You need to login to be able to chat with others on this domain."); } diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index fde77334f4..b101b50b2f 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -30,6 +30,9 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; +const float OPACITY_ACTIVE = 1.0; +const float OPACITY_INACTIVE = 0.6; + const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?)|(?:hifi))://\\S+)"); const QRegularExpression regexHifiLinks("([#@]\\S+)"); const QString mentionSoundsPath("/mention-sounds/"); @@ -108,7 +111,7 @@ ChatWindow::~ChatWindow() { void ChatWindow::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Escape) { - hide(); + Application::getInstance()->getWindow()->activateWindow(); } else { FramelessDialog::keyPressEvent(event); } @@ -383,3 +386,12 @@ void ChatWindow::scrollToBottom() { QScrollBar* verticalScrollBar = ui->messagesScrollArea->verticalScrollBar(); verticalScrollBar->setValue(verticalScrollBar->maximum()); } + +bool ChatWindow::event(QEvent* event) { + if (event->type() == QEvent::WindowActivate) { + setWindowOpacity(OPACITY_ACTIVE); + } else if (event->type() == QEvent::WindowDeactivate) { + setWindowOpacity(OPACITY_INACTIVE); + } + return FramelessDialog::event(event); +} diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h index 1e0f533e9e..652dcb5b08 100644 --- a/interface/src/ui/ChatWindow.h +++ b/interface/src/ui/ChatWindow.h @@ -50,6 +50,7 @@ protected: virtual void keyPressEvent(QKeyEvent *event); virtual void showEvent(QShowEvent* event); + virtual bool event(QEvent* event); private: #ifdef HAVE_QXMPP From c7ad3da47d4432e7bb90ae82a49281cb503cde1e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Jun 2014 10:55:57 -0700 Subject: [PATCH 057/105] stubbery for processing collisions PhysicsSimulation tells CollisionInfos to apply() themselves CollisionInfo knows how to apply() itself to affected shapes Shape gets _mass and some stubbed methods for accumulating movement VerletPoint gets movement accumulator --- libraries/shared/src/CollisionInfo.cpp | 30 ++++++++++++++++++++++ libraries/shared/src/CollisionInfo.h | 30 ++++------------------ libraries/shared/src/PhysicsEntity.cpp | 1 + libraries/shared/src/PhysicsSimulation.cpp | 20 ++++++++++++++- libraries/shared/src/Ragdoll.cpp | 14 ++++++++++ libraries/shared/src/Ragdoll.h | 10 +++++++- libraries/shared/src/Shape.h | 21 ++++++++++++++- 7 files changed, 98 insertions(+), 28 deletions(-) diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index 39b49ed90e..3dd3610edb 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -11,6 +11,19 @@ #include "CollisionInfo.h" +#include "Shape.h" + +CollisionInfo::CollisionInfo() : + _data(NULL), + _intData(0), + _shapeA(NULL), + _shapeB(NULL), + _damping(0.f), + _elasticity(1.f), + _contactPoint(0.f), + _penetration(0.f), + _addedVelocity(0.f) { +} CollisionList::CollisionList(int maxSize) : _maxSize(maxSize), @@ -18,6 +31,23 @@ CollisionList::CollisionList(int maxSize) : _collisions.resize(_maxSize); } +void CollisionInfo::apply() { + assert(_shapeA); + // NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients + Shape* shapeA = const_cast(_shapeA); + float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint); + float massB = Shape::MAX_MASS; + float totalMass = massA + massB; + if (_shapeB) { + Shape* shapeB = const_cast(_shapeB); + massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration); + totalMass = massA + massB; + shapeB->accumulateDelta(massA / totalMass, -_penetration); + } + // NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass() + shapeA->accumulateDelta(massB / totalMass, _penetration); +} + CollisionInfo* CollisionList::getNewCollision() { // return pointer to existing CollisionInfo, or NULL of list is full return (_size < _maxSize) ? &(_collisions[_size++]) : NULL; diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 7f14f6b5dc..1ab06e2ef5 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -32,38 +32,18 @@ const quint32 VALID_COLLISION_GROUPS = 0x0f; class CollisionInfo { public: - CollisionInfo() - : _data(NULL), - _intData(0), - _shapeA(NULL), - _shapeB(NULL), - _damping(0.f), - _elasticity(1.f), - _contactPoint(0.f), - _penetration(0.f), - _addedVelocity(0.f) { - } - - CollisionInfo(qint32 type) - : _data(NULL), - _intData(0), - _shapeA(NULL), - _shapeB(NULL), - _damping(0.f), - _elasticity(1.f), - _contactPoint(0.f), - _penetration(0.f), - _addedVelocity(0.f) { - } - + CollisionInfo(); ~CollisionInfo() {} - // the value of the *Data fields depend on the type + // TODO: Andrew to get rid of these data members void* _data; int _intData; float _floatData; glm::vec3 _vecData; + /// accumulates position changes for the shapes in this collision to resolve penetration + void apply(); + Shape* getShapeA() const { return const_cast(_shapeA); } Shape* getShapeB() const { return const_cast(_shapeB); } diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 2955192fbf..71f972652b 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -149,6 +149,7 @@ bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sp } bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { + // TODO: Andrew to reimplement this or make it unecessary bool collided = false; PlaneShape planeShape(plane); for (int i = 0; i < _shapes.size(); i++) { diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 09e59b7b84..2891ea8853 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -189,7 +189,25 @@ void PhysicsSimulation::computeCollisions() { } void PhysicsSimulation::processCollisions() { - // TODO: Andrew to implement this + // walk all collisions, accumulate movement on shapes, and build a list of affected shapes + QSet shapes; + int numCollisions = _collisionList.size(); + for (int i = 0; i < numCollisions; ++i) { + CollisionInfo* collision = _collisionList.getCollision(i); + collision->apply(); + // there is always a shapeA + shapes.insert(collision->getShapeA()); + // but need to check for valid shapeB + if (collision->_shapeB) { + shapes.insert(collision->getShapeB()); + } + } + // walk all affected shapes and apply accumulated movement + QSet::const_iterator shapeItr = shapes.constBegin(); + while (shapeItr != shapes.constEnd()) { + (*shapeItr)->applyAccumulatedDelta(); + ++shapeItr; + } } void PhysicsSimulation::enforceConstraints(float minError, int maxIterations, quint64 maxUsec) { diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 76d8ce4d98..20828018e5 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -16,6 +16,20 @@ #include "SharedUtil.h" #include "SphereShape.h" +// ---------------------------------------------------------------------------- +// VerletPoint +// ---------------------------------------------------------------------------- +void VerletPoint::accumulatePush(const glm::vec3& delta) { + _accumulatedPush += delta; + ++_numPushes; +} + +void VerletPoint::applyAccumulatedPush() { + _lastPosition = _position; + _position += _accumulatedPush / (float)_numPushes; + _numPushes = 0; +} + // ---------------------------------------------------------------------------- // FixedConstraint // ---------------------------------------------------------------------------- diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 788a46d4d1..e6622e8c85 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -21,10 +21,18 @@ class Shape; class VerletPoint { public: - VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(0.0f) {} + VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(0.0f), _accumulatedPush(0.0f), _numPushes(0) {} + + void accumulatePush(const glm::vec3& delta); + void applyAccumulatedPush(); + glm::vec3 _position; glm::vec3 _lastPosition; float _mass; + +protected: + glm::vec3 _accumulatedPush; + int _numPushes; }; class Constraint { diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 4926f598ba..40429b3d8d 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -19,6 +19,8 @@ class PhysicsEntity; class Shape { public: + static const float MAX_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) + enum Type{ UNKNOWN_SHAPE = 0, SPHERE_SHAPE, @@ -27,7 +29,7 @@ public: LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() { } + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation(), _mass(MAX_MASS) { } virtual ~Shape() {} int getType() const { return _type; } @@ -43,8 +45,24 @@ public: virtual void setTranslation(const glm::vec3& translation) { _translation = translation; } virtual const glm::vec3& getTranslation() const { return _translation; } + virtual void setMass(float mass) { _mass = mass; } + virtual float getMass() const { return _mass; } + virtual bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const = 0; + /// \param penetration of collision + /// \param contactPoint of collision + /// \return the effective mass for the collision + /// For most shapes has side effects: computes and caches the partial Lagrangian coefficients which will be + /// used in the next accumulateDelta() call. + virtual float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { return _mass; } + + /// \param relativeMassFactor the final ingredient for partial Lagrangian coefficients from computeEffectiveMass() + /// \param penetration the delta movement + virtual void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) {} + + virtual void applyAccumulatedDelta() {} + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() {} @@ -62,6 +80,7 @@ protected: float _boundingRadius; glm::vec3 _translation; glm::quat _rotation; + float _mass; }; #endif // hifi_Shape_h From 633746ab168fa5fc7cbb30895570f20066984696 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Fri, 20 Jun 2014 13:30:04 -0700 Subject: [PATCH 058/105] Different fullscreen shortcuts for Mac and Windows Revert to the original Mac OSX key combination and provide alternative for Windows. --- interface/src/Menu.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9df3e25bf8..22a832278f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -249,12 +249,21 @@ Menu::Menu() : QMenu* viewMenu = addMenu("View"); +#ifdef Q_OS_MAC + addCheckableActionToQMenuAndActionHash(viewMenu, + MenuOption::Fullscreen, + Qt::CTRL | Qt::META | Qt::Key_F, + false, + appInstance, + SLOT(setFullscreen(bool))); +#else addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, Qt::CTRL | Qt::Key_F, false, appInstance, SLOT(setFullscreen(bool))); +#endif addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true, appInstance,SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); From ce778f47b1c6fc29999a5f9a865ba1c1514f866f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Jun 2014 14:08:39 -0700 Subject: [PATCH 059/105] More work on stream testing, fixed bug with differently-ordered edits. --- libraries/metavoxels/src/MetavoxelData.cpp | 21 +++++++++++++++++---- tests/metavoxels/src/MetavoxelTests.cpp | 8 ++++---- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index d7528d1959..6d1031d3cf 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1153,8 +1153,23 @@ int MetavoxelVisitor::encodeOrder(const glm::vec3& direction) { indexDistances.at(6).index, indexDistances.at(7).index); } +const int ORDER_ELEMENT_BITS = 3; +const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; + int MetavoxelVisitor::encodeRandomOrder() { - return DEFAULT_ORDER; + // see http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_.22inside-out.22_algorithm + int order; + int randomValues = rand(); + for (int i = 0, iShift = 0; i < MetavoxelNode::CHILD_COUNT; i++, iShift += ORDER_ELEMENT_BITS) { + int j = (randomValues >> iShift) % (i + 1); + int jShift = j * ORDER_ELEMENT_BITS; + if (j != i) { + int jValue = (order >> jShift) & ORDER_ELEMENT_MASK; + order = (order & ~(ORDER_ELEMENT_MASK << iShift)) | (jValue << iShift); + } + order = (order & ~(ORDER_ELEMENT_MASK << jShift)) | (i << jShift); + } + return order; } const int MetavoxelVisitor::DEFAULT_ORDER = encodeOrder(0, 1, 2, 3, 4, 5, 6, 7); @@ -1350,8 +1365,6 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { QVector(visitation.outputNodes.size()) } }; for (int i = 0; i < MetavoxelNode::CHILD_COUNT; i++) { // the encoded order tells us the child indices for each iteration - const int ORDER_ELEMENT_BITS = 3; - const int ORDER_ELEMENT_MASK = (1 << ORDER_ELEMENT_BITS) - 1; int index = encodedOrder & ORDER_ELEMENT_MASK; encodedOrder >>= ORDER_ELEMENT_BITS; for (int j = 0; j < visitation.inputNodes.size(); j++) { @@ -1392,7 +1405,7 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) { } } MetavoxelNode* node = visitation.outputNodes.at(j); - MetavoxelNode* child = node->getChild(i); + MetavoxelNode* child = node->getChild(index); if (child) { child->decrementReferenceCount(value.getAttribute()); } else { diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index d261d9d926..6aef40eab1 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -598,17 +598,17 @@ public: private: - bool _finished; + int _mutationsRemaining; }; MutateVisitor::MutateVisitor() : MetavoxelVisitor(QVector(), QVector() << AttributeRegistry::getInstance()->getColorAttribute()), - _finished(false) { + _mutationsRemaining(randIntInRange(2, 4)) { } int MutateVisitor::visit(MetavoxelInfo& info) { - if (_finished) { + if (_mutationsRemaining <= 0) { return STOP_RECURSION; } if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { @@ -616,7 +616,7 @@ int MutateVisitor::visit(MetavoxelInfo& info) { } info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randIntInRange(0, 255), randIntInRange(0, 255), randIntInRange(0, 255)))); - _finished = true; + _mutationsRemaining--; return STOP_RECURSION; } From a5457eb86b7e6d766b46d4106dfa098ba3a49a4d Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 20 Jun 2014 14:43:18 -0700 Subject: [PATCH 060/105] fixed bug in Audio.cpp where numSilentSamples was written to wrong place in packet added more debug stuff, other minor changes and fixes --- .../src/audio/AudioMixerClientData.cpp | 5 +- .../src/audio/AvatarAudioRingBuffer.cpp | 2 +- interface/src/Audio.cpp | 12 ++-- libraries/audio/src/AudioRingBuffer.cpp | 1 + .../audio/src/InjectedAudioRingBuffer.cpp | 2 +- .../audio/src/PositionalAudioRingBuffer.cpp | 63 +++++++++++++------ .../audio/src/PositionalAudioRingBuffer.h | 10 ++- 7 files changed, 63 insertions(+), 32 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 3ee571fc46..0c41cc70f9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -119,15 +119,14 @@ void AudioMixerClientData::checkBuffersBeforeFrameSend(AABox* checkSourceZone, A } void AudioMixerClientData::pushBuffersAfterFrameSend() { + QList::iterator i = _ringBuffers.begin(); while (i != _ringBuffers.end()) { // this was a used buffer, push the output pointer forwards PositionalAudioRingBuffer* audioBuffer = *i; if (audioBuffer->willBeAddedToMix()) { - audioBuffer->shiftReadPosition(audioBuffer->isStereo() - ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL); - + audioBuffer->shiftReadPosition(audioBuffer->getSamplesPerFrame()); audioBuffer->setWillBeAddedToMix(false); } else if (audioBuffer->getType() == PositionalAudioRingBuffer::Injector && audioBuffer->hasStarted() && audioBuffer->isStarved()) { diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 6658e446cf..0df50f99db 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -20,7 +20,7 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { _interframeTimeGapHistory.frameReceived(); - updateDesiredJitterBufferNumSamples(); + updateDesiredJitterBufferFrames(); _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); return PositionalAudioRingBuffer::parseData(packet); diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 271bcd5279..47e55ddc90 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -461,8 +461,8 @@ void Audio::handleAudioInput() { int16_t* inputAudioSamples = new int16_t[inputSamplesRequired]; _inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired); - int numNetworkBytes = _isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; - int numNetworkSamples = _isStereoInput ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + const int numNetworkBytes = _isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; + const int numNetworkSamples = _isStereoInput ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; // zero out the monoAudioSamples array and the locally injected audio memset(networkAudioSamples, 0, numNetworkBytes); @@ -622,6 +622,7 @@ void Audio::handleAudioInput() { SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer && audioMixer->getActiveSocket()) { + MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition(); glm::quat headOrientation = interfaceAvatar->getHead()->getFinalOrientationInWorldFrame(); @@ -634,12 +635,11 @@ void Audio::handleAudioInput() { packetType = PacketTypeSilentAudioFrame; // we need to indicate how many silent samples this is to the audio mixer - audioDataPacket[0] = _isStereoInput - ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO - : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; + networkAudioSamples[0] = numNetworkSamples; numAudioBytes = sizeof(int16_t); } else { - numAudioBytes = _isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; + numAudioBytes = numNetworkBytes; + //_isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; if (Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio)) { packetType = PacketTypeMicrophoneAudioWithEcho; diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 2101fcb9cd..7b1e68ffbd 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -158,6 +158,7 @@ const int16_t& AudioRingBuffer::operator[] (const int index) const { void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); + printf("\n mixed. %d samples remaining\n", samplesAvailable()); } unsigned int AudioRingBuffer::samplesAvailable() const { diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index ffe5876bfd..80bcda5acb 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -32,7 +32,7 @@ const uchar MAX_INJECTOR_VOLUME = 255; int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { _interframeTimeGapHistory.frameReceived(); - updateDesiredJitterBufferNumSamples(); + updateDesiredJitterBufferFrames(); // setup a data stream to read from this packet QDataStream packetStream(packet); diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index b537e5c6d6..813323ecb1 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -33,6 +33,10 @@ InterframeTimeGapHistory::InterframeTimeGapHistory() } void InterframeTimeGapHistory::frameReceived() { + + static QQueue gaps; + static quint64 gapsSum = 0; + quint64 now = usecTimestampNow(); // make sure this isn't the first time frameReceived() is called, meaning there's actually a gap to calculate. @@ -87,9 +91,9 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: _shouldOutputStarveDebug(true), _isStereo(isStereo), _listenerUnattenuatedZone(NULL), - _desiredJitterBufferNumSamples(getNumSamplesPerFrame()) + _desiredJitterBufferFrames(1), + _currentJitterBufferFrames(0) { - } int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { @@ -114,10 +118,15 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { if (numSilentSamples > 0) { addSilentFrame(numSilentSamples); } + printf("\nparsed silent packet of %d samples\n", numSilentSamples); } else { // there is audio data to read - readBytes += writeData(packet.data() + readBytes, packet.size() - readBytes); + int dataBytes = writeData(packet.data() + readBytes, packet.size() - readBytes); + readBytes += dataBytes; + + printf("\nparsed packet of %d data bytes\n", dataBytes); } + printf("%d samples available\n", samplesAvailable()); return readBytes; } @@ -166,40 +175,58 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { bool PositionalAudioRingBuffer::shouldBeAddedToMix() { - int samplesPerFrame = getNumSamplesPerFrame(); + int samplesPerFrame = getSamplesPerFrame(); + int currentJitterBufferSamples = 3 * samplesPerFrame; //_currentJitterBufferFrames * samplesPerFrame; + +//printf("\nsamples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); + if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + currentJitterBufferSamples)) { - if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + _desiredJitterBufferNumSamples)) { +//printf("\nMIXING DELAYED! waiting for jitter buffer to fill after being starved\n"); +//printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); + // if the buffer was starved and hasn't filled back up all the way, don't mix yet if (_shouldOutputStarveDebug) { _shouldOutputStarveDebug = false; } - - return false; + + return false; + } else if (samplesAvailable() < samplesPerFrame) { + +//printf("\nMIXING DELAYED! jitter buffer is starved!!!\n"); +//printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); + // if the buffer doesn't have a full frame of samples for mixing, it is starved _isStarved = true; // reset our _shouldOutputStarveDebug to true so the next is printed _shouldOutputStarveDebug = true; + + // if buffer was starved, we've effectively increased the jitter buffer by one frame + // by "holding back" this ring buffer's contents until the next client frame is prepared. + _currentJitterBufferFrames++; +//printf("jbuffer size increased: new size: %d\n", _currentJitterBufferFrames); return false; - } else { - // good buffer, add this to the mix - _isStarved = false; - // since we've read data from ring buffer at least once - we've started - _hasStarted = true; - - return true; } +//printf("WILL MIX\n"); - return false; + // good buffer, add this to the mix + _isStarved = false; + + // since we've read data from ring buffer at least once - we've started + _hasStarted = true; + + return true; } -void PositionalAudioRingBuffer::updateDesiredJitterBufferNumSamples() { +void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; if (_interframeTimeGapHistory.hasNewWindowMaxGapAvailable()) { - int desiredJitterBufferNumFrames = (int)((float)_interframeTimeGapHistory.getWindowMaxGap() / USECS_PER_FRAME + 0.5f); - _desiredJitterBufferNumSamples = desiredJitterBufferNumFrames * getNumSamplesPerFrame(); + _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapHistory.getWindowMaxGap() / USECS_PER_FRAME); + if (_desiredJitterBufferFrames < 1) { + _desiredJitterBufferFrames = 1; + } } } \ No newline at end of file diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index de731c6136..87a53038a3 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -29,6 +29,7 @@ public: void frameReceived(); bool hasNewWindowMaxGapAvailable() const { return _newWindowMaxGapAvailable; } + quint64 peekWindowMaxGap() const { return _windowMaxGap; } quint64 getWindowMaxGap(); private: @@ -75,14 +76,14 @@ public: AABox* getListenerUnattenuatedZone() const { return _listenerUnattenuatedZone; } void setListenerUnattenuatedZone(AABox* listenerUnattenuatedZone) { _listenerUnattenuatedZone = listenerUnattenuatedZone; } - int getNumSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; } + int getSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } protected: // disallow copying of PositionalAudioRingBuffer objects PositionalAudioRingBuffer(const PositionalAudioRingBuffer&); PositionalAudioRingBuffer& operator= (const PositionalAudioRingBuffer&); - void updateDesiredJitterBufferNumSamples(); + void updateDesiredJitterBufferFrames(); PositionalAudioRingBuffer::Type _type; glm::vec3 _position; @@ -96,7 +97,10 @@ protected: AABox* _listenerUnattenuatedZone; InterframeTimeGapHistory _interframeTimeGapHistory; - int _desiredJitterBufferNumSamples; + int _desiredJitterBufferFrames; + int _currentJitterBufferFrames; + +quint64 _lastMixTime; }; #endif // hifi_PositionalAudioRingBuffer_h From 0e6abe33597dd0fc39a63d6f044d1374fdb0795d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 20 Jun 2014 16:31:22 -0700 Subject: [PATCH 061/105] Update inactive chat window opacity to 0.8 --- interface/src/ui/ChatWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index b101b50b2f..a7d2c212d2 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -31,7 +31,7 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20; const float OPACITY_ACTIVE = 1.0; -const float OPACITY_INACTIVE = 0.6; +const float OPACITY_INACTIVE = 0.8; const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?)|(?:hifi))://\\S+)"); const QRegularExpression regexHifiLinks("([#@]\\S+)"); From c17b8fdb6078a7a2bbd779132c4a49b6d29b036b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Jun 2014 16:31:39 -0700 Subject: [PATCH 062/105] collisions update shapes VerletShapes keep pointers to VerletPoints (rather than to glm::vec3's) VerletShapes compute lagrangian coefficients VerletShapes send their movement accumulations to their VerletPoints --- interface/src/avatar/SkeletonModel.cpp | 4 +- libraries/shared/src/CollisionInfo.cpp | 11 ++- libraries/shared/src/PhysicsSimulation.cpp | 16 +++- libraries/shared/src/Ragdoll.cpp | 16 ++-- libraries/shared/src/Ragdoll.h | 21 ++++-- libraries/shared/src/VerletCapsuleShape.cpp | 84 ++++++++++++++++----- libraries/shared/src/VerletCapsuleShape.h | 20 ++++- libraries/shared/src/VerletSphereShape.cpp | 26 ++++++- libraries/shared/src/VerletSphereShape.h | 11 ++- tests/physics/src/VerletShapeTests.cpp | 35 ++++----- 10 files changed, 177 insertions(+), 67 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index b28dd08663..0598e2aabd 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -580,12 +580,12 @@ void SkeletonModel::buildShapes() { } Shape* shape = NULL; if (type == Shape::SPHERE_SHAPE) { - shape = new VerletSphereShape(radius, &(_ragdollPoints[i]._position)); + shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); shape->setEntity(this); } else if (type == Shape::CAPSULE_SHAPE) { int parentIndex = joint.parentIndex; assert(parentIndex != -1); - shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]._position), &(_ragdollPoints[i]._position)); + shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); shape->setEntity(this); } _shapes.push_back(shape); diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index 3dd3610edb..c76c99e747 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -12,6 +12,7 @@ #include "CollisionInfo.h" #include "Shape.h" +#include "SharedUtil.h" CollisionInfo::CollisionInfo() : _data(NULL), @@ -42,10 +43,16 @@ void CollisionInfo::apply() { Shape* shapeB = const_cast(_shapeB); massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration); totalMass = massA + massB; - shapeB->accumulateDelta(massA / totalMass, -_penetration); + if (totalMass < EPSILON) { + massA = massB = 1.0f; + totalMass = 2.0f; + } + // remember that _penetration points from A into B + shapeB->accumulateDelta(massA / totalMass, _penetration); } // NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass() - shapeA->accumulateDelta(massB / totalMass, _penetration); + // remember that _penetration points from A into B + shapeA->accumulateDelta(massB / totalMass, -_penetration); } CollisionInfo* CollisionList::getNewCollision() { diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 2891ea8853..c15d0fb541 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -128,7 +128,7 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { } } } -// TODO: Andrew need to implement: +// TODO: Andrew to implement: // DONE (1) joints pull points (SpecialCapsuleShape would help solve this) // DONE (2) points slam shapes (SpecialCapsuleShape would help solve this) // DONE (3) detect collisions @@ -136,9 +136,16 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { // DONE (5) enforce constraints // DONE (6) make sure MyAvatar creates shapes, adds to simulation with ragdoll support // DONE (7) support for pairwise collision bypass -// (8) process collisions -// (9) add and enforce angular contraints for joints +// DONE (8) process collisions +// DONE (8a) stubbery +// DONE (8b) shapes actually accumulate movement +// DONE (9) verify that avatar shapes self collide +// (10) slave rendered SkeletonModel to physical shapes +// (10a) give SkeletonModel duplicate JointState data +// (10b) figure out how to slave dupe JointStates to physical shapes +// (11) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { + static int adebug = 0; ++adebug; quint64 startTime = usecTimestampNow(); quint64 expiry = startTime + maxUsec; @@ -148,6 +155,9 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter enforceConstraints(minError, maxIterations, expiry - usecTimestampNow()); _stepTime = usecTimestampNow()- startTime; + if (0 == (adebug % 200)) { + std::cout << " adebug nC = " << _numCollisions << " i = " << _numIterations << " e = " << _constraintError << " t = " << _stepTime << std::endl; // adebug + } } void PhysicsSimulation::moveRagdolls(float deltaTime) { diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 20828018e5..be1ee07a20 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -19,15 +19,17 @@ // ---------------------------------------------------------------------------- // VerletPoint // ---------------------------------------------------------------------------- -void VerletPoint::accumulatePush(const glm::vec3& delta) { - _accumulatedPush += delta; - ++_numPushes; +void VerletPoint::accumulateDelta(const glm::vec3& delta) { + _accumulatedDelta += delta; + ++_numDeltas; } -void VerletPoint::applyAccumulatedPush() { - _lastPosition = _position; - _position += _accumulatedPush / (float)_numPushes; - _numPushes = 0; +void VerletPoint::applyAccumulatedDelta() { + if (_numDeltas > 0) { + _position += _accumulatedDelta / (float)_numDeltas; + _accumulatedDelta = glm::vec3(0.0f); + _numDeltas = 0; + } } // ---------------------------------------------------------------------------- diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index e6622e8c85..5c9f47841b 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -19,20 +19,29 @@ class Shape; +// TODO: Andrew to move VerletPoint class to its own file class VerletPoint { public: - VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(0.0f), _accumulatedPush(0.0f), _numPushes(0) {} + VerletPoint() : _position(0.0f), _lastPosition(0.0f), _mass(1.0f), _accumulatedDelta(0.0f), _numDeltas(0) {} - void accumulatePush(const glm::vec3& delta); - void applyAccumulatedPush(); + void accumulateDelta(const glm::vec3& delta); + void applyAccumulatedDelta(); + + glm::vec3 getAccumulatedDelta() const { + glm::vec3 foo(0.0f); + if (_numDeltas > 0) { + foo = _accumulatedDelta / (float)_numDeltas; + } + return foo; + } glm::vec3 _position; glm::vec3 _lastPosition; float _mass; -protected: - glm::vec3 _accumulatedPush; - int _numPushes; +private: + glm::vec3 _accumulatedDelta; + int _numDeltas; }; class Constraint { diff --git a/libraries/shared/src/VerletCapsuleShape.cpp b/libraries/shared/src/VerletCapsuleShape.cpp index b4117e40f7..3ac4899682 100644 --- a/libraries/shared/src/VerletCapsuleShape.cpp +++ b/libraries/shared/src/VerletCapsuleShape.cpp @@ -10,21 +10,24 @@ // #include "VerletCapsuleShape.h" + +#include "Ragdoll.h" // for VerletPoint #include "SharedUtil.h" -VerletCapsuleShape::VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint) : - CapsuleShape(), _startPoint(startPoint), _endPoint(endPoint) { +VerletCapsuleShape::VerletCapsuleShape(VerletPoint* startPoint, VerletPoint* endPoint) : + CapsuleShape(), _startPoint(startPoint), _endPoint(endPoint), _startLagrangeCoef(0.5f), _endLagrangeCoef(0.5f) { assert(startPoint); assert(endPoint); - _halfHeight = 0.5f * glm::distance(*_startPoint, *_endPoint); + _halfHeight = 0.5f * glm::distance(_startPoint->_position, _endPoint->_position); updateBoundingRadius(); } -VerletCapsuleShape::VerletCapsuleShape(float radius, glm::vec3* startPoint, glm::vec3* endPoint) : - CapsuleShape(radius, 1.0f), _startPoint(startPoint), _endPoint(endPoint) { +VerletCapsuleShape::VerletCapsuleShape(float radius, VerletPoint* startPoint, VerletPoint* endPoint) : + CapsuleShape(radius, 1.0f), _startPoint(startPoint), _endPoint(endPoint), + _startLagrangeCoef(0.5f), _endLagrangeCoef(0.5f) { assert(startPoint); assert(endPoint); - _halfHeight = 0.5f * glm::distance(*_startPoint, *_endPoint); + _halfHeight = 0.5f * glm::distance(_startPoint->_position, _endPoint->_position); updateBoundingRadius(); } @@ -46,8 +49,8 @@ void VerletCapsuleShape::setRotation(const glm::quat& rotation) { glm::vec3 center = getTranslation(); float halfHeight = getHalfHeight(); glm::vec3 axis = rotation * DEFAULT_CAPSULE_AXIS; - *_startPoint = center - halfHeight * axis; - *_endPoint = center + halfHeight * axis; + _startPoint->_position = center - halfHeight * axis; + _endPoint->_position = center + halfHeight * axis; } void VerletCapsuleShape::setTranslation(const glm::vec3& position) { @@ -56,35 +59,78 @@ void VerletCapsuleShape::setTranslation(const glm::vec3& position) { // update the points such that their center is at position glm::vec3 movement = position - getTranslation(); - *_startPoint += movement; - *_endPoint += movement; + _startPoint->_position += movement; + _endPoint->_position += movement; } const glm::vec3& VerletCapsuleShape::getTranslation() const { // the "translation" of this shape must be computed on the fly VerletCapsuleShape* thisCapsule = const_cast(this); - thisCapsule->_translation = 0.5f * ((*_startPoint) + (*_endPoint)); + thisCapsule->_translation = 0.5f * (_startPoint->_position + _endPoint->_position); return _translation; } +float VerletCapsuleShape::computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { + glm::vec3 startLeg = _startPoint->_position - contactPoint; + glm::vec3 endLeg = _endPoint->_position - contactPoint; + + // TODO: use fast approximate distance calculations here + float startLength = glm::length(startLeg); + float endlength = glm::length(endLeg); + + // The raw coefficient is proportional to the other leg's length multiplied by the dot-product + // of the penetration and this leg direction. We don't worry about the common penetration length + // because it is normalized out later. + float startCoef = glm::abs(glm::dot(startLeg, penetration)) * endlength / (startLength + EPSILON); + float endCoef = glm::abs(glm::dot(endLeg, penetration)) * startLength / (endlength + EPSILON); + + float maxCoef = glm::max(startCoef, endCoef); + if (maxCoef > EPSILON) { + // One of these coeficients will be 1.0, the other will be less --> + // one endpoint will move the full amount while the other will move less. + _startLagrangeCoef = startCoef / maxCoef; + _endLagrangeCoef = endCoef / maxCoef; + assert(!glm::isnan(_startLagrangeCoef)); + assert(!glm::isnan(_startLagrangeCoef)); + } else { + // The coefficients are the same --> the collision will move both equally + // as if the object were solid. + _startLagrangeCoef = 1.0f; + _endLagrangeCoef = 1.0f; + } + // the effective mass is the weighted sum of the two endpoints + return _startLagrangeCoef * _startPoint->_mass + _endLagrangeCoef * _endPoint->_mass; +} + +void VerletCapsuleShape::accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) { + assert(!glm::isnan(relativeMassFactor)); + _startPoint->accumulateDelta(relativeMassFactor * _startLagrangeCoef * penetration); + _endPoint->accumulateDelta(relativeMassFactor * _endLagrangeCoef * penetration); +} + +void VerletCapsuleShape::applyAccumulatedDelta() { + _startPoint->applyAccumulatedDelta(); + _endPoint->applyAccumulatedDelta(); +} + // virtual float VerletCapsuleShape::getHalfHeight() const { - return 0.5f * glm::distance(*_startPoint, *_endPoint); + return 0.5f * glm::distance(_startPoint->_position, _endPoint->_position); } // virtual void VerletCapsuleShape::getStartPoint(glm::vec3& startPoint) const { - startPoint = *_startPoint; + startPoint = _startPoint->_position; } // virtual void VerletCapsuleShape::getEndPoint(glm::vec3& endPoint) const { - endPoint = *_endPoint; + endPoint = _endPoint->_position; } // virtual void VerletCapsuleShape::computeNormalizedAxis(glm::vec3& axis) const { - glm::vec3 unormalizedAxis = *_endPoint - *_startPoint; + glm::vec3 unormalizedAxis = _endPoint->_position - _startPoint->_position; float fullLength = glm::length(unormalizedAxis); if (fullLength > EPSILON) { axis = unormalizedAxis / fullLength; @@ -101,8 +147,8 @@ void VerletCapsuleShape::setHalfHeight(float halfHeight) { glm::vec3 center = getTranslation(); glm::vec3 axis; computeNormalizedAxis(axis); - *_startPoint = center - halfHeight * axis; - *_endPoint = center + halfHeight * axis; + _startPoint->_position = center - halfHeight * axis; + _endPoint->_position = center + halfHeight * axis; _boundingRadius = _radius + halfHeight; } @@ -114,7 +160,7 @@ void VerletCapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) // virtual void VerletCapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) { - *_startPoint = startPoint; - *_endPoint = endPoint; + _startPoint->_position = startPoint; + _endPoint->_position = endPoint; updateBoundingRadius(); } diff --git a/libraries/shared/src/VerletCapsuleShape.h b/libraries/shared/src/VerletCapsuleShape.h index 72cc8b7b0e..1fd84f5b1e 100644 --- a/libraries/shared/src/VerletCapsuleShape.h +++ b/libraries/shared/src/VerletCapsuleShape.h @@ -32,16 +32,21 @@ // down in a hot simulation loop, such as when processing collision results. Best to // just let the verlet simulation do its thing and not try to constantly force a rotation. +class VerletPoint; + class VerletCapsuleShape : public CapsuleShape { public: - VerletCapsuleShape(glm::vec3* startPoint, glm::vec3* endPoint); - VerletCapsuleShape(float radius, glm::vec3* startPoint, glm::vec3* endPoint); + VerletCapsuleShape(VerletPoint* startPoint, VerletPoint* endPoint); + VerletCapsuleShape(float radius, VerletPoint* startPoint, VerletPoint* endPoint); // virtual overrides from Shape const glm::quat& getRotation() const; void setRotation(const glm::quat& rotation); void setTranslation(const glm::vec3& position); const glm::vec3& getTranslation() const; + float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint); + void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration); + void applyAccumulatedDelta(); //float getRadius() const { return _radius; } virtual float getHalfHeight() const; @@ -64,8 +69,15 @@ public: protected: // NOTE: VerletCapsuleShape does NOT own the data in its points. - glm::vec3* _startPoint; - glm::vec3* _endPoint; + VerletPoint* _startPoint; + VerletPoint* _endPoint; + + // The LagrangeCoef's are numerical weights for distributing collision movement + // between the relevant VerletPoints associated with this shape. They are functions + // of the movement parameters and are computed (and cached) in computeEffectiveMass() + // and then used in the subsequent accumulateDelta(). + float _startLagrangeCoef; + float _endLagrangeCoef; }; #endif // hifi_VerletCapsuleShape_h diff --git a/libraries/shared/src/VerletSphereShape.cpp b/libraries/shared/src/VerletSphereShape.cpp index 2c659b8159..10c40c6611 100644 --- a/libraries/shared/src/VerletSphereShape.cpp +++ b/libraries/shared/src/VerletSphereShape.cpp @@ -11,22 +11,40 @@ #include "VerletSphereShape.h" -VerletSphereShape::VerletSphereShape(glm::vec3* centerPoint) : SphereShape() { +#include "Ragdoll.h" // for VerletPoint + +VerletSphereShape::VerletSphereShape(VerletPoint* centerPoint) : SphereShape() { assert(centerPoint); _point = centerPoint; } -VerletSphereShape::VerletSphereShape(float radius, glm::vec3* centerPoint) : SphereShape(radius) { +VerletSphereShape::VerletSphereShape(float radius, VerletPoint* centerPoint) : SphereShape(radius) { assert(centerPoint); _point = centerPoint; } // virtual from Shape class void VerletSphereShape::setTranslation(const glm::vec3& position) { - *_point = position; + _point->_position = position; + _point->_lastPosition = position; } // virtual from Shape class const glm::vec3& VerletSphereShape::getTranslation() const { - return *_point; + return _point->_position; +} + +// virtual +float VerletSphereShape::computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { + return _point->_mass; +} + +// virtual +void VerletSphereShape::accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) { + _point->accumulateDelta(relativeMassFactor * penetration); +} + +// virtual +void VerletSphereShape::applyAccumulatedDelta() { + _point->applyAccumulatedDelta(); } diff --git a/libraries/shared/src/VerletSphereShape.h b/libraries/shared/src/VerletSphereShape.h index 395f5901e6..65da3b2597 100644 --- a/libraries/shared/src/VerletSphereShape.h +++ b/libraries/shared/src/VerletSphereShape.h @@ -24,19 +24,24 @@ // (2) A VerletShape doesn't own the points that it uses, so you must be careful not to // leave dangling pointers around. +class VerletPoint; + class VerletSphereShape : public SphereShape { public: - VerletSphereShape(glm::vec3* centerPoint); + VerletSphereShape(VerletPoint* point); - VerletSphereShape(float radius, glm::vec3* centerPoint); + VerletSphereShape(float radius, VerletPoint* centerPoint); // virtual overrides from Shape void setTranslation(const glm::vec3& position); const glm::vec3& getTranslation() const; + float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint); + void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration); + void applyAccumulatedDelta(); protected: // NOTE: VerletSphereShape does NOT own its _point - glm::vec3* _point; + VerletPoint* _point; }; #endif // hifi_VerletSphereShape_h diff --git a/tests/physics/src/VerletShapeTests.cpp b/tests/physics/src/VerletShapeTests.cpp index 6ae839392e..3a3bd43278 100644 --- a/tests/physics/src/VerletShapeTests.cpp +++ b/tests/physics/src/VerletShapeTests.cpp @@ -17,6 +17,7 @@ #include #include +#include // for VerletPoint #include #include #include @@ -33,16 +34,16 @@ static const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); void VerletShapeTests::setSpherePosition() { float radius = 1.0f; glm::vec3 offset(1.23f, 4.56f, 7.89f); - glm::vec3 point; + VerletPoint point; VerletSphereShape sphere(radius, &point); - point = glm::vec3(0.f); + point._position = glm::vec3(0.f); float d = glm::distance(glm::vec3(0.0f), sphere.getTranslation()); if (d != 0.0f) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should be at origin" << std::endl; } - point = offset; + point._position = offset; d = glm::distance(glm::vec3(0.0f), sphere.getTranslation()); if (d != glm::length(offset)) { std::cout << __FILE__ << ":" << __LINE__ << " ERROR: sphere should be at offset" << std::endl; @@ -60,15 +61,15 @@ void VerletShapeTests::sphereMissesSphere() { float offsetDistance = alpha * radiusA + beta * radiusB; // create points for the sphere centers - glm::vec3 points[2]; + VerletPoint points[2]; // give pointers to the spheres VerletSphereShape sphereA(radiusA, (points + 0)); VerletSphereShape sphereB(radiusB, (points + 1)); // set the positions of the spheres by slamming the points directly - points[0] = origin; - points[1] = offsetDistance * offsetDirection; + points[0]._position = origin; + points[1]._position = offsetDistance * offsetDirection; CollisionList collisions(16); @@ -118,15 +119,15 @@ void VerletShapeTests::sphereTouchesSphere() { glm::vec3 expectedPenetration = expectedPenetrationDistance * offsetDirection; // create two points for the sphere centers - glm::vec3 points[2]; + VerletPoint points[2]; // give pointers to the spheres VerletSphereShape sphereA(radiusA, points+0); VerletSphereShape sphereB(radiusB, points+1); // set the positions of the spheres by slamming the points directly - points[0] = origin; - points[1] = offsetDistance * offsetDirection; + points[0]._position = origin; + points[1]._position = offsetDistance * offsetDirection; CollisionList collisions(16); int numCollisions = 0; @@ -213,9 +214,9 @@ void VerletShapeTests::sphereMissesCapsule() { float radialOffset = 1.2f * radiusA + 1.3f * radiusB; // create points for the sphere + capsule - glm::vec3 points[3]; + VerletPoint points[3]; for (int i = 0; i < 3; ++i) { - points[i] = glm::vec3(0.0f); + points[i]._position = glm::vec3(0.0f); } // give the points to the shapes @@ -277,9 +278,9 @@ void VerletShapeTests::sphereTouchesCapsule() { float radialOffset = alpha * radiusA + beta * radiusB; // create points for the sphere + capsule - glm::vec3 points[3]; + VerletPoint points[3]; for (int i = 0; i < 3; ++i) { - points[i] = glm::vec3(0.0f); + points[i]._position = glm::vec3(0.0f); } // give the points to the shapes @@ -496,9 +497,9 @@ void VerletShapeTests::capsuleMissesCapsule() { float totalHalfLength = totalRadius + halfHeightA + halfHeightB; // create points for the shapes - glm::vec3 points[4]; + VerletPoint points[4]; for (int i = 0; i < 4; ++i) { - points[i] = glm::vec3(0.0f); + points[i]._position = glm::vec3(0.0f); } // give the points to the shapes @@ -574,9 +575,9 @@ void VerletShapeTests::capsuleTouchesCapsule() { float totalHalfLength = totalRadius + halfHeightA + halfHeightB; // create points for the shapes - glm::vec3 points[4]; + VerletPoint points[4]; for (int i = 0; i < 4; ++i) { - points[i] = glm::vec3(0.0f); + points[i]._position = glm::vec3(0.0f); } // give the points to the shapes From dcd9d8871d4d5acb0948d547c047a87cb789b142 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Jun 2014 16:36:44 -0700 Subject: [PATCH 063/105] re-enabling some temporary debug stuff --- libraries/shared/src/PhysicsSimulation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index c15d0fb541..a1f8c249c6 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -10,6 +10,7 @@ // #include +#include // adebug #include "PhysicsSimulation.h" From cb6681f6f88e21196e97d3f6c5dd65a046cdd71d Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 20 Jun 2014 17:13:42 -0700 Subject: [PATCH 064/105] completed code for dynamicly resizing jitter buffer --- libraries/audio/src/AudioRingBuffer.cpp | 2 +- .../audio/src/PositionalAudioRingBuffer.cpp | 81 ++++++++++++++----- 2 files changed, 60 insertions(+), 23 deletions(-) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 7b1e68ffbd..ae100dcdf1 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -158,7 +158,7 @@ const int16_t& AudioRingBuffer::operator[] (const int index) const { void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); - printf("\n mixed. %d samples remaining\n", samplesAvailable()); +//printf("\nmixed. %d samples remaining\n", samplesAvailable()); } unsigned int AudioRingBuffer::samplesAvailable() const { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 813323ecb1..04559fc910 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -33,8 +33,7 @@ InterframeTimeGapHistory::InterframeTimeGapHistory() } void InterframeTimeGapHistory::frameReceived() { - - static QQueue gaps; + static quint64 gapsSum = 0; quint64 now = usecTimestampNow(); @@ -46,6 +45,14 @@ void InterframeTimeGapHistory::frameReceived() { // update the current interval max if (gap > _currentIntervalMaxGap) { _currentIntervalMaxGap = gap; + + // keep the window max gap at least as large as the current interval max + // this allows the window max gap to respond immediately to a sudden spike in gap times + // also, this prevents the window max gap from staying at 0 until the first interval of samples filled up + if (_currentIntervalMaxGap > _windowMaxGap) { + _windowMaxGap = _currentIntervalMaxGap; + _newWindowMaxGapAvailable = true; + } } _numSamplesInCurrentInterval++; @@ -116,17 +123,35 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { readBytes += sizeof(int16_t); if (numSilentSamples > 0) { - addSilentFrame(numSilentSamples); + + if (_currentJitterBufferFrames > _desiredJitterBufferFrames) { + // our current jitter buffer size exceeds its desired value, so ignore some silent + // frames to get that size as close to desired as possible + int samplesPerFrame = getSamplesPerFrame(); + int numSilentFrames = numSilentSamples / samplesPerFrame; + int numFramesToDropDesired = _currentJitterBufferFrames - _desiredJitterBufferFrames; + + if (numSilentFrames > numFramesToDropDesired) { + // we have more than enough frames to drop to get the jitter buffer to its desired length + int numSilentFramesToAdd = numSilentFrames - numFramesToDropDesired; + addSilentFrame(numSilentFramesToAdd * samplesPerFrame); + _currentJitterBufferFrames = _desiredJitterBufferFrames; + + } else { + // we need to drop all frames to get the jitter buffer close as possible to its desired length + _currentJitterBufferFrames -= numSilentFrames; + printf("dropped silent packet! jbuffer size is now %d ###############", _currentJitterBufferFrames); + } + } else { + addSilentFrame(numSilentSamples); + } } - printf("\nparsed silent packet of %d samples\n", numSilentSamples); } else { // there is audio data to read int dataBytes = writeData(packet.data() + readBytes, packet.size() - readBytes); readBytes += dataBytes; - - printf("\nparsed packet of %d data bytes\n", dataBytes); } - printf("%d samples available\n", samplesAvailable()); +//printf("parsed packet. %d samples available\n", samplesAvailable()); return readBytes; } @@ -174,16 +199,18 @@ void PositionalAudioRingBuffer::updateNextOutputTrailingLoudness() { } bool PositionalAudioRingBuffer::shouldBeAddedToMix() { - int samplesPerFrame = getSamplesPerFrame(); - int currentJitterBufferSamples = 3 * samplesPerFrame; //_currentJitterBufferFrames * samplesPerFrame; + int desiredJitterBufferSamples = _desiredJitterBufferFrames * samplesPerFrame; //printf("\nsamples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + currentJitterBufferSamples)) { + if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + _desiredJitterBufferFrames)) { + +printf("\nMIXING DELAYED! waiting for jitter buffer to fill after being starved\n"); +printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); + + // if the buffer was starved, allow it to accrue at least the desired number of + // jitter buffer frames before we start taking frames from it for mixing -//printf("\nMIXING DELAYED! waiting for jitter buffer to fill after being starved\n"); -//printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - // if the buffer was starved and hasn't filled back up all the way, don't mix yet if (_shouldOutputStarveDebug) { _shouldOutputStarveDebug = false; } @@ -192,26 +219,34 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { } else if (samplesAvailable() < samplesPerFrame) { -//printf("\nMIXING DELAYED! jitter buffer is starved!!!\n"); -//printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - // if the buffer doesn't have a full frame of samples for mixing, it is starved +printf("\nMIXING DELAYED! jitter buffer is starved!!!\n"); +printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); + + // if the buffer doesn't have a full frame of samples to take for mixing, it is starved _isStarved = true; + + // set to 0 to indicate the jitter buffer is starved + _currentJitterBufferFrames = 0; // reset our _shouldOutputStarveDebug to true so the next is printed _shouldOutputStarveDebug = true; - // if buffer was starved, we've effectively increased the jitter buffer by one frame - // by "holding back" this ring buffer's contents until the next client frame is prepared. - _currentJitterBufferFrames++; -//printf("jbuffer size increased: new size: %d\n", _currentJitterBufferFrames); - return false; } //printf("WILL MIX\n"); // good buffer, add this to the mix - _isStarved = false; + + if (_isStarved) { + + // if this buffer was just finished replenishing after being starved, the number of frames in it now + // is the length of the jitter buffer + _currentJitterBufferFrames = samplesAvailable() / samplesPerFrame; + printf("jitter buffer length is now %d; desired: %d --------\n", _currentJitterBufferFrames, _desiredJitterBufferFrames); + + _isStarved = false; + } // since we've read data from ring buffer at least once - we've started _hasStarted = true; @@ -228,5 +263,7 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; } + + printf(" desired jbuffer size is now %d --------------------------------\n", _desiredJitterBufferFrames); } } \ No newline at end of file From 124fba35db2e4d2330234d35e6f144c6469cfb37 Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 20 Jun 2014 17:39:05 -0700 Subject: [PATCH 065/105] fixed minor comment typo --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 04559fc910..adadf5af91 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -240,7 +240,7 @@ printf("samples available: %d frames available: %d\n", samplesAvailable(), samp if (_isStarved) { - // if this buffer was just finished replenishing after being starved, the number of frames in it now + // if this buffer has just finished replenishing after being starved, the number of frames in it now // is the length of the jitter buffer _currentJitterBufferFrames = samplesAvailable() / samplesPerFrame; printf("jitter buffer length is now %d; desired: %d --------\n", _currentJitterBufferFrames, _desiredJitterBufferFrames); From 6b72b161d3f7ac33838369f51e3a9756bffe860b Mon Sep 17 00:00:00 2001 From: wangyix Date: Fri, 20 Jun 2014 17:52:46 -0700 Subject: [PATCH 066/105] fixed typo with frames/samples confusion. --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index adadf5af91..93f68b4dff 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -140,7 +140,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { } else { // we need to drop all frames to get the jitter buffer close as possible to its desired length _currentJitterBufferFrames -= numSilentFrames; - printf("dropped silent packet! jbuffer size is now %d ###############", _currentJitterBufferFrames); + printf("dropped silent packet! jbuffer size is now %d ###############\n", _currentJitterBufferFrames); } } else { addSilentFrame(numSilentSamples); @@ -203,7 +203,7 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { int desiredJitterBufferSamples = _desiredJitterBufferFrames * samplesPerFrame; //printf("\nsamples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + _desiredJitterBufferFrames)) { + if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + desiredJitterBufferSamples)) { printf("\nMIXING DELAYED! waiting for jitter buffer to fill after being starved\n"); printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); @@ -241,8 +241,8 @@ printf("samples available: %d frames available: %d\n", samplesAvailable(), samp if (_isStarved) { // if this buffer has just finished replenishing after being starved, the number of frames in it now - // is the length of the jitter buffer - _currentJitterBufferFrames = samplesAvailable() / samplesPerFrame; + // minus one (since a frame will be read immediately after this) is the length of the jitter buffer + _currentJitterBufferFrames = samplesAvailable() / samplesPerFrame - 1; printf("jitter buffer length is now %d; desired: %d --------\n", _currentJitterBufferFrames, _desiredJitterBufferFrames); _isStarved = false; From de0c45a919b96b55a670069c469469e813d29fe1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Jun 2014 18:09:51 -0700 Subject: [PATCH 067/105] Spanner mutation in test. --- .../metavoxels/src/AttributeRegistry.cpp | 49 +++++++++++++++++++ libraries/metavoxels/src/AttributeRegistry.h | 13 +++++ libraries/metavoxels/src/Bitstream.cpp | 24 +++++++++ libraries/metavoxels/src/Bitstream.h | 6 +++ libraries/metavoxels/src/MetavoxelData.cpp | 17 ++++++- libraries/metavoxels/src/MetavoxelData.h | 4 ++ libraries/metavoxels/src/SharedObject.cpp | 14 ++++-- libraries/shared/src/SharedUtil.h | 2 +- tests/metavoxels/src/MetavoxelTests.cpp | 42 +++++++++++++--- tests/metavoxels/src/MetavoxelTests.h | 2 + 10 files changed, 161 insertions(+), 12 deletions(-) diff --git a/libraries/metavoxels/src/AttributeRegistry.cpp b/libraries/metavoxels/src/AttributeRegistry.cpp index e7a7f41850..33ce298859 100644 --- a/libraries/metavoxels/src/AttributeRegistry.cpp +++ b/libraries/metavoxels/src/AttributeRegistry.cpp @@ -211,6 +211,11 @@ void Attribute::writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelSt root.writeSubdivision(state); } +bool Attribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod) { + return firstRoot.deepEquals(this, secondRoot, minimum, size, lod); +} + FloatAttribute::FloatAttribute(const QString& name, float defaultValue) : SimpleInlineAttribute(name, defaultValue) { } @@ -449,6 +454,12 @@ void SharedObjectAttribute::write(Bitstream& out, void* value, bool isLeaf) cons } } +bool SharedObjectAttribute::deepEqual(void* first, void* second) const { + SharedObjectPointer firstObject = decodeInline(first); + SharedObjectPointer secondObject = decodeInline(second); + return firstObject ? firstObject->equals(secondObject) : !secondObject; +} + bool SharedObjectAttribute::merge(void*& parent, void* children[], bool postRead) const { SharedObjectPointer firstChild = decodeInline(children[0]); for (int i = 1; i < MERGE_COUNT; i++) { @@ -489,6 +500,35 @@ MetavoxelNode* SharedObjectSetAttribute::createMetavoxelNode( return new MetavoxelNode(value, original); } +static bool setsEqual(const SharedObjectSet& firstSet, const SharedObjectSet& secondSet) { + if (firstSet.size() != secondSet.size()) { + return false; + } + // some hackiness here: we assume that the local ids of the first set correspond to the remote ids of the second, + // so that this will work with the tests + foreach (const SharedObjectPointer& firstObject, firstSet) { + int id = firstObject->getID(); + bool found = false; + foreach (const SharedObjectPointer& secondObject, secondSet) { + if (secondObject->getRemoteID() == id) { + if (!firstObject->equals(secondObject)) { + return false; + } + found = true; + break; + } + } + if (!found) { + return false; + } + } + return true; +} + +bool SharedObjectSetAttribute::deepEqual(void* first, void* second) const { + return setsEqual(decodeInline(first), decodeInline(second)); +} + bool SharedObjectSetAttribute::merge(void*& parent, void* children[], bool postRead) const { for (int i = 0; i < MERGE_COUNT; i++) { if (!decodeInline(children[i]).isEmpty()) { @@ -563,3 +603,12 @@ void SpannerSetAttribute::writeMetavoxelSubdivision(const MetavoxelNode& root, M state.stream << SharedObjectPointer(); } +bool SpannerSetAttribute::metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod) { + + SharedObjectSet firstSet; + firstRoot.getSpanners(this, minimum, size, lod, firstSet); + SharedObjectSet secondSet; + secondRoot.getSpanners(this, minimum, size, lod, secondSet); + return setsEqual(firstSet, secondSet); +} diff --git a/libraries/metavoxels/src/AttributeRegistry.h b/libraries/metavoxels/src/AttributeRegistry.h index 00d974b8b6..7dc2e110b8 100644 --- a/libraries/metavoxels/src/AttributeRegistry.h +++ b/libraries/metavoxels/src/AttributeRegistry.h @@ -27,6 +27,7 @@ class QScriptValue; class Attribute; class MetavoxelData; +class MetavoxelLOD; class MetavoxelNode; class MetavoxelStreamState; @@ -213,6 +214,11 @@ public: virtual bool equal(void* first, void* second) const = 0; + virtual bool deepEqual(void* first, void* second) const { return equal(first, second); } + + virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod); + /// Merges the value of a parent and its children. /// \param postRead whether or not the merge is happening after a read /// \return whether or not the children and parent values are all equal @@ -406,6 +412,8 @@ public: virtual void read(Bitstream& in, void*& value, bool isLeaf) const; virtual void write(Bitstream& out, void* value, bool isLeaf) const; + virtual bool deepEqual(void* first, void* second) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual void* createFromVariant(const QVariant& value) const; @@ -434,6 +442,8 @@ public: virtual MetavoxelNode* createMetavoxelNode(const AttributeValue& value, const MetavoxelNode* original) const; + virtual bool deepEqual(void* first, void* second) const; + virtual bool merge(void*& parent, void* children[], bool postRead = false) const; virtual AttributeValue inherit(const AttributeValue& parentValue) const; @@ -462,6 +472,9 @@ public: virtual void readMetavoxelSubdivision(MetavoxelData& data, MetavoxelStreamState& state); virtual void writeMetavoxelSubdivision(const MetavoxelNode& root, MetavoxelStreamState& state); + + virtual bool metavoxelRootsEqual(const MetavoxelNode& firstRoot, const MetavoxelNode& secondRoot, + const glm::vec3& minimum, float size, const MetavoxelLOD& lod); }; #endif // hifi_AttributeRegistry_h diff --git a/libraries/metavoxels/src/Bitstream.cpp b/libraries/metavoxels/src/Bitstream.cpp index 44342abe33..0ef86e85c0 100644 --- a/libraries/metavoxels/src/Bitstream.cpp +++ b/libraries/metavoxels/src/Bitstream.cpp @@ -110,6 +110,10 @@ const TypeStreamer* Bitstream::getTypeStreamer(int type) { return getTypeStreamers().value(type); } +const ObjectStreamer* Bitstream::getObjectStreamer(const QMetaObject* metaObject) { + return getObjectStreamers().value(metaObject); +} + const QMetaObject* Bitstream::getMetaObject(const QByteArray& className) { return getMetaObjects().value(className); } @@ -2316,6 +2320,15 @@ QObject* MappedObjectStreamer::putJSONData(JSONReader& reader, const QJsonObject return object; } +bool MappedObjectStreamer::equal(const QObject* first, const QObject* second) const { + foreach (const StreamerPropertyPair& property, _properties) { + if (!property.first->equal(property.second.read(first), property.second.read(second))) { + return false; + } + } + return true; +} + void MappedObjectStreamer::write(Bitstream& out, const QObject* object) const { foreach (const StreamerPropertyPair& property, _properties) { property.first->write(out, property.second.read(object)); @@ -2433,6 +2446,17 @@ QObject* GenericObjectStreamer::putJSONData(JSONReader& reader, const QJsonObjec return object; } +bool GenericObjectStreamer::equal(const QObject* first, const QObject* second) const { + const QVariantList& firstValues = static_cast(first)->getValues(); + const QVariantList& secondValues = static_cast(second)->getValues(); + for (int i = 0; i < _properties.size(); i++) { + if (!_properties.at(i).first->equal(firstValues.at(i), secondValues.at(i))) { + return false; + } + } + return true; +} + void GenericObjectStreamer::write(Bitstream& out, const QObject* object) const { const QVariantList& values = static_cast(object)->getValues(); for (int i = 0; i < _properties.size(); i++) { diff --git a/libraries/metavoxels/src/Bitstream.h b/libraries/metavoxels/src/Bitstream.h index b116bd4b49..e32f93dbe2 100644 --- a/libraries/metavoxels/src/Bitstream.h +++ b/libraries/metavoxels/src/Bitstream.h @@ -278,6 +278,9 @@ public: /// Returns the streamer registered for the supplied type, if any. static const TypeStreamer* getTypeStreamer(int type); + /// Returns the streamer registered for the supplied object, if any. + static const ObjectStreamer* getObjectStreamer(const QMetaObject* metaObject); + /// Returns the meta-object registered under the supplied class name, if any. static const QMetaObject* getMetaObject(const QByteArray& className); @@ -1022,6 +1025,7 @@ public: virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const = 0; virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const = 0; + virtual bool equal(const QObject* first, const QObject* second) const = 0; virtual void write(Bitstream& out, const QObject* object) const = 0; virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const = 0; virtual QObject* read(Bitstream& in, QObject* object = NULL) const = 0; @@ -1047,6 +1051,7 @@ public: virtual QJsonObject getJSONMetadata(JSONWriter& writer) const; virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const; virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const; + virtual bool equal(const QObject* first, const QObject* second) const; virtual void write(Bitstream& out, const QObject* object) const; virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; @@ -1070,6 +1075,7 @@ public: virtual QJsonObject getJSONMetadata(JSONWriter& writer) const; virtual QJsonObject getJSONData(JSONWriter& writer, const QObject* object) const; virtual QObject* putJSONData(JSONReader& reader, const QJsonObject& jsonObject) const; + virtual bool equal(const QObject* first, const QObject* second) const; virtual void write(Bitstream& out, const QObject* object) const; virtual void writeRawDelta(Bitstream& out, const QObject* object, const QObject* reference) const; virtual QObject* read(Bitstream& in, QObject* object = NULL) const; diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 6d1031d3cf..7caa336cd2 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -697,7 +697,7 @@ bool MetavoxelData::deepEquals(const MetavoxelData& other, const MetavoxelLOD& l glm::vec3 minimum = getMinimum(); for (QHash::const_iterator it = _roots.constBegin(); it != _roots.constEnd(); it++) { MetavoxelNode* otherNode = other._roots.value(it.key()); - if (!(otherNode && it.value()->deepEquals(it.key(), *otherNode, minimum, _size, lod))) { + if (!(otherNode && it.key()->metavoxelRootsEqual(*it.value(), *otherNode, minimum, _size, lod))) { return false; } } @@ -1102,7 +1102,7 @@ void MetavoxelNode::clearChildren(const AttributePointer& attribute) { bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const { - if (!attribute->equal(_attributeValue, other._attributeValue)) { + if (!attribute->deepEqual(_attributeValue, other._attributeValue)) { return false; } if (!lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { @@ -1125,6 +1125,19 @@ bool MetavoxelNode::deepEquals(const AttributePointer& attribute, const Metavoxe return true; } +void MetavoxelNode::getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, + float size, const MetavoxelLOD& lod, SharedObjectSet& results) const { + results.unite(decodeInline(_attributeValue)); + if (isLeaf() || !lod.shouldSubdivide(minimum, size, attribute->getLODThresholdMultiplier())) { + return; + } + float nextSize = size * 0.5f; + for (int i = 0; i < CHILD_COUNT; i++) { + glm::vec3 nextMinimum = getNextMinimum(minimum, nextSize, i); + _children[i]->getSpanners(attribute, nextMinimum, nextSize, lod, results); + } +} + int MetavoxelVisitor::encodeOrder(int first, int second, int third, int fourth, int fifth, int sixth, int seventh, int eighth) { return first | (second << 3) | (third << 6) | (fourth << 9) | diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 0df97a7a4c..7496bf567d 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -221,6 +221,10 @@ public: /// Performs a deep comparison between this and the specified other node. bool deepEquals(const AttributePointer& attribute, const MetavoxelNode& other, const glm::vec3& minimum, float size, const MetavoxelLOD& lod) const; + + /// Retrieves all spanners satisfying the LOD constraint, placing them in the provided set. + void getSpanners(const AttributePointer& attribute, const glm::vec3& minimum, + float size, const MetavoxelLOD& lod, SharedObjectSet& results) const; private: Q_DISABLE_COPY(MetavoxelNode) diff --git a/libraries/metavoxels/src/SharedObject.cpp b/libraries/metavoxels/src/SharedObject.cpp index 05af5f1bf8..d0a1842d31 100644 --- a/libraries/metavoxels/src/SharedObject.cpp +++ b/libraries/metavoxels/src/SharedObject.cpp @@ -84,11 +84,19 @@ bool SharedObject::equals(const SharedObject* other, bool sharedAncestry) const if (metaObject != other->metaObject() && !sharedAncestry) { return false; } - for (int i = 0; i < metaObject->propertyCount(); i++) { - QMetaProperty property = metaObject->property(i); - if (property.isStored() && property.read(this) != property.read(other)) { + // use the streamer, if we have one + const ObjectStreamer* streamer = Bitstream::getObjectStreamer(metaObject); + if (streamer) { + if (!streamer->equal(this, other)) { return false; } + } else { + for (int i = 0; i < metaObject->propertyCount(); i++) { + QMetaProperty property = metaObject->property(i); + if (property.isStored() && property.read(this) != property.read(other)) { + return false; + } + } } QList dynamicPropertyNames = this->dynamicPropertyNames(); if (dynamicPropertyNames.size() != other->dynamicPropertyNames().size()) { diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index dbbfb02365..e5c2a0afc9 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -68,7 +68,7 @@ float randFloat(); int randIntInRange (int min, int max); float randFloatInRange (float min,float max); float randomSign(); /// \return -1.0 or 1.0 -unsigned char randomColorValue(int minimum); +unsigned char randomColorValue(int minimum = 0); bool randomBoolean(); glm::quat safeMix(const glm::quat& q1, const glm::quat& q2, float alpha); diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 6aef40eab1..f22a7ef189 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -47,6 +47,8 @@ static int sharedObjectsDestroyed = 0; static int objectMutationsPerformed = 0; static int scriptObjectsCreated = 0; static int scriptMutationsPerformed = 0; +static int metavoxelMutationsPerformed = 0; +static int spannerMutationsPerformed = 0; static QByteArray createRandomBytes(int minimumSize, int maximumSize) { QByteArray bytes(randIntInRange(minimumSize, maximumSize), 0); @@ -395,6 +397,8 @@ bool MetavoxelTests::run() { qDebug() << "Sent" << datagramsSent << "datagrams with" << bytesSent << "bytes, received" << datagramsReceived << "with" << bytesReceived << "bytes"; qDebug() << "Max" << maxDatagramsPerPacket << "datagrams," << maxBytesPerPacket << "bytes per packet"; + qDebug() << "Performed" << metavoxelMutationsPerformed << "metavoxel mutations," << spannerMutationsPerformed << + "spanner mutations"; } qDebug() << "All tests passed!"; @@ -472,6 +476,12 @@ Endpoint::Endpoint(const QByteArray& datagramHeader, Mode mode) : RandomVisitor visitor; _data.guide(visitor); qDebug() << "Created" << visitor.leafCount << "base leaves"; + + _data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), new Sphere()); + + _sphere = new Sphere(); + static_cast(_sphere.data())->setScale(0.01f); + _data.insert(AttributeRegistry::getInstance()->getSpannersAttribute(), _sphere); return; } // create the object that represents out delta-encoded state @@ -614,9 +624,10 @@ int MutateVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return encodeRandomOrder(); } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randIntInRange(0, 255), - randIntInRange(0, 255), randIntInRange(0, 255)))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), + randomColorValue(), randomColorValue()))); _mutationsRemaining--; + metavoxelMutationsPerformed++; return STOP_RECURSION; } @@ -651,6 +662,21 @@ bool Endpoint::simulate(int iterationNumber) { MutateVisitor visitor; _data.guide(visitor); + // perhaps mutate the spanner + if (randomBoolean()) { + SharedObjectPointer oldSphere = _sphere; + _sphere = _sphere->clone(true); + Sphere* newSphere = static_cast(_sphere.data()); + if (randomBoolean()) { + newSphere->setColor(QColor(randomColorValue(), randomColorValue(), randomColorValue())); + } else { + newSphere->setTranslation(newSphere->getTranslation() + glm::vec3(randFloatInRange(-0.01f, 0.01f), + randFloatInRange(-0.01f, 0.01f), randFloatInRange(-0.01f, 0.01f))); + } + _data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), oldSphere, _sphere); + spannerMutationsPerformed++; + } + // wait until we have a valid lod before sending if (!_lod.isValid()) { return false; @@ -772,7 +798,8 @@ void Endpoint::readMessage(Bitstream& in) { foreach (const SendRecord& sendRecord, _other->_sendRecords) { if (sendRecord.packetNumber == packetNumber) { if (!sendRecord.data.deepEquals(_data, _sendRecords.first().lod)) { - throw QString("Sent/received metavoxel data mismatch."); + qDebug() << "Sent/received metavoxel data mismatch."; + exit(true); } break; } @@ -807,17 +834,20 @@ void Endpoint::readMessage(Bitstream& in) { it != _other->_unreliableMessagesSent.end(); it++) { if (it->sequenceNumber == message.sequenceNumber) { if (!messagesEqual(it->submessage, message.submessage)) { - throw QString("Sent/received unreliable message mismatch."); + qDebug() << "Sent/received unreliable message mismatch."; + exit(true); } if (!it->state->equals(message.state)) { - throw QString("Delta-encoded object mismatch."); + qDebug() << "Delta-encoded object mismatch."; + exit(true); } _other->_unreliableMessagesSent.erase(_other->_unreliableMessagesSent.begin(), it + 1); unreliableMessagesReceived++; return; } } - throw QString("Received unsent/already sent unreliable message."); + qDebug() << "Received unsent/already sent unreliable message."; + exit(true); } void Endpoint::handleReliableMessage(const QVariant& message) { diff --git a/tests/metavoxels/src/MetavoxelTests.h b/tests/metavoxels/src/MetavoxelTests.h index 1de355661d..c340d78963 100644 --- a/tests/metavoxels/src/MetavoxelTests.h +++ b/tests/metavoxels/src/MetavoxelTests.h @@ -93,6 +93,8 @@ private: MetavoxelData _data; MetavoxelLOD _lod; + SharedObjectPointer _sphere; + Endpoint* _other; QList > _delayedDatagrams; float _highPriorityMessagesToSend; From 49f1d427eca90cbc3fff15fae45fe8f64b0cbe5b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Jun 2014 18:12:08 -0700 Subject: [PATCH 068/105] Removed incremental streaming stubs. --- libraries/metavoxels/src/MetavoxelData.cpp | 77 ---------------------- libraries/metavoxels/src/MetavoxelData.h | 5 -- 2 files changed, 82 deletions(-) diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 7caa336cd2..3e70e0f09b 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -602,83 +602,6 @@ void MetavoxelData::writeDelta(const MetavoxelData& reference, const MetavoxelLO } } -void MetavoxelData::readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& in, const MetavoxelLOD& lod) { - - // shallow copy the reference - *this = reference; - - // if nothing changed, return - bool changed; - in >> changed; - if (!changed) { - return; - } - - // expand if the size has changed - bool sizeChanged; - in >> sizeChanged; - if (sizeChanged) { - float size; - in >> size; - while (_size < size) { - expand(); - } - } - - // read the nodes removed - forever { - AttributePointer attribute; - in >> attribute; - if (!attribute) { - break; - } - _roots.take(attribute)->decrementReferenceCount(attribute); - } -} - -void MetavoxelData::writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& out, const MetavoxelLOD& lod) const { - - // first things first: there might be no change whatsoever - glm::vec3 minimum = getMinimum(); - bool becameSubdivided = lod.becameSubdivided(minimum, _size, referenceLOD); - if (_size == reference._size && _roots == reference._roots && !becameSubdivided) { - out << false; - return; - } - out << true; - - // compare the size; if changed (rare), we must compare to the expanded reference - const MetavoxelData* expandedReference = &reference; - if (_size == reference._size) { - out << false; - } else { - out << true; - out << _size; - - MetavoxelData* expanded = new MetavoxelData(reference); - while (expanded->_size < _size) { - expanded->expand(); - } - expandedReference = expanded; - } - - // write the nodes removed - for (QHash::const_iterator it = expandedReference->_roots.constBegin(); - it != expandedReference->_roots.constEnd(); it++) { - if (!_roots.contains(it.key())) { - out << it.key(); - } - } - out << AttributePointer(); - - // delete the expanded reference if we had to expand - if (expandedReference != &reference) { - delete expandedReference; - } -} - MetavoxelNode* MetavoxelData::createRoot(const AttributePointer& attribute) { MetavoxelNode*& root = _roots[attribute]; if (root) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index 7496bf567d..6a7ba33eb5 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -118,11 +118,6 @@ public: void writeDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, Bitstream& out, const MetavoxelLOD& lod) const; - void readIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& in, const MetavoxelLOD& lod); - void writeIncrementalDelta(const MetavoxelData& reference, const MetavoxelLOD& referenceLOD, - Bitstream& out, const MetavoxelLOD& lod) const; - MetavoxelNode* getRoot(const AttributePointer& attribute) const { return _roots.value(attribute); } MetavoxelNode* createRoot(const AttributePointer& attribute); From 626bd42b7b60a29e1a838aa19dc6e6a62e525b9f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 20 Jun 2014 18:22:36 -0700 Subject: [PATCH 069/105] Use randomColorValue, since we have it. --- tests/metavoxels/src/MetavoxelTests.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index f22a7ef189..287d3a648c 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -437,8 +437,8 @@ int RandomVisitor::visit(MetavoxelInfo& info) { if (info.size > MAXIMUM_LEAF_SIZE || (info.size > MINIMUM_LEAF_SIZE && randomBoolean())) { return DEFAULT_ORDER; } - info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randIntInRange(0, 255), - randIntInRange(0, 255), randIntInRange(0, 255)))); + info.outputValues[0] = OwnedAttributeValue(_outputs.at(0), encodeInline(qRgb(randomColorValue(), + randomColorValue(), randomColorValue()))); leafCount++; return STOP_RECURSION; } From ea83a97b7561afc873ac884c5d5afb60ae9973b1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 08:36:45 -0700 Subject: [PATCH 070/105] Add getVolume() method to Shape --- libraries/shared/src/CapsuleShape.h | 4 ++++ libraries/shared/src/Shape.h | 3 +++ libraries/shared/src/SphereShape.h | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/libraries/shared/src/CapsuleShape.h b/libraries/shared/src/CapsuleShape.h index e97a6a4917..8d84e32a97 100644 --- a/libraries/shared/src/CapsuleShape.h +++ b/libraries/shared/src/CapsuleShape.h @@ -14,6 +14,8 @@ #include "Shape.h" +#include "SharedUtil.h" + // default axis of CapsuleShape is Y-axis const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f); @@ -47,6 +49,8 @@ public: bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); } + protected: virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); } static glm::quat computeNewRotation(const glm::vec3& newAxis); diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 40429b3d8d..8d4c1898b5 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -63,6 +63,9 @@ public: virtual void applyAccumulatedDelta() {} + /// \return volume of shape in cubic meters + virtual float getVolume() const { return 1.0; } + protected: // these ctors are protected (used by derived classes only) Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() {} diff --git a/libraries/shared/src/SphereShape.h b/libraries/shared/src/SphereShape.h index f500190505..d2f2a8596f 100644 --- a/libraries/shared/src/SphereShape.h +++ b/libraries/shared/src/SphereShape.h @@ -14,6 +14,8 @@ #include "Shape.h" +#include "SharedUtil.h" + class SphereShape : public Shape { public: SphereShape() : Shape(Shape::SPHERE_SHAPE) {} @@ -33,6 +35,8 @@ public: void setRadius(float radius) { _boundingRadius = radius; } bool findRayIntersection(const glm::vec3& rayStart, const glm::vec3& rayDirection, float& distance) const; + + float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; } }; #endif // hifi_SphereShape_h From b2ea8c0bf989a4cac8ab646b815e538e53f4257e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 08:37:11 -0700 Subject: [PATCH 071/105] Compute more correct masses for ragdoll parts also pin root ragdoll shape at the local-frame origin for stability --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/SkeletonModel.cpp | 63 +++++++++++++--------- interface/src/avatar/SkeletonModel.h | 1 - libraries/shared/src/PhysicsEntity.cpp | 9 ++-- libraries/shared/src/PhysicsEntity.h | 2 +- libraries/shared/src/PhysicsSimulation.cpp | 4 -- libraries/shared/src/Ragdoll.cpp | 11 ++-- libraries/shared/src/Ragdoll.h | 6 +-- 8 files changed, 56 insertions(+), 44 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1f7116a254..94d6e2b393 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -199,8 +199,8 @@ void MyAvatar::simulate(float deltaTime) { if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); const int minError = 0.005f; - const float maxIterations = 4; - const quint64 maxUsec = 1000; + const float maxIterations = 8; + const quint64 maxUsec = 2000; _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); } diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0598e2aabd..516cbd51fb 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -31,6 +31,13 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : void SkeletonModel::setJointStates(QVector states) { Model::setJointStates(states); + // the SkeletonModel override of updateJointState() will clear the translation part + // of its root joint and we need that done before we try to build shapes hence we + // recompute all joint transforms at this time. + for (int i = 0; i < _jointStates.size(); i++) { + updateJointState(i); + } + clearShapes(); clearRagdollConstraintsAndPoints(); if (_enableShapes) { @@ -95,26 +102,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } - - simulateRagdoll(deltaTime); -} - -void SkeletonModel::simulateRagdoll(float deltaTime) { - const int numStates = _jointStates.size(); - assert(numStates == _ragdollPoints.size()); - - float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes - moveShapesTowardJoints(fraction); - - // enforce the constraints - float MIN_CONSTRAINT_ERROR = 0.005f; // 5mm - int MAX_ITERATIONS = 4; - int iterations = 0; - float delta = 0.0f; - do { - delta = enforceRagdollConstraints(); - ++iterations; - } while (delta > MIN_CONSTRAINT_ERROR && iterations < MAX_ITERATIONS); + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + const int numStates = _jointStates.size(); + assert(numStates == _ragdollPoints.size()); + float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes + moveShapesTowardJoints(fraction); + } } void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { @@ -541,7 +534,7 @@ void SkeletonModel::buildRagdollConstraints() { const FBXJoint& joint = state.getFBXJoint(); int parentIndex = joint.parentIndex; if (parentIndex == -1) { - FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]._position), glm::vec3(0.0f)); + FixedConstraint* anchor = new FixedConstraint(&(_ragdollPoints[i]), glm::vec3(0.0f)); _ragdollConstraints.push_back(anchor); } else { DistanceConstraint* bone = new DistanceConstraint(&(_ragdollPoints[i]), &(_ragdollPoints[parentIndex])); @@ -554,6 +547,10 @@ void SkeletonModel::buildRagdollConstraints() { void SkeletonModel::stepRagdollForward(float deltaTime) { } +float DENSITY_OF_WATER = 1000.0f; // kg/m^3 +float MIN_JOINT_MASS = 1.0f; +float VERY_BIG_MASS = 1.0e6f; + // virtual void SkeletonModel::buildShapes() { if (!_geometry || _rootIndex == -1) { @@ -570,7 +567,8 @@ void SkeletonModel::buildShapes() { float uniformScale = extractUniformScale(_scale); const int numStates = _jointStates.size(); for (int i = 0; i < numStates; i++) { - const FBXJoint& joint = geometry.joints[i]; + JointState& state = _jointStates[i]; + const FBXJoint& joint = state.getFBXJoint(); float radius = uniformScale * joint.boneRadius; float halfHeight = 0.5f * uniformScale * joint.distanceToParent; Shape::Type type = joint.shapeType; @@ -579,27 +577,40 @@ void SkeletonModel::buildShapes() { type = Shape::SPHERE_SHAPE; } Shape* shape = NULL; + int parentIndex = joint.parentIndex; if (type == Shape::SPHERE_SHAPE) { shape = new VerletSphereShape(radius, &(_ragdollPoints[i])); shape->setEntity(this); + _ragdollPoints[i]._mass = glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()); } else if (type == Shape::CAPSULE_SHAPE) { - int parentIndex = joint.parentIndex; assert(parentIndex != -1); shape = new VerletCapsuleShape(radius, &(_ragdollPoints[parentIndex]), &(_ragdollPoints[i])); shape->setEntity(this); + _ragdollPoints[i]._mass = glm::max(MIN_JOINT_MASS, DENSITY_OF_WATER * shape->getVolume()); } + if (parentIndex != -1) { + // always disable collisions between joint and its parent + disableCollisions(i, parentIndex); + } else { + // give the base joint a very large mass since it doesn't actually move + // in the local-frame simulation (it defines the origin) + _ragdollPoints[i]._mass = VERY_BIG_MASS; + } _shapes.push_back(shape); } - // This method moves the shapes to their default positions in Model frame + // This method moves the shapes to their default positions in Model frame. computeBoundingShape(geometry); - // while the shapes are in their default position... - disableSelfCollisions(); + // While the shapes are in their default position we disable collisions between + // joints that are currently colliding. + disableCurrentSelfCollisions(); + buildRagdollConstraints(); // ... then move shapes back to current joint positions moveShapesTowardJoints(1.0f); + enforceRagdollConstraints(); } void SkeletonModel::moveShapesTowardJoints(float fraction) { diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 66ede8ee19..57e039c3cc 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -30,7 +30,6 @@ public: void setJointStates(QVector states); void simulate(float deltaTime, bool fullUpdate = true); - void simulateRagdoll(float deltaTime); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 71f972652b..22ac850483 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -164,8 +164,8 @@ bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& c } // ----------------------------------------------------------- -// TODO: enforce this maximum when shapes are built. The gotcha here is that -// the Model class (derived from PhysicsEntity) expects numShapes == numJoints, +// TODO: enforce this maximum when shapes are actually built. The gotcha here is +// that the Model class (derived from PhysicsEntity) expects numShapes == numJoints, // so we have to modify that code to be safe. const int MAX_SHAPES_PER_ENTITY = 256; @@ -211,7 +211,7 @@ bool PhysicsEntity::collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const return false; } -void PhysicsEntity::disableSelfCollisions() { +void PhysicsEntity::disableCurrentSelfCollisions() { CollisionList collisions(10); int numShapes = _shapes.size(); for (int i = 0; i < numShapes; ++i) { @@ -220,6 +220,9 @@ void PhysicsEntity::disableSelfCollisions() { continue; } for (int j = i+1; j < numShapes; ++j) { + if (!collisionsAreEnabled(i, j)) { + continue; + } const Shape* otherShape = _shapes.at(j); if (otherShape && ShapeCollider::collideShapes(shape, otherShape, collisions)) { disableCollisions(i, j); diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index 4d2e0ae889..bb4d5ceb68 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -58,7 +58,7 @@ public: void disableCollisions(int shapeIndexA, int shapeIndexB); bool collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const; - void disableSelfCollisions(); + void disableCurrentSelfCollisions(); protected: glm::vec3 _translation; diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index a1f8c249c6..9fdfa89382 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -146,7 +146,6 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { // (10b) figure out how to slave dupe JointStates to physical shapes // (11) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { - static int adebug = 0; ++adebug; quint64 startTime = usecTimestampNow(); quint64 expiry = startTime + maxUsec; @@ -156,9 +155,6 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter enforceConstraints(minError, maxIterations, expiry - usecTimestampNow()); _stepTime = usecTimestampNow()- startTime; - if (0 == (adebug % 200)) { - std::cout << " adebug nC = " << _numCollisions << " i = " << _numIterations << " e = " << _constraintError << " t = " << _stepTime << std::endl; // adebug - } } void PhysicsSimulation::moveRagdolls(float deltaTime) { diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index be1ee07a20..833bcdb607 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -35,18 +35,21 @@ void VerletPoint::applyAccumulatedDelta() { // ---------------------------------------------------------------------------- // FixedConstraint // ---------------------------------------------------------------------------- -FixedConstraint::FixedConstraint(glm::vec3* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { +FixedConstraint::FixedConstraint(VerletPoint* point, const glm::vec3& anchor) : _point(point), _anchor(anchor) { } float FixedConstraint::enforce() { assert(_point != NULL); - float distance = glm::distance(_anchor, *_point); - *_point = _anchor; + // TODO: use fast approximate sqrt here + float distance = glm::distance(_anchor, _point->_position); + _point->_position = _anchor; return distance; } -void FixedConstraint::setPoint(glm::vec3* point) { +void FixedConstraint::setPoint(VerletPoint* point) { + assert(point); _point = point; + _point->_mass = Shape::MAX_MASS; } void FixedConstraint::setAnchor(const glm::vec3& anchor) { diff --git a/libraries/shared/src/Ragdoll.h b/libraries/shared/src/Ragdoll.h index 5c9f47841b..59c1291725 100644 --- a/libraries/shared/src/Ragdoll.h +++ b/libraries/shared/src/Ragdoll.h @@ -59,12 +59,12 @@ protected: class FixedConstraint : public Constraint { public: - FixedConstraint(glm::vec3* point, const glm::vec3& anchor); + FixedConstraint(VerletPoint* point, const glm::vec3& anchor); float enforce(); - void setPoint(glm::vec3* point); + void setPoint(VerletPoint* point); void setAnchor(const glm::vec3& anchor); private: - glm::vec3* _point; + VerletPoint* _point; glm::vec3 _anchor; }; From c1c2df593333dd5ed4eb0cc140e77effc0d71e30 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 08:53:58 -0700 Subject: [PATCH 072/105] remove debug include --- libraries/shared/src/PhysicsSimulation.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 9fdfa89382..ce47a189d0 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -10,7 +10,6 @@ // #include -#include // adebug #include "PhysicsSimulation.h" From 8b24e9cd67e1fa96906fb399176d53bc186b7aad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 09:39:14 -0700 Subject: [PATCH 073/105] Update SkeletonModel::_boundingShape position --- interface/src/avatar/SkeletonModel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 516cbd51fb..fd37409367 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -108,6 +108,9 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes moveShapesTowardJoints(fraction); } + + _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); + _boundingShape.setRotation(_rotation); } void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { From eba03ddb8a1790e25ba4d0adaae61ea5d19b6f38 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:05:51 -0700 Subject: [PATCH 074/105] re-enable some basic avatar collisions --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 94d6e2b393..156fc45761 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -204,7 +204,6 @@ void MyAvatar::simulate(float deltaTime) { _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); } - /* TODO: Andrew to make this work again // now that we're done stepping the avatar forward in time, compute new collisions if (_collisionGroups != 0) { PerformanceTimer perfTimer("MyAvatar::simulate/_collisionGroups"); @@ -225,12 +224,13 @@ void MyAvatar::simulate(float deltaTime) { } else { _trapDuration = 0.0f; } + /* TODO: Andrew to make this work if (_collisionGroups & COLLISION_GROUP_AVATARS) { PerformanceTimer perfTimer("MyAvatar::simulate/updateCollisionWithAvatars"); updateCollisionWithAvatars(deltaTime); } - } */ + } // consider updating our billboard maybeUpdateBillboard(); From 7196814ec3b2a087f966816becd7803475f5c810 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 23 Jun 2014 10:22:16 -0700 Subject: [PATCH 075/105] added debug prints to look at injected ring buffers --- assignment-client/src/audio/AudioMixerClientData.cpp | 2 ++ libraries/audio/src/AudioRingBuffer.cpp | 10 +++++++--- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 -- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 0c41cc70f9..0080af4716 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -90,6 +90,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { // we don't have a matching injected audio ring buffer, so add it matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); _ringBuffers.push_back(matchingInjectedRingBuffer); +printf("created InjectedRingBuffer %p\n", (void*)matchingInjectedRingBuffer); } matchingInjectedRingBuffer->parseData(packet); @@ -132,6 +133,7 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { && audioBuffer->hasStarted() && audioBuffer->isStarved()) { // this is an empty audio buffer that has starved, safe to delete delete audioBuffer; +printf("deleted injector %p\n", (void*)audioBuffer); i = _ringBuffers.erase(i); continue; } diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index ae100dcdf1..8ce48a39e3 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -125,16 +125,19 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { std::less less; std::less_equal lessEqual; + // TODO: why is less(_endOfLastWrite, _nextOutput) a condition here? if (_hasStarted + /* && (less(_endOfLastWrite, _nextOutput) - && lessEqual(_nextOutput, shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy)))) { + && lessEqual(_nextOutput, shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy)))*/ + && samplesToCopy > _sampleCapacity - samplesAvailable()) { // this read will cross the next output, so call us starved and reset the buffer qDebug() << "Filled the ring buffer. Resetting."; _endOfLastWrite = _buffer; _nextOutput = _buffer; _isStarved = true; } - + if (_endOfLastWrite + samplesToCopy <= _buffer + _sampleCapacity) { memcpy(_endOfLastWrite, data, samplesToCopy * sizeof(int16_t)); } else { @@ -145,6 +148,7 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy); +printf("%p: writeData. %d samples available\n", (void*)this, samplesAvailable()); return samplesToCopy * sizeof(int16_t); } @@ -158,7 +162,7 @@ const int16_t& AudioRingBuffer::operator[] (const int index) const { void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); -//printf("\nmixed. %d samples remaining\n", samplesAvailable()); +printf("%p: mixed. %d samples remaining\n", (void*)this, samplesAvailable()); } unsigned int AudioRingBuffer::samplesAvailable() const { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 93f68b4dff..03ac36d09e 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -151,8 +151,6 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { int dataBytes = writeData(packet.data() + readBytes, packet.size() - readBytes); readBytes += dataBytes; } -//printf("parsed packet. %d samples available\n", samplesAvailable()); - return readBytes; } From 8986be5f1c07086564bcd78aa9ba17eaf9f60690 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:36:31 -0700 Subject: [PATCH 076/105] remove old collideAgaintsOurself() code --- interface/src/avatar/Avatar.cpp | 5 ++-- interface/src/avatar/Hand.cpp | 35 -------------------------- interface/src/avatar/Hand.h | 1 - interface/src/avatar/MyAvatar.cpp | 1 - libraries/shared/src/PhysicsEntity.cpp | 25 ++---------------- libraries/shared/src/PhysicsEntity.h | 2 +- 6 files changed, 5 insertions(+), 64 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index fcb1d876a1..57452f687f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -576,9 +576,8 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc } bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) { - int skeletonSkipIndex = -1; - return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, skeletonSkipIndex); - // Temporarily disabling collisions against the head because most of its collision proxies are bad. + return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions); + // TODO: Andrew to fix: Temporarily disabling collisions against the head //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index ab8e06cfff..94f734ba06 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -94,41 +94,6 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { } } -void Hand::collideAgainstOurself() { - /* TODO: Andrew to re-implement this - if (!Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { - return; - } - - int leftPalmIndex, rightPalmIndex; - getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); - - const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel(); - for (int i = 0; i < int(getNumPalms()); i++) { - PalmData& palm = getPalms()[i]; - if (!palm.isActive()) { - continue; - } - // ignoring everything below the parent of the parent of the last free joint - int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( - skeletonModel.getLastFreeJointIndex((int(i) == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : - (int(i) == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); - - handCollisions.clear(); - if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) { - glm::vec3 totalPenetration; - for (int j = 0; j < handCollisions.size(); ++j) { - CollisionInfo* collision = handCollisions.getCollision(j); - totalPenetration = addPenetrations(totalPenetration, collision->_penetration); - } - // resolve penetration - palm.addToPenetration(totalPenetration); - } - } - */ -} - void Hand::resolvePenetrations() { for (size_t i = 0; i < getNumPalms(); ++i) { PalmData& palm = getPalms()[i]; diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 5d171f2809..ed2fa3e1ab 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -54,7 +54,6 @@ public: void render(bool isMine, Model::RenderMode renderMode = Model::DEFAULT_RENDER_MODE); void collideAgainstAvatar(Avatar* avatar, bool isMyHand); - void collideAgainstOurself(); void resolvePenetrations(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 156fc45761..6fee71b27c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -161,7 +161,6 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("MyAvatar::simulate/hand Collision,simulate"); // update avatar skeleton and simulate hand and head - getHand()->collideAgainstOurself(); getHand()->simulate(deltaTime, true); } diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 22ac850483..76817591ad 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -72,13 +72,12 @@ void PhysicsEntity::clearShapes() { } bool PhysicsEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const { - /* TODO: Andrew to make this work int numShapes = _shapes.size(); float minDistance = FLT_MAX; for (int j = 0; j < numShapes; ++j) { const Shape* shape = _shapes[j]; float thisDistance = FLT_MAX; - if (shape && ShapeCollider::findRayIntersection(ourShape, origin, direction, thisDistance)) { + if (shape && shape->findRayIntersection(origin, direction, thisDistance)) { if (thisDistance < minDistance) { minDistance = thisDistance; } @@ -88,7 +87,6 @@ bool PhysicsEntity::findRayIntersection(const glm::vec3& origin, const glm::vec3 distance = minDistance; return true; } - */ return false; } @@ -111,40 +109,21 @@ bool PhysicsEntity::findCollisions(const QVector shapes, Collision return collided; } -bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, - CollisionList& collisions, int skipIndex) { +bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions) { bool collided = false; - // TODO: Andrew to implement this or make it unecessary - /* SphereShape sphere(sphereRadius, sphereCenter); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _shapes.size(); i++) { Shape* shape = _shapes[i]; if (!shape) { continue; } - const FBXJoint& joint = geometry.joints[i]; - if (joint.parentIndex != -1) { - if (skipIndex != -1) { - int ancestorIndex = joint.parentIndex; - do { - if (ancestorIndex == skipIndex) { - goto outerContinue; - } - ancestorIndex = geometry.joints[ancestorIndex].parentIndex; - - } while (ancestorIndex != -1); - } - } if (ShapeCollider::collideShapes(&sphere, shape, collisions)) { CollisionInfo* collision = collisions.getLastCollision(); collision->_data = (void*)(this); collision->_intData = i; collided = true; } - outerContinue: ; } - */ return collided; } diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index bb4d5ceb68..693c712c11 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -52,7 +52,7 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; bool findCollisions(const QVector shapes, CollisionList& collisions); - bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions, int skipIndex); + bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions); bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions); void disableCollisions(int shapeIndexA, int shapeIndexB); From b50bf09c93be6f843c20beade3aa6ad36c95e8cb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:39:50 -0700 Subject: [PATCH 077/105] findPlaneCollisions() works in theory, not used --- libraries/shared/src/PhysicsEntity.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/shared/src/PhysicsEntity.cpp b/libraries/shared/src/PhysicsEntity.cpp index 76817591ad..37d1a88d67 100644 --- a/libraries/shared/src/PhysicsEntity.cpp +++ b/libraries/shared/src/PhysicsEntity.cpp @@ -128,7 +128,6 @@ bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sp } bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) { - // TODO: Andrew to reimplement this or make it unecessary bool collided = false; PlaneShape planeShape(plane); for (int i = 0; i < _shapes.size(); i++) { From e5d7d471e34b7037b9726c381f00d36247d880ff Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 23 Jun 2014 10:55:30 -0700 Subject: [PATCH 078/105] removed debug code --- .../src/audio/AudioMixerClientData.cpp | 2 -- interface/src/Audio.cpp | 2 -- libraries/audio/src/AudioRingBuffer.cpp | 13 ++----- libraries/audio/src/AudioRingBuffer.h | 3 +- .../audio/src/PositionalAudioRingBuffer.cpp | 36 ++++--------------- .../audio/src/PositionalAudioRingBuffer.h | 3 -- 6 files changed, 10 insertions(+), 49 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 0080af4716..0c41cc70f9 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -90,7 +90,6 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { // we don't have a matching injected audio ring buffer, so add it matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); _ringBuffers.push_back(matchingInjectedRingBuffer); -printf("created InjectedRingBuffer %p\n", (void*)matchingInjectedRingBuffer); } matchingInjectedRingBuffer->parseData(packet); @@ -133,7 +132,6 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { && audioBuffer->hasStarted() && audioBuffer->isStarved()) { // this is an empty audio buffer that has starved, safe to delete delete audioBuffer; -printf("deleted injector %p\n", (void*)audioBuffer); i = _ringBuffers.erase(i); continue; } diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 47e55ddc90..a0a85f8888 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -622,7 +622,6 @@ void Audio::handleAudioInput() { SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); if (audioMixer && audioMixer->getActiveSocket()) { - MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition(); glm::quat headOrientation = interfaceAvatar->getHead()->getFinalOrientationInWorldFrame(); @@ -639,7 +638,6 @@ void Audio::handleAudioInput() { numAudioBytes = sizeof(int16_t); } else { numAudioBytes = numNetworkBytes; - //_isStereoInput ? NETWORK_BUFFER_LENGTH_BYTES_STEREO : NETWORK_BUFFER_LENGTH_BYTES_PER_CHANNEL; if (Menu::getInstance()->isOptionChecked(MenuOption::EchoServerAudio)) { packetType = PacketTypeMicrophoneAudioWithEcho; diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 8ce48a39e3..1b6bdaa5d8 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -124,13 +124,8 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { std::less less; std::less_equal lessEqual; - - // TODO: why is less(_endOfLastWrite, _nextOutput) a condition here? - if (_hasStarted - /* - && (less(_endOfLastWrite, _nextOutput) - && lessEqual(_nextOutput, shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy)))*/ - && samplesToCopy > _sampleCapacity - samplesAvailable()) { + + if (_hasStarted && samplesToCopy > _sampleCapacity - samplesAvailable()) { // this read will cross the next output, so call us starved and reset the buffer qDebug() << "Filled the ring buffer. Resetting."; _endOfLastWrite = _buffer; @@ -147,8 +142,7 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { } _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, samplesToCopy); - -printf("%p: writeData. %d samples available\n", (void*)this, samplesAvailable()); + return samplesToCopy * sizeof(int16_t); } @@ -162,7 +156,6 @@ const int16_t& AudioRingBuffer::operator[] (const int index) const { void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); -printf("%p: mixed. %d samples remaining\n", (void*)this, samplesAvailable()); } unsigned int AudioRingBuffer::samplesAvailable() const { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 3d2864f373..04cc67c8ac 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -74,14 +74,13 @@ public: bool hasStarted() const { return _hasStarted; } void addSilentFrame(int numSilentSamples); - protected: // disallow copying of AudioRingBuffer objects AudioRingBuffer(const AudioRingBuffer&); AudioRingBuffer& operator= (const AudioRingBuffer&); int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const; - + int _sampleCapacity; int _numFrameSamples; int16_t* _nextOutput; diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 03ac36d09e..7c2369d7e5 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -33,12 +33,9 @@ InterframeTimeGapHistory::InterframeTimeGapHistory() } void InterframeTimeGapHistory::frameReceived() { - - static quint64 gapsSum = 0; - quint64 now = usecTimestampNow(); - // make sure this isn't the first time frameReceived() is called, meaning there's actually a gap to calculate. + // make sure this isn't the first time frameReceived() is called so can actually calculate a gap. if (_lastFrameReceivedTime != 0) { quint64 gap = now - _lastFrameReceivedTime; @@ -123,7 +120,6 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { readBytes += sizeof(int16_t); if (numSilentSamples > 0) { - if (_currentJitterBufferFrames > _desiredJitterBufferFrames) { // our current jitter buffer size exceeds its desired value, so ignore some silent // frames to get that size as close to desired as possible @@ -140,7 +136,6 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { } else { // we need to drop all frames to get the jitter buffer close as possible to its desired length _currentJitterBufferFrames -= numSilentFrames; - printf("dropped silent packet! jbuffer size is now %d ###############\n", _currentJitterBufferFrames); } } else { addSilentFrame(numSilentSamples); @@ -148,8 +143,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { } } else { // there is audio data to read - int dataBytes = writeData(packet.data() + readBytes, packet.size() - readBytes); - readBytes += dataBytes; + readBytes += writeData(packet.data() + readBytes, packet.size() - readBytes); } return readBytes; } @@ -200,12 +194,7 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { int samplesPerFrame = getSamplesPerFrame(); int desiredJitterBufferSamples = _desiredJitterBufferFrames * samplesPerFrame; -//printf("\nsamples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); if (!isNotStarvedOrHasMinimumSamples(samplesPerFrame + desiredJitterBufferSamples)) { - -printf("\nMIXING DELAYED! waiting for jitter buffer to fill after being starved\n"); -printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - // if the buffer was starved, allow it to accrue at least the desired number of // jitter buffer frames before we start taking frames from it for mixing @@ -214,15 +203,10 @@ printf("samples available: %d frames available: %d\n", samplesAvailable(), samp } return false; - - } else if (samplesAvailable() < samplesPerFrame) { - -printf("\nMIXING DELAYED! jitter buffer is starved!!!\n"); -printf("samples available: %d frames available: %d\n", samplesAvailable(), samplesAvailable() / samplesPerFrame); - + } else if (samplesAvailable() < samplesPerFrame) { // if the buffer doesn't have a full frame of samples to take for mixing, it is starved _isStarved = true; - + // set to 0 to indicate the jitter buffer is starved _currentJitterBufferFrames = 0; @@ -230,19 +214,13 @@ printf("samples available: %d frames available: %d\n", samplesAvailable(), samp _shouldOutputStarveDebug = true; return false; - } -//printf("WILL MIX\n"); - + // good buffer, add this to the mix - if (_isStarved) { - // if this buffer has just finished replenishing after being starved, the number of frames in it now // minus one (since a frame will be read immediately after this) is the length of the jitter buffer _currentJitterBufferFrames = samplesAvailable() / samplesPerFrame - 1; - printf("jitter buffer length is now %d; desired: %d --------\n", _currentJitterBufferFrames, _desiredJitterBufferFrames); - _isStarved = false; } @@ -261,7 +239,5 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; } - - printf(" desired jbuffer size is now %d --------------------------------\n", _desiredJitterBufferFrames); } -} \ No newline at end of file +} diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 87a53038a3..5fa24ff782 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -43,7 +43,6 @@ private: bool _newWindowMaxGapAvailable; }; - class PositionalAudioRingBuffer : public AudioRingBuffer { public: enum Type { @@ -99,8 +98,6 @@ protected: InterframeTimeGapHistory _interframeTimeGapHistory; int _desiredJitterBufferFrames; int _currentJitterBufferFrames; - -quint64 _lastMixTime; }; #endif // hifi_PositionalAudioRingBuffer_h From 9a2197a23c8b3c1ab7f226cfc6cc231cffb0dc50 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:55:34 -0700 Subject: [PATCH 079/105] draw skeleton shapes at at correct positions --- interface/src/renderer/Model.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5dfdd3c801..3b5cda4fd2 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1098,9 +1098,10 @@ void Model::renderJointCollisionShapes(float alpha) { } glPushMatrix(); + // NOTE: the shapes are in the avatar local-frame if (shape->getType() == Shape::SPHERE_SHAPE) { // shapes are stored in world-frame, so we have to transform into model frame - glm::vec3 position = shape->getTranslation() - _translation; + glm::vec3 position = _rotation * shape->getTranslation(); glTranslatef(position.x, position.y, position.z); const glm::quat& rotation = shape->getRotation(); glm::vec3 axis = glm::axis(rotation); @@ -1115,7 +1116,7 @@ void Model::renderJointCollisionShapes(float alpha) { // draw a blue sphere at the capsule endpoint glm::vec3 endPoint; capsule->getEndPoint(endPoint); - endPoint = endPoint - _translation; + endPoint = _rotation * endPoint; glTranslatef(endPoint.x, endPoint.y, endPoint.z); glColor4f(0.6f, 0.6f, 0.8f, alpha); glutSolidSphere(capsule->getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS); @@ -1123,7 +1124,7 @@ void Model::renderJointCollisionShapes(float alpha) { // draw a yellow sphere at the capsule startpoint glm::vec3 startPoint; capsule->getStartPoint(startPoint); - startPoint = startPoint - _translation; + startPoint = _rotation * startPoint; glm::vec3 axis = endPoint - startPoint; glTranslatef(-axis.x, -axis.y, -axis.z); glColor4f(0.8f, 0.8f, 0.6f, alpha); From 8ad2a679c45cf62754a9e5aba19ad6fba632af31 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 11:16:18 -0700 Subject: [PATCH 080/105] move code to SkeletonModel::stepRagdollForward() --- interface/src/avatar/SkeletonModel.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index fd37409367..0cc80db539 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -102,12 +102,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } - if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - const int numStates = _jointStates.size(); - assert(numStates == _ragdollPoints.size()); - float fraction = 0.1f; // fraction = 0.1f left intentionally low for demo purposes - moveShapesTowardJoints(fraction); - } _boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset); _boundingShape.setRotation(_rotation); @@ -548,6 +542,9 @@ void SkeletonModel::buildRagdollConstraints() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { + const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.1f; + float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); + moveShapesTowardJoints(fraction); } float DENSITY_OF_WATER = 1000.0f; // kg/m^3 @@ -617,6 +614,8 @@ void SkeletonModel::buildShapes() { } void SkeletonModel::moveShapesTowardJoints(float fraction) { + const int numStates = _jointStates.size(); + assert(numStates == _ragdollPoints.size()); assert(fraction >= 0.0f && fraction <= 1.0f); if (_ragdollPoints.size() == _jointStates.size()) { float oneMinusFraction = 1.0f - fraction; From ffc20b8876e86fea8364a7535870ad9e8b4f8c6d Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 23 Jun 2014 11:22:04 -0700 Subject: [PATCH 081/105] changed InterframeTimeGapHistory to InterframeTimeGapStats --- assignment-client/src/audio/AvatarAudioRingBuffer.cpp | 2 +- libraries/audio/src/InjectedAudioRingBuffer.cpp | 2 +- libraries/audio/src/PositionalAudioRingBuffer.cpp | 10 +++++----- libraries/audio/src/PositionalAudioRingBuffer.h | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 0df50f99db..79ad7ae411 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -19,7 +19,7 @@ AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : } int AvatarAudioRingBuffer::parseData(const QByteArray& packet) { - _interframeTimeGapHistory.frameReceived(); + _interframeTimeGapStats.frameReceived(); updateDesiredJitterBufferFrames(); _shouldLoopbackForNode = (packetTypeForPacket(packet) == PacketTypeMicrophoneAudioWithEcho); diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index 80bcda5acb..ed0476f6bd 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -31,7 +31,7 @@ InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) const uchar MAX_INJECTOR_VOLUME = 255; int InjectedAudioRingBuffer::parseData(const QByteArray& packet) { - _interframeTimeGapHistory.frameReceived(); + _interframeTimeGapStats.frameReceived(); updateDesiredJitterBufferFrames(); // setup a data stream to read from this packet diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 7c2369d7e5..a22f57f3d7 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -21,7 +21,7 @@ #include "PositionalAudioRingBuffer.h" #include "SharedUtil.h" -InterframeTimeGapHistory::InterframeTimeGapHistory() +InterframeTimeGapStats::InterframeTimeGapStats() : _lastFrameReceivedTime(0), _numSamplesInCurrentInterval(0), _currentIntervalMaxGap(0), @@ -32,7 +32,7 @@ InterframeTimeGapHistory::InterframeTimeGapHistory() memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW*sizeof(quint64)); } -void InterframeTimeGapHistory::frameReceived() { +void InterframeTimeGapStats::frameReceived() { quint64 now = usecTimestampNow(); // make sure this isn't the first time frameReceived() is called so can actually calculate a gap. @@ -79,7 +79,7 @@ void InterframeTimeGapHistory::frameReceived() { _lastFrameReceivedTime = now; } -quint64 InterframeTimeGapHistory::getWindowMaxGap() { +quint64 InterframeTimeGapStats::getWindowMaxGap() { _newWindowMaxGapAvailable = false; return _windowMaxGap; } @@ -234,8 +234,8 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; - if (_interframeTimeGapHistory.hasNewWindowMaxGapAvailable()) { - _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapHistory.getWindowMaxGap() / USECS_PER_FRAME); + if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { + _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 5fa24ff782..8c2122f29e 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -23,9 +23,9 @@ const int TIME_GAP_NUM_SAMPLES_IN_INTERVAL = 500; const int TIME_GAP_NUM_INTERVALS_IN_WINDOW = 10; // class used to track time between incoming frames for the purpose of varying the jitter buffer length -class InterframeTimeGapHistory { +class InterframeTimeGapStats { public: - InterframeTimeGapHistory(); + InterframeTimeGapStats(); void frameReceived(); bool hasNewWindowMaxGapAvailable() const { return _newWindowMaxGapAvailable; } @@ -95,7 +95,7 @@ protected: float _nextOutputTrailingLoudness; AABox* _listenerUnattenuatedZone; - InterframeTimeGapHistory _interframeTimeGapHistory; + InterframeTimeGapStats _interframeTimeGapStats; int _desiredJitterBufferFrames; int _currentJitterBufferFrames; }; From fbdca59d3777a9b9561cd212e8644f14fd5bce43 Mon Sep 17 00:00:00 2001 From: wangyix Date: Mon, 23 Jun 2014 11:56:14 -0700 Subject: [PATCH 082/105] fixed spacing issue --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index a22f57f3d7..0e2e785973 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -29,7 +29,7 @@ InterframeTimeGapStats::InterframeTimeGapStats() _windowMaxGap(0), _newWindowMaxGapAvailable(false) { - memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW*sizeof(quint64)); + memset(_intervalMaxGaps, 0, TIME_GAP_NUM_INTERVALS_IN_WINDOW * sizeof(quint64)); } void InterframeTimeGapStats::frameReceived() { From 9713e3d5136b29d8a8ee6282e499d3d7be4d0a91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 12:30:53 -0700 Subject: [PATCH 083/105] remove some unused var warnings in Release build --- interface/src/avatar/SkeletonModel.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0cc80db539..0baf123829 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -523,8 +523,7 @@ void SkeletonModel::buildRagdollConstraints() { // NOTE: the length of DistanceConstraints is computed and locked in at this time // so make sure the ragdoll positions are in a normal configuration before here. const int numPoints = _ragdollPoints.size(); - const int numJoints = _jointStates.size(); - assert(numPoints == numJoints); + assert(numPoints == _jointStates.size()); for (int i = 0; i < numPoints; ++i) { const JointState& state = _jointStates.at(i); @@ -615,9 +614,9 @@ void SkeletonModel::buildShapes() { void SkeletonModel::moveShapesTowardJoints(float fraction) { const int numStates = _jointStates.size(); - assert(numStates == _ragdollPoints.size()); + assert(_jointStates.size() == _ragdollPoints.size()); assert(fraction >= 0.0f && fraction <= 1.0f); - if (_ragdollPoints.size() == _jointStates.size()) { + if (_ragdollPoints.size() == numStates) { float oneMinusFraction = 1.0f - fraction; int numJoints = _jointStates.size(); for (int i = 0; i < numJoints; ++i) { From add556719c4a6d37259605fadd8a2bdbc6cfdca9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 12:31:36 -0700 Subject: [PATCH 084/105] fixed perf stats on phys simulation --- interface/src/avatar/MyAvatar.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6fee71b27c..a90a65b5ed 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -195,12 +195,16 @@ void MyAvatar::simulate(float deltaTime) { head->simulate(deltaTime, true); } - if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { - PerformanceTimer perfTimer("MyAvatar::simulate/head Simulate"); - const int minError = 0.005f; - const float maxIterations = 8; - const quint64 maxUsec = 2000; - _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); + { + PerformanceTimer perfTimer("MyAvatar::simulate/ragdoll"); + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) { + const int minError = 0.01f; + const float maxIterations = 10; + const quint64 maxUsec = 2000; + _physicsSimulation.stepForward(deltaTime, minError, maxIterations, maxUsec); + } else { + _skeletonModel.moveShapesTowardJoints(1.0f); + } } // now that we're done stepping the avatar forward in time, compute new collisions From 3e0673418f61dd20e0163650c8b09ebc84f77fc8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 12:32:23 -0700 Subject: [PATCH 085/105] more complete physics iteration for stability --- libraries/shared/src/PhysicsSimulation.cpp | 54 +++++++++++++--------- libraries/shared/src/PhysicsSimulation.h | 1 - 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index ce47a189d0..637a5e955c 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -10,6 +10,7 @@ // #include +#include #include "PhysicsSimulation.h" @@ -145,15 +146,41 @@ void PhysicsSimulation::removeRagdoll(Ragdoll* doll) { // (10b) figure out how to slave dupe JointStates to physical shapes // (11) add and enforce angular contraints for joints void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec) { - quint64 startTime = usecTimestampNow(); + quint64 now = usecTimestampNow(); + quint64 startTime = now; quint64 expiry = startTime + maxUsec; moveRagdolls(deltaTime); - computeCollisions(); - processCollisions(); - enforceConstraints(minError, maxIterations, expiry - usecTimestampNow()); + int numDolls = _dolls.size(); + _numCollisions = 0; + int iterations = 0; + float error = 0.0f; + do { + computeCollisions(); + processCollisions(); + + // enforce constraints + error = 0.0f; + for (int i = 0; i < numDolls; ++i) { + error = glm::max(error, _dolls[i]->enforceRagdollConstraints()); + } + ++iterations; + + now = usecTimestampNow(); + } while (_numCollisions != 0 && (iterations < maxIterations) && (error > minError) && (now < expiry)); + + _numIterations = iterations; + _constraintError = error; _stepTime = usecTimestampNow()- startTime; + +#ifdef ANDREW_DEBUG + // temporary debug info for watching simulation performance + static int adebug = 0; ++adebug; + if (0 == (adebug % 100)) { + std::cout << "adebug Ni = " << _numIterations << " E = " << error << " t = " << _stepTime << std::endl; // adebug + } +#endif // ANDREW_DEBUG } void PhysicsSimulation::moveRagdolls(float deltaTime) { @@ -215,22 +242,3 @@ void PhysicsSimulation::processCollisions() { ++shapeItr; } } - -void PhysicsSimulation::enforceConstraints(float minError, int maxIterations, quint64 maxUsec) { - quint64 now = usecTimestampNow(); - quint64 expiry = now + maxUsec; - int iterations = 0; - float error = 0.0f; - int numDolls = _dolls.size(); - do { - error = 0.0f; - for (int i = 0; i < numDolls; ++i) { - error = glm::max(error, _dolls[i]->enforceRagdollConstraints()); - } - ++iterations; - now = usecTimestampNow(); - } while (iterations < maxIterations && error > minError && now < expiry); - _numIterations = iterations; - _constraintError = error; -} - diff --git a/libraries/shared/src/PhysicsSimulation.h b/libraries/shared/src/PhysicsSimulation.h index 9f8ffa4486..c611e06870 100644 --- a/libraries/shared/src/PhysicsSimulation.h +++ b/libraries/shared/src/PhysicsSimulation.h @@ -44,7 +44,6 @@ public: void moveRagdolls(float deltaTime); void computeCollisions(); void processCollisions(); - void enforceConstraints(float minError, int maxIterations, quint64 maxUsec); private: CollisionList _collisionList; From b5aca45ab681b78be5073f2571b05c52741a0ef7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 12:59:38 -0700 Subject: [PATCH 086/105] smaller timescale for slaving shapes to skeleton --- interface/src/avatar/SkeletonModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0baf123829..fdb3ce03d8 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -541,7 +541,7 @@ void SkeletonModel::buildRagdollConstraints() { // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { - const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.1f; + const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f; float fraction = glm::clamp(deltaTime / RAGDOLL_FOLLOWS_JOINTS_TIMESCALE, 0.0f, 1.0f); moveShapesTowardJoints(fraction); } From ed5c05a61b9536d98bedd85d0d2b080667bfa4e0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 23 Jun 2014 13:25:32 -0700 Subject: [PATCH 087/105] fix rounding of credit balance in application title --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75c3f442ca..d7e3b7453d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3189,7 +3189,7 @@ void Application::updateWindowTitle(){ float creditBalance = accountManager.getAccountInfo().getBalance() / SATOSHIS_PER_CREDIT; QString creditBalanceString; - creditBalanceString.sprintf("%.8f", creditBalance); + creditBalanceString.sprintf("%.8f", floor(creditBalance + 0.5)); title += " - ₵" + creditBalanceString; } From 415dfd98aad481fc91a5af0645abe85adf365b5e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 13:40:50 -0700 Subject: [PATCH 088/105] remove one line of cuft --- interface/src/avatar/SkeletonModel.h | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 57e039c3cc..b91c112b6a 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -98,7 +98,6 @@ public: virtual void stepRagdollForward(float deltaTime); void moveShapesTowardJoints(float fraction); - //void updateShapePositionsLegacy(); // TODO: Andrew to remove this when done with ragdoll work void computeBoundingShape(const FBXGeometry& geometry); void renderBoundingCollisionShapes(float alpha); From e2204ab78e9a22058a3e52be94d703b133d3a5cb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 13:59:44 -0700 Subject: [PATCH 089/105] friend foo --> friend class foo --- libraries/shared/src/PhysicsEntity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/PhysicsEntity.h b/libraries/shared/src/PhysicsEntity.h index 693c712c11..3407ac8421 100644 --- a/libraries/shared/src/PhysicsEntity.h +++ b/libraries/shared/src/PhysicsEntity.h @@ -71,7 +71,7 @@ protected: private: // PhysicsSimulation is a friend so that it can set the protected _simulation backpointer - friend PhysicsSimulation; + friend class PhysicsSimulation; PhysicsSimulation* _simulation; }; From df740dd82a57e80213a13f17aeb77c379f6a6abb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 14:07:09 -0700 Subject: [PATCH 090/105] Fix bad static const inside class. --- libraries/shared/src/CollisionInfo.cpp | 2 +- libraries/shared/src/Ragdoll.cpp | 2 +- libraries/shared/src/Shape.h | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp index c76c99e747..e862a22f4a 100644 --- a/libraries/shared/src/CollisionInfo.cpp +++ b/libraries/shared/src/CollisionInfo.cpp @@ -37,7 +37,7 @@ void CollisionInfo::apply() { // NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients Shape* shapeA = const_cast(_shapeA); float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint); - float massB = Shape::MAX_MASS; + float massB = MAX_SHAPE_MASS; float totalMass = massA + massB; if (_shapeB) { Shape* shapeB = const_cast(_shapeB); diff --git a/libraries/shared/src/Ragdoll.cpp b/libraries/shared/src/Ragdoll.cpp index 833bcdb607..1d24e74864 100644 --- a/libraries/shared/src/Ragdoll.cpp +++ b/libraries/shared/src/Ragdoll.cpp @@ -49,7 +49,7 @@ float FixedConstraint::enforce() { void FixedConstraint::setPoint(VerletPoint* point) { assert(point); _point = point; - _point->_mass = Shape::MAX_MASS; + _point->_mass = MAX_SHAPE_MASS; } void FixedConstraint::setAnchor(const glm::vec3& anchor) { diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h index 8d4c1898b5..09ed30a116 100644 --- a/libraries/shared/src/Shape.h +++ b/libraries/shared/src/Shape.h @@ -17,9 +17,10 @@ class PhysicsEntity; +const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) + class Shape { public: - static const float MAX_MASS = 1.0e18f; // something less than sqrt(FLT_MAX) enum Type{ UNKNOWN_SHAPE = 0, @@ -29,7 +30,7 @@ public: LIST_SHAPE }; - Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation(), _mass(MAX_MASS) { } + Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation(), _mass(MAX_SHAPE_MASS) { } virtual ~Shape() {} int getType() const { return _type; } From d892a9c00fd04ec8972b0422b7833fdb2373fc18 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 14:07:31 -0700 Subject: [PATCH 091/105] fixed warning about possible uninitialized variable. --- libraries/fbx/src/FBXReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 9aeb81a2a3..33b7fca0ef 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1717,7 +1717,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 boneEnd = extractTranslation(transformJointToMesh); glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; - float boneLength; + float boneLength = 0.0f; if (joint.parentIndex != -1) { boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform); boneDirection = boneEnd - boneBegin; @@ -1779,7 +1779,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) glm::vec3 boneBegin = boneEnd; glm::vec3 boneDirection; - float boneLength; + float boneLength = 0.0f; if (joint.parentIndex != -1) { boneBegin = extractTranslation(inverseModelTransform * geometry.joints[joint.parentIndex].bindTransform); boneDirection = boneEnd - boneBegin; From 48e779cf8e02f259c2ce3fca95f9312e42e8c707 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 14:08:47 -0700 Subject: [PATCH 092/105] added some performance tests --- tests/octree/CMakeLists.txt | 15 ++- tests/octree/src/ModelTests.cpp | 226 ++++++++++++++++++++++++++++++++ tests/octree/src/ModelTests.h | 20 +++ tests/octree/src/main.cpp | 2 + 4 files changed, 258 insertions(+), 5 deletions(-) create mode 100644 tests/octree/src/ModelTests.cpp create mode 100644 tests/octree/src/ModelTests.h diff --git a/tests/octree/CMakeLists.txt b/tests/octree/CMakeLists.txt index cbdfd02054..1697064ff4 100644 --- a/tests/octree/CMakeLists.txt +++ b/tests/octree/CMakeLists.txt @@ -12,9 +12,9 @@ set(MACRO_DIR ${ROOT_DIR}/cmake/macros) # setup for find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") -#find_package(Qt5Network REQUIRED) -#find_package(Qt5Script REQUIRED) -#find_package(Qt5Widgets REQUIRED) +find_package(Qt5Network REQUIRED) +find_package(Qt5Script REQUIRED) +find_package(Qt5Widgets REQUIRED) include(${MACRO_DIR}/SetupHifiProject.cmake) setup_hifi_project(${TARGET_NAME} TRUE) @@ -22,7 +22,7 @@ setup_hifi_project(${TARGET_NAME} TRUE) include(${MACRO_DIR}/AutoMTC.cmake) auto_mtc(${TARGET_NAME} ${ROOT_DIR}) -#qt5_use_modules(${TARGET_NAME} Network Script Widgets) +qt5_use_modules(${TARGET_NAME} Network Script Widgets) #include glm include(${MACRO_DIR}/IncludeGLM.cmake) @@ -30,8 +30,13 @@ include_glm(${TARGET_NAME} ${ROOT_DIR}) # link in the shared libraries include(${MACRO_DIR}/LinkHifiLibrary.cmake) -link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(animation ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(audio ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(fbx ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(networking ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(models ${TARGET_NAME} ${ROOT_DIR}) link_hifi_library(octree ${TARGET_NAME} ${ROOT_DIR}) +link_hifi_library(shared ${TARGET_NAME} ${ROOT_DIR}) IF (WIN32) #target_link_libraries(${TARGET_NAME} Winmm Ws2_32) diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp new file mode 100644 index 0000000000..391b54cea9 --- /dev/null +++ b/tests/octree/src/ModelTests.cpp @@ -0,0 +1,226 @@ +// +// ModelTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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 +// +// TODO: +// * need to add expected results and accumulation of test success/failure +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ModelTests.h" + + +void ModelTests::modelTreeTests(bool verbose) { + int testsTaken = 0; + int testsPassed = 0; + int testsFailed = 0; + + if (verbose) { + qDebug() << "******************************************************************************************"; + } + + qDebug() << "ModelTests::modelTreeTests()"; + + // Tree, id, and model properties used in many tests below... + ModelTree tree; + uint32_t id = 1; + ModelItemID modelID(id); + modelID.isKnownID = false; // this is a temporary workaround to allow local tree models to be added with known IDs + ModelItemProperties properties; + float oneMeter = 1.0f; + float halfMeter = oneMeter / 2.0f; + float halfOfDomain = TREE_SCALE * 0.5f; + glm::vec3 positionNearOriginInMeters(oneMeter, oneMeter, oneMeter); // when using properties, these are in meter not tree units + glm::vec3 positionAtCenterInMeters(halfOfDomain, halfOfDomain, halfOfDomain); + glm::vec3 positionNearOriginInTreeUnits = positionNearOriginInMeters / (float)TREE_SCALE; + glm::vec3 positionAtCenterInTreeUnits = positionAtCenterInMeters / (float)TREE_SCALE; + + { + testsTaken++; + QString testName = "add model to tree and search"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + properties.setPosition(positionAtCenterInMeters); + properties.setRadius(halfMeter); + properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx"); + + tree.addModel(modelID, properties); + + float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units + const ModelItem* foundModelByRadius = tree.findClosestModel(positionAtCenterInTreeUnits, targetRadius); + const ModelItem* foundModelByID = tree.findModelByID(id); + + if (verbose) { + qDebug() << "foundModelByRadius=" << foundModelByRadius; + qDebug() << "foundModelByID=" << foundModelByID; + } + + bool passed = foundModelByRadius && foundModelByID && (foundModelByRadius == foundModelByID); + if (passed) { + testsPassed++; + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + } + } + + modelID.isKnownID = true; // this is a temporary workaround to allow local tree models to be added with known IDs + + { + testsTaken++; + QString testName = "change position of model in tree"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + glm::vec3 newPosition = positionNearOriginInMeters; + + properties.setPosition(newPosition); + + tree.updateModel(modelID, properties); + + float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units + const ModelItem* foundModelByRadius = tree.findClosestModel(positionNearOriginInTreeUnits, targetRadius); + const ModelItem* foundModelByID = tree.findModelByID(id); + + if (verbose) { + qDebug() << "foundModelByRadius=" << foundModelByRadius; + qDebug() << "foundModelByID=" << foundModelByID; + } + + // NOTE: This test is currently expected to fail in the production code. There's a bug in ModelTree::updateModel() + // that does not update the actual location of the model into the correct element when modified locally. So this + // test will fail. There's a new optimized and correctly working version of updateModel() that fixes this problem. + bool passed = foundModelByRadius && foundModelByID && (foundModelByRadius == foundModelByID); + if (passed) { + testsPassed++; + qDebug() << "NOTE: Expected to FAIL - Test" << testsTaken <<":" << qPrintable(testName); + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + qDebug() << "NOTE: Expected to FAIL - Test" << testsTaken <<":" << qPrintable(testName); + } + } + + { + testsTaken++; + QString testName = "change position of model in tree back to center"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + glm::vec3 newPosition = positionAtCenterInMeters; + + properties.setPosition(newPosition); + + tree.updateModel(modelID, properties); + + float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units + const ModelItem* foundModelByRadius = tree.findClosestModel(positionAtCenterInTreeUnits, targetRadius); + const ModelItem* foundModelByID = tree.findModelByID(id); + + if (verbose) { + qDebug() << "foundModelByRadius=" << foundModelByRadius; + qDebug() << "foundModelByID=" << foundModelByID; + } + + bool passed = foundModelByRadius && foundModelByID && (foundModelByRadius == foundModelByID); + if (passed) { + testsPassed++; + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + } + } + + { + testsTaken++; + QString testName = "Performance - findClosestModel() 1,000,000 times"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units + const int TEST_ITERATIONS = 1000000; + quint64 start = usecTimestampNow(); + const ModelItem* foundModelByRadius = NULL; + for (int i = 0; i < TEST_ITERATIONS; i++) { + foundModelByRadius = tree.findClosestModel(positionAtCenterInTreeUnits, targetRadius); + } + quint64 end = usecTimestampNow(); + + if (verbose) { + qDebug() << "foundModelByRadius=" << foundModelByRadius; + } + + bool passed = foundModelByRadius; + if (passed) { + testsPassed++; + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + } + float USECS_PER_MSECS = 1000.0f; + float elapsedInMSecs = (float)(end - start) / USECS_PER_MSECS; + qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs"; + } + + { + testsTaken++; + QString testName = "Performance - findModelByID() 1,000,000 times"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + const int TEST_ITERATIONS = 1000000; + quint64 start = usecTimestampNow(); + const ModelItem* foundModelByID = NULL; + for (int i = 0; i < TEST_ITERATIONS; i++) { + foundModelByID = tree.findModelByID(id); + } + quint64 end = usecTimestampNow(); + + if (verbose) { + qDebug() << "foundModelByID=" << foundModelByID; + } + + bool passed = foundModelByID; + if (passed) { + testsPassed++; + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + } + float USECS_PER_MSECS = 1000.0f; + float elapsedInMSecs = (float)(end - start) / USECS_PER_MSECS; + qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs"; + } + + qDebug() << " tests passed:" << testsPassed << "out of" << testsTaken; + if (verbose) { + qDebug() << "******************************************************************************************"; + } +} + + +void ModelTests::runAllTests(bool verbose) { + modelTreeTests(verbose); +} + diff --git a/tests/octree/src/ModelTests.h b/tests/octree/src/ModelTests.h new file mode 100644 index 0000000000..dd764edf9d --- /dev/null +++ b/tests/octree/src/ModelTests.h @@ -0,0 +1,20 @@ +// +// ModelTests.h +// tests/octree/src +// +// Created by Brad Hefta-Gaub on 06/04/2014. +// 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_ModelTests_h +#define hifi_ModelTests_h + +namespace ModelTests { + void modelTreeTests(bool verbose = false); + void runAllTests(bool verbose = false); +} + +#endif // hifi_ModelTests_h diff --git a/tests/octree/src/main.cpp b/tests/octree/src/main.cpp index de7b3926ae..590df268c3 100644 --- a/tests/octree/src/main.cpp +++ b/tests/octree/src/main.cpp @@ -8,11 +8,13 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ModelTests.h" #include "OctreeTests.h" #include "AABoxCubeTests.h" int main(int argc, char** argv) { OctreeTests::runAllTests(); AABoxCubeTests::runAllTests(); + ModelTests::runAllTests(true); return 0; } From 52b61ebab8f05b72d6ff5f76c0929596cfccfa24 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 14:48:12 -0700 Subject: [PATCH 093/105] added addModel() performance test --- tests/octree/src/ModelTests.cpp | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp index 391b54cea9..92b0b87cd8 100644 --- a/tests/octree/src/ModelTests.cpp +++ b/tests/octree/src/ModelTests.cpp @@ -213,6 +213,45 @@ void ModelTests::modelTreeTests(bool verbose) { qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs"; } + { + testsTaken++; + QString testName = "Performance - add model to tree 10,000 times"; + if (verbose) { + qDebug() << "Test" << testsTaken <<":" << qPrintable(testName); + } + + const int TEST_ITERATIONS = 10000; + quint64 start = usecTimestampNow(); + for (int i = 0; i < TEST_ITERATIONS; i++) { + uint32_t id = i + 2; // make sure it doesn't collide with previous model ids + ModelItemID modelID(id); + modelID.isKnownID = false; // this is a temporary workaround to allow local tree models to be added with known IDs + + float randomX = randFloatInRange(0.0f ,(float)TREE_SCALE); + float randomY = randFloatInRange(0.0f ,(float)TREE_SCALE); + float randomZ = randFloatInRange(0.0f ,(float)TREE_SCALE); + glm::vec3 randomPositionInMeters(randomX,randomY,randomZ); + + properties.setPosition(randomPositionInMeters); + properties.setRadius(halfMeter); + properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx"); + + tree.addModel(modelID, properties); + } + quint64 end = usecTimestampNow(); + + bool passed = true; + if (passed) { + testsPassed++; + } else { + testsFailed++; + qDebug() << "FAILED - Test" << testsTaken <<":" << qPrintable(testName); + } + float USECS_PER_MSECS = 1000.0f; + float elapsedInMSecs = (float)(end - start) / USECS_PER_MSECS; + qDebug() << "TIME - Test" << testsTaken <<":" << qPrintable(testName) << "elapsed=" << elapsedInMSecs << "msecs"; + } + qDebug() << " tests passed:" << testsPassed << "out of" << testsTaken; if (verbose) { qDebug() << "******************************************************************************************"; From 076bc115456ba43e585ea8cf2a81dd14386318ba Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 15:19:13 -0700 Subject: [PATCH 094/105] added server side audio mixer jitter stats --- assignment-client/src/audio/AudioMixer.cpp | 13 +++++++++++ .../src/audio/AudioMixerClientData.cpp | 23 +++++++++++++++++++ .../src/audio/AudioMixerClientData.h | 3 +++ .../audio/src/PositionalAudioRingBuffer.h | 3 +++ 4 files changed, 42 insertions(+) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 42be1aea5a..15b51ae7d5 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -424,6 +424,19 @@ void AudioMixer::sendStatsPacket() { } else { statsObject["average_mixes_per_listener"] = 0.0; } + + // add stats for each listerner + NodeList* nodeList = NodeList::getInstance(); + int clientNumber = 0; + foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { + clientNumber++; + AudioMixerClientData* clientData = static_cast(node->getLinkedData()); + if (clientData) { + QString property = "jitterStats." + QString::number(clientNumber); + statsObject[qPrintable(property)] = clientData->getJitterBufferStats(); + } + } + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 0c41cc70f9..1c0f68d5a3 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -138,3 +138,26 @@ void AudioMixerClientData::pushBuffersAfterFrameSend() { i++; } } + +QString AudioMixerClientData::getJitterBufferStats() const { + QString result; + AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); + if (avatarRingBuffer) { + int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames(); + int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames(); + result += "mic.desired:" + QString::number(desiredJitterBuffer) + " current:" + QString::number(currentJitterBuffer); + } else { + result = "mic unknown"; + } + + for (int i = 0; i < _ringBuffers.size(); i++) { + if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector) { + int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames(); + int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames(); + result += "| injected["+QString::number(i)+"].desired:" + + QString::number(desiredJitterBuffer) + " current:" + QString::number(currentJitterBuffer); + } + } + + return result; +} diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 3c4ddd3459..1760b96f5d 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -29,6 +29,9 @@ public: int parseData(const QByteArray& packet); void checkBuffersBeforeFrameSend(AABox* checkSourceZone = NULL, AABox* listenerZone = NULL); void pushBuffersAfterFrameSend(); + + QString getJitterBufferStats() const; + private: QList _ringBuffers; }; diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 8c2122f29e..4c7148abbe 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -77,6 +77,9 @@ public: int getSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } + int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; } + int getCurrentJitterBufferFrames() const { return _currentJitterBufferFrames; } + protected: // disallow copying of PositionalAudioRingBuffer objects PositionalAudioRingBuffer(const PositionalAudioRingBuffer&); From b2c982976e58652480c363f5ab12a096dd7a947b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 15:46:58 -0700 Subject: [PATCH 095/105] break up audio mixer stats across multiple packets --- assignment-client/src/audio/AudioMixer.cpp | 38 ++++++++++++++----- .../audio/src/PositionalAudioRingBuffer.cpp | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 15b51ae7d5..ccf7a3248d 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -425,7 +425,20 @@ void AudioMixer::sendStatsPacket() { statsObject["average_mixes_per_listener"] = 0.0; } - // add stats for each listerner + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); + _sumListeners = 0; + _sumMixes = 0; + _numStatFrames = 0; + + + // NOTE: These stats can be too large to fit in an MTU, so we break it up into multiple packts... + QJsonObject statsObject2; + + // add stats for each listerner + bool somethingToSend = false; + int sizeOfStats = 0; + int TOO_BIG_FOR_MTU = 1200; // some extra space for JSONification + NodeList* nodeList = NodeList::getInstance(); int clientNumber = 0; foreach (const SharedNodePointer& node, nodeList->getNodeHash()) { @@ -433,16 +446,23 @@ void AudioMixer::sendStatsPacket() { AudioMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { QString property = "jitterStats." + QString::number(clientNumber); - statsObject[qPrintable(property)] = clientData->getJitterBufferStats(); + QString value = clientData->getJitterBufferStats(); + statsObject2[qPrintable(property)] = value; + somethingToSend = true; + sizeOfStats += property.size() + value.size(); + } + + // if we're too large, send the packet + if (sizeOfStats > TOO_BIG_FOR_MTU) { + nodeList->sendStatsToDomainServer(statsObject2); + sizeOfStats = 0; + statsObject2 = QJsonObject(); // clear it } } - - - ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject); - - _sumListeners = 0; - _sumMixes = 0; - _numStatFrames = 0; + + if (somethingToSend) { + nodeList->sendStatsToDomainServer(statsObject2); + } } void AudioMixer::run() { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 0e2e785973..258150a1c5 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -235,6 +235,7 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { + _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; From 4becb66e2a307b0ac8c555f61ea38cf197270033 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 16:10:51 -0700 Subject: [PATCH 096/105] use node uuid in stats --- assignment-client/src/audio/AudioMixer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index ccf7a3248d..1d0b51a437 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -445,7 +445,7 @@ void AudioMixer::sendStatsPacket() { clientNumber++; AudioMixerClientData* clientData = static_cast(node->getLinkedData()); if (clientData) { - QString property = "jitterStats." + QString::number(clientNumber); + QString property = "jitterStats." + node->getUUID().toString(); QString value = clientData->getJitterBufferStats(); statsObject2[qPrintable(property)] = value; somethingToSend = true; @@ -457,6 +457,7 @@ void AudioMixer::sendStatsPacket() { nodeList->sendStatsToDomainServer(statsObject2); sizeOfStats = 0; statsObject2 = QJsonObject(); // clear it + somethingToSend = false; } } From 8a633743b3be83d5eb6320246d33a6fb72bd2e5e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 16:40:38 -0700 Subject: [PATCH 097/105] ignore the numSilentSamples from client, always assume one frame --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 258150a1c5..115a99b453 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -119,6 +119,9 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { readBytes += sizeof(int16_t); + // NOTE: fixes a bug in old clients that would send garbage for their number of silentSamples + numSilentSamples = getNumSamplesPerFrame(); + if (numSilentSamples > 0) { if (_currentJitterBufferFrames > _desiredJitterBufferFrames) { // our current jitter buffer size exceeds its desired value, so ignore some silent From 77f262561c0087e34a15a418b9ae2c2e5c5425e4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 16:47:10 -0700 Subject: [PATCH 098/105] typo --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 115a99b453..a2e0d08b91 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -120,7 +120,7 @@ int PositionalAudioRingBuffer::parseData(const QByteArray& packet) { readBytes += sizeof(int16_t); // NOTE: fixes a bug in old clients that would send garbage for their number of silentSamples - numSilentSamples = getNumSamplesPerFrame(); + numSilentSamples = getSamplesPerFrame(); if (numSilentSamples > 0) { if (_currentJitterBufferFrames > _desiredJitterBufferFrames) { From 6434c0a2a49177e159d1df92dae7fe8069fcdf57 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 17:28:39 -0700 Subject: [PATCH 099/105] add more stats --- .../src/audio/AudioMixerClientData.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 1c0f68d5a3..cde1377053 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -145,7 +145,12 @@ QString AudioMixerClientData::getJitterBufferStats() const { if (avatarRingBuffer) { int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames(); int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames(); - result += "mic.desired:" + QString::number(desiredJitterBuffer) + " current:" + QString::number(currentJitterBuffer); + int samplesAvailable = avatarRingBuffer->samplesAvailable(); + int framesAvailable = (samplesAvailable / avatarRingBuffer->getSamplesPerFrame()); + result += "mic.desired:" + QString::number(desiredJitterBuffer) + + " current:" + QString::number(currentJitterBuffer) + + " available:" + QString::number(framesAvailable) + + " samples:" + QString::number(samplesAvailable); } else { result = "mic unknown"; } @@ -154,8 +159,12 @@ QString AudioMixerClientData::getJitterBufferStats() const { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector) { int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames(); int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames(); - result += "| injected["+QString::number(i)+"].desired:" - + QString::number(desiredJitterBuffer) + " current:" + QString::number(currentJitterBuffer); + int samplesAvailable = _ringBuffers[i]->samplesAvailable(); + int framesAvailable = (samplesAvailable / _ringBuffers[i]->getSamplesPerFrame()); + result += "| injected["+QString::number(i)+"].desired:" + QString::number(desiredJitterBuffer) + + " current:" + QString::number(currentJitterBuffer) + + " available:" + QString::number(framesAvailable) + + " samples:" + QString::number(samplesAvailable); } } From 355ab2cb957bd628de25139ced9e1a5fe9feb2e7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 17:58:55 -0700 Subject: [PATCH 100/105] max desired must be one less than the max frames in our ringbuffer --- libraries/audio/src/AudioRingBuffer.cpp | 3 --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 1b6bdaa5d8..71fd2fe19d 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -122,9 +122,6 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { int samplesToCopy = std::min((quint64)(maxSize / sizeof(int16_t)), (quint64)_sampleCapacity); - std::less less; - std::less_equal lessEqual; - if (_hasStarted && samplesToCopy > _sampleCapacity - samplesAvailable()) { // this read will cross the next output, so call us starved and reset the buffer qDebug() << "Filled the ring buffer. Resetting."; diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index a2e0d08b91..2ba407242c 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -243,5 +243,9 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; } + const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1; + if (_desiredJitterBufferFrames > maxDesired) { + _desiredJitterBufferFrames = maxDesired; + } } } From fda60cc8d5ea957d91d4bbca28aa37945fc8e75e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Jun 2014 18:45:24 -0700 Subject: [PATCH 101/105] remove resizing of jitter buffers for now, only ask for 1 frame --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 2ba407242c..5b6d5138d1 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -234,11 +234,12 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { } void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { - - const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; - if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { + _desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence + /* + const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; + _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); if (_desiredJitterBufferFrames < 1) { _desiredJitterBufferFrames = 1; @@ -247,5 +248,6 @@ void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_desiredJitterBufferFrames > maxDesired) { _desiredJitterBufferFrames = maxDesired; } + */ } } From c40c5d78c8db6e6d7e5ef0312ece8e830e89814e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 24 Jun 2014 08:50:39 -0700 Subject: [PATCH 102/105] Fix default audio devices on Windows Windows 8 provides the full friendly device name, not just the first 31 characters. --- interface/src/Audio.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index a0a85f8888..65912f83e8 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -218,9 +218,14 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); pPropertyStore = NULL; - //QAudio devices seems to only take the 31 first characters of the Friendly Device Name. - const DWORD QT_WIN_MAX_AUDIO_DEVICENAME_LEN = 31; - deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal).left(QT_WIN_MAX_AUDIO_DEVICENAME_LEN); + deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); + const DWORD WINDOWS7_MAJOR_VERSION = 6; + const DWORD WINDOWS7_MINOR_VERSION = 1; + if (osvi.dwMajorVersion <= WINDOWS7_MAJOR_VERSION && osvi.dwMinorVersion <= WINDOWS7_MINOR_VERSION) { + // Windows 7 provides only the 31 first characters of the device name. + const DWORD QT_WIN7_MAX_AUDIO_DEVICENAME_LEN = 31; + deviceName = deviceName.left(QT_WIN7_MAX_AUDIO_DEVICENAME_LEN); + } qDebug() << (mode == QAudio::AudioOutput ? "output" : "input") << " device:" << deviceName; PropVariantClear(&pv); } From 5c188ea81e7c14507282bce8ef2931a55c3415c0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 09:41:29 -0700 Subject: [PATCH 103/105] adding more stats --- assignment-client/src/audio/AudioMixerClientData.cpp | 4 ++++ libraries/audio/src/PositionalAudioRingBuffer.cpp | 11 +++++++++++ libraries/audio/src/PositionalAudioRingBuffer.h | 1 + 3 files changed, 16 insertions(+) diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index cde1377053..def9b8c953 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -144,10 +144,12 @@ QString AudioMixerClientData::getJitterBufferStats() const { AvatarAudioRingBuffer* avatarRingBuffer = getAvatarAudioRingBuffer(); if (avatarRingBuffer) { int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames(); + int calculatedJitterBuffer = avatarRingBuffer->getCalculatedDesiredJitterBufferFrames(); int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames(); int samplesAvailable = avatarRingBuffer->samplesAvailable(); int framesAvailable = (samplesAvailable / avatarRingBuffer->getSamplesPerFrame()); result += "mic.desired:" + QString::number(desiredJitterBuffer) + + " calculated:" + QString::number(calculatedJitterBuffer) + " current:" + QString::number(currentJitterBuffer) + " available:" + QString::number(framesAvailable) + " samples:" + QString::number(samplesAvailable); @@ -158,10 +160,12 @@ QString AudioMixerClientData::getJitterBufferStats() const { for (int i = 0; i < _ringBuffers.size(); i++) { if (_ringBuffers[i]->getType() == PositionalAudioRingBuffer::Injector) { int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames(); + int calculatedJitterBuffer = _ringBuffers[i]->getCalculatedDesiredJitterBufferFrames(); int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames(); int samplesAvailable = _ringBuffers[i]->samplesAvailable(); int framesAvailable = (samplesAvailable / _ringBuffers[i]->getSamplesPerFrame()); result += "| injected["+QString::number(i)+"].desired:" + QString::number(desiredJitterBuffer) + + " calculated:" + QString::number(calculatedJitterBuffer) + " current:" + QString::number(currentJitterBuffer) + " available:" + QString::number(framesAvailable) + " samples:" + QString::number(samplesAvailable); diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 5b6d5138d1..e3c123e592 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -233,6 +233,17 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { return true; } +int PositionalAudioRingBuffer::getCalculatedDesiredJitterBufferFrames() const { + int calculatedDesiredJitterBufferFrames = 1; + const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; + + calculatedDesiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.peekWindowMaxGap() / USECS_PER_FRAME); + if (calculatedDesiredJitterBufferFrames < 1) { + calculatedDesiredJitterBufferFrames = 1; + } + return calculatedDesiredJitterBufferFrames; +} + void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index 4c7148abbe..b9caeff605 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -77,6 +77,7 @@ public: int getSamplesPerFrame() const { return _isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL; } + int getCalculatedDesiredJitterBufferFrames() const; /// returns what we would calculate our desired as if asked int getDesiredJitterBufferFrames() const { return _desiredJitterBufferFrames; } int getCurrentJitterBufferFrames() const { return _currentJitterBufferFrames; } From 1d390faad877e1b5be734e3980100f2306bc0a6a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 10:29:38 -0700 Subject: [PATCH 104/105] add support for commandline/config value for dynamic jitter buffers --- assignment-client/src/audio/AudioMixer.cpp | 12 +++++++ assignment-client/src/audio/AudioMixer.h | 4 +++ .../src/audio/AudioMixerClientData.cpp | 14 ++++++--- .../src/audio/AvatarAudioRingBuffer.cpp | 4 +-- .../src/audio/AvatarAudioRingBuffer.h | 2 +- libraries/audio/src/AudioRingBuffer.cpp | 2 ++ libraries/audio/src/AudioRingBuffer.h | 3 ++ .../audio/src/InjectedAudioRingBuffer.cpp | 4 +-- libraries/audio/src/InjectedAudioRingBuffer.h | 2 +- .../audio/src/PositionalAudioRingBuffer.cpp | 31 ++++++++++--------- .../audio/src/PositionalAudioRingBuffer.h | 3 +- 11 files changed, 56 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index 1d0b51a437..8e4ce04f0b 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -64,6 +64,8 @@ void attachNewBufferToNode(Node *newNode) { } } +bool AudioMixer::_useDynamicJitterBuffers = false; + AudioMixer::AudioMixer(const QByteArray& packet) : ThreadedAssignment(packet), _trailingSleepRatio(1.0f), @@ -502,6 +504,16 @@ void AudioMixer::run() { << QString("%1, %2, %3").arg(destinationCenter.x).arg(destinationCenter.y).arg(destinationCenter.z); } + // check the payload to see if we have asked for dynamicJitterBuffer support + const QString DYNAMIC_JITTER_BUFFER_REGEX_STRING = "--dynamicJitterBuffer"; + QRegExp dynamicJitterBufferMatch(DYNAMIC_JITTER_BUFFER_REGEX_STRING); + if (dynamicJitterBufferMatch.indexIn(_payload) != -1) { + qDebug() << "Enable dynamic jitter buffers."; + _useDynamicJitterBuffers = true; + } else { + qDebug() << "Dynamic jitter buffers disabled, using old behavior."; + } + int nextFrame = 0; QElapsedTimer timer; timer.start(); diff --git a/assignment-client/src/audio/AudioMixer.h b/assignment-client/src/audio/AudioMixer.h index 39f8cf63ae..8560978689 100644 --- a/assignment-client/src/audio/AudioMixer.h +++ b/assignment-client/src/audio/AudioMixer.h @@ -34,6 +34,9 @@ public slots: void readPendingDatagrams(); void sendStatsPacket(); + + static bool getUseDynamicJitterBuffers() { return _useDynamicJitterBuffers; } + private: /// adds one buffer to the mix for a listening node void addBufferToMixForListeningNodeWithBuffer(PositionalAudioRingBuffer* bufferToAdd, @@ -54,6 +57,7 @@ private: int _sumMixes; AABox* _sourceUnattenuatedZone; AABox* _listenerUnattenuatedZone; + static bool _useDynamicJitterBuffers; }; #endif // hifi_AudioMixer_h diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index def9b8c953..395cebf9c2 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -16,6 +16,7 @@ #include "InjectedAudioRingBuffer.h" +#include "AudioMixer.h" #include "AudioMixerClientData.h" AudioMixerClientData::AudioMixerClientData() : @@ -65,7 +66,7 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { if (!avatarRingBuffer) { // we don't have an AvatarAudioRingBuffer yet, so add it - avatarRingBuffer = new AvatarAudioRingBuffer(isStereo); + avatarRingBuffer = new AvatarAudioRingBuffer(isStereo, AudioMixer::getUseDynamicJitterBuffers()); _ringBuffers.push_back(avatarRingBuffer); } @@ -88,7 +89,8 @@ int AudioMixerClientData::parseData(const QByteArray& packet) { if (!matchingInjectedRingBuffer) { // we don't have a matching injected audio ring buffer, so add it - matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier); + matchingInjectedRingBuffer = new InjectedAudioRingBuffer(streamIdentifier, + AudioMixer::getUseDynamicJitterBuffers()); _ringBuffers.push_back(matchingInjectedRingBuffer); } @@ -146,13 +148,15 @@ QString AudioMixerClientData::getJitterBufferStats() const { int desiredJitterBuffer = avatarRingBuffer->getDesiredJitterBufferFrames(); int calculatedJitterBuffer = avatarRingBuffer->getCalculatedDesiredJitterBufferFrames(); int currentJitterBuffer = avatarRingBuffer->getCurrentJitterBufferFrames(); + int resetCount = avatarRingBuffer->getResetCount(); int samplesAvailable = avatarRingBuffer->samplesAvailable(); int framesAvailable = (samplesAvailable / avatarRingBuffer->getSamplesPerFrame()); result += "mic.desired:" + QString::number(desiredJitterBuffer) + " calculated:" + QString::number(calculatedJitterBuffer) + " current:" + QString::number(currentJitterBuffer) + " available:" + QString::number(framesAvailable) - + " samples:" + QString::number(samplesAvailable); + + " samples:" + QString::number(samplesAvailable) + + " resets:" + QString::number(resetCount); } else { result = "mic unknown"; } @@ -162,13 +166,15 @@ QString AudioMixerClientData::getJitterBufferStats() const { int desiredJitterBuffer = _ringBuffers[i]->getDesiredJitterBufferFrames(); int calculatedJitterBuffer = _ringBuffers[i]->getCalculatedDesiredJitterBufferFrames(); int currentJitterBuffer = _ringBuffers[i]->getCurrentJitterBufferFrames(); + int resetCount = _ringBuffers[i]->getResetCount(); int samplesAvailable = _ringBuffers[i]->samplesAvailable(); int framesAvailable = (samplesAvailable / _ringBuffers[i]->getSamplesPerFrame()); result += "| injected["+QString::number(i)+"].desired:" + QString::number(desiredJitterBuffer) + " calculated:" + QString::number(calculatedJitterBuffer) + " current:" + QString::number(currentJitterBuffer) + " available:" + QString::number(framesAvailable) - + " samples:" + QString::number(samplesAvailable); + + " samples:" + QString::number(samplesAvailable) + + " resets:" + QString::number(resetCount); } } diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp index 79ad7ae411..9c6cc32f57 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.cpp +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.cpp @@ -13,8 +13,8 @@ #include "AvatarAudioRingBuffer.h" -AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo) : - PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone, isStereo) { +AvatarAudioRingBuffer::AvatarAudioRingBuffer(bool isStereo, bool dynamicJitterBuffer) : + PositionalAudioRingBuffer(PositionalAudioRingBuffer::Microphone, isStereo, dynamicJitterBuffer) { } diff --git a/assignment-client/src/audio/AvatarAudioRingBuffer.h b/assignment-client/src/audio/AvatarAudioRingBuffer.h index f842c2aa33..e227e70958 100644 --- a/assignment-client/src/audio/AvatarAudioRingBuffer.h +++ b/assignment-client/src/audio/AvatarAudioRingBuffer.h @@ -18,7 +18,7 @@ class AvatarAudioRingBuffer : public PositionalAudioRingBuffer { public: - AvatarAudioRingBuffer(bool isStereo = false); + AvatarAudioRingBuffer(bool isStereo = false, bool dynamicJitterBuffer = false); int parseData(const QByteArray& packet); private: diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index 71fd2fe19d..ee4027841b 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -21,6 +21,7 @@ AudioRingBuffer::AudioRingBuffer(int numFrameSamples, bool randomAccessMode) : NodeData(), + _resetCount(0), _sampleCapacity(numFrameSamples * RING_BUFFER_LENGTH_FRAMES), _numFrameSamples(numFrameSamples), _isStarved(true), @@ -128,6 +129,7 @@ qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { _endOfLastWrite = _buffer; _nextOutput = _buffer; _isStarved = true; + _resetCount++; } if (_endOfLastWrite + samplesToCopy <= _buffer + _sampleCapacity) { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 04cc67c8ac..1ddcadeceb 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -71,6 +71,7 @@ public: bool isStarved() const { return _isStarved; } void setIsStarved(bool isStarved) { _isStarved = isStarved; } + int getResetCount() const { return _resetCount; } /// how many times has the ring buffer written past the end and reset bool hasStarted() const { return _hasStarted; } void addSilentFrame(int numSilentSamples); @@ -80,6 +81,8 @@ protected: AudioRingBuffer& operator= (const AudioRingBuffer&); int16_t* shiftedPositionAccomodatingWrap(int16_t* position, int numSamplesShift) const; + + int _resetCount; /// how many times has the ring buffer written past the end and done a reset int _sampleCapacity; int _numFrameSamples; diff --git a/libraries/audio/src/InjectedAudioRingBuffer.cpp b/libraries/audio/src/InjectedAudioRingBuffer.cpp index ed0476f6bd..9b6529b49f 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.cpp +++ b/libraries/audio/src/InjectedAudioRingBuffer.cpp @@ -19,8 +19,8 @@ #include "InjectedAudioRingBuffer.h" -InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier) : - PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector), +InjectedAudioRingBuffer::InjectedAudioRingBuffer(const QUuid& streamIdentifier, bool dynamicJitterBuffer) : + PositionalAudioRingBuffer(PositionalAudioRingBuffer::Injector, /* isStereo=*/ false , dynamicJitterBuffer), _streamIdentifier(streamIdentifier), _radius(0.0f), _attenuationRatio(0) diff --git a/libraries/audio/src/InjectedAudioRingBuffer.h b/libraries/audio/src/InjectedAudioRingBuffer.h index fd766e2848..4e3fea672b 100644 --- a/libraries/audio/src/InjectedAudioRingBuffer.h +++ b/libraries/audio/src/InjectedAudioRingBuffer.h @@ -18,7 +18,7 @@ class InjectedAudioRingBuffer : public PositionalAudioRingBuffer { public: - InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid()); + InjectedAudioRingBuffer(const QUuid& streamIdentifier = QUuid(), bool dynamicJitterBuffer = false); int parseData(const QByteArray& packet); diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index e3c123e592..23e258fe87 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -85,7 +85,9 @@ quint64 InterframeTimeGapStats::getWindowMaxGap() { } -PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo) : +PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, + bool isStereo, bool dynamicJitterBuffers) : + AudioRingBuffer(isStereo ? NETWORK_BUFFER_LENGTH_SAMPLES_STEREO : NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL), _type(type), _position(0.0f, 0.0f, 0.0f), @@ -96,7 +98,8 @@ PositionalAudioRingBuffer::PositionalAudioRingBuffer(PositionalAudioRingBuffer:: _isStereo(isStereo), _listenerUnattenuatedZone(NULL), _desiredJitterBufferFrames(1), - _currentJitterBufferFrames(0) + _currentJitterBufferFrames(0), + _dynamicJitterBuffers(dynamicJitterBuffers) { } @@ -246,19 +249,19 @@ int PositionalAudioRingBuffer::getCalculatedDesiredJitterBufferFrames() const { void PositionalAudioRingBuffer::updateDesiredJitterBufferFrames() { if (_interframeTimeGapStats.hasNewWindowMaxGapAvailable()) { - - _desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence - /* - const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; + if (!_dynamicJitterBuffers) { + _desiredJitterBufferFrames = 1; // HACK to see if this fixes the audio silence + } else { + const float USECS_PER_FRAME = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * USECS_PER_SECOND / (float)SAMPLE_RATE; - _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); - if (_desiredJitterBufferFrames < 1) { - _desiredJitterBufferFrames = 1; + _desiredJitterBufferFrames = ceilf((float)_interframeTimeGapStats.getWindowMaxGap() / USECS_PER_FRAME); + if (_desiredJitterBufferFrames < 1) { + _desiredJitterBufferFrames = 1; + } + const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1; + if (_desiredJitterBufferFrames > maxDesired) { + _desiredJitterBufferFrames = maxDesired; + } } - const int maxDesired = RING_BUFFER_LENGTH_FRAMES - 1; - if (_desiredJitterBufferFrames > maxDesired) { - _desiredJitterBufferFrames = maxDesired; - } - */ } } diff --git a/libraries/audio/src/PositionalAudioRingBuffer.h b/libraries/audio/src/PositionalAudioRingBuffer.h index b9caeff605..b204dc766b 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.h +++ b/libraries/audio/src/PositionalAudioRingBuffer.h @@ -50,7 +50,7 @@ public: Injector }; - PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo = false); + PositionalAudioRingBuffer(PositionalAudioRingBuffer::Type type, bool isStereo = false, bool dynamicJitterBuffers = false); int parseData(const QByteArray& packet); int parsePositionalData(const QByteArray& positionalByteArray); @@ -102,6 +102,7 @@ protected: InterframeTimeGapStats _interframeTimeGapStats; int _desiredJitterBufferFrames; int _currentJitterBufferFrames; + bool _dynamicJitterBuffers; }; #endif // hifi_PositionalAudioRingBuffer_h From cdbda02765f90b4f7bf5a2b6e97428379106b5d8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 12:09:58 -0700 Subject: [PATCH 105/105] make default ring buffer much larger --- libraries/audio/src/AudioRingBuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 1ddcadeceb..33fb0d238a 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -31,7 +31,7 @@ const int NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL = NETWORK_BUFFER_LENGTH_BYTE const unsigned int BUFFER_SEND_INTERVAL_USECS = floorf((NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL / (float) SAMPLE_RATE) * 1000 * 1000); -const short RING_BUFFER_LENGTH_FRAMES = 10; +const short RING_BUFFER_LENGTH_FRAMES = 100; const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); const int MIN_SAMPLE_VALUE = std::numeric_limits::min();