From 831a8cf580a7aea49a4156fb401d20eab48b98a3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 5 Mar 2014 12:15:34 -0800 Subject: [PATCH 01/29] remove unused cruft from Model class API --- interface/src/renderer/Model.cpp | 16 ---------------- interface/src/renderer/Model.h | 16 ---------------- 2 files changed, 32 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 1e2c445f37..5055b16c4d 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -418,18 +418,10 @@ bool Model::getRightHandRotation(glm::quat& rotation) const { return getJointRotation(getRightHandJointIndex(), rotation); } -bool Model::setLeftHandPosition(const glm::vec3& position) { - return setJointPosition(getLeftHandJointIndex(), position); -} - bool Model::restoreLeftHandPosition(float percent) { return restoreJointPosition(getLeftHandJointIndex(), percent); } -bool Model::setLeftHandRotation(const glm::quat& rotation) { - return setJointRotation(getLeftHandJointIndex(), rotation); -} - bool Model::getLeftShoulderPosition(glm::vec3& position) const { return getJointPosition(getLastFreeJointIndex(getLeftHandJointIndex()), position); } @@ -438,18 +430,10 @@ float Model::getLeftArmLength() const { return getLimbLength(getLeftHandJointIndex()); } -bool Model::setRightHandPosition(const glm::vec3& position) { - return setJointPosition(getRightHandJointIndex(), position); -} - bool Model::restoreRightHandPosition(float percent) { return restoreJointPosition(getRightHandJointIndex(), percent); } -bool Model::setRightHandRotation(const glm::quat& rotation) { - return setJointRotation(getRightHandJointIndex(), rotation); -} - bool Model::getRightShoulderPosition(glm::vec3& position) const { return getJointPosition(getLastFreeJointIndex(getRightHandJointIndex()), position); } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index fc4643374f..4891c86779 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -125,19 +125,11 @@ public: /// \return true whether or not the rotation was found bool getRightHandRotation(glm::quat& rotation) const; - /// Sets the position of the left hand using inverse kinematics. - /// \return whether or not the left hand joint was found - bool setLeftHandPosition(const glm::vec3& position); - /// Restores some percentage of the default position of the left hand. /// \param percent the percentage of the default position to restore /// \return whether or not the left hand joint was found bool restoreLeftHandPosition(float percent = 1.0f); - /// Sets the rotation of the left hand. - /// \return whether or not the left hand joint was found - bool setLeftHandRotation(const glm::quat& rotation); - /// Gets the position of the left shoulder. /// \return whether or not the left shoulder joint was found bool getLeftShoulderPosition(glm::vec3& position) const; @@ -145,19 +137,11 @@ public: /// Returns the extended length from the left hand to its last free ancestor. float getLeftArmLength() const; - /// Sets the position of the right hand using inverse kinematics. - /// \return whether or not the right hand joint was found - bool setRightHandPosition(const glm::vec3& position); - /// Restores some percentage of the default position of the right hand. /// \param percent the percentage of the default position to restore /// \return whether or not the right hand joint was found bool restoreRightHandPosition(float percent = 1.0f); - /// Sets the rotation of the right hand. - /// \return whether or not the right hand joint was found - bool setRightHandRotation(const glm::quat& rotation); - /// Gets the position of the right shoulder. /// \return whether or not the right shoulder joint was found bool getRightShoulderPosition(glm::vec3& position) const; From 778bdec6d840ff7547cb65cb24fc639c7782fd29 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 6 Mar 2014 12:34:21 -0800 Subject: [PATCH 02/29] Model now knows its bounding radius --- interface/src/renderer/Model.cpp | 10 +++++++++- interface/src/renderer/Model.h | 6 +++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5055b16c4d..7ef87eaa47 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -7,6 +7,7 @@ // #include +#include #include @@ -23,7 +24,8 @@ Model::Model(QObject* parent) : QObject(parent), _shapesAreDirty(true), _lodDistance(0.0f), - _pupilDilation(0.0f) { + _pupilDilation(0.0f), + _boundingRadius(0.f) { // we may have been created in the network thread, but we live in the main thread moveToThread(Application::getInstance()->thread()); } @@ -136,6 +138,7 @@ void Model::createCollisionShapes() { void Model::updateShapePositions() { if (_shapesAreDirty && _shapes.size() == _jointStates.size()) { + _boundingRadius = 0.f; float uniformScale = extractUniformScale(_scale); const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { @@ -145,7 +148,12 @@ void Model::updateShapePositions() { glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation; _shapes[i]->setPosition(worldPosition); _shapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation); + float distance2 = glm::distance2(worldPosition, _translation); + if (distance2 > _boundingRadius) { + _boundingRadius = distance2; + } } + _boundingRadius = sqrtf(_boundingRadius); _shapesAreDirty = false; } } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 4891c86779..88bfbfd3aa 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -172,6 +172,8 @@ public: /// Use the collision to affect the model void applyCollision(CollisionInfo& collision); + float getBoundingRadius() const { return _boundingRadius; } + protected: QSharedPointer _geometry; @@ -228,7 +230,7 @@ protected: /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's /// first free ancestor. float getLimbLength(int jointIndex) const; - + void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true); private: @@ -257,6 +259,8 @@ private: QVector _blendedNormals; QVector _attachments; + + float _boundingRadius; static ProgramObject _program; static ProgramObject _normalMapProgram; From bca0ea15021e7856d97f22409bf720e3a4dc04b3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 6 Mar 2014 12:36:52 -0800 Subject: [PATCH 03/29] Adding per-palm penetration accumulator --- libraries/avatars/src/HandData.cpp | 11 ++++++----- libraries/avatars/src/HandData.h | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/libraries/avatars/src/HandData.cpp b/libraries/avatars/src/HandData.cpp index e4bb187f28..67d284d368 100644 --- a/libraries/avatars/src/HandData.cpp +++ b/libraries/avatars/src/HandData.cpp @@ -67,11 +67,12 @@ void HandData::getLeftRightPalmIndices(int& leftPalmIndex, int& rightPalmIndex) } PalmData::PalmData(HandData* owningHandData) : -_rawRotation(0, 0, 0, 1), -_rawPosition(0, 0, 0), -_rawNormal(0, 1, 0), -_rawVelocity(0, 0, 0), -_rotationalVelocity(0, 0, 0), +_rawRotation(0.f, 0.f, 0.f, 1.f), +_rawPosition(0.f), +_rawNormal(0.f, 1.f, 0.f), +_rawVelocity(0.f), +_rotationalVelocity(0.f), +_totalPenetration(0.f), _controllerButtons(0), _isActive(false), _leapID(LEAPID_INVALID), diff --git a/libraries/avatars/src/HandData.h b/libraries/avatars/src/HandData.h index cdab9f71e9..0d1371b79d 100755 --- a/libraries/avatars/src/HandData.h +++ b/libraries/avatars/src/HandData.h @@ -168,6 +168,9 @@ public: void setRawVelocity(const glm::vec3& velocity) { _rawVelocity = velocity; } const glm::vec3& getRawVelocity() const { return _rawVelocity; } void addToPosition(const glm::vec3& delta); + + void addToPenetration(const glm::vec3& penetration) { _totalPenetration += penetration; } + void resolvePenetrations() { addToPosition(-_totalPenetration); _totalPenetration = glm::vec3(0.f); } void setTipPosition(const glm::vec3& position) { _tipPosition = position; } const glm::vec3 getTipPosition() const { return _owningHandData->leapPositionToWorldPosition(_tipPosition); } @@ -217,6 +220,7 @@ private: glm::vec3 _tipPosition; glm::vec3 _tipVelocity; + glm::vec3 _totalPenetration; // accumulator for per-frame penetrations int _controllerButtons; int _lastControllerButtons; float _trigger; From 37b088ebfa5d457b66148fc423b4ee6339c7ca60 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 6 Mar 2014 12:38:07 -0800 Subject: [PATCH 04/29] Hands can collide with avatar head again --- interface/src/avatar/Avatar.cpp | 31 ++++++++------- interface/src/avatar/Avatar.h | 7 ++-- interface/src/avatar/Hand.cpp | 33 ++++------------ interface/src/avatar/Hand.h | 4 -- interface/src/avatar/Head.cpp | 35 +++++++---------- interface/src/avatar/Head.h | 3 +- interface/src/avatar/MyAvatar.cpp | 52 ++++++++++++++------------ interface/src/avatar/SkeletonModel.cpp | 26 ++++++++++--- interface/src/avatar/SkeletonModel.h | 5 ++- 9 files changed, 96 insertions(+), 100 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 3963a84724..9c27d81dcb 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -489,13 +489,19 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet //return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); } -bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { +void Avatar::updateShapePositions() { _skeletonModel.updateShapePositions(); - bool collided = _skeletonModel.findCollisions(shapes, collisions); - Model& headModel = getHead()->getFaceModel(); headModel.updateShapePositions(); - collided = headModel.findCollisions(shapes, collisions); +} + +bool Avatar::findCollisions(const QVector& shapes, CollisionList& collisions) { + // TODO: Andrew to fix: also collide against _skeleton + //bool collided = _skeletonModel.findCollisions(shapes, collisions); + + Model& headModel = getHead()->getFaceModel(); + //collided = headModel.findCollisions(shapes, collisions) || collided; + bool collided = headModel.findCollisions(shapes, collisions); return collided; } @@ -710,15 +716,14 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { return false; } -void Avatar::applyCollision(CollisionInfo& collision) { - if (!collision._data || collision._type != MODEL_COLLISION) { - return; - } - // TODO: make skeleton also respond to collisions - Model* model = static_cast(collision._data); - if (model == &(getHead()->getFaceModel())) { - getHead()->applyCollision(collision); - } +void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { + // ATM we only support collision with head + getHead()->applyCollision(contactPoint, penetration); +} + +float Avatar::getBoundingRadius() const { + // TODO: also use head model when computing the avatar's bounding radius + return _skeletonModel.getBoundingRadius(); } float Avatar::getPelvisFloatingHeight() const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 1bbf7df039..2aa7eac737 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -138,10 +138,11 @@ public: /// \return true if we expect the avatar would move as a result of the collision bool collisionWouldMoveAvatar(CollisionInfo& collision) const; - /// \param collision a data structure for storing info about collisions against Models - void applyCollision(CollisionInfo& collision); + void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); - float getBoundingRadius() const { return 0.5f * getSkeletonHeight(); } + /// \return bounding radius of avatar + virtual float getBoundingRadius() const; + void updateShapePositions(); public slots: void updateCollisionFlags(); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index ce2f2a242e..a154e76a05 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -145,10 +145,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { jointIndices[0] = skeletonModel.getLeftHandJointIndex(); jointIndices[1] = skeletonModel.getRightHandJointIndex(); - palmIndices[1] = -1; // adebug temporarily disable right hand - jointIndices[1] = -1; // adebug temporarily disable right hand - - for (size_t i = 0; i < 1; i++) { + for (size_t i = 0; i < 2; i++) { int palmIndex = palmIndices[i]; int jointIndex = jointIndices[i]; if (palmIndex == -1 || jointIndex == -1) { @@ -165,9 +162,8 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { handCollisions.clear(); QVector shapes; skeletonModel.getHandShapes(jointIndex, shapes); - bool collided = isMyHand ? avatar->findCollisions(shapes, handCollisions) : avatar->findCollisions(shapes, handCollisions); - if (collided) { - //if (avatar->findCollisions(shapes, handCollisions)) { + + if (avatar->findCollisions(shapes, handCollisions)) { glm::vec3 averagePenetration; glm::vec3 averageContactPoint; for (int j = 0; j < handCollisions.size(); ++j) { @@ -180,13 +176,14 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { // our hand against other avatar // for now we resolve it to test shapes/collisions // TODO: only partially resolve this penetration - palm.addToPosition(-averagePenetration); + palm.addToPenetration(averagePenetration); } else { // someone else's hand against MyAvatar // TODO: submit collision info to MyAvatar which should lean accordingly averageContactPoint /= float(handCollisions.size()); + avatar->applyCollision(averageContactPoint, averagePenetration); } - } + } } } @@ -218,7 +215,7 @@ void Hand::collideAgainstOurself() { totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } // resolve penetration - palm.addToPosition(-totalPenetration); + palm.addToPenetration(totalPenetration); } } } @@ -428,19 +425,3 @@ void Hand::renderLeapHands(bool isMine) { glPopMatrix(); } - -void Hand::setLeapHands(const std::vector& handPositions, - const std::vector& handNormals) { - for (size_t i = 0; i < getNumPalms(); ++i) { - PalmData& palm = getPalms()[i]; - if (i < handPositions.size()) { - palm.setActive(true); - palm.setRawPosition(handPositions[i]); - palm.setRawNormal(handNormals[i]); - } - else { - palm.setActive(false); - } - } -} - diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index a1b1875424..6f64f62c9c 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -79,10 +79,6 @@ private: float _collisionAge; float _collisionDuration; - // private methods - void setLeapHands(const std::vector& handPositions, - const std::vector& handNormals); - void renderLeapHands(bool isMine); void renderLeapFingerTrails(); diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 74bf983eb1..7e9d549509 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -213,32 +213,25 @@ float Head::getTweakedRoll() const { return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } -void Head::applyCollision(CollisionInfo& collision) { - // HACK: the collision proxies for the FaceModel are bad. As a temporary workaround - // we collide against a hard coded collision proxy. - // TODO: get a better collision proxy here. - const float HEAD_RADIUS = 0.15f; - const glm::vec3 HEAD_CENTER = _position; - - // collide the contactPoint against the collision proxy to obtain a new penetration - // NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere) - glm::vec3 penetration; - if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) { - // compute lean angles - Avatar* owningAvatar = static_cast(_owningAvatar); - glm::quat bodyRotation = owningAvatar->getOrientation(); - glm::vec3 neckPosition; - if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { - glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); - glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); +void Head::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { + // compute lean angles + Avatar* owningAvatar = static_cast(_owningAvatar); + glm::quat bodyRotation = owningAvatar->getOrientation(); + glm::vec3 neckPosition; + if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { + glm::vec3 yAxis = bodyRotation * glm::vec3(0.f, 1.f, 0.f); + glm::vec3 leverArm = _position - neckPosition; + if (glm::dot(leverArm, yAxis) > 0.f) { float neckLength = glm::length(_position - neckPosition); if (neckLength > 0.f) { - float forward = glm::dot(collision._penetration, zAxis) / neckLength; - float sideways = - glm::dot(collision._penetration, xAxis) / neckLength; + glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + float forward = glm::dot(penetration, zAxis) / neckLength; + float sideways = - glm::dot(penetration, xAxis) / neckLength; addLean(sideways, forward); } } - } + } } void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 7e7a96a3a7..d127912555 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -78,7 +78,8 @@ public: virtual float getTweakedYaw() const; virtual float getTweakedRoll() const; - void applyCollision(CollisionInfo& collisionInfo); + // move the head to avoid given collision info + void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); private: // disallow copies of the Head, copy of owning Avatar is disallowed too diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index de07ed55fa..da08f7a5ee 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -182,26 +182,6 @@ void MyAvatar::simulate(float deltaTime) { _velocity += _scale * _gravity * (GRAVITY_EARTH * deltaTime); } - if (_collisionFlags != 0) { - Camera* myCamera = Application::getInstance()->getCamera(); - - float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; - if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { - radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); - radius *= COLLISION_RADIUS_SCALAR; - } - - if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) { - updateCollisionWithEnvironment(deltaTime, radius); - } - if (_collisionFlags & COLLISION_GROUP_VOXELS) { - updateCollisionWithVoxels(deltaTime, radius); - } - if (_collisionFlags & COLLISION_GROUP_AVATARS) { - updateCollisionWithAvatars(deltaTime); - } - } - // add thrust to velocity _velocity += _thrust * deltaTime; @@ -318,6 +298,30 @@ void MyAvatar::simulate(float deltaTime) { // Zero thrust out now that we've added it to velocity in this frame _thrust = glm::vec3(0, 0, 0); + + // now that we're done stepping the avatar forward in time, compute new collisions + if (_collisionFlags != 0) { + Camera* myCamera = Application::getInstance()->getCamera(); + + float radius = getSkeletonHeight() * COLLISION_RADIUS_SCALE; + if (myCamera->getMode() == CAMERA_MODE_FIRST_PERSON && !OculusManager::isConnected()) { + radius = myCamera->getAspectRatio() * (myCamera->getNearClip() / cos(myCamera->getFieldOfView() / 2.f)); + radius *= COLLISION_RADIUS_SCALAR; + } + + if (_collisionFlags & COLLISION_GROUP_ENVIRONMENT) { + updateCollisionWithEnvironment(deltaTime, radius); + } + if (_collisionFlags & COLLISION_GROUP_VOXELS) { + updateCollisionWithVoxels(deltaTime, radius); + } + if (_collisionFlags & COLLISION_GROUP_AVATARS) { + updateCollisionWithAvatars(deltaTime); + } + } + + // resolve collision results + _skeletonModel.syncToPalms(); // consider updating our billboard maybeUpdateBillboard(); @@ -919,14 +923,17 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // no need to compute a bunch of stuff if we have one or fewer avatars return; } + updateShapePositions(); float myBoundingRadius = getBoundingRadius(); + /* TODO: Andrew to fix Avatar-Avatar body collisions // HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis // TODO: make the collision work without assuming avatar orientation Extents myStaticExtents = _skeletonModel.getStaticExtents(); glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum; float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z); float myCapsuleHeight = staticScale.y; + */ CollisionInfo collisionInfo; foreach (const AvatarSharedPointer& avatarPointer, avatars) { @@ -935,16 +942,13 @@ 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; } float theirBoundingRadius = avatar->getBoundingRadius(); if (distance < myBoundingRadius + theirBoundingRadius) { - _skeletonModel.updateShapePositions(); - Model& headModel = getHead()->getFaceModel(); - headModel.updateShapePositions(); - /* TODO: Andrew to fix Avatar-Avatar body collisions Extents theirStaticExtents = _skeletonModel.getStaticExtents(); glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum; diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 7c7202566a..cf542a41d5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -62,6 +62,19 @@ void SkeletonModel::simulate(float deltaTime, bool delayLoad) { } } +void SkeletonModel::syncToPalms() { + int leftPalmIndex, rightPalmIndex; + Hand* hand = _owningAvatar->getHand(); + hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + if (leftPalmIndex != -1 && rightPalmIndex != -1) { + applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, + hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, + hand->getPalms()[rightPalmIndex]); + } +} + bool SkeletonModel::render(float alpha) { if (_jointStates.isEmpty()) { @@ -154,7 +167,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin // no point in continuing if there are no fingers if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) { - stretchArm(jointIndex, palm.getPosition()); + stretchArm(jointIndex, palm); return; } @@ -173,7 +186,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJoin setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true); } - stretchArm(jointIndex, palm.getPosition()); + stretchArm(jointIndex, palm); } void SkeletonModel::updateJointState(int index) { @@ -196,7 +209,8 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::angleAxis(-_owningAvatar->getHead()->getLeanForward(), glm::normalize(inverse * axes[0])) * joint.rotation; } -void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { +void SkeletonModel::stretchArm(int jointIndex, PalmData& palm) { +// const glm::vec3& position) { // find out where the hand is pointing glm::quat handRotation; getJointRotation(jointIndex, handRotation, true); @@ -212,11 +226,11 @@ void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) { glm::quat elbowRotation; getJointRotation(joint.parentIndex, elbowRotation, true); applyRotationDelta(joint.parentIndex, rotationBetween(elbowRotation * forwardVector, handVector), false); - + // set position according to normal length float scale = extractUniformScale(_scale); - glm::vec3 handPosition = position - _translation; - glm::vec3 elbowPosition = handPosition - handVector * joint.distanceToParent * scale; + palm.resolvePenetrations(); + glm::vec3 elbowPosition = palm.getPosition() - _translation; - handVector * (joint.distanceToParent * scale); // set shoulder orientation to point to elbow const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex); diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 3d95d805ea..1c6091a5cb 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -23,12 +23,13 @@ public: SkeletonModel(Avatar* owningAvatar); void simulate(float deltaTime, bool delayLoad = false); + void syncToPalms(); bool render(float alpha); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes void getHandShapes(int jointIndex, QVector& shapes) const; - + protected: void applyHandPosition(int jointIndex, const glm::vec3& position); @@ -45,7 +46,7 @@ private: /// Using the current position and rotation of the identified (hand) joint, computes a /// reasonable stretched configuration for the connected arm. - void stretchArm(int jointIndex, const glm::vec3& position); + void stretchArm(int jointIndex, PalmData& palm); Avatar* _owningAvatar; }; From 817ae8e0525983e727b6643af21d1aa5b5af293d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 10 Mar 2014 08:38:17 -0700 Subject: [PATCH 05/29] degrees instead of radians for head lean deltas --- interface/src/avatar/Head.cpp | 4 ++-- libraries/shared/src/SharedUtil.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 7e9d549509..1d1af08645 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -226,8 +226,8 @@ void Head::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetr if (neckLength > 0.f) { glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); - float forward = glm::dot(penetration, zAxis) / neckLength; - float sideways = - glm::dot(penetration, xAxis) / neckLength; + float forward = DEGREES_PER_RADIAN * glm::dot(penetration, zAxis) / neckLength; + float sideways = DEGREES_PER_RADIAN * glm::dot(penetration, xAxis) / neckLength; addLean(sideways, forward); } } diff --git a/libraries/shared/src/SharedUtil.h b/libraries/shared/src/SharedUtil.h index b08e0413a6..0c14dbec91 100644 --- a/libraries/shared/src/SharedUtil.h +++ b/libraries/shared/src/SharedUtil.h @@ -50,6 +50,7 @@ static const float ONE_THIRD = 0.333333f; static const float PIE = 3.141592f; static const float PI_TIMES_TWO = 3.141592f * 2.0f; static const float PI_OVER_180 = 3.141592f / 180.0f; +static const float DEGREES_PER_RADIAN = 180.0f / PIE; static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f); static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f); From 5e494d522d0c9852c15e90106fb57caa0fe9d0cb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Mar 2014 15:49:17 -0700 Subject: [PATCH 06/29] fixing signed/unsigned comparison --- interface/src/avatar/Hand.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 830e997c80..13bbf23045 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -196,7 +196,7 @@ void Hand::collideAgainstOurself() { float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); const Model& skeletonModel = _owningAvatar->getSkeletonModel(); - for (size_t i = 0; i < getNumPalms(); i++) { + for (int i = 0; i < int(getNumPalms()); i++) { PalmData& palm = getPalms()[i]; if (!palm.isActive()) { continue; From cd7da205de5ee91da3a0e95f644edb551297ca07 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 12 Mar 2014 15:49:46 -0700 Subject: [PATCH 07/29] improved index sanity checking --- 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 f58391601c..5cd4e368d0 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -91,7 +91,7 @@ bool SkeletonModel::render(float alpha) { } void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { - if (jointIndex == -1) { + if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { return; } if (jointIndex == getLeftHandJointIndex() From 5934a17475176c547713098e87cbd024ef578303 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Mar 2014 15:27:28 -0700 Subject: [PATCH 08/29] fix sphere-capsule collisions at the capsule caps --- libraries/shared/src/ShapeCollider.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp index e83e6842a9..70f9b9c4bf 100644 --- a/libraries/shared/src/ShapeCollider.cpp +++ b/libraries/shared/src/ShapeCollider.cpp @@ -86,7 +86,8 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col if (absAxialDistance < totalRadius + capsuleB->getHalfHeight()) { glm::vec3 radialAxis = BA + axialDistance * capsuleAxis; // points from A to axis of B float radialDistance2 = glm::length2(radialAxis); - if (radialDistance2 > totalRadius * totalRadius) { + float totalRadius2 = totalRadius * totalRadius; + if (radialDistance2 > totalRadius2) { // sphere is too far from capsule axis return false; } @@ -95,6 +96,9 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col float sign = (axialDistance > 0.f) ? 1.f : -1.f; radialAxis = BA + (sign * capsuleB->getHalfHeight()) * capsuleAxis; radialDistance2 = glm::length2(radialAxis); + if (radialDistance2 > totalRadius2) { + return false; + } } if (radialDistance2 > EPSILON * EPSILON) { CollisionInfo* collision = collisions.getNewCollision(); @@ -147,7 +151,8 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col if (absAxialDistance < totalRadius + capsuleA->getHalfHeight()) { glm::vec3 radialAxis = AB + axialDistance * capsuleAxis; // from sphereB to axis of capsuleA float radialDistance2 = glm::length2(radialAxis); - if (radialDistance2 > totalRadius * totalRadius) { + float totalRadius2 = totalRadius * totalRadius; + if (radialDistance2 > totalRadius2) { // sphere is too far from capsule axis return false; } @@ -162,6 +167,9 @@ bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, Col closestApproach = capsuleA->getPosition() + (sign * capsuleA->getHalfHeight()) * capsuleAxis; radialAxis = closestApproach - sphereB->getPosition(); radialDistance2 = glm::length2(radialAxis); + if (radialDistance2 > totalRadius2) { + return false; + } } if (radialDistance2 > EPSILON * EPSILON) { CollisionInfo* collision = collisions.getNewCollision(); From 06fbd49ffabb4b0954e57b3dfc0a7ee658c872ef Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Mar 2014 15:29:22 -0700 Subject: [PATCH 09/29] Adding StreamUtils for common debug output --- libraries/shared/src/StreamUtils.cpp | 75 ++++++++++++++++++++++++ libraries/shared/src/StreamUtils.h | 39 ++++++++++++ tests/physics/src/CollisionInfoTests.cpp | 7 ++- tests/physics/src/ShapeColliderTests.cpp | 5 +- 4 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 libraries/shared/src/StreamUtils.cpp create mode 100644 libraries/shared/src/StreamUtils.h diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp new file mode 100644 index 0000000000..44c81f9464 --- /dev/null +++ b/libraries/shared/src/StreamUtils.cpp @@ -0,0 +1,75 @@ +// +// StreamUtils.cpp +// +// Created by Andrew Meadows on 2014.02.21 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include + +#include "StreamUtils.h" + +const char* hex_digits = "0123456789abcdef"; + +void StreamUtil::dump(std::ostream& s, const QByteArray& buffer) { + int row_size = 32; + int i = 0; + while (i < buffer.size()) { + for(int j = 0; i < buffer.size() && j < row_size; ++j) { + char byte = buffer[i]; + s << hex_digits[(byte >> 4) & 0x0f] << hex_digits[byte & 0x0f] << " "; + ++i; + } + s << "\n"; + } +} + +std::ostream& operator<<(std::ostream& s, const glm::vec3& v) { + s << "<" << v.x << " " << v.y << " " << v.z << ">"; + return s; +} + +std::ostream& operator<<(std::ostream& s, const glm::quat& q) { + s << "<" << q.x << " " << q.y << " " << q.z << " " << q.w << ">"; + return s; +} + +std::ostream& operator<<(std::ostream& s, const glm::mat4& m) { + s << "["; + for (int j = 0; j < 4; ++j) { + s << " " << m[0][j] << " " << m[1][j] << " " << m[2][j] << " " << m[3][j] << ";"; + } + s << " ]"; + return s; +} + +// less common utils can be enabled with DEBUG +#ifdef DEBUG + +std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) { + s << "{penetration=" << c._penetration + << ", contactPoint=" << c._contactPoint + << ", addedVelocity=" << c._addedVelocity + << "}"; + return s; +} + +std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) { + s << "{type='sphere', center=" << sphere.getPosition() + << ", radius=" << sphere.getRadius() + << "}"; + return s; +} + +std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) { + s << "{type='capsule', center=" << capsule.getPosition() + << ", radius=" << capsule.getRadius() + << ", length=" << (2.f * capsule.getHalfHeight()) + << ", begin=" << capsule.getStartPoint() + << ", end=" << capsule.getEndPoint() + << "}"; + return s; +} + +#endif // DEBUG + diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h new file mode 100644 index 0000000000..1a17a94658 --- /dev/null +++ b/libraries/shared/src/StreamUtils.h @@ -0,0 +1,39 @@ +// +// StreamUtils.h +// +// Created by Andrew Meadows on 2014.02.21 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __tests__StreamUtils__ +#define __tests__StreamUtils__ + +#include + +#include + +#include +#include + + +namespace StreamUtil { + // dump the buffer, 32 bytes per row, each byte in hex, separated by whitespace + void dump(std::ostream& s, const QByteArray& buffer); +} + +std::ostream& operator<<(std::ostream& s, const glm::vec3& v); +std::ostream& operator<<(std::ostream& s, const glm::quat& q); +std::ostream& operator<<(std::ostream& s, const glm::mat4& m); + +// less common utils can be enabled with DEBUG +#ifdef DEBUG +#include "CollisionInfo.h" +#include "SphereShape.h" +#include "CapsuleShape.h" +std::ostream& operator<<(std::ostream& s, const CollisionInfo& c); +std::ostream& operator<<(std::ostream& s, const SphereShape& shape); +std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule); +#endif // DEBUG + + +#endif // __tests__StreamUtils__ diff --git a/tests/physics/src/CollisionInfoTests.cpp b/tests/physics/src/CollisionInfoTests.cpp index 43601978ae..241204d036 100644 --- a/tests/physics/src/CollisionInfoTests.cpp +++ b/tests/physics/src/CollisionInfoTests.cpp @@ -13,12 +13,17 @@ #include #include +#include #include "CollisionInfoTests.h" -#include "PhysicsTestUtil.h" /* + +static glm::vec3 xAxis(1.f, 0.f, 0.f); +static glm::vec3 xZxis(0.f, 1.f, 0.f); +static glm::vec3 xYxis(0.f, 0.f, 1.f); + void CollisionInfoTests::rotateThenTranslate() { CollisionInfo collision; collision._penetration = xAxis; diff --git a/tests/physics/src/ShapeColliderTests.cpp b/tests/physics/src/ShapeColliderTests.cpp index 6d7e9a6db1..4a2e648323 100644 --- a/tests/physics/src/ShapeColliderTests.cpp +++ b/tests/physics/src/ShapeColliderTests.cpp @@ -16,11 +16,14 @@ #include #include #include +#include -#include "PhysicsTestUtil.h" #include "ShapeColliderTests.h" const glm::vec3 origin(0.f); +static const glm::vec3 xAxis(1.f, 0.f, 0.f); +static const glm::vec3 yAxis(0.f, 1.f, 0.f); +static const glm::vec3 zAxis(0.f, 0.f, 1.f); void ShapeColliderTests::sphereMissesSphere() { // non-overlapping spheres of unequal size From d90b325b1c4208ade3adcee7777044aba082b4d0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 25 Mar 2014 15:31:46 -0700 Subject: [PATCH 10/29] Add penetration resolution helper for Hand class --- interface/src/avatar/Hand.cpp | 8 ++++++++ interface/src/avatar/Hand.h | 2 ++ interface/src/avatar/MyAvatar.cpp | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 61eecae696..65dce3bda2 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -9,6 +9,7 @@ #include #include +#include #include "Application.h" #include "Avatar.h" @@ -219,6 +220,13 @@ void Hand::collideAgainstOurself() { } } +void Hand::resolvePenetrations() { + for (size_t i = 0; i < getNumPalms(); ++i) { + PalmData& palm = getPalms()[i]; + palm.resolvePenetrations(); + } +} + void Hand::handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime) { // Collision between finger and a voxel plays sound const float LOWEST_FREQUENCY = 100.f; diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 6f64f62c9c..93a40707cc 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -60,6 +60,8 @@ public: void collideAgainstAvatar(Avatar* avatar, bool isMyHand); void collideAgainstOurself(); + void resolvePenetrations(); + private: // disallow copies of the Hand, copy of owning Avatar is disallowed too Hand(const Hand&); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 961ab5b431..c1d05d9e7a 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -895,7 +895,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { float myCapsuleHeight = staticScale.y; */ - CollisionInfo collisionInfo; foreach (const AvatarSharedPointer& avatarPointer, avatars) { Avatar* avatar = static_cast(avatarPointer.data()); if (static_cast(this) == avatar) { @@ -926,10 +925,13 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // collide our hands against them getHand()->collideAgainstAvatar(avatar, true); + /* TODO: Andrew to fix them-into-us collision checks // collide their hands against us avatar->getHand()->collideAgainstAvatar(this, false); + */ } } + getHand()->resolvePenetrations(); } class SortedAvatar { From c3aab7337f5b7e2f9e8dace5bbea4af860c289c7 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 11:36:35 -0700 Subject: [PATCH 11/29] added more performance stats tracking to octree server --- .../src/octree/OctreeSendThread.cpp | 15 + assignment-client/src/octree/OctreeServer.cpp | 409 ++++++++++++++---- assignment-client/src/octree/OctreeServer.h | 35 ++ 3 files changed, 374 insertions(+), 85 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index a215d9b3c3..40c045a4c1 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -45,6 +45,7 @@ OctreeSendThread::~OctreeSendThread() { qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; OctreeServer::clientDisconnected(); + OctreeServer::stopTrackingThread(this); } void OctreeSendThread::setIsShuttingDown() { @@ -54,7 +55,14 @@ void OctreeSendThread::setIsShuttingDown() { bool OctreeSendThread::process() { + OctreeServer::didProcess(this); + + float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; + quint64 lockWaitStart = usecTimestampNow(); QMutexLocker locker(&_processLock); + quint64 lockWaitEnd = usecTimestampNow(); + lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); + OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); if (_isShuttingDown) { return false; // exit early if we're shutting down @@ -119,6 +127,8 @@ quint64 OctreeSendThread::_totalPackets = 0; int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent) { + + OctreeServer::didHandlePacketSend(this); // if we're shutting down, then exit early if (nodeData->isShuttingDown()) { @@ -175,10 +185,12 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, } // actually send it + OctreeServer::didCallWriteDatagram(this); NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node)); packetSent = true; } else { // not enough room in the packet, send two packets + OctreeServer::didCallWriteDatagram(this); NodeList::getInstance()->writeDatagram((char*) statsMessage, statsMessageLength, SharedNodePointer(node)); // since a stats message is only included on end of scene, don't consider any of these bytes "wasted", since @@ -197,6 +209,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, truePacketsSent++; packetsSent++; + OctreeServer::didCallWriteDatagram(this); NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), SharedNodePointer(node)); @@ -217,6 +230,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, // If there's actually a packet waiting, then send it. if (nodeData->isPacketWaiting() && !nodeData->isShuttingDown()) { // just send the voxel packet + OctreeServer::didCallWriteDatagram(this); NodeList::getInstance()->writeDatagram((char*) nodeData->getPacket(), nodeData->getPacketLength(), SharedNodePointer(node)); packetSent = true; @@ -246,6 +260,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { + OctreeServer::didPacketDistributor(this); // if shutting down, exit early if (nodeData->isShuttingDown()) { diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 6f604c5fd5..6c17c80e5f 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -6,9 +6,10 @@ // Copyright (c) 2013 HighFidelity, Inc. All rights reserved. // -#include -#include -#include +#include +#include +#include +#include #include #include @@ -59,6 +60,15 @@ int OctreeServer::_noCompress = 0; SimpleMovingAverage OctreeServer::_averagePacketSendingTime(MOVING_AVERAGE_SAMPLE_COUNTS); int OctreeServer::_noSend = 0; +SimpleMovingAverage OctreeServer::_averageProcessWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageProcessShortWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageProcessLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageProcessExtraLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +int OctreeServer::_extraLongProcessWait = 0; +int OctreeServer::_longProcessWait = 0; +int OctreeServer::_shortProcessWait = 0; +int OctreeServer::_noProcessWait = 0; + void OctreeServer::resetSendingStats() { _averageLoopTime.reset(); @@ -95,6 +105,15 @@ void OctreeServer::resetSendingStats() { _averagePacketSendingTime.reset(); _noSend = 0; + + _averageProcessWaitTime.reset(); + _averageProcessShortWaitTime.reset(); + _averageProcessLongWaitTime.reset(); + _averageProcessExtraLongWaitTime.reset(); + _extraLongProcessWait = 0; + _longProcessWait = 0; + _shortProcessWait = 0; + _noProcessWait = 0; } void OctreeServer::trackEncodeTime(float time) { @@ -164,6 +183,24 @@ void OctreeServer::trackPacketSendingTime(float time) { } +void OctreeServer::trackProcessWaitTime(float time) { + const float MAX_SHORT_TIME = 10.0f; + const float MAX_LONG_TIME = 100.0f; + if (time == SKIP_TIME) { + _noProcessWait++; + time = 0.0f; + } else if (time <= MAX_SHORT_TIME) { + _shortProcessWait++; + _averageProcessShortWaitTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longProcessWait++; + _averageProcessLongWaitTime.updateAverage(time); + } else { + _extraLongProcessWait++; + _averageProcessExtraLongWaitTime.updateAverage(time); + } + _averageProcessWaitTime.updateAverage(time); +} void OctreeServer::attachQueryNodeToNode(Node* newNode) { if (!newNode->getLinkedData()) { @@ -289,41 +326,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "\r\n"; - quint64 now = usecTimestampNow(); - const int USECS_PER_MSEC = 1000; - quint64 msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC; - const int MSECS_PER_SEC = 1000; - const int SECS_PER_MIN = 60; - const int MIN_PER_HOUR = 60; - const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN; - - float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC; - int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; - int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); - - statsString += "Uptime: "; - - if (hours > 0) { - statsString += QString("%1 hour").arg(hours); - if (hours > 1) { - statsString += QString("s"); - } - } - if (minutes > 0) { - if (hours > 0) { - statsString += QString(" "); - } - statsString += QString("%1 minute").arg(minutes); - if (minutes > 1) { - statsString += QString("s"); - } - } - if (seconds > 0) { - if (hours > 0 || minutes > 0) { - statsString += QString(" "); - } - statsString += QString().sprintf("%.3f seconds", seconds); - } + statsString += "Uptime: " + getUptime(); statsString += "\r\n\r\n"; // display voxel file load time @@ -336,33 +339,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "\r\n"; - quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;; - float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC; - int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; - int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); - statsString += QString("%1 File Load Took ").arg(getMyServerName()); - if (hours > 0) { - statsString += QString("%1 hour").arg(hours); - if (hours > 1) { - statsString += QString("s"); - } - } - if (minutes > 0) { - if (hours > 0) { - statsString += QString(" "); - } - statsString += QString("%1 minute").arg(minutes); - if (minutes > 1) { - statsString += QString("s"); - } - } - if (seconds >= 0) { - if (hours > 0 || minutes > 0) { - statsString += QString(" "); - } - statsString += QString().sprintf("%.3f seconds", seconds); - } + statsString += getFileLoadTime(); statsString += "\r\n"; } else { @@ -371,27 +349,31 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "\r\n\r\n"; statsString += "Configuration:\r\n"; + statsString += getConfiguration() + "\r\n"; //one to end the config line + statsString += "\r\n"; + + const int COLUMN_WIDTH = 19; + QLocale locale(QLocale::English); + const float AS_PERCENT = 100.0; + + statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n") + .arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n") + .arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); - for (int i = 1; i < _argc; i++) { - statsString += _argv[i]; - } - statsString += "\r\n"; //one to end the config line - statsString += "\r\n\r\n"; // two more for spacing // display scene stats unsigned long nodeCount = OctreeElement::getNodeCount(); unsigned long internalNodeCount = OctreeElement::getInternalNodeCount(); unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); - QLocale locale(QLocale::English); - const float AS_PERCENT = 100.0; - statsString += "Current Nodes in scene:\r\n"; - statsString += QString(" Total Nodes: %1 nodes\r\n").arg(locale.toString((uint)nodeCount).rightJustified(16, ' ')); - statsString += QString().sprintf(" Internal Nodes: %s nodes (%5.2f%%)\r\n", + statsString += "Current Elements in scene:\r\n"; + statsString += QString(" Total Elements: %1 nodes\r\n").arg(locale.toString((uint)nodeCount).rightJustified(16, ' ')); + statsString += QString().sprintf(" Internal Elements: %s nodes (%5.2f%%)\r\n", locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(), ((float)internalNodeCount / (float)nodeCount) * AS_PERCENT); - statsString += QString().sprintf(" Leaf Nodes: %s nodes (%5.2f%%)\r\n", + statsString += QString().sprintf(" Leaf Elements: %s nodes (%5.2f%%)\r\n", locale.toString((uint)leafNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(), ((float)leafNodeCount / (float)nodeCount) * AS_PERCENT); statsString += "\r\n"; @@ -408,20 +390,65 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks(); quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor(); - const int COLUMN_WIDTH = 19; - statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n") - .arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); - statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n") - .arg(locale.toString((uint)getPacketsTotalPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); - statsString += QString(" Total Clients Connected: %1 clients\r\n\r\n") + statsString += QString(" Total Clients Connected: %1 clients\r\n") .arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' ')); + quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND; + + statsString += QString(" process() last second: %1 clients\r\n") + .arg(locale.toString((uint)howManyThreadsDidProcess(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" packetDistributor() last second: %1 clients\r\n") + .arg(locale.toString((uint)howManyThreadsDidPacketDistributor(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" handlePacketSend() last second: %1 clients\r\n") + .arg(locale.toString((uint)howManyThreadsDidHandlePacketSend(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' ')); + statsString += QString(" writeDatagram() last second: %1 clients\r\n\r\n") + .arg(locale.toString((uint)howManyThreadsDidCallWriteDatagram(oneSecondAgo)).rightJustified(COLUMN_WIDTH, ' ')); + float averageLoopTime = getAverageLoopTime(); - statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs\r\n", averageLoopTime); + statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs" + " samples: %12d \r\n", + averageLoopTime, _averageLoopTime.getSampleCount()); float averageInsideTime = getAverageInsideTime(); - statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs\r\n\r\n", averageInsideTime); + statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs" + " samples: %12d \r\n\r\n", + averageInsideTime, _averageInsideTime.getSampleCount()); + + // Process Wait + { + int allWaitTimes = _extraLongProcessWait +_longProcessWait + _shortProcessWait + _noProcessWait; + + float averageProcessWaitTime = getAverageProcessWaitTime(); + statsString += QString().sprintf(" Average process lock wait time:" + " %9.2f usecs samples: %12d \r\n", + averageProcessWaitTime, allWaitTimes); + + float zeroVsTotal = (allWaitTimes > 0) ? ((float)_noProcessWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" No Lock Wait:" + " (%6.2f%%) samples: %12d \r\n", + zeroVsTotal * AS_PERCENT, _noProcessWait); + + float shortVsTotal = (allWaitTimes > 0) ? ((float)_shortProcessWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" Avg process lock short wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageProcessShortWaitTime.getAverage(), + shortVsTotal * AS_PERCENT, _shortProcessWait); + + float longVsTotal = (allWaitTimes > 0) ? ((float)_longProcessWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" Avg process lock long wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageProcessLongWaitTime.getAverage(), + longVsTotal * AS_PERCENT, _longProcessWait); + + float extraLongVsTotal = (allWaitTimes > 0) ? ((float)_extraLongProcessWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf("Avg process lock extralong wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n", + _averageProcessExtraLongWaitTime.getAverage(), + extraLongVsTotal * AS_PERCENT, _extraLongProcessWait); + } + + // Tree Wait int allWaitTimes = _extraLongTreeWait +_longTreeWait + _shortTreeWait + _noTreeWait; float averageTreeWaitTime = getAverageTreeWaitTime(); @@ -452,6 +479,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url _averageTreeExtraLongWaitTime.getAverage(), extraLongVsTotal * AS_PERCENT, _extraLongTreeWait); + // encode float averageEncodeTime = getAverageEncodeTime(); statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", averageEncodeTime); @@ -839,8 +867,8 @@ void OctreeServer::run() { const char* STATUS_PORT = "--statusPort"; const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); if (statusPort) { - int statusPortNumber = atoi(statusPort); - initHTTPManager(statusPortNumber); + _statusPortNumber = atoi(statusPort); + initHTTPManager(_statusPortNumber); } @@ -1017,3 +1045,214 @@ void OctreeServer::aboutToFinish() { qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish..."; } +QString OctreeServer::getUptime() { + QString formattedUptime; + quint64 now = usecTimestampNow(); + const int USECS_PER_MSEC = 1000; + quint64 msecsElapsed = (now - _startedUSecs) / USECS_PER_MSEC; + const int MSECS_PER_SEC = 1000; + const int SECS_PER_MIN = 60; + const int MIN_PER_HOUR = 60; + const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN; + + float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC; + int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; + int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); + + if (hours > 0) { + formattedUptime += QString("%1 hour").arg(hours); + if (hours > 1) { + formattedUptime += QString("s"); + } + } + if (minutes > 0) { + if (hours > 0) { + formattedUptime += QString(" "); + } + formattedUptime += QString("%1 minute").arg(minutes); + if (minutes > 1) { + formattedUptime += QString("s"); + } + } + if (seconds > 0) { + if (hours > 0 || minutes > 0) { + formattedUptime += QString(" "); + } + formattedUptime += QString().sprintf("%.3f seconds", seconds); + } + return formattedUptime; +} + +QString OctreeServer::getFileLoadTime() { + QString result; + if (isInitialLoadComplete()) { + + const int USECS_PER_MSEC = 1000; + const int MSECS_PER_SEC = 1000; + const int SECS_PER_MIN = 60; + const int MIN_PER_HOUR = 60; + const int MSECS_PER_MIN = MSECS_PER_SEC * SECS_PER_MIN; + + quint64 msecsElapsed = getLoadElapsedTime() / USECS_PER_MSEC;; + float seconds = (msecsElapsed % MSECS_PER_MIN)/(float)MSECS_PER_SEC; + int minutes = (msecsElapsed/(MSECS_PER_MIN)) % MIN_PER_HOUR; + int hours = (msecsElapsed/(MSECS_PER_MIN * MIN_PER_HOUR)); + + if (hours > 0) { + result += QString("%1 hour").arg(hours); + if (hours > 1) { + result += QString("s"); + } + } + if (minutes > 0) { + if (hours > 0) { + result += QString(" "); + } + result += QString("%1 minute").arg(minutes); + if (minutes > 1) { + result += QString("s"); + } + } + if (seconds >= 0) { + if (hours > 0 || minutes > 0) { + result += QString(" "); + } + result += QString().sprintf("%.3f seconds", seconds); + } + } else { + result = "Not yet loaded..."; + } + return result; +} + +QString OctreeServer::getConfiguration() { + QString result; + for (int i = 1; i < _argc; i++) { + result += _argv[i] + QString(" "); + } + return result; +} + +void OctreeServer::sendStatsPacket() { + + // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and + // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the + // the following features: + // 1) remember last state sent + // 2) only send new data + // 3) automatically break up into multiple packets + static QJsonObject statsObject1; + + QString baseName = getMyServerName() + QString("Server"); + + statsObject1[baseName + QString(".0.1.configuration")] = getConfiguration(); + + QString detailedStats= QString("http://") + QHostAddress(getHostOrderLocalAddress()).toString() + + QString(":%1").arg(_statusPortNumber); + + detailedStats = ""+detailedStats+""; + statsObject1[baseName + QString(".0.2.detailed_stats_url")] = detailedStats; + + statsObject1[baseName + QString(".0.3.uptime")] = getUptime(); + statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime(); + statsObject1[baseName + QString(".0.5.clients")] = getCurrentClientCount(); + + statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount(); + statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount(); + statsObject1[baseName + QString(".1.3.octree.leafElementCount")] = (double)OctreeElement::getLeafNodeCount(); + + ThreadedAssignment::addPacketStatsAndSendStatsPacket(statsObject1); + + static QJsonObject statsObject2; + + statsObject2[baseName + QString(".2.outbound.data.totalPackets")] = (double)OctreeSendThread::_totalPackets; + statsObject2[baseName + QString(".2.outbound.data.totalBytes")] = (double)OctreeSendThread::_totalBytes; + statsObject2[baseName + QString(".2.outbound.data.totalBytesWasted")] = (double)OctreeSendThread::_totalWastedBytes; + statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] = (double)OctreePacketData::getTotalBytesOfOctalCodes(); + statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfBitMasks(); + statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfColor(); + + statsObject2[baseName + QString(".2.outbound.timing.1.avgLoopTime")] = getAverageLoopTime(); + statsObject2[baseName + QString(".2.outbound.timing.2.avgInsideTime")] = getAverageInsideTime(); + statsObject2[baseName + QString(".2.outbound.timing.3.avgTreeLockTime")] = getAverageTreeWaitTime(); + statsObject2[baseName + QString(".2.outbound.timing.4.avgEncodeTime")] = getAverageEncodeTime(); + statsObject2[baseName + QString(".2.outbound.timing.5.avgCompressAndWriteTime")] = getAverageCompressAndWriteTime(); + statsObject2[baseName + QString(".2.outbound.timing.5.avgSendTime")] = getAveragePacketSendingTime(); + statsObject2[baseName + QString(".2.outbound.timing.5.nodeWaitTime")] = getAverageNodeWaitTime(); + + NodeList::getInstance()->sendStatsToDomainServer(statsObject2); + + static QJsonObject statsObject3; + + statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); + statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = (double)_octreeInboundPacketProcessor->getTotalElementsProcessed(); + statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] = (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement(); + statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] = (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement(); + + NodeList::getInstance()->sendStatsToDomainServer(statsObject3); +} + +QMap OctreeServer::_threadsDidProcess; +QMap OctreeServer::_threadsDidPacketDistributor; +QMap OctreeServer::_threadsDidHandlePacketSend; +QMap OctreeServer::_threadsDidCallWriteDatagram; + + +void OctreeServer::didProcess(OctreeSendThread* thread) { + _threadsDidProcess[thread] = usecTimestampNow(); +} + +void OctreeServer::didPacketDistributor(OctreeSendThread* thread) { + _threadsDidPacketDistributor[thread] = usecTimestampNow(); +} + +void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) { + _threadsDidHandlePacketSend[thread] = usecTimestampNow(); +} + +void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { + _threadsDidCallWriteDatagram[thread] = usecTimestampNow(); +} + + +void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { + _threadsDidProcess.remove(thread); + _threadsDidPacketDistributor.remove(thread); + _threadsDidHandlePacketSend.remove(thread); +} + +int howManyThreadsDidSomething(QMap& something, quint64 since) { + if (since == 0) { + return something.size(); + } + int count = 0; + QMap::const_iterator i = something.constBegin(); + while (i != something.constEnd()) { + if (i.value() > since) { + count++; + } + ++i; + } + return count; +} + + +int OctreeServer::howManyThreadsDidProcess(quint64 since) { + return howManyThreadsDidSomething(_threadsDidProcess, since); +} + +int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) { + return howManyThreadsDidSomething(_threadsDidPacketDistributor, since); +} + +int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) { + return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since); +} + +int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) { + return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since); +} + diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 2664499b6a..1e90239852 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -97,6 +97,21 @@ public: static void trackPacketSendingTime(float time); static float getAveragePacketSendingTime() { return _averagePacketSendingTime.getAverage(); } + static void trackProcessWaitTime(float time); + static float getAverageProcessWaitTime() { return _averageProcessWaitTime.getAverage(); } + + // these methods allow us to track which threads got to various states + static void didProcess(OctreeSendThread* thread); + static void didPacketDistributor(OctreeSendThread* thread); + static void didHandlePacketSend(OctreeSendThread* thread); + static void didCallWriteDatagram(OctreeSendThread* thread); + static void stopTrackingThread(OctreeSendThread* thread); + + static int howManyThreadsDidProcess(quint64 since = 0); + static int howManyThreadsDidPacketDistributor(quint64 since = 0); + static int howManyThreadsDidHandlePacketSend(quint64 since = 0); + static int howManyThreadsDidCallWriteDatagram(quint64 since = 0); + bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url); virtual void aboutToFinish(); @@ -107,17 +122,22 @@ public slots: void readPendingDatagrams(); void nodeAdded(SharedNodePointer node); void nodeKilled(SharedNodePointer node); + void sendStatsPacket(); protected: void parsePayload(); void initHTTPManager(int port); void resetSendingStats(); + QString getUptime(); + QString getFileLoadTime(); + QString getConfiguration(); int _argc; const char** _argv; char** _parsedArgV; HTTPManager* _httpManager; + int _statusPortNumber; char _persistFilename[MAX_FILENAME_LENGTH]; int _packetsPerClientPerInterval; @@ -151,6 +171,7 @@ protected: static int _noEncode; static SimpleMovingAverage _averageInsideTime; + static SimpleMovingAverage _averageTreeWaitTime; static SimpleMovingAverage _averageTreeShortWaitTime; static SimpleMovingAverage _averageTreeLongWaitTime; @@ -174,6 +195,20 @@ protected: static SimpleMovingAverage _averagePacketSendingTime; static int _noSend; + static SimpleMovingAverage _averageProcessWaitTime; + static SimpleMovingAverage _averageProcessShortWaitTime; + static SimpleMovingAverage _averageProcessLongWaitTime; + static SimpleMovingAverage _averageProcessExtraLongWaitTime; + static int _extraLongProcessWait; + static int _longProcessWait; + static int _shortProcessWait; + static int _noProcessWait; + + static QMap _threadsDidProcess; + static QMap _threadsDidPacketDistributor; + static QMap _threadsDidHandlePacketSend; + static QMap _threadsDidCallWriteDatagram; + }; #endif // __octree_server__OctreeServer__ From bc68ed13001c226215f0c5d0a0e19cd4aef8226b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 11:59:55 -0700 Subject: [PATCH 12/29] moved stopTrackingThread() to setIsShuttingDown() to reduce chance of object conflict --- assignment-client/src/octree/OctreeSendThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 40c045a4c1..2fa6397ac5 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -45,12 +45,12 @@ OctreeSendThread::~OctreeSendThread() { qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; OctreeServer::clientDisconnected(); - OctreeServer::stopTrackingThread(this); } void OctreeSendThread::setIsShuttingDown() { QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete _isShuttingDown = true; + OctreeServer::stopTrackingThread(this); } From 18eda501181410793d6cd74d1dbea15e283bae02 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 13:13:55 -0700 Subject: [PATCH 13/29] added thread counts to json stats --- assignment-client/src/octree/OctreeServer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index 6c17c80e5f..c1ad08dbeb 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1157,6 +1157,13 @@ void OctreeServer::sendStatsPacket() { statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime(); statsObject1[baseName + QString(".0.5.clients")] = getCurrentClientCount(); + quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND; + + statsObject1[baseName + QString(".0.6.threads.1.processing")] = (double)howManyThreadsDidProcess(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] = (double)howManyThreadsDidPacketDistributor(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] = (double)howManyThreadsDidHandlePacketSend(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] = (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo); + statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount(); statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount(); statsObject1[baseName + QString(".1.3.octree.leafElementCount")] = (double)OctreeElement::getLeafNodeCount(); From 9a4db152ffa42532b76b9d5794a3de2b44a9e422 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Mar 2014 13:20:18 -0700 Subject: [PATCH 14/29] avatar responds to hand-vs-head collisions again --- interface/src/avatar/Avatar.cpp | 18 ++++++++++++-- interface/src/avatar/Hand.cpp | 31 ++++++----------------- interface/src/avatar/Hand.h | 1 + interface/src/avatar/MyAvatar.cpp | 12 ++++----- interface/src/avatar/SkeletonModel.cpp | 34 ++++++++++++++------------ interface/src/avatar/SkeletonModel.h | 1 - 6 files changed, 48 insertions(+), 49 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 718fcb824e..de9b33d9c7 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -759,8 +759,22 @@ bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { } void Avatar::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { - // ATM we only support collision with head - getHead()->applyCollision(contactPoint, penetration); + // compute lean angles + glm::vec3 leverAxis = contactPoint - getPosition(); + float leverLength = glm::length(leverAxis); + if (leverLength > EPSILON) { + glm::quat bodyRotation = getOrientation(); + glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + + leverAxis = leverAxis / leverLength; + glm::vec3 effectivePenetration = penetration - glm::dot(penetration, leverAxis) * leverAxis; + // we use the small-angle approximation for sine below to compute the length of + // the opposite side of a narrow right triangle + float sideways = - glm::dot(effectivePenetration, xAxis) / leverLength; + float forward = glm::dot(effectivePenetration, zAxis) / leverLength; + getHead()->addLean(sideways, forward); + } } float Avatar::getBoundingRadius() const { diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 531ad0e8f2..750fae7b2a 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -110,8 +110,6 @@ void Hand::collideAgainstAvatarOld(Avatar* avatar, bool isMyHand) { for (int j = 0; j < handCollisions.size(); ++j) { CollisionInfo* collision = handCollisions.getCollision(j); if (isMyHand) { - // we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is - // not expected to respond to the collision (hand hit unmovable part of their Avatar) totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } } @@ -129,54 +127,39 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { return; } - // 2 = NUM_HANDS - int palmIndices[2]; - getLeftRightPalmIndices(*palmIndices, *(palmIndices + 1)); - const SkeletonModel& skeletonModel = _owningAvatar->getSkeletonModel(); int jointIndices[2]; jointIndices[0] = skeletonModel.getLeftHandJointIndex(); jointIndices[1] = skeletonModel.getRightHandJointIndex(); for (size_t i = 0; i < 2; i++) { - int palmIndex = palmIndices[i]; int jointIndex = jointIndices[i]; - if (palmIndex == -1 || jointIndex == -1) { + if (jointIndex < 0) { continue; } - PalmData& palm = _palms[palmIndex]; - if (!palm.isActive()) { - continue; - } - if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { - playSlaps(palm, avatar); - } handCollisions.clear(); QVector shapes; skeletonModel.getHandShapes(jointIndex, shapes); if (avatar->findCollisions(shapes, handCollisions)) { - glm::vec3 averagePenetration; + glm::vec3 totalPenetration(0.f); glm::vec3 averageContactPoint; for (int j = 0; j < handCollisions.size(); ++j) { CollisionInfo* collision = handCollisions.getCollision(j); - averagePenetration += collision->_penetration; + totalPenetration += collision->_penetration; averageContactPoint += collision->_contactPoint; } - averagePenetration /= float(handCollisions.size()); if (isMyHand) { // our hand against other avatar - // for now we resolve it to test shapes/collisions - // TODO: only partially resolve this penetration - palm.addToPenetration(averagePenetration); + // TODO: resolve this penetration when we don't think the other avatar will yield + //palm.addToPenetration(averagePenetration); } else { // someone else's hand against MyAvatar - // TODO: submit collision info to MyAvatar which should lean accordingly averageContactPoint /= float(handCollisions.size()); - avatar->applyCollision(averageContactPoint, averagePenetration); + avatar->applyCollision(averageContactPoint, totalPenetration); } - } + } } } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 427c33e9c3..3be3bc0f77 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -82,3 +82,4 @@ private: }; #endif + diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 14e948835d..5fc9f874aa 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -322,9 +322,6 @@ void MyAvatar::simulate(float deltaTime) { } } - // resolve collision results - _skeletonModel.syncToPalms(); - // consider updating our billboard maybeUpdateBillboard(); } @@ -925,15 +922,16 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { */ // collide our hands against them - getHand()->collideAgainstAvatar(avatar, true); + // TODO: make this work when we can figure out when the other avatar won't yeild + // (for example, we're colling against their chest or leg) + //getHand()->collideAgainstAvatar(avatar, true); - /* TODO: Andrew to fix them-into-us collision checks // collide their hands against us avatar->getHand()->collideAgainstAvatar(this, false); - */ } } - getHand()->resolvePenetrations(); + // TODO: uncomment this when we handle collisions that won't affect other avatar + //getHand()->resolvePenetrations(); } class SortedAvatar { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 510b37782e..b4746a39d2 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -62,27 +62,31 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } } -void SkeletonModel::syncToPalms() { - int leftPalmIndex, rightPalmIndex; - Hand* hand = _owningAvatar->getHand(); - hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - if (leftPalmIndex != -1 && rightPalmIndex != -1) { - applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[rightPalmIndex]); - } -} - void SkeletonModel::getHandShapes(int jointIndex, QVector& shapes) const { if (jointIndex < 0 || jointIndex >= int(_shapes.size())) { return; } if (jointIndex == getLeftHandJointIndex() || jointIndex == getRightHandJointIndex()) { - // TODO: also add fingers and other hand-parts - shapes.push_back(_shapes[jointIndex]); + // get all shapes that have this hand as an ancestor in the skeleton heirarchy + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + const FBXJoint& joint = geometry.joints[i]; + if (i == jointIndex) { + // this shape is the hand + shapes.push_back(_shapes[i]); + } else { + int parentIndex = joint.parentIndex; + while (parentIndex != -1) { + if (parentIndex == jointIndex) { + // this shape is a child of the hand + shapes.push_back(_shapes[i]); + break; + } + parentIndex = geometry.joints[parentIndex].parentIndex; + } + } + } } } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index 0c8ff72f5a..213a53d9ed 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -23,7 +23,6 @@ public: SkeletonModel(Avatar* owningAvatar); void simulate(float deltaTime, bool fullUpdate = true); - void syncToPalms(); /// \param jointIndex index of hand joint /// \param shapes[out] list in which is stored pointers to hand shapes From 0451ce455f1a13e3cfeea1936a6af40746a2d854 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 13:25:38 -0700 Subject: [PATCH 15/29] added --statusHost config option to allow us to specify the host name of the status page --- assignment-client/src/octree/OctreeServer.cpp | 32 +++++++++++++++---- assignment-client/src/octree/OctreeServer.h | 4 ++- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c1ad08dbeb..b9d3c5963e 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -216,6 +216,7 @@ OctreeServer::OctreeServer(const QByteArray& packet) : _argv(NULL), _parsedArgV(NULL), _httpManager(NULL), + _statusPort(0), _packetsPerClientPerInterval(10), _packetsTotalPerInterval(DEFAULT_PACKETS_PER_INTERVAL), _tree(NULL), @@ -867,9 +868,19 @@ void OctreeServer::run() { const char* STATUS_PORT = "--statusPort"; const char* statusPort = getCmdOption(_argc, _argv, STATUS_PORT); if (statusPort) { - _statusPortNumber = atoi(statusPort); - initHTTPManager(_statusPortNumber); + _statusPort = atoi(statusPort); + initHTTPManager(_statusPort); } + + const char* STATUS_HOST = "--statusHost"; + const char* statusHost = getCmdOption(_argc, _argv, STATUS_HOST); + if (statusHost) { + qDebug("--statusHost=%s", statusHost); + _statusHost = statusHost; + } else { + _statusHost = QHostAddress(getHostOrderLocalAddress()).toString(); + } + qDebug("statusHost=%s", qPrintable(_statusHost)); const char* JURISDICTION_FILE = "--jurisdictionFile"; @@ -1133,6 +1144,17 @@ QString OctreeServer::getConfiguration() { return result; } +QString OctreeServer::getStatusLink() { + QString result; + if (_statusPort > 0) { + QString detailedStats= QString("http://") + _statusHost + QString(":%1").arg(_statusPort); + result = ""+detailedStats+""; + } else { + result = "Status port not enabled."; + } + return result; +} + void OctreeServer::sendStatsPacket() { // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and @@ -1147,11 +1169,7 @@ void OctreeServer::sendStatsPacket() { statsObject1[baseName + QString(".0.1.configuration")] = getConfiguration(); - QString detailedStats= QString("http://") + QHostAddress(getHostOrderLocalAddress()).toString() - + QString(":%1").arg(_statusPortNumber); - - detailedStats = ""+detailedStats+""; - statsObject1[baseName + QString(".0.2.detailed_stats_url")] = detailedStats; + statsObject1[baseName + QString(".0.2.detailed_stats_url")] = getStatusLink(); statsObject1[baseName + QString(".0.3.uptime")] = getUptime(); statsObject1[baseName + QString(".0.4.persistFileLoadTime")] = getFileLoadTime(); diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 1e90239852..3dac42709f 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -131,13 +131,15 @@ protected: QString getUptime(); QString getFileLoadTime(); QString getConfiguration(); + QString getStatusLink(); int _argc; const char** _argv; char** _parsedArgV; HTTPManager* _httpManager; - int _statusPortNumber; + int _statusPort; + QString _statusHost; char _persistFilename[MAX_FILENAME_LENGTH]; int _packetsPerClientPerInterval; From c56efb9e4cf0d5c14ecd3a08470f3f1913291491 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 13:41:49 -0700 Subject: [PATCH 16/29] formatting tweaks --- assignment-client/src/octree/OctreeServer.cpp | 70 +++++++++++-------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index b9d3c5963e..fc01b1d04a 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -369,7 +369,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url unsigned long leafNodeCount = OctreeElement::getLeafNodeCount(); statsString += "Current Elements in scene:\r\n"; - statsString += QString(" Total Elements: %1 nodes\r\n").arg(locale.toString((uint)nodeCount).rightJustified(16, ' ')); + statsString += QString(" Total Elements: %1 nodes\r\n") + .arg(locale.toString((uint)nodeCount).rightJustified(16, ' ')); statsString += QString().sprintf(" Internal Elements: %s nodes (%5.2f%%)\r\n", locale.toString((uint)internalNodeCount).rightJustified(16, ' ').toLocal8Bit().constData(), @@ -511,7 +512,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url float averageCompressAndWriteTime = getAverageCompressAndWriteTime(); - statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n", averageCompressAndWriteTime); + statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n", + averageCompressAndWriteTime); int allCompressTimes = _noCompress + _shortCompress + _longCompress + _extraLongCompress; @@ -697,8 +699,8 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url for (int i=0; i <= NUMBER_OF_CHILDREN; i++) { checkSum += OctreeElement::getChildrenCount(i); statsString += QString().sprintf(" Nodes with %d children: %s nodes (%5.2f%%)\r\n", i, - locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(), - ((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT); + locale.toString((uint)OctreeElement::getChildrenCount(i)).rightJustified(16, ' ').toLocal8Bit().constData(), + ((float)OctreeElement::getChildrenCount(i) / (float)nodeCount) * AS_PERCENT); } statsString += " ----------------------\r\n"; statsString += QString(" Total: %1 nodes\r\n") @@ -709,23 +711,23 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url statsString += "OctreeElement Children Encoding Statistics...\r\n"; statsString += QString().sprintf(" Single or No Children: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getSingleChildrenCount(), - ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT)); + OctreeElement::getSingleChildrenCount(), + ((float)OctreeElement::getSingleChildrenCount() / (float)nodeCount) * AS_PERCENT)); statsString += QString().sprintf(" Two Children as Offset: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getTwoChildrenOffsetCount(), - ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT)); + OctreeElement::getTwoChildrenOffsetCount(), + ((float)OctreeElement::getTwoChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT)); statsString += QString().sprintf(" Two Children as External: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getTwoChildrenExternalCount(), - ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); + OctreeElement::getTwoChildrenExternalCount(), + ((float)OctreeElement::getTwoChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); statsString += QString().sprintf(" Three Children as Offset: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getThreeChildrenOffsetCount(), - ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT); + OctreeElement::getThreeChildrenOffsetCount(), + ((float)OctreeElement::getThreeChildrenOffsetCount() / (float)nodeCount) * AS_PERCENT); statsString += QString().sprintf(" Three Children as External: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getThreeChildrenExternalCount(), - ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); + OctreeElement::getThreeChildrenExternalCount(), + ((float)OctreeElement::getThreeChildrenExternalCount() / (float)nodeCount) * AS_PERCENT); statsString += QString().sprintf(" Children as External Array: %10.llu nodes (%5.2f%%)\r\n", - OctreeElement::getExternalChildrenCount(), - ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT); + OctreeElement::getExternalChildrenCount(), + ((float)OctreeElement::getExternalChildrenCount() / (float)nodeCount) * AS_PERCENT); checkSum = OctreeElement::getSingleChildrenCount() + OctreeElement::getTwoChildrenOffsetCount() + OctreeElement::getTwoChildrenExternalCount() + @@ -1178,9 +1180,12 @@ void OctreeServer::sendStatsPacket() { quint64 oneSecondAgo = usecTimestampNow() - USECS_PER_SECOND; statsObject1[baseName + QString(".0.6.threads.1.processing")] = (double)howManyThreadsDidProcess(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] = (double)howManyThreadsDidPacketDistributor(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] = (double)howManyThreadsDidHandlePacketSend(oneSecondAgo); - statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] = (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.2.packetDistributor")] = + (double)howManyThreadsDidPacketDistributor(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.3.handlePacektSend")] = + (double)howManyThreadsDidHandlePacketSend(oneSecondAgo); + statsObject1[baseName + QString(".0.6.threads.4.writeDatagram")] = + (double)howManyThreadsDidCallWriteDatagram(oneSecondAgo); statsObject1[baseName + QString(".1.1.octree.elementCount")] = (double)OctreeElement::getNodeCount(); statsObject1[baseName + QString(".1.2.octree.internalElementCount")] = (double)OctreeElement::getInternalNodeCount(); @@ -1193,8 +1198,10 @@ void OctreeServer::sendStatsPacket() { statsObject2[baseName + QString(".2.outbound.data.totalPackets")] = (double)OctreeSendThread::_totalPackets; statsObject2[baseName + QString(".2.outbound.data.totalBytes")] = (double)OctreeSendThread::_totalBytes; statsObject2[baseName + QString(".2.outbound.data.totalBytesWasted")] = (double)OctreeSendThread::_totalWastedBytes; - statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] = (double)OctreePacketData::getTotalBytesOfOctalCodes(); - statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfBitMasks(); + statsObject2[baseName + QString(".2.outbound.data.totalBytesOctalCodes")] = + (double)OctreePacketData::getTotalBytesOfOctalCodes(); + statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = + (double)OctreePacketData::getTotalBytesOfBitMasks(); statsObject2[baseName + QString(".2.outbound.data.totalBytesBitMasks")] = (double)OctreePacketData::getTotalBytesOfColor(); statsObject2[baseName + QString(".2.outbound.timing.1.avgLoopTime")] = getAverageLoopTime(); @@ -1209,13 +1216,20 @@ void OctreeServer::sendStatsPacket() { static QJsonObject statsObject3; - statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); - statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = (double)_octreeInboundPacketProcessor->getTotalElementsProcessed(); - statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] = (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); - statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] = (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement(); - statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] = (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement(); + statsObject3[baseName + QString(".3.inbound.data.1.totalPackets")] = + (double)_octreeInboundPacketProcessor->getTotalPacketsProcessed(); + statsObject3[baseName + QString(".3.inbound.data.2.totalElements")] = + (double)_octreeInboundPacketProcessor->getTotalElementsProcessed(); + statsObject3[baseName + QString(".3.inbound.timing.1.avgTransitTimePerPacket")] = + (double)_octreeInboundPacketProcessor->getAverageTransitTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.2.avgProcessTimePerPacket")] = + (double)_octreeInboundPacketProcessor->getAverageProcessTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.3.avgLockWaitTimePerPacket")] = + (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerPacket(); + statsObject3[baseName + QString(".3.inbound.timing.4.avgProcessTimePerElement")] = + (double)_octreeInboundPacketProcessor->getAverageProcessTimePerElement(); + statsObject3[baseName + QString(".3.inbound.timing.5.avgLockWaitTimePerElement")] = + (double)_octreeInboundPacketProcessor->getAverageLockWaitTimePerElement(); NodeList::getInstance()->sendStatsToDomainServer(statsObject3); } From 7a649084bd5364a9cc950466f66d8e56fcb9abd9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Mar 2014 14:07:21 -0700 Subject: [PATCH 17/29] reduce the lean recovery timescale --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5fc9f874aa..618cda1199 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -362,7 +362,7 @@ void MyAvatar::updateFromGyros(float deltaTime) { } } else { // restore rotation, lean to neutral positions - const float RESTORE_PERIOD = 1.f; // seconds + const float RESTORE_PERIOD = 0.25f; // seconds float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage)); head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage)); From 63c4af895049dac5bb652f6c36833271373b3d6b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 26 Mar 2014 14:10:43 -0700 Subject: [PATCH 18/29] remove cruft: Head::applyCollision() --- interface/src/avatar/Head.cpp | 21 --------------------- interface/src/avatar/Head.h | 3 --- 2 files changed, 24 deletions(-) diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index b844c08e9a..b1265a1e51 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -212,27 +212,6 @@ float Head::getTweakedRoll() const { return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } -void Head::applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration) { - // compute lean angles - Avatar* owningAvatar = static_cast(_owningAvatar); - glm::quat bodyRotation = owningAvatar->getOrientation(); - glm::vec3 neckPosition; - if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { - glm::vec3 yAxis = bodyRotation * glm::vec3(0.f, 1.f, 0.f); - glm::vec3 leverArm = _position - neckPosition; - if (glm::dot(leverArm, yAxis) > 0.f) { - float neckLength = glm::length(_position - neckPosition); - if (neckLength > 0.f) { - glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); - glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); - float forward = DEGREES_PER_RADIAN * glm::dot(penetration, zAxis) / neckLength; - float sideways = DEGREES_PER_RADIAN * glm::dot(penetration, xAxis) / neckLength; - addLean(sideways, forward); - } - } - } -} - void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { Application::getInstance()->getGlowEffect()->begin(); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index ef38ae3ed0..733323abc5 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -82,9 +82,6 @@ public: virtual float getTweakedPitch() const; virtual float getTweakedYaw() const; virtual float getTweakedRoll() const; - - // move the head to avoid given collision info - void applyCollision(const glm::vec3& contactPoint, const glm::vec3& penetration); private: // disallow copies of the Head, copy of owning Avatar is disallowed too From 2257afd390e59ece7ef5398906df44ad95a50158 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 15:20:27 -0700 Subject: [PATCH 19/29] removed unneeded call to disconnect() --- interface/src/Audio.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c2d5f535a7..1575375d4b 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -840,7 +840,6 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) // cleanup any previously initialized device if (_audioOutput) { _audioOutput->stop(); - disconnect(_outputDevice, 0, 0, 0); _outputDevice = NULL; delete _audioOutput; From 411ebf805a11148cf81fbcd90b8dfbf7b469f7b1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 15:42:53 -0700 Subject: [PATCH 20/29] use InvokeMethod() for all menu scripting operations --- interface/src/Menu.h | 4 ++-- interface/src/scripting/MenuScriptingInterface.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9452ba220d..983264ea55 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -66,8 +66,6 @@ public: static Menu* getInstance(); ~Menu(); - bool isOptionChecked(const QString& menuOption); - void setIsOptionChecked(const QString& menuOption, bool isChecked); void triggerOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption); @@ -133,6 +131,8 @@ public slots: void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); void removeMenuItem(const QString& menuName, const QString& menuitem); + bool isOptionChecked(const QString& menuOption); + void setIsOptionChecked(const QString& menuOption, bool isChecked); private slots: void aboutApp(); diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index ccb3fe5eb9..4f9003b288 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -65,9 +65,15 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& }; bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { - return Menu::getInstance()->isOptionChecked(menuOption); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menuOption)); + return result; } void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { - return Menu::getInstance()->setIsOptionChecked(menuOption, isChecked); + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); } From 52c8a865f1a815e6b8c78ca3c4933664a55da4d5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 15:53:19 -0700 Subject: [PATCH 21/29] Provide a means of tracking resource download progress. Until we have a user-friendly progress meter/throbber, put the download progress in the extended stats. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 15 +++++++++++++-- libraries/shared/src/ResourceCache.cpp | 11 +++++++---- libraries/shared/src/ResourceCache.h | 17 ++++++++++++++++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index f5c7f225df..711af2ae2d 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13c05fa702..edeb454f11 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2676,7 +2676,7 @@ void Application::displayStats() { glm::vec3 avatarPos = _myAvatar->getPosition(); - lines = _statsExpanded ? 4 : 3; + lines = _statsExpanded ? 5 : 3; displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -2713,12 +2713,23 @@ void Application::displayStats() { verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT); + + stringstream downloadStats; + downloadStats << "Downloads: "; + foreach (Resource* resource, ResourceCache::getLoadingRequests()) { + const float MAXIMUM_PERCENTAGE = 100.0f; + downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% "; + } + downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)"; + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT); } verticalOffset = 0; horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410); - lines = _statsExpanded ? 11 : 3; + lines = _statsExpanded ? 12 : 3; displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 6cca856b78..c7858e6e20 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -63,10 +63,12 @@ void ResourceCache::attemptRequest(Resource* resource) { return; } _requestLimit--; + _loadingRequests.append(resource); resource->makeRequest(); } -void ResourceCache::requestCompleted() { +void ResourceCache::requestCompleted(Resource* resource) { + _loadingRequests.removeOne(resource); _requestLimit++; // look for the highest priority pending request @@ -96,6 +98,7 @@ const int DEFAULT_REQUEST_LIMIT = 10; int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; QList > ResourceCache::_pendingRequests; +QList ResourceCache::_loadingRequests; Resource::Resource(const QUrl& url, bool delayLoad) : _url(url), @@ -121,7 +124,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) : Resource::~Resource() { if (_reply) { - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); delete _reply; } } @@ -215,7 +218,7 @@ void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { _replyTimer->disconnect(this); _replyTimer->deleteLater(); _replyTimer = NULL; - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); downloadFinished(reply); } @@ -250,7 +253,7 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) _replyTimer->disconnect(this); _replyTimer->deleteLater(); _replyTimer = NULL; - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); // retry for certain types of failures switch (error) { diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index a544f43731..2fcda0bb98 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -37,6 +37,10 @@ public: static void setRequestLimit(int limit) { _requestLimit = limit; } static int getRequestLimit() { return _requestLimit; } + static const QList& getLoadingRequests() { return _loadingRequests; } + + static int getPendingRequestCount() { return _pendingRequests.size(); } + ResourceCache(QObject* parent = NULL); virtual ~ResourceCache(); @@ -58,7 +62,7 @@ protected: void addUnusedResource(const QSharedPointer& resource); static void attemptRequest(Resource* resource); - static void requestCompleted(); + static void requestCompleted(Resource* resource); private: @@ -70,6 +74,7 @@ private: static QNetworkAccessManager* _networkAccessManager; static int _requestLimit; static QList > _pendingRequests; + static QList _loadingRequests; }; /// Base class for resources. @@ -102,6 +107,15 @@ public: /// Checks whether the resource has loaded. bool isLoaded() const { return _loaded; } + /// For loading resources, returns the number of bytes received. + qint64 getBytesReceived() const { return _bytesReceived; } + + /// For loading resources, returns the number of total bytes (or zero if unknown). + qint64 getBytesTotal() const { return _bytesTotal; } + + /// For loading resources, returns the load progress. + float getProgress() const { return (_bytesTotal == 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } + void setSelf(const QWeakPointer& self) { _self = self; } void setCache(ResourceCache* cache) { _cache = cache; } @@ -152,6 +166,7 @@ private: int _lruKey; QNetworkReply* _reply; QTimer* _replyTimer; + int _index; qint64 _bytesReceived; qint64 _bytesTotal; int _attempts; From 1cae0dc94c89efca470543e42c6b2f4e3beb55cb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 16:01:07 -0700 Subject: [PATCH 22/29] Adjust avatar LOD faster. Closes #2496. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 14 +++++++++----- interface/src/Menu.h | 1 + 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 711af2ae2d..da8827d89d 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 30be26ee96..e0da1c0318 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -62,7 +62,8 @@ Menu* Menu::getInstance() { const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f}; const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f; const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f; -const int FIVE_SECONDS_OF_FRAMES = 5 * 60; +const int ONE_SECOND_OF_FRAMES = 60; +const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES; Menu::Menu() : _actionHash(), @@ -82,6 +83,7 @@ Menu::Menu() : _lastAdjust(usecTimestampNow()), _lastAvatarDetailDrop(usecTimestampNow()), _fpsAverage(FIVE_SECONDS_OF_FRAMES), + _fastFPSAverage(ONE_SECOND_OF_FRAMES), _loginAction(NULL) { Application *appInstance = Application::getInstance(); @@ -1192,17 +1194,19 @@ void Menu::autoAdjustLOD(float currentFPS) { currentFPS = ASSUMED_FPS; } _fpsAverage.updateAverage(currentFPS); + _fastFPSAverage.updateAverage(currentFPS); quint64 now = usecTimestampNow(); - if (_fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS) { - if (now - _lastAvatarDetailDrop > ADJUST_LOD_DOWN_DELAY) { + const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000; + if (_fastFPSAverage.getAverage() < ADJUST_LOD_DOWN_FPS) { + if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { // attempt to lower the detail in proportion to the fps difference float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f; - _avatarLODDistanceMultiplier *= (targetFps / _fpsAverage.getAverage()); + _avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); _lastAvatarDetailDrop = now; } - } else if (_fpsAverage.getAverage() > ADJUST_LOD_UP_FPS) { + } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { // let the detail level creep slowly upwards const float DISTANCE_DECREASE_RATE = 0.01f; const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9452ba220d..23ddb8d3dc 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -211,6 +211,7 @@ private: quint64 _lastAdjust; quint64 _lastAvatarDetailDrop; SimpleMovingAverage _fpsAverage; + SimpleMovingAverage _fastFPSAverage; QAction* _loginAction; QAction* _chatAction; }; From 26da3bcefd86be8ee4096930a16e6d0669144b96 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 16:28:03 -0700 Subject: [PATCH 23/29] Bump up the rate at which we increase detail. --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e0da1c0318..79b0a23ce5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1208,7 +1208,7 @@ void Menu::autoAdjustLOD(float currentFPS) { } } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { // let the detail level creep slowly upwards - const float DISTANCE_DECREASE_RATE = 0.01f; + const float DISTANCE_DECREASE_RATE = 0.02f; const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f; _avatarLODDistanceMultiplier = qMax(MINIMUM_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE); From 11c089c2219e65945dc4c94216db0fc8ed7e17c4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 16:48:57 -0700 Subject: [PATCH 24/29] add support for getting the actual default audio devices in windows by name --- interface/src/Audio.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 1575375d4b..75f624af7d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -143,6 +143,28 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } #endif +#ifdef WIN32 + QString deviceName; + if (mode == QAudio::AudioInput) { + WAVEINCAPS wic; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); + //Use the received manufacturer id to get the device's real name + waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); + qDebug() << "input device:" << wic.szPname; + deviceName = wic.szPname; + } else { + WAVEOUTCAPS woc; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); + //Use the received manufacturer id to get the device's real name + waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); + qDebug() << "output device:" << woc.szPname; + deviceName = woc.szPname; + } + return getNamedAudioDeviceForMode(mode, deviceName); +#endif + // fallback for failed lookup is the default device return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice(); From 0435b16668830623440b6e96695e038814a98edf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 16:52:24 -0700 Subject: [PATCH 25/29] fix formatting --- interface/src/Audio.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 75f624af7d..321a4ea2ad 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -144,25 +144,25 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } #endif #ifdef WIN32 - QString deviceName; - if (mode == QAudio::AudioInput) { - WAVEINCAPS wic; - // first use WAVE_MAPPER to get the default devices manufacturer ID - waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); - //Use the received manufacturer id to get the device's real name - waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); - qDebug() << "input device:" << wic.szPname; - deviceName = wic.szPname; - } else { - WAVEOUTCAPS woc; - // first use WAVE_MAPPER to get the default devices manufacturer ID - waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); - //Use the received manufacturer id to get the device's real name - waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); - qDebug() << "output device:" << woc.szPname; - deviceName = woc.szPname; - } - return getNamedAudioDeviceForMode(mode, deviceName); + QString deviceName; + if (mode == QAudio::AudioInput) { + WAVEINCAPS wic; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); + //Use the received manufacturer id to get the device's real name + waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); + qDebug() << "input device:" << wic.szPname; + deviceName = wic.szPname; + } else { + WAVEOUTCAPS woc; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); + //Use the received manufacturer id to get the device's real name + waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); + qDebug() << "output device:" << woc.szPname; + deviceName = woc.szPname; + } + return getNamedAudioDeviceForMode(mode, deviceName); #endif From 7eab276122185e1af6e630062144a8ac0c7bcf54 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 26 Mar 2014 17:19:23 -0700 Subject: [PATCH 26/29] suppress debug in ScriptEngine, fix spatialization from FS head changes --- interface/src/Audio.cpp | 2 +- interface/src/avatar/Head.cpp | 3 --- libraries/script-engine/src/ScriptEngine.cpp | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c2d5f535a7..c222e2ebd2 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -500,7 +500,7 @@ void Audio::handleAudioInput() { if (audioMixer && audioMixer->getActiveSocket()) { MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition(); - glm::quat headOrientation = interfaceAvatar->getHead()->getOrientation(); + glm::quat headOrientation = interfaceAvatar->getHead()->getTweakedOrientation(); // we need the amount of bytes in the buffer + 1 for type // + 12 for 3 floats for position + float for bearing + 1 attenuation byte diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 4a81df8b74..abbc408650 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -55,9 +55,6 @@ void Head::reset() { _faceModel.reset(); } - - - void Head::simulate(float deltaTime, bool isMine, bool billboard) { // Update audio trailing average for rendering facial animations diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c0834aad9d..1f1eab6baf 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -463,7 +463,6 @@ void ScriptEngine::timerFired() { if (!callingTimer->isActive()) { // this timer is done, we can kill it - qDebug() << "Deleting a single shot timer"; delete callingTimer; } } From c6c4b586992db4820dd4531df9bf9b6d660f44fb Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:31:39 -0700 Subject: [PATCH 27/29] adds reset of octree element population statistics --- libraries/octree/src/OctreeElement.cpp | 5 +++++ libraries/octree/src/OctreeElement.h | 1 + 2 files changed, 6 insertions(+) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index cdc4f419c0..7697338a28 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -29,6 +29,11 @@ quint64 OctreeElement::_externalChildrenMemoryUsage = 0; quint64 OctreeElement::_voxelNodeCount = 0; quint64 OctreeElement::_voxelNodeLeafCount = 0; +void OctreeElement::resetPopulationStatistics() { + _voxelNodeCount = 0; + _voxelNodeLeafCount = 0; +} + OctreeElement::OctreeElement() { // Note: you must call init() from your subclass, otherwise the OctreeElement will not be properly // initialized. You will see DEADBEEF in your memory debugger if you have not properly called init() diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index ca2fa0b540..a37866bdbe 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -152,6 +152,7 @@ public: static void addUpdateHook(OctreeElementUpdateHook* hook); static void removeUpdateHook(OctreeElementUpdateHook* hook); + static void resetPopulationStatistics(); static unsigned long getNodeCount() { return _voxelNodeCount; } static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; } static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; } From 55b9dd1aee2da5deac9b69ebab1884b1303605d0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:32:19 -0700 Subject: [PATCH 28/29] remove non-locking version of nodeWithUUID() --- libraries/shared/src/NodeList.cpp | 15 +++------------ libraries/shared/src/NodeList.h | 3 +-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 6eb6eb77d8..dc5a419295 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -357,18 +357,9 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) { return 0; } -SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) { - SharedNodePointer node; - // if caller wants us to block and guarantee the correct answer, then honor that request - if (blockingLock) { - // this will block till we can get access - QMutexLocker locker(&_nodeHashMutex); - node = _nodeHash.value(nodeUUID); - } else if (_nodeHashMutex.tryLock()) { // some callers are willing to get wrong answers but not block - node = _nodeHash.value(nodeUUID); - _nodeHashMutex.unlock(); - } - return node; +SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { + QMutexLocker locker(&_nodeHashMutex); + return _nodeHash.value(nodeUUID); } SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 0a59537a46..f10e01f3f4 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -103,8 +103,7 @@ public: QByteArray constructPingReplyPacket(const QByteArray& pingPacket); void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); - /// passing false for blockingLock, will tryLock, and may return NULL when a node with the UUID actually does exist - SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); + SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, From 9fdfa4a7a810b45ed7a41810fd61ce5a9d671b8b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:33:32 -0700 Subject: [PATCH 29/29] switch to using SharedNodePointer instead of UUID in octree server send thread --- .../src/octree/OctreeQueryNode.cpp | 4 +-- .../src/octree/OctreeQueryNode.h | 2 +- .../src/octree/OctreeSendThread.cpp | 31 +++++-------------- .../src/octree/OctreeSendThread.h | 5 ++- assignment-client/src/octree/OctreeServer.cpp | 4 ++- 5 files changed, 15 insertions(+), 31 deletions(-) diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index e0ff29effd..984ce42631 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -76,9 +76,9 @@ void OctreeQueryNode::deleteLater() { } -void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) { +void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node) { // Create octree sending thread... - _octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer); + _octreeSendThread = new OctreeSendThread(octreeServer, node); _octreeSendThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index b7e68e805d..17d6f6337c 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -83,7 +83,7 @@ public: OctreeSceneStats stats; - void initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID); + void initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node); bool isOctreeSendThreadInitalized() { return _octreeSendThread; } void dumpOutOfView(); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 2fa6397ac5..382d8aa528 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -19,11 +19,10 @@ quint64 startSceneSleepTime = 0; quint64 endSceneSleepTime = 0; -OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) : - _nodeUUID(nodeUUID), +OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : _myServer(myServer), + _node(node), _packetData(), - _nodeMissingCount(0), _processLock(), _isShuttingDown(false) { @@ -44,7 +43,8 @@ OctreeSendThread::~OctreeSendThread() { } qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; - OctreeServer::clientDisconnected(); + _node.clear(); + OctreeServer::clientDisconnected(); } void OctreeSendThread::setIsShuttingDown() { @@ -68,35 +68,18 @@ bool OctreeSendThread::process() { return false; // exit early if we're shutting down } - const int MAX_NODE_MISSING_CHECKS = 10; - if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) { - qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount - << "times we checked, we are going to stop attempting to send."; - return false; // stop processing and shutdown, our node no longer exists - } - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - - // see if we can get access to our node, but don't wait on the lock, if the nodeList is busy - // it might not return a node that is known, but that's ok we can handle that case. - SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); - - if (node) { - _nodeMissingCount = 0; - OctreeQueryNode* nodeData = NULL; - - nodeData = (OctreeQueryNode*) node->getLinkedData(); + if (!_node.isNull()) { + OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(node, nodeData, viewFrustumChanged); + packetDistributor(_node, nodeData, viewFrustumChanged); } - } else { - _nodeMissingCount++; } } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index ab88121ee8..4e18ee9b2a 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -21,7 +21,7 @@ class OctreeSendThread : public GenericThread { Q_OBJECT public: - OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); + OctreeSendThread(OctreeServer* myServer, SharedNodePointer node); virtual ~OctreeSendThread(); void setIsShuttingDown(); @@ -38,7 +38,7 @@ protected: virtual bool process(); private: - QUuid _nodeUUID; + SharedNodePointer _node; OctreeServer* _myServer; int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); @@ -46,7 +46,6 @@ private: OctreePacketData _packetData; - int _nodeMissingCount; QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fc01b1d04a..fa087cced2 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -835,7 +835,7 @@ void OctreeServer::readPendingDatagrams() { if (debug) { qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode; } - nodeData->initializeOctreeSendThread(this, matchingNode->getUUID()); + nodeData->initializeOctreeSendThread(this, matchingNode); } } } else if (packetType == PacketTypeJurisdictionRequest) { @@ -852,7 +852,9 @@ void OctreeServer::readPendingDatagrams() { void OctreeServer::run() { _safeServerName = getMyServerName(); + // Before we do anything else, create our tree... + OctreeElement::resetPopulationStatistics(); _tree = createTree(); // use common init to setup common timers and logging