From 52511b2baf4fc74689ba8fc478930d53f1d99dbc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 13:59:03 -0800 Subject: [PATCH 01/12] Adding AABox::touches(box) --- libraries/octree/src/AABox.cpp | 7 +++++++ libraries/octree/src/AABox.h | 1 + 2 files changed, 8 insertions(+) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 670e2b8a6b..18b45becf5 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -117,6 +117,13 @@ bool AABox::contains(const AABox& otherBox) const { return true; } +bool AABox::touches(const AABox& otherBox) const { + glm::vec3 relativeCenter = _corner - otherBox._corner + (glm::vec3(_scale - otherBox._scale) * 0.5f); + float totalScale = _scale + otherBox._scale; + return fabs(relativeCenter.x) <= totalScale && + fabs(relativeCenter.y) <= totalScale && + fabs(relativeCenter.z) <= totalScale; +} // determines whether a value is within the expanded extents static bool isWithinExpanded(float value, float corner, float size, float expansion) { diff --git a/libraries/octree/src/AABox.h b/libraries/octree/src/AABox.h index aec0fff450..731b82be4c 100644 --- a/libraries/octree/src/AABox.h +++ b/libraries/octree/src/AABox.h @@ -61,6 +61,7 @@ public: bool contains(const glm::vec3& point) const; bool contains(const AABox& otherBox) const; + bool touches(const AABox& otherBox) const; bool expandedContains(const glm::vec3& point, float expansion) const; bool expandedIntersectsSegment(const glm::vec3& start, const glm::vec3& end, float expansion) const; bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face) const; From f6a49c96a72156ff09ea7b705b9e0a99abc02fa6 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 13:59:35 -0800 Subject: [PATCH 02/12] Adding ParticleTreeElement::findParticles(box, QVector) --- libraries/particles/src/ParticleTreeElement.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 8493d59bb8..d8bbbb3a54 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -93,6 +93,10 @@ public: bool removeParticleWithID(uint32_t id); + /// finds all particles that touch a box + /// \param box the query box + /// \param particles[out] vector of Particle pointers + void findParticles(const AABox& box, QVector& foundParticles); protected: virtual void init(unsigned char * octalCode); From 998b772be442f1fc1863dc4dea758dbde38c6111 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 14:01:03 -0800 Subject: [PATCH 03/12] Adding ParticleTreeElement::findParticles(box, QVector) --- libraries/particles/src/ParticleTreeElement.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 7994909004..2f7919b104 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -211,7 +211,21 @@ bool ParticleTreeElement::removeParticleWithID(uint32_t id) { return foundParticle; } - +void ParticleTreeElement::findParticles(const AABox& box, QVector& foundParticles) { + QList::iterator particleItr = _particles->begin(); + QList::iterator particleEnd = _particles->end(); + AABox particleBox; + while(particleItr != particleEnd) { + Particle* particle = &(*particleItr); + float radius = particle->getRadius(); + particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius); + // TODO: replace particleBox-box query with sphere-box + if (particleBox.touches(_box)) { + foundParticles.push_back(particle); + } + ++particleItr; + } +} int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { From 5cc2b029cffca882fad8baef93d3e7cbebe49543 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 14:01:34 -0800 Subject: [PATCH 04/12] Adding ParticleTree::findParticles(box, QVector) method --- libraries/particles/src/ParticleTree.cpp | 28 ++++++++++++++++++++++++ libraries/particles/src/ParticleTree.h | 6 +++++ 2 files changed, 34 insertions(+) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index 73e8dd6711..ea70d13c16 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -458,3 +458,31 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi recurseTreeWithOperation(findAndDeleteOperation, &args); } } + +class FindParticlesArgs { +public: + FindParticlesArgs(const AABox& box) + : _box(box), _foundParticles() { + } + + AABox _box; + QVector _foundParticles; +}; + +bool findOperation(OctreeElement* element, void* extraData) { + FindParticlesArgs* args = static_cast< FindParticlesArgs*>(extraData); + const AABox& elementBox = element->getAABox(); + if (elementBox.touches(args->_box)) { + ParticleTreeElement* particleTreeElement = static_cast(element); + particleTreeElement->findParticles(args->_box, args->_foundParticles); + return true; + } + return false; +} + +void ParticleTree::findParticles(const AABox& box, QVector foundParticles) { + FindParticlesArgs args(box); + recurseTreeWithOperation(findOperation, &args); + // swap the two lists of particle pointers instead of copy + foundParticles.swap(args._foundParticles); +} diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 33e5d5fe75..e0679f192b 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -53,6 +53,12 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); + /// finds all particles that touch a box + /// \param box the query box + /// \param particles[out] vector of Particle pointer + /// \remark Side effect: any initial contents in particles will be lost + void findParticles(const AABox& box, QVector particles); + private: static bool updateOperation(OctreeElement* element, void* extraData); From 2f02ee0b315a8c0ff2c3a58f924a615fbb0ef00c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 16:29:44 -0800 Subject: [PATCH 05/12] Moving applyHardCollision to the Particle class. Adding daming and elasticity to CollisionInfo. --- libraries/particles/src/Particle.cpp | 43 +++++++++++++++ libraries/particles/src/Particle.h | 3 + .../particles/src/ParticleCollisionSystem.cpp | 55 ++++--------------- .../particles/src/ParticleCollisionSystem.h | 2 +- libraries/shared/src/CollisionInfo.h | 10 +++- 5 files changed, 68 insertions(+), 45 deletions(-) diff --git a/libraries/particles/src/Particle.cpp b/libraries/particles/src/Particle.cpp index 5cf5e9248d..206f075df4 100644 --- a/libraries/particles/src/Particle.cpp +++ b/libraries/particles/src/Particle.cpp @@ -668,7 +668,50 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz } } +// HALTING_* params are determined using expected acceleration of gravity over some timescale. +// This is a HACK for particles that bounce in a 1.0 gravitational field and should eventually be made more universal. +const float HALTING_PARTICLE_PERIOD = 0.0167f; // ~1/60th of a second +const float HALTING_PARTICLE_SPEED = 9.8 * HALTING_PARTICLE_PERIOD / (float)(TREE_SCALE); + +void Particle::applyHardCollision(const CollisionInfo& collisionInfo) { + // + // Update the particle in response to a hard collision. Position will be reset exactly + // to outside the colliding surface. Velocity will be modified according to elasticity. + // + // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) + // if elasticity = 1.0, collision is 100% elastic. + // + glm::vec3 position = getPosition(); + glm::vec3 velocity = getVelocity(); + + const float EPSILON = 0.0f; + glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; + float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); + if (velocityDotPenetration < EPSILON) { + // particle is moving into collision surface + // + // TODO: do something smarter here by comparing the mass of the particle vs that of the other thing + // (other's mass could be stored in the Collision Info). The smaller mass should surrender more + // position offset and should slave more to the other's velocity in the static-friction case. + position -= collisionInfo._penetration; + + if (glm::length(relativeVelocity) < HALTING_PARTICLE_SPEED) { + // static friction kicks in and particle moves with colliding object + velocity = collisionInfo._addedVelocity; + } else { + glm::vec3 direction = glm::normalize(collisionInfo._penetration); + velocity += glm::dot(relativeVelocity, direction) * (1.0f + collisionInfo._elasticity) * direction; // dynamic reflection + velocity += glm::clamp(collisionInfo._damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction + } + } + + // change the local particle too... + setPosition(position); + setVelocity(velocity); +} + // MIN_VALID_SPEED is obtained by computing speed gained at one gravity during the shortest expected frame period +// This is a HACK for particles that bounce in a 1.0 gravitational field and should eventually be made more universal. const float MIN_EXPECTED_FRAME_PERIOD = 0.005f; // 1/200th of a second const float MIN_VALID_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE); diff --git a/libraries/particles/src/Particle.h b/libraries/particles/src/Particle.h index e534c7b418..96e58f0693 100644 --- a/libraries/particles/src/Particle.h +++ b/libraries/particles/src/Particle.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -240,6 +241,8 @@ public: static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); + void applyHardCollision(const CollisionInfo& collisionInfo); + void update(const uint64_t& now); void collisionWithParticle(Particle* other); void collisionWithVoxel(VoxelDetail* voxel); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 7e8866e7d4..d2eb291a9d 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -73,6 +73,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { const float DAMPING = 0.05f; const float COLLISION_FREQUENCY = 0.5f; CollisionInfo collisionInfo; + collisionInfo._damping = DAMPING; + collisionInfo._elasticity = ELASTICITY; VoxelDetail* voxelDetails = NULL; if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) { @@ -81,7 +83,8 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) { updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); collisionInfo._penetration /= (float)(TREE_SCALE); - applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo); + particle->applyHardCollision(collisionInfo); + queueParticlePropertiesUpdate(particle); delete voxelDetails; // cleanup returned details } @@ -161,6 +164,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { if (_selfAvatar) { AvatarData* avatar = (AvatarData*)_selfAvatar; CollisionInfo collisionInfo; + collisionInfo._damping = DAMPING; + collisionInfo._elasticity = ELASTICITY; if (avatar->findSphereCollision(center, radius, collisionInfo)) { collisionInfo._addedVelocity /= (float)(TREE_SCALE); glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity(); @@ -185,7 +190,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); collisionInfo._penetration /= (float)(TREE_SCALE); - applyHardCollision(particle, elasticity, damping, collisionInfo); + particle->applyHardCollision(collisionInfo); + queueParticlePropertiesUpdate(particle); } } } @@ -224,51 +230,14 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { // } } -// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity -void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo) { - // - // Update the particle in response to a hard collision. Position will be reset exactly - // to outside the colliding surface. Velocity will be modified according to elasticity. - // - // if elasticity = 0.0, collision is inelastic (vel normal to collision is lost) - // if elasticity = 1.0, collision is 100% elastic. - // - glm::vec3 position = particle->getPosition(); - glm::vec3 velocity = particle->getVelocity(); - - const float EPSILON = 0.0f; - glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity; - float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration); - if (velocityDotPenetration < EPSILON) { - // particle is moving into collision surface - position -= collisionInfo._penetration; - - if (glm::length(relativeVelocity) < HALTING_SPEED) { - // static friction kicks in and particle moves with colliding object - velocity = collisionInfo._addedVelocity; - } else { - glm::vec3 direction = glm::normalize(collisionInfo._penetration); - velocity += glm::dot(relativeVelocity, direction) * (1.0f + elasticity) * direction; // dynamic reflection - velocity += glm::clamp(damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction - } - } - const bool wantDebug = false; - if (wantDebug) { - printf("ParticleCollisionSystem::applyHardCollision() particle id:%d new velocity:%f,%f,%f inHand:%s\n", - particle->getID(), velocity.x, velocity.y, velocity.z, debug::valueOf(particle->getInHand())); - } - - // send off the result to the particle server +void ParticleCollisionSystem::queueParticlePropertiesUpdate(Particle* particle) { + // queue the result for sending to the particle server ParticleProperties properties; ParticleID particleID(particle->getID()); properties.copyFromParticle(*particle); - properties.setPosition(position * (float)TREE_SCALE); - properties.setVelocity(velocity * (float)TREE_SCALE); + properties.setPosition(particle->getPosition() * (float)TREE_SCALE); + properties.setVelocity(particle->getVelocity() * (float)TREE_SCALE); _packetSender->queueParticleEditMessage(PACKET_TYPE_PARTICLE_ADD_OR_EDIT, particleID, properties); - - // change the local particle too... - particle->setPosition(position); - particle->setVelocity(velocity); } diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index cf52d01a7a..b96d3f07f3 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -47,7 +47,7 @@ public: void updateCollisionWithVoxels(Particle* particle); void updateCollisionWithParticles(Particle* particle); void updateCollisionWithAvatars(Particle* particle); - void applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo); + void queueParticlePropertiesUpdate(Particle* particle); void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency); private: diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 1e4801788e..38ae64e30c 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -13,11 +13,19 @@ class CollisionInfo { public: - CollisionInfo() : _penetration(0.f), _addedVelocity(0.f) { } + CollisionInfo() + : _damping(0.f), + _elasticity(1.f), + _penetration(0.f), + _addedVelocity(0.f) { + } + ~CollisionInfo() {} //glm::vec3 _point; //glm::vec3 _normal; + float _damping; + float _elasticity; glm::vec3 _penetration; // depth that bodyA is penetrates bodyB glm::vec3 _addedVelocity; }; From 31a2e467f4a00d8577d724fb168f7ff6f18fadc9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sun, 26 Jan 2014 16:14:11 -0800 Subject: [PATCH 06/12] more efficient ParticleTree::getParticles() and adding ParticleTree::getParticlesForUpdate() --- libraries/particles/src/ParticleTree.cpp | 74 +++++++++---------- libraries/particles/src/ParticleTree.h | 20 +++-- .../particles/src/ParticleTreeElement.cpp | 47 ++++++------ libraries/particles/src/ParticleTreeElement.h | 16 ++-- .../src/ParticlesScriptingInterface.cpp | 3 +- 5 files changed, 85 insertions(+), 75 deletions(-) diff --git a/libraries/particles/src/ParticleTree.cpp b/libraries/particles/src/ParticleTree.cpp index d0e5060b4c..3e224be48b 100644 --- a/libraries/particles/src/ParticleTree.cpp +++ b/libraries/particles/src/ParticleTree.cpp @@ -170,16 +170,14 @@ public: bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData) { FindAllNearPointArgs* args = static_cast(extraData); - ParticleTreeElement* particleTreeElement = static_cast(element); - glm::vec3 penetration; - bool sphereIntersection = particleTreeElement->getAABox().findSpherePenetration(args->position, + bool sphereIntersection = element->getAABox().findSpherePenetration(args->position, args->targetRadius, penetration); - // If this particleTreeElement contains the point, then search it... + // If this element contains the point, then search it... if (sphereIntersection) { - QVector moreParticles = particleTreeElement->getParticles(args->position, args->targetRadius); - args->particles << moreParticles; + ParticleTreeElement* particleTreeElement = static_cast(element); + particleTreeElement->getParticles(args->position, args->targetRadius, args->particles); return true; // keep searching in case children have closer particles } @@ -187,13 +185,43 @@ bool ParticleTree::findInSphereOperation(OctreeElement* element, void* extraData return false; } -QVector ParticleTree::findParticles(const glm::vec3& center, float radius) { - QVector result; +void ParticleTree::findParticles(const glm::vec3& center, float radius, QVector& foundParticles) { FindAllNearPointArgs args = { center, radius }; lockForRead(); recurseTreeWithOperation(findInSphereOperation, &args); unlock(); - return args.particles; + // swap the two lists of particle pointers instead of copy + foundParticles.swap(args.particles); +} + +class FindParticlesInBoxArgs { +public: + FindParticlesInBoxArgs(const AABox& box) + : _box(box), _foundParticles() { + } + + AABox _box; + QVector _foundParticles; +}; + +bool findInBoxForUpdateOperation(OctreeElement* element, void* extraData) { + FindParticlesInBoxArgs* args = static_cast< FindParticlesInBoxArgs*>(extraData); + const AABox& elementBox = element->getAABox(); + if (elementBox.touches(args->_box)) { + ParticleTreeElement* particleTreeElement = static_cast(element); + particleTreeElement->getParticlesForUpdate(args->_box, args->_foundParticles); + return true; + } + return false; +} + +void ParticleTree::findParticlesForUpdate(const AABox& box, QVector foundParticles) { + FindParticlesInBoxArgs args(box); + lockForRead(); + recurseTreeWithOperation(findInBoxForUpdateOperation, &args); + unlock(); + // swap the two lists of particle pointers instead of copy + foundParticles.swap(args._foundParticles); } class FindByIDArgs { @@ -495,31 +523,3 @@ void ParticleTree::processEraseMessage(const QByteArray& dataByteArray, const Hi recurseTreeWithOperation(findAndDeleteOperation, &args); } } - -class FindParticlesArgs { -public: - FindParticlesArgs(const AABox& box) - : _box(box), _foundParticles() { - } - - AABox _box; - QVector _foundParticles; -}; - -bool findOperation(OctreeElement* element, void* extraData) { - FindParticlesArgs* args = static_cast< FindParticlesArgs*>(extraData); - const AABox& elementBox = element->getAABox(); - if (elementBox.touches(args->_box)) { - ParticleTreeElement* particleTreeElement = static_cast(element); - particleTreeElement->findParticles(args->_box, args->_foundParticles); - return true; - } - return false; -} - -void ParticleTree::findParticles(const AABox& box, QVector foundParticles) { - FindParticlesArgs args(box); - recurseTreeWithOperation(findOperation, &args); - // swap the two lists of particle pointers instead of copy - foundParticles.swap(args._foundParticles); -} diff --git a/libraries/particles/src/ParticleTree.h b/libraries/particles/src/ParticleTree.h index 44db78814c..a19bad9892 100644 --- a/libraries/particles/src/ParticleTree.h +++ b/libraries/particles/src/ParticleTree.h @@ -42,7 +42,19 @@ public: void storeParticle(const Particle& particle, Node* senderNode = NULL); const Particle* findClosestParticle(glm::vec3 position, float targetRadius); const Particle* findParticleByID(uint32_t id, bool alreadyLocked = false); - QVector findParticles(const glm::vec3& center, float radius); + + /// finds all particles that touch a sphere + /// \param center the center of the sphere + /// \param radius the radius of the sphere + /// \param foundParticles[out] vector of const Particle* + /// \remark Side effect: any initial contents in foundParticles will be lost + void findParticles(const glm::vec3& center, float radius, QVector& foundParticles); + + /// finds all particles that touch a box + /// \param box the query box + /// \param foundParticles[out] vector of non-const Particle* + /// \remark Side effect: any initial contents in particles will be lost + void findParticlesForUpdate(const AABox& box, QVector foundParticles); void addNewlyCreatedHook(NewlyCreatedParticleHook* hook); void removeNewlyCreatedHook(NewlyCreatedParticleHook* hook); @@ -54,12 +66,6 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const HifiSockAddr& senderSockAddr, Node* sourceNode); - /// finds all particles that touch a box - /// \param box the query box - /// \param particles[out] vector of Particle pointer - /// \remark Side effect: any initial contents in particles will be lost - void findParticles(const AABox& box, QVector particles); - private: static bool updateOperation(OctreeElement* element, void* extraData); diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index 0878436e31..72d3e1b7b0 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -185,21 +185,34 @@ const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) cons return closestParticle; } -QVector ParticleTreeElement::getParticles(glm::vec3 searchPosition, float searchRadius) const { - QVector results; +void ParticleTreeElement::getParticles(const glm::vec3& searchPosition, float searchRadius, QVector& foundParticles) const { uint16_t numberOfParticles = _particles->size(); for (uint16_t i = 0; i < numberOfParticles; i++) { const Particle* particle = &(*_particles)[i]; glm::vec3 particlePosition = particle->getPosition(); - float particleRadius = particle->getRadius(); - glm::vec3 penetration; - - // check to see that the particle (penetrator) penetrates the search area - if (findSphereSpherePenetration(particlePosition, particleRadius, searchPosition, searchRadius, penetration)) { - results << particle; + float distance = glm::length(particle->getPosition() - searchPosition); + if (distance < searchRadius + particle->getRadius()) { + foundParticles.push_back(particle); } } - return results; +} + +void ParticleTreeElement::getParticlesForUpdate(const AABox& box, QVector& foundParticles) { + QList::iterator particleItr = _particles->begin(); + QList::iterator particleEnd = _particles->end(); + AABox particleBox; + while(particleItr != particleEnd) { + Particle* particle = &(*particleItr); + float radius = particle->getRadius(); + // NOTE: we actually do box-box collision queries here, which is sloppy but good enough for now + // TODO: decide whether to replace particleBox-box query with sphere-box (requires a square root + // but will be slightly more accurate). + particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius); + if (particleBox.touches(_box)) { + foundParticles.push_back(particle); + } + ++particleItr; + } } const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const { @@ -228,22 +241,6 @@ bool ParticleTreeElement::removeParticleWithID(uint32_t id) { return foundParticle; } -void ParticleTreeElement::findParticles(const AABox& box, QVector& foundParticles) { - QList::iterator particleItr = _particles->begin(); - QList::iterator particleEnd = _particles->end(); - AABox particleBox; - while(particleItr != particleEnd) { - Particle* particle = &(*particleItr); - float radius = particle->getRadius(); - particleBox.setBox(particle->getPosition() - glm::vec3(radius), 2.f * radius); - // TODO: replace particleBox-box query with sphere-box - if (particleBox.touches(_box)) { - foundParticles.push_back(particle); - } - ++particleItr; - } -} - int ParticleTreeElement::readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index e967d9bd23..1d30bfbdfd 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -89,15 +89,21 @@ public: bool containsParticle(const Particle& particle) const; bool updateParticle(const Particle& particle); const Particle* getClosestParticle(glm::vec3 position) const; - QVector getParticles(glm::vec3 position, float radius) const; - const Particle* getParticleWithID(uint32_t id) const; - bool removeParticleWithID(uint32_t id); + /// finds all particles that touch a sphere + /// \param position the center of the query sphere + /// \param radius the radius of the query sphere + /// \param particles[out] vector of const Particle* + void getParticles(const glm::vec3& position, float radius, QVector& foundParticles) const; /// finds all particles that touch a box /// \param box the query box - /// \param particles[out] vector of Particle pointers - void findParticles(const AABox& box, QVector& foundParticles); + /// \param particles[out] vector of non-const Particle* + void getParticlesForUpdate(const AABox& box, QVector& foundParticles); + + const Particle* getParticleWithID(uint32_t id) const; + + bool removeParticleWithID(uint32_t id); protected: virtual void init(unsigned char * octalCode); diff --git a/libraries/particles/src/ParticlesScriptingInterface.cpp b/libraries/particles/src/ParticlesScriptingInterface.cpp index 0641cdba7c..2bbade964d 100644 --- a/libraries/particles/src/ParticlesScriptingInterface.cpp +++ b/libraries/particles/src/ParticlesScriptingInterface.cpp @@ -150,7 +150,8 @@ ParticleID ParticlesScriptingInterface::findClosestParticle(const glm::vec3& cen QVector ParticlesScriptingInterface::findParticles(const glm::vec3& center, float radius) const { QVector result; if (_particleTree) { - QVector particles = _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE); + QVector particles; + _particleTree->findParticles(center/(float)TREE_SCALE, radius/(float)TREE_SCALE, particles); foreach (const Particle* particle, particles) { ParticleID thisParticleID(particle->getID(), UNKNOWN_TOKEN, true); From 2fa3fc523ddea4335364db8b2e670110c62ef430 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jan 2014 17:51:05 -0800 Subject: [PATCH 07/12] Fix for AABox::touches(otherBox) --- libraries/octree/src/AABox.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/octree/src/AABox.cpp b/libraries/octree/src/AABox.cpp index 18b45becf5..1313111765 100644 --- a/libraries/octree/src/AABox.cpp +++ b/libraries/octree/src/AABox.cpp @@ -119,10 +119,10 @@ bool AABox::contains(const AABox& otherBox) const { bool AABox::touches(const AABox& otherBox) const { glm::vec3 relativeCenter = _corner - otherBox._corner + (glm::vec3(_scale - otherBox._scale) * 0.5f); - float totalScale = _scale + otherBox._scale; - return fabs(relativeCenter.x) <= totalScale && - fabs(relativeCenter.y) <= totalScale && - fabs(relativeCenter.z) <= totalScale; + float totalHalfScale = 0.5f * (_scale + otherBox._scale); + return fabs(relativeCenter.x) <= totalHalfScale && + fabs(relativeCenter.y) <= totalHalfScale && + fabs(relativeCenter.z) <= totalHalfScale; } // determines whether a value is within the expanded extents From 2b5675b077682beae29ba75002c07ea0910d674c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jan 2014 18:01:53 -0800 Subject: [PATCH 08/12] Fix issue #1684 : particles collide against avatars again. This a quick and dirty fix to resurrect particle-avatar collisions. We rebuild and pass a list of AvatarData* to the ParticleCollisionManager every call to update(). --- interface/src/Application.cpp | 9 +++- interface/src/avatar/AvatarManager.cpp | 9 +++- interface/src/avatar/AvatarManager.h | 2 + .../particles/src/ParticleCollisionSystem.cpp | 47 +++---------------- .../particles/src/ParticleCollisionSystem.h | 12 +++-- 5 files changed, 31 insertions(+), 48 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 285a395c07..258ce6037d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1796,7 +1796,7 @@ void Application::init() { _metavoxels.init(); - _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_myAvatar); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio); _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); @@ -2310,7 +2310,12 @@ void Application::update(float deltaTime) { updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... - _particleCollisionSystem.update(); // handle collisions for the particles... + + // collide the particles... + QVector avatars; + avatars.push_back(&_myAvatar); + _avatarManager.getAvatarBasePointers(avatars); + _particleCollisionSystem.update(avatars); } void Application::updateAvatar(float deltaTime) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..5ac521d838 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -230,4 +230,11 @@ void AvatarManager::clearHash() { while (removeAvatar != _avatarHash.end()) { removeAvatar = removeAvatarAtHashIterator(removeAvatar); } -} \ No newline at end of file +} + +void AvatarManager::getAvatarBasePointers(QVector& avatars) { + AvatarHash::iterator avatarItr = _avatarHash.begin(); + while (avatarItr != _avatarHash.end()) { + avatars.push_back( static_cast(avatarItr.value().data()) ); + } +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 7d0f11646d..82fe279254 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -36,6 +36,8 @@ public: void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); void clearHash(); + + void getAvatarBasePointers(QVector& avatars); public slots: void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index d2eb291a9d..d53898ac05 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -20,17 +20,16 @@ #include "ParticleTree.h" ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, - ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { - init(packetSender, particles, voxels, audio, selfAvatar); + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { + init(packetSender, particles, voxels, audio); } void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, - ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, AvatarData* selfAvatar) { + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { _packetSender = packetSender; _particles = particles; _voxels = voxels; _audio = audio; - _selfAvatar = selfAvatar; } ParticleCollisionSystem::~ParticleCollisionSystem() { @@ -52,8 +51,9 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr } -void ParticleCollisionSystem::update() { +void ParticleCollisionSystem::update(QVector& avatars) { // update all particles + _avatars.swap(avatars); _particles->lockForWrite(); _particles->recurseTreeWithOperation(updateOperation, this); _particles->unlock(); @@ -161,8 +161,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { glm::vec3 penetration; // first check the selfAvatar if set... - if (_selfAvatar) { - AvatarData* avatar = (AvatarData*)_selfAvatar; + for (int i = 0; i < _avatars.size(); ++i) { + AvatarData* avatar = _avatars[i]; CollisionInfo collisionInfo; collisionInfo._damping = DAMPING; collisionInfo._elasticity = ELASTICITY; @@ -195,39 +195,6 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { } } } - - // loop through all the other avatars for potential interactions... -// foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { -// //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n"; -// if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { -// AvatarData* avatar = static_cast(node->getLinkedData()); -// CollisionInfo collisionInfo; -// if (avatar->findSphereCollision(center, radius, collisionInfo)) { -// collisionInfo._addedVelocity /= (float)(TREE_SCALE); -// glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity(); -// if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) { -// // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar. -// // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle. -// // TODO: make this less hacky when we have more per-collision details -// float elasticity = ELASTICITY; -// float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED; -// float damping = DAMPING; -// if (attenuationFactor < 1.f) { -// collisionInfo._addedVelocity *= attenuationFactor; -// elasticity *= attenuationFactor; -// // NOTE: the math below keeps the damping piecewise continuous, -// // while ramping it up to 1.0 when attenuationFactor = 0 -// damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING); -// } -// // HACK END -// -// updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); -// collisionInfo._penetration /= (float)(TREE_SCALE); -// applyHardCollision(particle, ELASTICITY, damping, collisionInfo); -// } -// } -// } -// } } void ParticleCollisionSystem::queueParticlePropertiesUpdate(Particle* particle) { diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index b96d3f07f3..b4293071bb 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -34,15 +34,17 @@ class ParticleCollisionSystem { public: ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, VoxelTree* voxels = NULL, - AbstractAudioInterface* audio = NULL, - AvatarData* selfAvatar = NULL); + AbstractAudioInterface* audio = NULL); void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, - AbstractAudioInterface* audio = NULL, AvatarData* selfAvatar = NULL); + AbstractAudioInterface* audio = NULL); ~ParticleCollisionSystem(); - void update(); + /// Compute collisions for particles against: other particles, voxels, and avatars + /// \param avatars the list of avatars to collide against during this frame + void update(QVector& avatars); + void checkParticle(Particle* particle); void updateCollisionWithVoxels(Particle* particle); void updateCollisionWithParticles(Particle* particle); @@ -58,7 +60,7 @@ private: ParticleTree* _particles; VoxelTree* _voxels; AbstractAudioInterface* _audio; - AvatarData* _selfAvatar; + QVector _avatars; // only used during update() }; #endif /* defined(__hifi__ParticleCollisionSystem__) */ From 167acb8873a0023db7f2ddb395e05c120a57049a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 14:16:34 -0800 Subject: [PATCH 09/12] Adding avatars back into ParticleCollisionSystem. Had to add Application::_myAvatar to the AvatarManager list so that particles would properly collide. --- interface/src/Application.cpp | 10 +- interface/src/avatar/AvatarManager.cpp | 174 +++++++++++------- interface/src/avatar/AvatarManager.h | 27 +-- interface/src/avatar/Hand.cpp | 13 +- interface/src/avatar/MyAvatar.cpp | 13 +- libraries/avatars/src/AvatarHashMap.cpp | 30 +++ libraries/avatars/src/AvatarHashMap.h | 37 ++++ .../particles/src/ParticleCollisionSystem.cpp | 15 +- .../particles/src/ParticleCollisionSystem.h | 9 +- 9 files changed, 216 insertions(+), 112 deletions(-) create mode 100644 libraries/avatars/src/AvatarHashMap.cpp create mode 100644 libraries/avatars/src/AvatarHashMap.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 838c5c1763..8bc845ff24 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1754,6 +1754,7 @@ void Application::init() { _myCamera.setMode(CAMERA_MODE_FIRST_PERSON); _myCamera.setModeShiftRate(1.0f); _myAvatar.setDisplayingLookatVectors(false); + _avatarManager.setMyAvatar(&_myAvatar); _mirrorCamera.setMode(CAMERA_MODE_MIRROR); _mirrorCamera.setAspectRatio((float)MIRROR_VIEW_WIDTH / (float)MIRROR_VIEW_HEIGHT); @@ -1800,7 +1801,7 @@ void Application::init() { _metavoxels.init(); - _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio); + _particleCollisionSystem.init(&_particleEditSender, _particles.getTree(), _voxels.getTree(), &_audio, &_avatarManager); _palette.init(_glWidget->width(), _glWidget->height()); _palette.addAction(Menu::getInstance()->getActionForOption(MenuOption::VoxelAddMode), 0, 0); @@ -2314,12 +2315,7 @@ void Application::update(float deltaTime) { updateCursor(deltaTime); // Handle cursor updates _particles.update(); // update the particles... - - // collide the particles... - QVector avatars; - avatars.push_back(&_myAvatar); - _avatarManager.getAvatarBasePointers(avatars); - _particleCollisionSystem.update(avatars); + _particleCollisionSystem.update(); // collide the particles... } void Application::updateAvatar(float deltaTime) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 5ac521d838..417cdcb2ac 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -16,17 +16,28 @@ #include "AvatarManager.h" + AvatarManager::AvatarManager(QObject* parent) : _lookAtTargetAvatar(), _lookAtOtherPosition(), _lookAtIndicatorScale(1.0f), - _avatarHash(), - _avatarFades() + _avatarFades(), + _myAvatar(NULL) { // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar qRegisterMetaType >("NodeWeakPointer"); } +void AvatarManager::setMyAvatar(MyAvatar* myAvatar) { + if (!_myAvatar) { + // can only ever set this once + _myAvatar = myAvatar; + // add _myAvatar to the list + AvatarSharedPointer myPointer = AvatarSharedPointer(_myAvatar); + _avatarHash.insert(MY_AVATAR_KEY, myPointer); + } +} + void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateLookatTargetAvatar()"); @@ -34,22 +45,26 @@ void AvatarManager::updateLookAtTargetAvatar(glm::vec3 &eyePosition) { Application* applicationInstance = Application::getInstance(); if (!applicationInstance->isMousePressed()) { - foreach (const AvatarSharedPointer& avatar, _avatarHash) { - float distance; - - if (avatar->findRayIntersection(applicationInstance->getMouseRayOrigin(), - applicationInstance->getMouseRayDirection(), distance)) { - // rescale to compensate for head embiggening - eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * - (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); - - _lookAtIndicatorScale = avatar->getHead().getScale(); - _lookAtOtherPosition = avatar->getHead().getPosition(); - - _lookAtTargetAvatar = avatar; - - // found the look at target avatar, return - return; + glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); + glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); + + foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { + Avatar* avatar = static_cast(avatarPointer.data()); + if (avatar != static_cast(_myAvatar)) { + float distance; + if (avatar->findRayIntersection(mouseOrigin, mouseDirection, distance)) { + // rescale to compensate for head embiggening + eyePosition = (avatar->getHead().calculateAverageEyePosition() - avatar->getHead().getScalePivot()) * + (avatar->getScale() / avatar->getHead().getScale()) + avatar->getHead().getScalePivot(); + + _lookAtIndicatorScale = avatar->getHead().getScale(); + _lookAtOtherPosition = avatar->getHead().getPosition(); + + _lookAtTargetAvatar = avatarPointer; + + // found the look at target avatar, return + return; + } } } @@ -61,20 +76,28 @@ void AvatarManager::updateAvatars(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateAvatars()"); + Application* applicationInstance = Application::getInstance(); + glm::vec3 mouseOrigin = applicationInstance->getMouseRayOrigin(); + glm::vec3 mouseDirection = applicationInstance->getMouseRayDirection(); + // simulate avatars - AvatarHash::iterator avatar = _avatarHash.begin(); - if (avatar != _avatarHash.end()) { - if (avatar->data()->getOwningAvatarMixer()) { + AvatarHash::iterator avatarIterator = _avatarHash.begin(); + while (avatarIterator != _avatarHash.end()) { + if (MY_AVATAR_KEY == avatarIterator.key()) { + // for now skip updates to _myAvatar because it is done explicitly in Application + // TODO: update _myAvatar in this context + ++avatarIterator; + continue; + } + Avatar* avatar = static_cast(avatarIterator.value().data()); + if (avatar->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it - avatar->data()->simulate(deltaTime, NULL); - - Application* applicationInstance = Application::getInstance(); - - avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(), - applicationInstance->getMouseRayDirection()); + avatar->simulate(deltaTime, NULL); + avatar->setMouseRay(mouseOrigin, mouseDirection); + ++avatarIterator; } else { // the mixer that owned this avatar is gone, give it to the vector of fades and kill it - avatar = removeAvatarAtHashIterator(avatar); + avatarIterator = erase(avatarIterator); } } @@ -88,41 +111,44 @@ void AvatarManager::renderAvatars(bool forceRenderHead, bool selfAvatarOnly) { } PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::renderAvatars()"); + bool renderLookAtVectors = Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors); if (!selfAvatarOnly) { - // Render avatars of other nodes - foreach (const AvatarSharedPointer& avatar, _avatarHash) { + foreach (const AvatarSharedPointer& avatarPointer, _avatarHash) { + Avatar* avatar = static_cast(avatarPointer.data()); if (!avatar->isInitialized()) { avatar->init(); } - avatar->render(false); - avatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); + if (avatar == static_cast(_myAvatar)) { + avatar->render(forceRenderHead); + } else { + avatar->render(false); + } + avatar->setDisplayingLookatVectors(renderLookAtVectors); } - renderAvatarFades(); + } else if (_myAvatar) { + // Render my own Avatar + _myAvatar->render(forceRenderHead); + _myAvatar->setDisplayingLookatVectors(renderLookAtVectors); } - - // Render my own Avatar - Avatar* myAvatar = Application::getInstance()->getAvatar(); - myAvatar->render(forceRenderHead); - myAvatar->setDisplayingLookatVectors(Menu::getInstance()->isOptionChecked(MenuOption::LookAtVectors)); } void AvatarManager::simulateAvatarFades(float deltaTime) { - QVector::iterator fadingAvatar = _avatarFades.begin(); + QVector::iterator fadingIterator = _avatarFades.begin(); - while (fadingAvatar != _avatarFades.end()) { - const float SHRINK_RATE = 0.9f; - - fadingAvatar->data()->setTargetScale(fadingAvatar->data()->getScale() * SHRINK_RATE); - - const float MIN_FADE_SCALE = 0.001f; - - if (fadingAvatar->data()->getTargetScale() < MIN_FADE_SCALE) { - fadingAvatar = _avatarFades.erase(fadingAvatar); + const float SHRINK_RATE = 0.9f; + const float MIN_FADE_SCALE = 0.001f; + + while (fadingIterator != _avatarFades.end()) { + Avatar* avatar = static_cast(fadingIterator->data()); + avatar->setTargetScale(avatar->getScale() * SHRINK_RATE); + if (avatar->getTargetScale() < MIN_FADE_SCALE) { + fadingIterator = _avatarFades.erase(fadingIterator); } else { - fadingAvatar->data()->simulate(deltaTime, NULL); + avatar->simulate(deltaTime, NULL); + ++fadingIterator; } } } @@ -132,29 +158,36 @@ void AvatarManager::renderAvatarFades() { Glower glower; foreach(const AvatarSharedPointer& fadingAvatar, _avatarFades) { - fadingAvatar->render(false); + Avatar* avatar = static_cast(fadingAvatar.data()); + avatar->render(false); } } void AvatarManager::processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList &valueList) { + QUuid avatarKey = QUuid(userString); + if (avatarKey == MY_AVATAR_KEY) { + // ignore updates to our own mesh + return; + } for (int i = 0; i < keyList.size(); i++) { if (valueList[i] != " ") { if (keyList[i] == DataServerKey::FaceMeshURL || keyList[i] == DataServerKey::SkeletonURL) { // mesh URL for a UUID, find avatar in our list - AvatarSharedPointer matchingAvatar = _avatarHash.value(QUuid(userString)); + AvatarSharedPointer matchingAvatar = _avatarHash.value(avatarKey); if (matchingAvatar) { + Avatar* avatar = static_cast(matchingAvatar.data()); if (keyList[i] == DataServerKey::FaceMeshURL) { qDebug() << "Changing mesh to" << valueList[i] << "for avatar with UUID" - << uuidStringWithoutCurlyBraces(QUuid(userString)); + << uuidStringWithoutCurlyBraces(avatarKey); - QMetaObject::invokeMethod(&matchingAvatar->getHead().getFaceModel(), + QMetaObject::invokeMethod(&(avatar->getHead().getFaceModel()), "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); } else if (keyList[i] == DataServerKey::SkeletonURL) { qDebug() << "Changing skeleton to" << valueList[i] << "for avatar with UUID" - << uuidStringWithoutCurlyBraces(QString(userString)); + << uuidStringWithoutCurlyBraces(avatarKey.toString()); - QMetaObject::invokeMethod(&matchingAvatar->getSkeletonModel(), + QMetaObject::invokeMethod(&(avatar->getSkeletonModel()), "setURL", Q_ARG(QUrl, QUrl(valueList[i]))); } } @@ -178,15 +211,17 @@ void AvatarManager::processAvatarMixerDatagram(const QByteArray& datagram, const // only add them if mixerWeakPointer points to something (meaning that mixer is still around) while (bytesRead < datagram.size() && mixerWeakPointer.data()) { QUuid nodeUUID = QUuid::fromRfc4122(datagram.mid(bytesRead, NUM_BYTES_RFC4122_UUID)); + // TODO: skip the data if nodeUUID is same as MY_AVATAR_KEY AvatarSharedPointer matchingAvatar = _avatarHash.value(nodeUUID); if (!matchingAvatar) { // construct a new Avatar for this node - matchingAvatar = AvatarSharedPointer(new Avatar()); - matchingAvatar->setOwningAvatarMixer(mixerWeakPointer); + Avatar* avatar = new Avatar(); + avatar->setOwningAvatarMixer(mixerWeakPointer); // insert the new avatar into our hash + matchingAvatar = AvatarSharedPointer(avatar); _avatarHash.insert(nodeUUID, matchingAvatar); // new UUID requires mesh and skeleton request to data-server @@ -213,28 +248,27 @@ void AvatarManager::processKillAvatar(const QByteArray& datagram) { // remove the avatar with that UUID from our hash, if it exists AvatarHash::iterator matchedAvatar = _avatarHash.find(nodeUUID); if (matchedAvatar != _avatarHash.end()) { - removeAvatarAtHashIterator(matchedAvatar); + erase(matchedAvatar); } } -AvatarHash::iterator AvatarManager::removeAvatarAtHashIterator(const AvatarHash::iterator& iterator) { - qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; - _avatarFades.push_back(iterator.value()); - return _avatarHash.erase(iterator); +AvatarHash::iterator AvatarManager::erase(const AvatarHash::iterator& iterator) { + if (iterator.key() != MY_AVATAR_KEY) { + qDebug() << "Removing Avatar with UUID" << iterator.key() << "from AvatarManager hash."; + _avatarFades.push_back(iterator.value()); + return AvatarHashMap::erase(iterator); + } else { + // never remove _myAvatar from the list + AvatarHash::iterator returnIterator = iterator; + return ++returnIterator; + } } void AvatarManager::clearHash() { // clear the AvatarManager hash - typically happens on the removal of the avatar-mixer AvatarHash::iterator removeAvatar = _avatarHash.begin(); - while (removeAvatar != _avatarHash.end()) { - removeAvatar = removeAvatarAtHashIterator(removeAvatar); + removeAvatar = erase(removeAvatar); } } -void AvatarManager::getAvatarBasePointers(QVector& avatars) { - AvatarHash::iterator avatarItr = _avatarHash.begin(); - while (avatarItr != _avatarHash.end()) { - avatars.push_back( static_cast(avatarItr.value().data()) ); - } -} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 82fe279254..f092789ded 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -13,32 +13,32 @@ #include #include +#include #include #include "Avatar.h" -typedef QSharedPointer AvatarSharedPointer; -typedef QHash AvatarHash; +class MyAvatar; -class AvatarManager : public QObject, public DataServerCallbackObject { +const QUuid MY_AVATAR_KEY; // NULL key + +class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap { Q_OBJECT public: AvatarManager(QObject* parent = 0); + + void setMyAvatar(MyAvatar* myAvatar); - const AvatarHash& getAvatarHash() { return _avatarHash; } - int size() const { return _avatarHash.size(); } - - Avatar* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } + AvatarData* getLookAtTargetAvatar() const { return _lookAtTargetAvatar.data(); } void updateLookAtTargetAvatar(glm::vec3& eyePosition); void updateAvatars(float deltaTime); void renderAvatars(bool forceRenderHead, bool selfAvatarOnly = false); + // virtual override void clearHash(); - void getAvatarBasePointers(QVector& avatars); - public slots: void processDataServerResponse(const QString& userString, const QStringList& keyList, const QStringList& valueList); @@ -46,17 +46,20 @@ public slots: void processKillAvatar(const QByteArray& datagram); private: + AvatarManager(const AvatarManager& other); + void simulateAvatarFades(float deltaTime); void renderAvatarFades(); - AvatarHash::iterator removeAvatarAtHashIterator(const AvatarHash::iterator& iterator); + // virtual override + AvatarHash::iterator erase(const AvatarHash::iterator& iterator); - QWeakPointer _lookAtTargetAvatar; + QWeakPointer _lookAtTargetAvatar; glm::vec3 _lookAtOtherPosition; float _lookAtIndicatorScale; - AvatarHash _avatarHash; QVector _avatarFades; + MyAvatar* _myAvatar; }; #endif /* defined(__hifi__AvatarManager__) */ diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 18d6031f98..b441000cc1 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -183,7 +183,12 @@ void Hand::updateCollisions() { glm::vec3 totalPenetration; // check other avatars - foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) { + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if (avatar == _owningAvatar) { + // don't collid with our own hands + continue; + } if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { // Check for palm collisions glm::vec3 myPalmPosition = palm.getPosition(); @@ -205,9 +210,9 @@ void Hand::updateCollisions() { const float PALM_COLLIDE_DURATION_MAX = 0.75f; const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, - PALM_COLLIDE_FREQUENCY, - PALM_COLLIDE_DURATION_MAX, - PALM_COLLIDE_DECAY_PER_SAMPLE); + PALM_COLLIDE_FREQUENCY, + PALM_COLLIDE_DURATION_MAX, + PALM_COLLIDE_DECAY_PER_SAMPLE); // If the other person's palm is in motion, move mine downward to show I was hit const float MIN_VELOCITY_FOR_SLAP = 0.05f; if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ca7a3b863b..6910826524 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -791,14 +791,15 @@ void MyAvatar::updateChatCircle(float deltaTime) { // find all circle-enabled members and sort by distance QVector sortedAvatars; - foreach (const AvatarSharedPointer& avatar, Application::getInstance()->getAvatarManager().getAvatarHash()) { - SortedAvatar sortedAvatar; - sortedAvatar.avatar = avatar.data(); - - if (!sortedAvatar.avatar->isChatCirclingEnabled()) { + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if ( ! avatar->isChatCirclingEnabled() || + avatar == static_cast(this)) { continue; } - + + SortedAvatar sortedAvatar; + sortedAvatar.avatar = avatar; sortedAvatar.distance = glm::distance(_position, sortedAvatar.avatar->getPosition()); sortedAvatars.append(sortedAvatar); } diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp new file mode 100644 index 0000000000..b1bb42edbf --- /dev/null +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -0,0 +1,30 @@ +// +// AvatarHashMap.cpp +// hifi +// +// Created by Stephen AndrewMeadows on 1/28/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#include "AvatarHashMap.h" + +AvatarHashMap::AvatarHashMap() : + _avatarHash() +{ +} + +void AvatarHashMap::insert(const QUuid& id, AvatarSharedPointer avatar) { + _avatarHash.insert(id, avatar); +} + +void AvatarHashMap::clearHash() { + AvatarHash::iterator removeAvatar = _avatarHash.begin(); + while (removeAvatar != _avatarHash.end()) { + removeAvatar = erase(removeAvatar); + } +} + +AvatarHash::iterator AvatarHashMap::erase(const AvatarHash::iterator& iterator) { + return _avatarHash.erase(iterator); +} + diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h new file mode 100644 index 0000000000..37e9004d6f --- /dev/null +++ b/libraries/avatars/src/AvatarHashMap.h @@ -0,0 +1,37 @@ +// +// AvatarHashMap.h +// hifi +// +// Created by Stephen AndrewMeadows on 1/28/2014. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// + +#ifndef __hifi__AvatarHashMap__ +#define __hifi__AvatarHashMap__ + +#include +#include +#include + +#include "AvatarData.h" + +typedef QSharedPointer AvatarSharedPointer; +typedef QHash AvatarHash; + +class AvatarHashMap { +public: + AvatarHashMap(); + + const AvatarHash& getAvatarHash() { return _avatarHash; } + int size() const { return _avatarHash.size(); } + + virtual void insert(const QUuid& id, AvatarSharedPointer avatar); + virtual void clearHash(); + +protected: + virtual AvatarHash::iterator erase(const AvatarHash::iterator& iterator); + + AvatarHash _avatarHash; +}; + +#endif /* defined(__hifi__AvatarHashMap__) */ diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index d53898ac05..4f5bc85d5a 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -25,11 +25,13 @@ ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packe } void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, - ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { _packetSender = packetSender; _particles = particles; _voxels = voxels; _audio = audio; + _avatars = avatars; } ParticleCollisionSystem::~ParticleCollisionSystem() { @@ -51,9 +53,8 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr } -void ParticleCollisionSystem::update(QVector& avatars) { +void ParticleCollisionSystem::update() { // update all particles - _avatars.swap(avatars); _particles->lockForWrite(); _particles->recurseTreeWithOperation(updateOperation, this); _particles->unlock(); @@ -147,9 +148,8 @@ const float MIN_EXPECTED_FRAME_PERIOD = 0.0167f; // 1/60th of a second const float HALTING_SPEED = 9.8 * MIN_EXPECTED_FRAME_PERIOD / (float)(TREE_SCALE); void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { - // particles that are in hand, don't collide with avatars - if (particle->getInHand()) { + if (!_avatars || particle->getInHand()) { return; } @@ -160,9 +160,8 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; - // first check the selfAvatar if set... - for (int i = 0; i < _avatars.size(); ++i) { - AvatarData* avatar = _avatars[i]; + foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { + AvatarData* avatar = avatarPointer.data(); CollisionInfo collisionInfo; collisionInfo._damping = DAMPING; collisionInfo._elasticity = ELASTICITY; diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index b4293071bb..352864134e 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -37,13 +38,11 @@ public: AbstractAudioInterface* audio = NULL); void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, - AbstractAudioInterface* audio = NULL); + AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL); ~ParticleCollisionSystem(); - /// Compute collisions for particles against: other particles, voxels, and avatars - /// \param avatars the list of avatars to collide against during this frame - void update(QVector& avatars); + void update(); void checkParticle(Particle* particle); void updateCollisionWithVoxels(Particle* particle); @@ -60,7 +59,7 @@ private: ParticleTree* _particles; VoxelTree* _voxels; AbstractAudioInterface* _audio; - QVector _avatars; // only used during update() + AvatarHashMap* _avatars; }; #endif /* defined(__hifi__ParticleCollisionSystem__) */ From 1cc992a7f819905b1519b6136d0ba15e240d97fc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 14:18:21 -0800 Subject: [PATCH 10/12] import --> include --- interface/src/ui/Snapshot.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 39548cdff8..26315678f9 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -9,9 +9,9 @@ #ifndef __hifi__Snapshot__ #define __hifi__Snapshot__ -#import -#import -#import +#include +#include +#include #include From 714dbf84937de6da8e7f89c6a37a205dfe3212eb Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 14:37:32 -0800 Subject: [PATCH 11/12] Moving MY_AVATAR_KEY back into AvatarManager.cpp --- interface/src/avatar/AvatarManager.cpp | 2 ++ interface/src/avatar/AvatarManager.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 20b1bc151d..fc3ac73044 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -16,6 +16,8 @@ #include "AvatarManager.h" +// We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key. +const QUuid MY_AVATAR_KEY; // NULL key AvatarManager::AvatarManager(QObject* parent) : _lookAtTargetAvatar(), diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index f092789ded..369444bcc7 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -20,8 +20,6 @@ class MyAvatar; -const QUuid MY_AVATAR_KEY; // NULL key - class AvatarManager : public QObject, public DataServerCallbackObject, public AvatarHashMap { Q_OBJECT public: From e13bc71f39b6172812c693ac0b1c2ced824ab003 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 14:43:36 -0800 Subject: [PATCH 12/12] Adding avatar info back into ParticleCollisionSystem ctor arguments. --- libraries/particles/src/ParticleCollisionSystem.cpp | 5 +++-- libraries/particles/src/ParticleCollisionSystem.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index 4f5bc85d5a..a2394935b1 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -20,8 +20,9 @@ #include "ParticleTree.h" ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, - ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio) { - init(packetSender, particles, voxels, audio); + ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, + AvatarHashMap* avatars) { + init(packetSender, particles, voxels, audio, avatars); } void ParticleCollisionSystem::init(ParticleEditPacketSender* packetSender, diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index 352864134e..4a61693fa6 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -34,8 +34,8 @@ const glm::vec3 NO_ADDED_VELOCITY = glm::vec3(0); class ParticleCollisionSystem { public: ParticleCollisionSystem(ParticleEditPacketSender* packetSender = NULL, ParticleTree* particles = NULL, - VoxelTree* voxels = NULL, - AbstractAudioInterface* audio = NULL); + VoxelTree* voxels = NULL, AbstractAudioInterface* audio = NULL, + AvatarHashMap* avatars = NULL); void init(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio = NULL, AvatarHashMap* _avatars = NULL);