From 52511b2baf4fc74689ba8fc478930d53f1d99dbc Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Jan 2014 13:59:03 -0800 Subject: [PATCH 01/17] 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/17] 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/17] 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/17] 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/17] 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/17] 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 4deb9936ba7d89895886ca9ab0493f3a846a6d46 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 27 Jan 2014 17:44:13 -0800 Subject: [PATCH 07/17] Don't add the specular alpha in the model fragment shaders. --- interface/resources/shaders/model.frag | 2 +- interface/resources/shaders/model_normal_map.frag | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/shaders/model.frag b/interface/resources/shaders/model.frag index 877cdca885..e035f9bfe9 100644 --- a/interface/resources/shaders/model.frag +++ b/interface/resources/shaders/model.frag @@ -25,5 +25,5 @@ void main(void) { // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + - pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; + vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0); } diff --git a/interface/resources/shaders/model_normal_map.frag b/interface/resources/shaders/model_normal_map.frag index 9740a4d4b1..9a0e964500 100644 --- a/interface/resources/shaders/model_normal_map.frag +++ b/interface/resources/shaders/model_normal_map.frag @@ -37,5 +37,5 @@ void main(void) { // modulate texture by base color and add specular contribution gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + - pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular; + vec4(pow(specular, gl_FrontMaterial.shininess) * gl_FrontLightProduct[0].specular.rgb, 0.0); } From 2fa3fc523ddea4335364db8b2e670110c62ef430 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Jan 2014 17:51:05 -0800 Subject: [PATCH 08/17] 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 09/17] 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 ade02c0259d18a085bdc548807813c7659dffb48 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 09:32:58 -0800 Subject: [PATCH 10/17] Bug fix: update ALL avatars in the AvatarManager (rather than just one). Reviewed with Birarda and pushed directly to upstream. --- interface/src/avatar/AvatarManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dd1686a0d2..91a356c952 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -63,7 +63,7 @@ void AvatarManager::updateAvatars(float deltaTime) { // simulate avatars AvatarHash::iterator avatar = _avatarHash.begin(); - if (avatar != _avatarHash.end()) { + while (avatar != _avatarHash.end()) { if (avatar->data()->getOwningAvatarMixer()) { // this avatar's mixer is still around, go ahead and simulate it avatar->data()->simulate(deltaTime, NULL); @@ -72,6 +72,7 @@ void AvatarManager::updateAvatars(float deltaTime) { avatar->data()->setMouseRay(applicationInstance->getMouseRayOrigin(), applicationInstance->getMouseRayDirection()); + avatar++; } else { // the mixer that owned this avatar is gone, give it to the vector of fades and kill it avatar = removeAvatarAtHashIterator(avatar); @@ -230,4 +231,4 @@ void AvatarManager::clearHash() { while (removeAvatar != _avatarHash.end()) { removeAvatar = removeAvatarAtHashIterator(removeAvatar); } -} \ No newline at end of file +} From 3edb81baf451f1f15ba5ef241d995c2cb1944cb3 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Jan 2014 12:01:32 -0800 Subject: [PATCH 11/17] Check for texture translucency, render translucent mesh parts after opaque ones, without alpha testing, and with back face culling enabled. --- interface/src/renderer/GeometryCache.cpp | 14 + interface/src/renderer/GeometryCache.h | 4 + interface/src/renderer/Model.cpp | 378 ++++++++++++----------- interface/src/renderer/Model.h | 1 + interface/src/renderer/TextureCache.cpp | 19 +- interface/src/renderer/TextureCache.h | 5 + 6 files changed, 239 insertions(+), 182 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 73248b413f..63a0c51f0b 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -508,3 +508,17 @@ void NetworkGeometry::maybeReadModelWithMapping() { _meshes.append(networkMesh); } } + +bool NetworkMeshPart::isTranslucent() const { + return diffuseTexture && diffuseTexture->isTranslucent(); +} + +int NetworkMesh::getTranslucentPartCount() const { + int count = 0; + foreach (const NetworkMeshPart& part, parts) { + if (part.isTranslucent()) { + count++; + } + } + return count; +} diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 312d8bcd91..e65aed31d4 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -93,6 +93,8 @@ public: QSharedPointer diffuseTexture; QSharedPointer normalTexture; + + bool isTranslucent() const; }; /// The state associated with a single mesh. @@ -103,6 +105,8 @@ public: GLuint vertexBufferID; QVector parts; + + int getTranslucentPartCount() const; }; #endif /* defined(__interface__GeometryCache__) */ diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c6c820a0f9..c25e513b3b 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -240,7 +240,6 @@ bool Model::render(float alpha) { // set up blended buffer ids on first render after load/simulate const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& networkMeshes = _geometry->getMeshes(); if (_blendedVertexBufferIDs.isEmpty()) { foreach (const FBXMesh& mesh, geometry.meshes) { GLuint id = 0; @@ -264,191 +263,28 @@ bool Model::render(float alpha) { glDisable(GL_COLOR_MATERIAL); + // render opaque meshes with alpha testing + glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f); - for (int i = 0; i < networkMeshes.size(); i++) { - const NetworkMesh& networkMesh = networkMeshes.at(i); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); - - const FBXMesh& mesh = geometry.meshes.at(i); - int vertexCount = mesh.vertices.size(); - - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); - - ProgramObject* program = &_program; - ProgramObject* skinProgram = &_skinProgram; - SkinLocations* skinLocations = &_skinLocations; - if (!mesh.tangents.isEmpty()) { - program = &_normalMapProgram; - skinProgram = &_skinNormalMapProgram; - skinLocations = &_skinNormalMapLocations; - } - - const MeshState& state = _meshStates.at(i); - ProgramObject* activeProgram = program; - int tangentLocation = _normalMapTangentLocation; - if (state.worldSpaceVertices.isEmpty()) { - glPushMatrix(); - Application::getInstance()->loadTranslatedViewMatrix(_translation); - - if (state.clusterMatrices.size() > 1) { - skinProgram->bind(); - glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, - (const float*)state.clusterMatrices.constData()); - int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + - mesh.texCoords.size() * sizeof(glm::vec2) + - (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); - skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); - skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, - offset + vertexCount * sizeof(glm::vec4), 4); - skinProgram->enableAttributeArray(skinLocations->clusterIndices); - skinProgram->enableAttributeArray(skinLocations->clusterWeights); - activeProgram = skinProgram; - tangentLocation = skinLocations->tangent; - - } else { - glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); - program->bind(); - } - } else { - program->bind(); - } - - if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { - if (!mesh.tangents.isEmpty()) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); - activeProgram->enableAttributeArray(tangentLocation); - } - glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - mesh.tangents.size() * sizeof(glm::vec3))); - glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + - (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - - } else { - if (!mesh.tangents.isEmpty()) { - activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); - activeProgram->enableAttributeArray(tangentLocation); - } - glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); - glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); - glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); - - if (!state.worldSpaceVertices.isEmpty()) { - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), - vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData()); - - } else { - _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); - _blendedNormals.resize(_blendedVertices.size()); - memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3)); - memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3)); - - // blend in each coefficient - for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) { - float coefficient = _blendshapeCoefficients[j]; - if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) { - continue; - } - const float NORMAL_COEFFICIENT_SCALE = 0.01f; - float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE; - const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData(); - const glm::vec3* normal = mesh.blendshapes[j].normals.constData(); - for (const int* index = mesh.blendshapes[j].indices.constData(), - *end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) { - _blendedVertices[*index] += *vertex * coefficient; - _blendedNormals[*index] += *normal * normalCoefficient; - } - } - - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), - vertexCount * sizeof(glm::vec3), _blendedNormals.constData()); - } - } - glVertexPointer(3, GL_FLOAT, 0, 0); - glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); - - if (!mesh.colors.isEmpty()) { - glEnableClientState(GL_COLOR_ARRAY); - } else { - glColor3f(1.0f, 1.0f, 1.0f); - } - if (!mesh.texCoords.isEmpty()) { - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - } - - qint64 offset = 0; - for (int j = 0; j < networkMesh.parts.size(); j++) { - const NetworkMeshPart& networkPart = networkMesh.parts.at(j); - const FBXMeshPart& part = mesh.parts.at(j); - - // apply material properties - glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); - glm::vec4 specular = glm::vec4(part.specularColor, alpha); - glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); - glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); - glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); - - Texture* diffuseMap = networkPart.diffuseTexture.data(); - if (mesh.isEye) { - if (diffuseMap != NULL) { - diffuseMap = (_dilatedTextures[i][j] = - static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); - } - } - glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ? - Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); - - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - Texture* normalMap = networkPart.normalTexture.data(); - glBindTexture(GL_TEXTURE_2D, normalMap == NULL ? - Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); - glActiveTexture(GL_TEXTURE0); - } - - glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); - offset += part.quadIndices.size() * sizeof(int); - glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), - GL_UNSIGNED_INT, (void*)offset); - offset += part.triangleIndices.size() * sizeof(int); - } - - if (!mesh.colors.isEmpty()) { - glDisableClientState(GL_COLOR_ARRAY); - } - if (!mesh.texCoords.isEmpty()) { - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - - if (!mesh.tangents.isEmpty()) { - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE0); - - activeProgram->disableAttributeArray(tangentLocation); - } - - if (state.worldSpaceVertices.isEmpty()) { - if (state.clusterMatrices.size() > 1) { - skinProgram->disableAttributeArray(skinLocations->clusterIndices); - skinProgram->disableAttributeArray(skinLocations->clusterWeights); - } - glPopMatrix(); - } - activeProgram->release(); - } + renderMeshes(alpha, false); + + glDisable(GL_ALPHA_TEST); + + // render translucent meshes afterwards, with back face culling and no depth writes + + glEnable(GL_CULL_FACE); + + renderMeshes(alpha, true); + + glDisable(GL_CULL_FACE); // deactivate vertex arrays after drawing glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisable(GL_ALPHA_TEST); - // bind with 0 to switch back to normal operation glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -882,3 +718,191 @@ void Model::deleteGeometry() { _jointStates.clear(); _meshStates.clear(); } + +void Model::renderMeshes(float alpha, bool translucent) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& networkMeshes = _geometry->getMeshes(); + + for (int i = 0; i < networkMeshes.size(); i++) { + // exit early if the translucency doesn't match what we're drawing + const NetworkMesh& networkMesh = networkMeshes.at(i); + if (translucent ? (networkMesh.getTranslucentPartCount() == 0) : + (networkMesh.getTranslucentPartCount() == networkMesh.parts.size())) { + continue; + } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); + + const FBXMesh& mesh = geometry.meshes.at(i); + int vertexCount = mesh.vertices.size(); + + glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); + + ProgramObject* program = &_program; + ProgramObject* skinProgram = &_skinProgram; + SkinLocations* skinLocations = &_skinLocations; + if (!mesh.tangents.isEmpty()) { + program = &_normalMapProgram; + skinProgram = &_skinNormalMapProgram; + skinLocations = &_skinNormalMapLocations; + } + + const MeshState& state = _meshStates.at(i); + ProgramObject* activeProgram = program; + int tangentLocation = _normalMapTangentLocation; + if (state.worldSpaceVertices.isEmpty()) { + glPushMatrix(); + Application::getInstance()->loadTranslatedViewMatrix(_translation); + + if (state.clusterMatrices.size() > 1) { + skinProgram->bind(); + glUniformMatrix4fvARB(skinLocations->clusterMatrices, state.clusterMatrices.size(), false, + (const float*)state.clusterMatrices.constData()); + int offset = (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3) + + mesh.texCoords.size() * sizeof(glm::vec2) + + (mesh.blendshapes.isEmpty() ? vertexCount * 2 * sizeof(glm::vec3) : 0); + skinProgram->setAttributeBuffer(skinLocations->clusterIndices, GL_FLOAT, offset, 4); + skinProgram->setAttributeBuffer(skinLocations->clusterWeights, GL_FLOAT, + offset + vertexCount * sizeof(glm::vec4), 4); + skinProgram->enableAttributeArray(skinLocations->clusterIndices); + skinProgram->enableAttributeArray(skinLocations->clusterWeights); + activeProgram = skinProgram; + tangentLocation = skinLocations->tangent; + + } else { + glMultMatrixf((const GLfloat*)&state.clusterMatrices[0]); + program->bind(); + } + } else { + program->bind(); + } + + if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, vertexCount * 2 * sizeof(glm::vec3), 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + mesh.tangents.size() * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)(vertexCount * 2 * sizeof(glm::vec3) + + (mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); + + } else { + if (!mesh.tangents.isEmpty()) { + activeProgram->setAttributeBuffer(tangentLocation, GL_FLOAT, 0, 3); + activeProgram->enableAttributeArray(tangentLocation); + } + glColorPointer(3, GL_FLOAT, 0, (void*)(mesh.tangents.size() * sizeof(glm::vec3))); + glTexCoordPointer(2, GL_FLOAT, 0, (void*)((mesh.tangents.size() + mesh.colors.size()) * sizeof(glm::vec3))); + glBindBuffer(GL_ARRAY_BUFFER, _blendedVertexBufferIDs.at(i)); + + if (!state.worldSpaceVertices.isEmpty()) { + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), state.worldSpaceVertices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), + vertexCount * sizeof(glm::vec3), state.worldSpaceNormals.constData()); + + } else { + _blendedVertices.resize(max(_blendedVertices.size(), vertexCount)); + _blendedNormals.resize(_blendedVertices.size()); + memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3)); + memcpy(_blendedNormals.data(), mesh.normals.constData(), vertexCount * sizeof(glm::vec3)); + + // blend in each coefficient + for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) { + float coefficient = _blendshapeCoefficients[j]; + if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) { + continue; + } + const float NORMAL_COEFFICIENT_SCALE = 0.01f; + float normalCoefficient = coefficient * NORMAL_COEFFICIENT_SCALE; + const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData(); + const glm::vec3* normal = mesh.blendshapes[j].normals.constData(); + for (const int* index = mesh.blendshapes[j].indices.constData(), + *end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++, normal++) { + _blendedVertices[*index] += *vertex * coefficient; + _blendedNormals[*index] += *normal * normalCoefficient; + } + } + + glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(glm::vec3), _blendedVertices.constData()); + glBufferSubData(GL_ARRAY_BUFFER, vertexCount * sizeof(glm::vec3), + vertexCount * sizeof(glm::vec3), _blendedNormals.constData()); + } + } + glVertexPointer(3, GL_FLOAT, 0, 0); + glNormalPointer(GL_FLOAT, 0, (void*)(vertexCount * sizeof(glm::vec3))); + + if (!mesh.colors.isEmpty()) { + glEnableClientState(GL_COLOR_ARRAY); + } else { + glColor3f(1.0f, 1.0f, 1.0f); + } + if (!mesh.texCoords.isEmpty()) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + } + + qint64 offset = 0; + for (int j = 0; j < networkMesh.parts.size(); j++) { + const NetworkMeshPart& networkPart = networkMesh.parts.at(j); + if (networkPart.isTranslucent() != translucent) { + continue; + } + const FBXMeshPart& part = mesh.parts.at(j); + + // apply material properties + glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); + glm::vec4 specular = glm::vec4(part.specularColor, alpha); + glMaterialfv(GL_FRONT, GL_AMBIENT, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_DIFFUSE, (const float*)&diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, (const float*)&specular); + glMaterialf(GL_FRONT, GL_SHININESS, part.shininess); + + Texture* diffuseMap = networkPart.diffuseTexture.data(); + if (mesh.isEye) { + if (diffuseMap != NULL) { + diffuseMap = (_dilatedTextures[i][j] = + static_cast(diffuseMap)->getDilatedTexture(_pupilDilation)).data(); + } + } + glBindTexture(GL_TEXTURE_2D, diffuseMap == NULL ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + Texture* normalMap = networkPart.normalTexture.data(); + glBindTexture(GL_TEXTURE_2D, normalMap == NULL ? + Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); + glActiveTexture(GL_TEXTURE0); + } + + glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); + offset += part.quadIndices.size() * sizeof(int); + glDrawRangeElementsEXT(GL_TRIANGLES, 0, vertexCount - 1, part.triangleIndices.size(), + GL_UNSIGNED_INT, (void*)offset); + offset += part.triangleIndices.size() * sizeof(int); + } + + if (!mesh.colors.isEmpty()) { + glDisableClientState(GL_COLOR_ARRAY); + } + if (!mesh.texCoords.isEmpty()) { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (!mesh.tangents.isEmpty()) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + activeProgram->disableAttributeArray(tangentLocation); + } + + if (state.worldSpaceVertices.isEmpty()) { + if (state.clusterMatrices.size() > 1) { + skinProgram->disableAttributeArray(skinLocations->clusterIndices); + skinProgram->disableAttributeArray(skinLocations->clusterWeights); + } + glPopMatrix(); + } + activeProgram->release(); + } +} diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 3e8cd2be9c..389020d1b1 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -214,6 +214,7 @@ protected: private: void deleteGeometry(); + void renderMeshes(float alpha, bool translucent); float _pupilDilation; std::vector _blendshapeCoefficients; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 96fe84b305..dc6883a5d0 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -257,7 +257,8 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : _request(url), _reply(NULL), _attempts(0), - _averageColor(1.0f, 1.0f, 1.0f, 1.0f) { + _averageColor(1.0f, 1.0f, 1.0f, 1.0f), + _translucent(false) { if (!url.isValid()) { return; @@ -300,19 +301,27 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32); - // sum up the colors for the average + // sum up the colors for the average and check for translucency glm::vec4 accumulated; + int translucentPixels = 0; + const int EIGHT_BIT_MAXIMUM = 255; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x++) { QRgb pixel = image.pixel(x, y); accumulated.r += qRed(pixel); accumulated.g += qGreen(pixel); accumulated.b += qBlue(pixel); - accumulated.a += qAlpha(pixel); + + int alpha = qAlpha(pixel); + if (alpha != 0 && alpha != EIGHT_BIT_MAXIMUM) { + translucentPixels++; + } + accumulated.a += alpha; } } - const float EIGHT_BIT_MAXIMUM = 255.0f; - _averageColor = accumulated / (image.width() * image.height() * EIGHT_BIT_MAXIMUM); + int imageArea = image.width() * image.height(); + _averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); + _translucent = (translucentPixels >= imageArea / 2); imageLoaded(image); glBindTexture(GL_TEXTURE_2D, getID()); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index d3f138254f..e560acf6f7 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -121,6 +121,10 @@ public: /// Returns the average color over the entire texture. const glm::vec4& getAverageColor() const { return _averageColor; } + /// Checks whether it "looks like" this texture is translucent + /// (majority of pixels neither fully opaque or fully transparent). + bool isTranslucent() const { return _translucent; } + protected: virtual void imageLoaded(const QImage& image); @@ -137,6 +141,7 @@ private: QNetworkReply* _reply; int _attempts; glm::vec4 _averageColor; + bool _translucent; }; /// Caches derived, dilated textures. From cf05cf6178ae122fa3a6e89d023a025df7255b17 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 28 Jan 2014 12:03:41 -0800 Subject: [PATCH 12/17] Didn't need to disable depth writes after all (for now). --- interface/src/renderer/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c25e513b3b..6d61b2df68 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -272,7 +272,7 @@ bool Model::render(float alpha) { glDisable(GL_ALPHA_TEST); - // render translucent meshes afterwards, with back face culling and no depth writes + // render translucent meshes afterwards, with back face culling glEnable(GL_CULL_FACE); From 167acb8873a0023db7f2ddb395e05c120a57049a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 28 Jan 2014 14:16:34 -0800 Subject: [PATCH 13/17] 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 14/17] 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 15/17] 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 16/17] 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); From d624654a29a0925ecadb20d838835435fd7780ca Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 28 Jan 2014 16:25:04 -0800 Subject: [PATCH 17/17] serve a 301 redirect when trailing slash is missing, closes #1712 --- .../embedded-webserver/src/HTTPConnection.cpp | 2 ++ .../embedded-webserver/src/HTTPConnection.h | 4 +++- .../embedded-webserver/src/HTTPManager.cpp | 22 ++++++++++++++----- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp index 3ffc96c9f4..50ce72e0cd 100755 --- a/libraries/embedded-webserver/src/HTTPConnection.cpp +++ b/libraries/embedded-webserver/src/HTTPConnection.cpp @@ -15,8 +15,10 @@ #include "HTTPManager.h" const char* HTTPConnection::StatusCode200 = "200 OK"; +const char* HTTPConnection::StatusCode301 = "301 Moved Permanently"; const char* HTTPConnection::StatusCode400 = "400 Bad Request"; const char* HTTPConnection::StatusCode404 = "404 Not Found"; +const char* HTTPConnection::DefaultContentType = "text/plain; charset=ISO-8859-1"; HTTPConnection::HTTPConnection (QTcpSocket* socket, HTTPManager* parentManager) : QObject(parentManager), diff --git a/libraries/embedded-webserver/src/HTTPConnection.h b/libraries/embedded-webserver/src/HTTPConnection.h index 9342c32849..dbf1e31f47 100644 --- a/libraries/embedded-webserver/src/HTTPConnection.h +++ b/libraries/embedded-webserver/src/HTTPConnection.h @@ -40,8 +40,10 @@ class HTTPConnection : public QObject { public: static const char* StatusCode200; + static const char* StatusCode301; static const char* StatusCode400; static const char* StatusCode404; + static const char* DefaultContentType; /// WebSocket close status codes. enum ReasonCode { NoReason = 0, NormalClosure = 1000, GoingAway = 1001 }; @@ -72,7 +74,7 @@ public: /// Sends a response and closes the connection. void respond (const char* code, const QByteArray& content = QByteArray(), - const char* contentType = "text/plain; charset=ISO-8859-1", + const char* contentType = DefaultContentType, const Headers& headers = Headers()); protected slots: diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp index 735300de7c..849bf593cd 100755 --- a/libraries/embedded-webserver/src/HTTPManager.cpp +++ b/libraries/embedded-webserver/src/HTTPManager.cpp @@ -23,7 +23,6 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p } // check to see if there is a file to serve from the document root for this path - QString subPath = path; // remove any slash at the beginning of the path @@ -33,6 +32,19 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p QString filePath; + if (QFileInfo(_documentRoot + subPath).isFile()) { + filePath = _documentRoot + subPath; + } else if (subPath.size() > 0 && !subPath.endsWith('/')) { + // this could be a directory with a trailing slash + // send a redirect to the path with a slash so we can + QString redirectLocation = '/' + subPath + '/'; + + QHash redirectHeader; + redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8()); + + connection->respond(HTTPConnection::StatusCode301, "", HTTPConnection::DefaultContentType, redirectHeader); + } + // if the last thing is a trailing slash then we want to look for index file if (subPath.endsWith('/') || subPath.size() == 0) { QStringList possibleIndexFiles = QStringList() << "index.html" << "index.shtml"; @@ -43,14 +55,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p break; } } - } else if (QFileInfo(_documentRoot + subPath).isFile()) { - filePath = _documentRoot + subPath; } - if (!filePath.isEmpty()) { // file exists, serve it - static QMimeDatabase mimeDatabase; QFile localFile(filePath); @@ -99,8 +107,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p } } - connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(), qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); + connection->respond(HTTPConnection::StatusCode200, localFileString.toLocal8Bit(), + qPrintable(mimeDatabase.mimeTypeForFile(filePath).name())); } else { + // respond with a 404 connection->respond(HTTPConnection::StatusCode404, "Resource not found."); }