From ca77eb9d1e927445b62c4cb96531ae702791bea9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 6 Jun 2014 10:51:22 -0700 Subject: [PATCH 01/69] 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 02/69] 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 03/69] 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 04/69] 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 05/69] 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 06/69] 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 07/69] 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 08/69] 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 09/69] 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 10/69] 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 11/69] 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 12/69] 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 13/69] 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 14/69] 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 15/69] 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 16/69] 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 17/69] 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 18/69] 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 19/69] 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 20/69] 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 21/69] 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 22/69] 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 23/69] 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 ecbf5043d74eb9f35412a38f0f8cda2afc453c3b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 17 Jun 2014 16:22:18 -0700 Subject: [PATCH 24/69] 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 25/69] 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 26/69] 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 27/69] 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 28/69] 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 29/69] 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 7cd1f752827cf4622c3553d1c7198e8efc2c8070 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 18 Jun 2014 20:47:57 -0700 Subject: [PATCH 30/69] 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 31/69] 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 32/69] 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 33/69] 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 34/69] 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 a69c6f8e29347cad604ddf1ffa279410765f9d4d Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Thu, 19 Jun 2014 23:58:31 +0530 Subject: [PATCH 35/69] Change pt to px to solve the Windows font size issue --- interface/src/ui/ScriptsTableWidget.cpp | 2 +- interface/ui/runningScriptsWidget.ui | 26 ++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/interface/src/ui/ScriptsTableWidget.cpp b/interface/src/ui/ScriptsTableWidget.cpp index 95acca052c..7b4f9e6b1f 100644 --- a/interface/src/ui/ScriptsTableWidget.cpp +++ b/interface/src/ui/ScriptsTableWidget.cpp @@ -23,7 +23,7 @@ ScriptsTableWidget::ScriptsTableWidget(QWidget* parent) : setShowGrid(false); setSelectionMode(QAbstractItemView::NoSelection); setEditTriggers(QAbstractItemView::NoEditTriggers); - setStyleSheet("QTableWidget { background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }"); + setStyleSheet("QTableWidget { border: none; background: transparent; color: #333333; } QToolTip { color: #000000; background: #f9f6e4; padding: 2px; }"); setToolTipDuration(200); setWordWrap(true); setGeometry(0, 0, parent->width(), parent->height()); diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index 6cb23f4c89..d8d95cabc6 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -25,18 +25,18 @@ QWidget { 37 - 29 + 20 251 - 20 + 31 color: #0e7077; -font-size: 20pt; +font-size: 20px; background: transparent; - <html><head/><body><p><span style=" font-size:18pt;">Running Scripts</span></p></body></html> + <html><head/><body><p><span style=" font-size:18px;">Running Scripts</span></p></body></html> 0 @@ -56,7 +56,7 @@ background: transparent; color: #0e7077; -font: bold 14pt; +font: bold 14px; background: transparent; @@ -82,7 +82,7 @@ background: transparent; background: #0e7077; color: #fff; border-radius: 4px; -font: bold 14pt; +font: bold 14px; padding-top: 3px; @@ -105,7 +105,7 @@ padding-top: 3px; background: #0e7077; color: #fff; border-radius: 4px; -font: bold 14pt; +font: bold 14px; padding-top: 3px; @@ -123,7 +123,7 @@ padding-top: 3px; color: #0e7077; -font: bold 14pt; +font: bold 14px; <html><head/><body><p><span style=" font-weight:600;">Recently loaded</span></p></body></html> @@ -140,7 +140,7 @@ font: bold 14pt; color: #95a5a6; -font-size: 14pt; +font-size: 14px; (click a script to load and run it) @@ -184,7 +184,7 @@ font-size: 14pt; - font: 14pt; + font: 14px; There are no scripts currently running. @@ -204,7 +204,7 @@ font-size: 14pt; background: transparent; -font-size: 14pt; +font-size: 14px; @@ -218,7 +218,7 @@ font-size: 14pt; background: transparent; -font-size: 14pt; +font-size: 14px; @@ -237,7 +237,7 @@ font-size: 14pt; background: #0e7077; color: #fff; border-radius: 4px; -font: bold 14pt; +font: bold 14px; padding-top: 3px; From fbafcb6fd8bf6bd2ec1c3a0c70c602dac584c5de Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 11:46:09 -0700 Subject: [PATCH 36/69] 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 37/69] 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 38/69] 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 39/69] 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 f0c2417206cc222bfe842a3b09326e2b9ea794ab Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 15:50:16 -0700 Subject: [PATCH 40/69] 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 4a0ce7a9aebe777c5d6d8b83f11a2add638bdc60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 19 Jun 2014 17:39:50 -0700 Subject: [PATCH 41/69] 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 c7ad3da47d4432e7bb90ae82a49281cb503cde1e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Jun 2014 10:55:57 -0700 Subject: [PATCH 42/69] 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 c17b8fdb6078a7a2bbd779132c4a49b6d29b036b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 20 Jun 2014 16:31:39 -0700 Subject: [PATCH 43/69] 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 44/69] 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 ad41f2da9a631e4e2f362dfba4dc74916b86a89e Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Sat, 21 Jun 2014 16:27:13 +0530 Subject: [PATCH 45/69] Make changes to other UI forms as well --- interface/ui/preferencesDialog.ui | 2 +- interface/ui/shareSnapshot.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/ui/preferencesDialog.ui b/interface/ui/preferencesDialog.ui index f00d7c4788..df33d09939 100644 --- a/interface/ui/preferencesDialog.ui +++ b/interface/ui/preferencesDialog.ui @@ -660,7 +660,7 @@ color: #0e7077 background: #0e7077; color: #fff; border-radius: 4px; -font: bold 14pt; +font: bold 14px; padding: 10px;margin-top:10px diff --git a/interface/ui/shareSnapshot.ui b/interface/ui/shareSnapshot.ui index df7fc4939f..19e0772f13 100644 --- a/interface/ui/shareSnapshot.ui +++ b/interface/ui/shareSnapshot.ui @@ -277,7 +277,7 @@ padding-left:20px; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Helvetica'; font-size:14pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Helvetica'; font-size:14px; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> From ea83a97b7561afc873ac884c5d5afb60ae9973b1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 08:36:45 -0700 Subject: [PATCH 46/69] 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 47/69] 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 48/69] 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 49/69] 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 50/69] 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 8986be5f1c07086564bcd78aa9ba17eaf9f60690 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:36:31 -0700 Subject: [PATCH 51/69] 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 52/69] 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 9a2197a23c8b3c1ab7f226cfc6cc231cffb0dc50 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 10:55:34 -0700 Subject: [PATCH 53/69] 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 54/69] 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 9713e3d5136b29d8a8ee6282e499d3d7be4d0a91 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 12:30:53 -0700 Subject: [PATCH 55/69] 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 56/69] 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 57/69] 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 58/69] 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 415dfd98aad481fc91a5af0645abe85adf365b5e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 23 Jun 2014 13:40:50 -0700 Subject: [PATCH 59/69] 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 60/69] 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 61/69] 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 62/69] 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 63/69] 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 64/69] 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 cdbda02765f90b4f7bf5a2b6e97428379106b5d8 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 12:09:58 -0700 Subject: [PATCH 65/69] 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(); From 7271f850413d56be65bbac7478181c82c3241a00 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Wed, 25 Jun 2014 01:49:17 +0530 Subject: [PATCH 66/69] Change font size values from pt to px in Chat Window. --- interface/src/ui/ChatWindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp index fde77334f4..d42c40ac76 100644 --- a/interface/src/ui/ChatWindow.cpp +++ b/interface/src/ui/ChatWindow.cpp @@ -178,7 +178,7 @@ void ChatWindow::addTimeStamp() { QLabel* timeLabel = new QLabel(timeString); timeLabel->setStyleSheet("color: #333333;" "background-color: white;" - "font-size: 14pt;" + "font-size: 14px;" "padding: 4px;"); timeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); timeLabel->setAlignment(Qt::AlignLeft); @@ -284,7 +284,7 @@ void ChatWindow::participantsChanged() { "padding-bottom: 2px;" "padding-left: 2px;" "border: 1px solid palette(shadow);" - "font-size: 14pt;" + "font-size: 14px;" "font-weight: bold"); userLabel->setProperty("user", participantName); userLabel->setCursor(Qt::PointingHandCursor); @@ -320,7 +320,7 @@ void ChatWindow::messageReceived(const QXmppMessage& message) { "padding-right: 20px;" "margin: 0px;" "color: #333333;" - "font-size: 14pt;" + "font-size: 14px;" "background-color: rgba(0, 0, 0, 0%);" "border: 0; }" "QMenu{ border: 2px outset gray; }"); From 5f4baaffa500a65f6c68e15f291f50839803ced8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 24 Jun 2014 16:25:41 -0700 Subject: [PATCH 67/69] revert credit balance rounding --- 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 d7e3b7453d..75c3f442ca 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", floor(creditBalance + 0.5)); + creditBalanceString.sprintf("%.8f", creditBalance); title += " - ₵" + creditBalanceString; } From 3da18ec1c0289d64fd52a0e6cb0358391d201435 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 17:23:09 -0700 Subject: [PATCH 68/69] try to disable model tests --- tests/octree/src/ModelTests.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/octree/src/ModelTests.cpp b/tests/octree/src/ModelTests.cpp index 92b0b87cd8..63653dac69 100644 --- a/tests/octree/src/ModelTests.cpp +++ b/tests/octree/src/ModelTests.cpp @@ -14,6 +14,7 @@ #include +#if 0 #include #include #include @@ -21,11 +22,12 @@ #include #include #include +#endif #include "ModelTests.h" - void ModelTests::modelTreeTests(bool verbose) { +#if 0 int testsTaken = 0; int testsPassed = 0; int testsFailed = 0; @@ -256,6 +258,7 @@ void ModelTests::modelTreeTests(bool verbose) { if (verbose) { qDebug() << "******************************************************************************************"; } +#endif } From bc5f563f5c55423920ca9204237750363a4ca05a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Jun 2014 17:25:45 -0700 Subject: [PATCH 69/69] and change ring buffer size --- 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 33fb0d238a..1ddcadeceb 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 = 100; +const short RING_BUFFER_LENGTH_FRAMES = 10; const int MAX_SAMPLE_VALUE = std::numeric_limits::max(); const int MIN_SAMPLE_VALUE = std::numeric_limits::min();