Merge pull request #1598 from AndrewMeadows/paddle-fixes-2

Paddle fixes 2
This commit is contained in:
ZappoMan 2014-01-21 02:12:47 -08:00
commit b48ec97f5b
7 changed files with 132 additions and 104 deletions

View file

@ -32,7 +32,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg
// we need to iterate the actual particles of the element // we need to iterate the actual particles of the element
ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element; ParticleTreeElement* particleTreeElement = (ParticleTreeElement*)element;
const std::vector<Particle>& particles = particleTreeElement->getParticles(); const QList<Particle>& particles = particleTreeElement->getParticles();
uint16_t numberOfParticles = particles.size(); uint16_t numberOfParticles = particles.size();

View file

@ -474,21 +474,21 @@ void Particle::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssiz
} }
} }
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity during the shortest expected frame period
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);
void Particle::update() { void Particle::update(const uint64_t& now) {
uint64_t now = usecTimestampNow(); float timeElapsed = (float)(now - _lastUpdated) / (float)(USECS_PER_SECOND);
float elapsed = static_cast<float>(now - _lastUpdated);
_lastUpdated = now; _lastUpdated = now;
float timeElapsed = elapsed / static_cast<float>(USECS_PER_SECOND);
// calculate our default shouldDie state... then allow script to change it if it wants... // calculate our default shouldDie state... then allow script to change it if it wants...
float velocityScalar = glm::length(getVelocity()); float speed = glm::length(_velocity);
const float STILL_MOVING = 0.05f / static_cast<float>(TREE_SCALE); bool isStopped = (speed < MIN_VALID_SPEED);
bool isStillMoving = (velocityScalar > STILL_MOVING); const uint64_t REALLY_OLD = 30 * USECS_PER_SECOND; // 30 seconds
const float REALLY_OLD = 30.0f; // 30 seconds bool isReallyOld = ((now - _created) > REALLY_OLD);
bool isReallyOld = (getLifetime() > REALLY_OLD);
bool isInHand = getInHand(); bool isInHand = getInHand();
bool shouldDie = getShouldDie() || (!isInHand && !isStillMoving && isReallyOld); bool shouldDie = getShouldDie() || (!isInHand && isStopped && isReallyOld);
setShouldDie(shouldDie); setShouldDie(shouldDie);
runUpdateScript(); // allow the javascript to alter our state runUpdateScript(); // allow the javascript to alter our state

View file

@ -117,7 +117,7 @@ public:
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew); static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew);
void update(); void update(const uint64_t& now);
void collisionWithParticle(Particle* other); void collisionWithParticle(Particle* other);
void collisionWithVoxel(VoxelDetail* voxel); void collisionWithVoxel(VoxelDetail* voxel);

View file

@ -11,7 +11,6 @@
#include <AbstractAudioInterface.h> #include <AbstractAudioInterface.h>
#include <VoxelTree.h> #include <VoxelTree.h>
#include <AvatarData.h> #include <AvatarData.h>
#include <CollisionInfo.h>
#include <HeadData.h> #include <HeadData.h>
#include <HandData.h> #include <HandData.h>
@ -43,7 +42,7 @@ bool ParticleCollisionSystem::updateOperation(OctreeElement* element, void* extr
ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element); ParticleTreeElement* particleTreeElement = static_cast<ParticleTreeElement*>(element);
// iterate the particles... // iterate the particles...
std::vector<Particle>& particles = particleTreeElement->getParticles(); QList<Particle>& particles = particleTreeElement->getParticles();
uint16_t numberOfParticles = particles.size(); uint16_t numberOfParticles = particles.size();
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
Particle* particle = &particles[i]; Particle* particle = &particles[i];
@ -72,18 +71,18 @@ void ParticleCollisionSystem::updateCollisionWithVoxels(Particle* particle) {
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
float radius = particle->getRadius() * (float)(TREE_SCALE); float radius = particle->getRadius() * (float)(TREE_SCALE);
const float ELASTICITY = 0.4f; const float ELASTICITY = 0.4f;
const float DAMPING = 0.0f; const float DAMPING = 0.05f;
const float COLLISION_FREQUENCY = 0.5f; const float COLLISION_FREQUENCY = 0.5f;
glm::vec3 penetration; CollisionInfo collisionInfo;
VoxelDetail* voxelDetails = NULL; VoxelDetail* voxelDetails = NULL;
if (_voxels->findSpherePenetration(center, radius, penetration, (void**)&voxelDetails)) { if (_voxels->findSpherePenetration(center, radius, collisionInfo._penetration, (void**)&voxelDetails)) {
// let the particles run their collision scripts if they have them // let the particles run their collision scripts if they have them
particle->collisionWithVoxel(voxelDetails); particle->collisionWithVoxel(voxelDetails);
penetration /= (float)(TREE_SCALE); collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, penetration, COLLISION_FREQUENCY); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
applyHardCollision(particle, penetration, ELASTICITY, DAMPING); applyHardCollision(particle, ELASTICITY, DAMPING, collisionInfo);
delete voxelDetails; // cleanup returned details delete voxelDetails; // cleanup returned details
} }
@ -136,6 +135,10 @@ void ParticleCollisionSystem::updateCollisionWithParticles(Particle* particleA)
} }
} }
// MIN_VALID_SPEED is obtained by computing speed gained at one gravity after the shortest expected frame
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) { void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
// particles that are in hand, don't collide with avatars // particles that are in hand, don't collide with avatars
@ -146,18 +149,18 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE); glm::vec3 center = particle->getPosition() * (float)(TREE_SCALE);
float radius = particle->getRadius() * (float)(TREE_SCALE); float radius = particle->getRadius() * (float)(TREE_SCALE);
const float ELASTICITY = 0.9f; const float ELASTICITY = 0.9f;
const float DAMPING = 0.0f; const float DAMPING = 0.1f;
const float COLLISION_FREQUENCY = 0.5f; const float COLLISION_FREQUENCY = 0.5f;
glm::vec3 penetration; glm::vec3 penetration;
// first check the selfAvatar if set... // first check the selfAvatar if set...
if (_selfAvatar) { if (_selfAvatar) {
AvatarData* avatar = (AvatarData*)_selfAvatar; AvatarData* avatar = (AvatarData*)_selfAvatar;
CollisionInfo collision; CollisionInfo collisionInfo;
if (avatar->findSphereCollision(center, radius, collision)) { if (avatar->findSphereCollision(center, radius, collisionInfo)) {
collision._addedVelocity /= (float)(TREE_SCALE); collisionInfo._addedVelocity /= (float)(TREE_SCALE);
glm::vec3 relativeVelocity = collision._addedVelocity - particle->getVelocity(); glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
if (glm::dot(relativeVelocity, collision._penetration) < 0.f) { if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
// only collide when particle and collision point are moving toward each other // only collide when particle and collision point are moving toward each other
// (doing this prevents some "collision snagging" when particle penetrates the object) // (doing this prevents some "collision snagging" when particle penetrates the object)
@ -165,17 +168,20 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
// NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle. // 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 // TODO: make this less hacky when we have more per-collision details
float elasticity = ELASTICITY; float elasticity = ELASTICITY;
float SLOW_PADDLE_SPEED = 5.0e-5f; float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
float attenuationFactor = glm::length(collision._addedVelocity) / SLOW_PADDLE_SPEED; float damping = DAMPING;
if (attenuationFactor < 1.f) { if (attenuationFactor < 1.f) {
collision._addedVelocity *= attenuationFactor; collisionInfo._addedVelocity *= attenuationFactor;
elasticity *= 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 // HACK END
collision._penetration /= (float)(TREE_SCALE); collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
applyHardCollision(particle, collision._penetration, elasticity, DAMPING, collision._addedVelocity); applyHardCollision(particle, elasticity, damping, collisionInfo);
} }
} }
} }
@ -185,36 +191,37 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) {
//qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n"; //qDebug() << "updateCollisionWithAvatars()... node:" << *node << "\n";
if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) { if (node->getLinkedData() && node->getType() == NODE_TYPE_AGENT) {
AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData()); AvatarData* avatar = static_cast<AvatarData*>(node->getLinkedData());
CollisionInfo collision; CollisionInfo collisionInfo;
if (avatar->findSphereCollision(center, radius, collision)) { if (avatar->findSphereCollision(center, radius, collisionInfo)) {
collision._addedVelocity /= (float)(TREE_SCALE); collisionInfo._addedVelocity /= (float)(TREE_SCALE);
glm::vec3 relativeVelocity = collision._addedVelocity - particle->getVelocity(); glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity();
if (glm::dot(relativeVelocity, collision._penetration) < 0.f) { if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) {
// HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar. // 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. // 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 // TODO: make this less hacky when we have more per-collision details
float elasticity = ELASTICITY; float elasticity = ELASTICITY;
float SLOW_PADDLE_SPEED = 5.0e-5f; float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED;
float attenuationFactor = glm::length(collision._addedVelocity) / SLOW_PADDLE_SPEED; float damping = DAMPING;
if (attenuationFactor < 1.f) { if (attenuationFactor < 1.f) {
collision._addedVelocity *= attenuationFactor; collisionInfo._addedVelocity *= attenuationFactor;
elasticity *= 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 // HACK END
collision._penetration /= (float)(TREE_SCALE); collisionInfo._penetration /= (float)(TREE_SCALE);
updateCollisionSound(particle, collision._penetration, COLLISION_FREQUENCY); updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY);
applyHardCollision(particle, collision._penetration, ELASTICITY, DAMPING, collision._addedVelocity); applyHardCollision(particle, ELASTICITY, damping, collisionInfo);
} }
} }
} }
} }
} }
// TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity // TODO: convert applyHardCollision() to take a CollisionInfo& instead of penetration + addedVelocity
void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::vec3& penetration, void ParticleCollisionSystem::applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo) {
float elasticity, float damping, const glm::vec3& addedVelocity) {
// //
// Update the particle in response to a hard collision. Position will be reset exactly // 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. // to outside the colliding surface. Velocity will be modified according to elasticity.
@ -226,20 +233,19 @@ void ParticleCollisionSystem::applyHardCollision(Particle* particle, const glm::
glm::vec3 velocity = particle->getVelocity(); glm::vec3 velocity = particle->getVelocity();
const float EPSILON = 0.0f; const float EPSILON = 0.0f;
float velocityDotPenetration = glm::dot(velocity, penetration); glm::vec3 relativeVelocity = collisionInfo._addedVelocity - velocity;
if (velocityDotPenetration > EPSILON) { float velocityDotPenetration = glm::dot(relativeVelocity, collisionInfo._penetration);
position -= penetration; if (velocityDotPenetration < EPSILON) {
static float HALTING_VELOCITY = 0.2f / (float)(TREE_SCALE); // particle is moving into collision surface
// cancel out the velocity component in the direction of penetration position -= collisionInfo._penetration;
float penetrationLength = glm::length(penetration); if (glm::length(relativeVelocity) < HALTING_SPEED) {
glm::vec3 direction = penetration / penetrationLength; // static friction kicks in and particle moves with colliding object
velocity -= (glm::dot(velocity, direction) * (1.0f + elasticity)) * direction; velocity = collisionInfo._addedVelocity;
velocity += addedVelocity; } else {
velocity *= glm::clamp(1.f - damping, 0.0f, 1.0f); glm::vec3 direction = glm::normalize(collisionInfo._penetration);
if (glm::length(velocity) < HALTING_VELOCITY) { velocity += glm::dot(relativeVelocity, direction) * (1.0f + elasticity) * direction; // dynamic reflection
// If moving really slowly after a collision, and not applying forces, stop altogether velocity += glm::clamp(damping, 0.0f, 1.0f) * (relativeVelocity - glm::dot(relativeVelocity, direction) * direction); // dynamic friction
velocity *= 0.f;
} }
} }
const bool wantDebug = false; const bool wantDebug = false;

View file

@ -16,6 +16,7 @@
#include <QtScript/QScriptEngine> #include <QtScript/QScriptEngine>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <CollisionInfo.h>
#include <SharedUtil.h> #include <SharedUtil.h>
#include <OctreePacketData.h> #include <OctreePacketData.h>
@ -46,8 +47,7 @@ public:
void updateCollisionWithVoxels(Particle* particle); void updateCollisionWithVoxels(Particle* particle);
void updateCollisionWithParticles(Particle* particle); void updateCollisionWithParticles(Particle* particle);
void updateCollisionWithAvatars(Particle* particle); void updateCollisionWithAvatars(Particle* particle);
void applyHardCollision(Particle* particle, const glm::vec3& penetration, float elasticity, float damping, void applyHardCollision(Particle* particle, float elasticity, float damping, const CollisionInfo& collisionInfo);
const glm::vec3& addedVelocity = NO_ADDED_VELOCITY);
void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency); void updateCollisionSound(Particle* particle, const glm::vec3 &penetration, float frequency);
private: private:

View file

@ -11,12 +11,14 @@
#include "ParticleTree.h" #include "ParticleTree.h"
#include "ParticleTreeElement.h" #include "ParticleTreeElement.h"
ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement() { ParticleTreeElement::ParticleTreeElement(unsigned char* octalCode) : OctreeElement(), _particles(NULL) {
init(octalCode); init(octalCode);
}; };
ParticleTreeElement::~ParticleTreeElement() { ParticleTreeElement::~ParticleTreeElement() {
_voxelMemoryUsage -= sizeof(ParticleTreeElement); _voxelMemoryUsage -= sizeof(ParticleTreeElement);
delete _particles;
_particles = NULL;
} }
// This will be called primarily on addChildAt(), which means we're adding a child of our // This will be called primarily on addChildAt(), which means we're adding a child of our
@ -31,6 +33,7 @@ OctreeElement* ParticleTreeElement::createNewElement(unsigned char* octalCode) c
void ParticleTreeElement::init(unsigned char* octalCode) { void ParticleTreeElement::init(unsigned char* octalCode) {
OctreeElement::init(octalCode); OctreeElement::init(octalCode);
_particles = new QList<Particle>;
_voxelMemoryUsage += sizeof(ParticleTreeElement); _voxelMemoryUsage += sizeof(ParticleTreeElement);
} }
@ -45,12 +48,12 @@ bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const
bool success = true; // assume the best... bool success = true; // assume the best...
// write our particles out... // write our particles out...
uint16_t numberOfParticles = _particles.size(); uint16_t numberOfParticles = _particles->size();
success = packetData->appendValue(numberOfParticles); success = packetData->appendValue(numberOfParticles);
if (success) { if (success) {
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
const Particle& particle = _particles[i]; const Particle& particle = (*_particles)[i];
success = particle.appendParticleData(packetData); success = particle.appendParticleData(packetData);
if (!success) { if (!success) {
break; break;
@ -62,35 +65,42 @@ bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const
void ParticleTreeElement::update(ParticleTreeUpdateArgs& args) { void ParticleTreeElement::update(ParticleTreeUpdateArgs& args) {
markWithChangedTime(); markWithChangedTime();
// TODO: early exit when _particles is empty
// update our contained particles // update our contained particles
uint16_t numberOfParticles = _particles.size(); QList<Particle>::iterator particleItr = _particles->begin();
while(particleItr != _particles->end()) {
for (uint16_t i = 0; i < numberOfParticles; i++) { Particle& particle = (*particleItr);
_particles[i].update(); particle.update(_lastChanged);
// If the particle wants to die, or if it's left our bounding box, then move it // If the particle wants to die, or if it's left our bounding box, then move it
// into the arguments moving particles. These will be added back or deleted completely // into the arguments moving particles. These will be added back or deleted completely
if (_particles[i].getShouldDie() || !_box.contains(_particles[i].getPosition())) { if (particle.getShouldDie() || !_box.contains(particle.getPosition())) {
args._movingParticles.push_back(_particles[i]); args._movingParticles.push_back(particle);
// erase this particle // erase this particle
_particles.erase(_particles.begin()+i); particleItr = _particles->erase(particleItr);
}
// reduce our index since we just removed this item else
i--; {
numberOfParticles--; ++particleItr;
} }
} }
// TODO: if _particles is empty after while loop consider freeing memory in _particles if
// internal array is too big (QList internal array does not decrease size except in dtor and
// assignment operator). Otherwise _particles could become a "resource leak" for large
// roaming piles of particles.
} }
bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float radius, bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const { glm::vec3& penetration, void** penetratedObject) const {
uint16_t numberOfParticles = _particles.size(); QList<Particle>::iterator particleItr = _particles->begin();
for (uint16_t i = 0; i < numberOfParticles; i++) { QList<Particle>::const_iterator particleEnd = _particles->end();
glm::vec3 particleCenter = _particles[i].getPosition(); while(particleItr != particleEnd) {
float particleRadius = _particles[i].getRadius(); Particle& particle = (*particleItr);
glm::vec3 particleCenter = particle.getPosition();
float particleRadius = particle.getRadius();
// don't penetrate yourself // don't penetrate yourself
if (particleCenter == center && particleRadius == radius) { if (particleCenter == center && particleRadius == radius) {
@ -102,23 +112,28 @@ bool ParticleTreeElement::findSpherePenetration(const glm::vec3& center, float r
const bool IN_HAND_PARTICLES_DONT_COLLIDE = false; const bool IN_HAND_PARTICLES_DONT_COLLIDE = false;
if (IN_HAND_PARTICLES_DONT_COLLIDE) { if (IN_HAND_PARTICLES_DONT_COLLIDE) {
// don't penetrate if the particle is "inHand" -- they don't collide // don't penetrate if the particle is "inHand" -- they don't collide
if (_particles[i].getInHand()) { if (particle.getInHand()) {
return false; ++particleItr;
continue;
} }
} }
if (findSphereSpherePenetration(center, radius, particleCenter, particleRadius, penetration)) { if (findSphereSpherePenetration(center, radius, particleCenter, particleRadius, penetration)) {
*penetratedObject = (void*)&_particles[i]; // return true on first valid particle penetration
*penetratedObject = (void*)(&particle);
return true; return true;
} }
++particleItr;
} }
return false; return false;
} }
bool ParticleTreeElement::containsParticle(const Particle& particle) const { bool ParticleTreeElement::containsParticle(const Particle& particle) const {
uint16_t numberOfParticles = _particles.size(); // TODO: remove this method and force callers to use getParticleWithID() instead
uint16_t numberOfParticles = _particles->size();
uint32_t particleID = particle.getID();
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
if (_particles[i].getID() == particle.getID()) { if ((*_particles)[i].getID() == particleID) {
return true; return true;
} }
} }
@ -126,13 +141,17 @@ bool ParticleTreeElement::containsParticle(const Particle& particle) const {
} }
bool ParticleTreeElement::updateParticle(const Particle& particle) { bool ParticleTreeElement::updateParticle(const Particle& particle) {
// NOTE: this method must first lookup the particle by ID, hence it is O(N)
// and "particle is not found" is worst-case (full N) but maybe we don't care?
// (guaranteed that num particles per elemen is small?)
const bool wantDebug = false; const bool wantDebug = false;
uint16_t numberOfParticles = _particles.size(); uint16_t numberOfParticles = _particles->size();
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
if (_particles[i].getID() == particle.getID()) { Particle& thisParticle = (*_particles)[i];
int difference = _particles[i].getLastUpdated() - particle.getLastUpdated(); if (thisParticle.getID() == particle.getID()) {
bool changedOnServer = _particles[i].getLastEdited() < particle.getLastEdited(); int difference = thisParticle.getLastUpdated() - particle.getLastUpdated();
bool localOlder = _particles[i].getLastUpdated() < particle.getLastUpdated(); bool changedOnServer = thisParticle.getLastEdited() < particle.getLastEdited();
bool localOlder = thisParticle.getLastUpdated() < particle.getLastUpdated();
if (changedOnServer || localOlder) { if (changedOnServer || localOlder) {
if (wantDebug) { if (wantDebug) {
printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n", printf("local particle [id:%d] %s and %s than server particle by %d, particle.isNewlyCreated()=%s\n",
@ -140,7 +159,7 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
(localOlder ? "OLDER" : "NEWER"), (localOlder ? "OLDER" : "NEWER"),
difference, debug::valueOf(particle.isNewlyCreated()) ); difference, debug::valueOf(particle.isNewlyCreated()) );
} }
_particles[i].copyChangedProperties(particle); thisParticle.copyChangedProperties(particle);
} else { } else {
if (wantDebug) { if (wantDebug) {
printf(">>> IGNORING SERVER!!! Would've caused jutter! <<< " printf(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
@ -159,22 +178,23 @@ bool ParticleTreeElement::updateParticle(const Particle& particle) {
const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const { const Particle* ParticleTreeElement::getClosestParticle(glm::vec3 position) const {
const Particle* closestParticle = NULL; const Particle* closestParticle = NULL;
float closestParticleDistance = FLT_MAX; float closestParticleDistance = FLT_MAX;
uint16_t numberOfParticles = _particles.size(); uint16_t numberOfParticles = _particles->size();
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
float distanceToParticle = glm::distance(position, _particles[i].getPosition()); float distanceToParticle = glm::distance(position, (*_particles)[i].getPosition());
if (distanceToParticle < closestParticleDistance) { if (distanceToParticle < closestParticleDistance) {
closestParticle = &_particles[i]; closestParticle = &(*_particles)[i];
} }
} }
return closestParticle; return closestParticle;
} }
const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const { const Particle* ParticleTreeElement::getParticleWithID(uint32_t id) const {
// NOTE: this lookup is O(N) but maybe we don't care? (guaranteed that num particles per elemen is small?)
const Particle* foundParticle = NULL; const Particle* foundParticle = NULL;
uint16_t numberOfParticles = _particles.size(); uint16_t numberOfParticles = _particles->size();
for (uint16_t i = 0; i < numberOfParticles; i++) { for (uint16_t i = 0; i < numberOfParticles; i++) {
if (_particles[i].getID() == id) { if ((*_particles)[i].getID() == id) {
foundParticle = &_particles[i]; foundParticle = &(*_particles)[i];
break; break;
} }
} }
@ -229,7 +249,7 @@ bool ParticleTreeElement::collapseChildren() {
void ParticleTreeElement::storeParticle(const Particle& particle, Node* senderNode) { void ParticleTreeElement::storeParticle(const Particle& particle, Node* senderNode) {
_particles.push_back(particle); _particles->push_back(particle);
markWithChangedTime(); markWithChangedTime();
} }

View file

@ -10,9 +10,10 @@
#ifndef __hifi__ParticleTreeElement__ #ifndef __hifi__ParticleTreeElement__
#define __hifi__ParticleTreeElement__ #define __hifi__ParticleTreeElement__
#include <vector> //#include <vector>
#include <OctreeElement.h> #include <OctreeElement.h>
#include <QList>
#include "Particle.h" #include "Particle.h"
#include "ParticleTree.h" #include "ParticleTree.h"
@ -22,7 +23,7 @@ class ParticleTreeElement;
class ParticleTreeUpdateArgs { class ParticleTreeUpdateArgs {
public: public:
std::vector<Particle> _movingParticles; QList<Particle> _movingParticles;
}; };
class ParticleTreeElement : public OctreeElement { class ParticleTreeElement : public OctreeElement {
@ -34,7 +35,6 @@ class ParticleTreeElement : public OctreeElement {
public: public:
virtual ~ParticleTreeElement(); virtual ~ParticleTreeElement();
virtual void init(unsigned char * octalCode);
// type safe versions of OctreeElement methods // type safe versions of OctreeElement methods
ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); } ParticleTreeElement* getChildAtIndex(int index) { return (ParticleTreeElement*)OctreeElement::getChildAtIndex(index); }
@ -79,9 +79,9 @@ public:
virtual bool findSpherePenetration(const glm::vec3& center, float radius, virtual bool findSpherePenetration(const glm::vec3& center, float radius,
glm::vec3& penetration, void** penetratedObject) const; glm::vec3& penetration, void** penetratedObject) const;
const std::vector<Particle>& getParticles() const { return _particles; } const QList<Particle>& getParticles() const { return *_particles; }
std::vector<Particle>& getParticles() { return _particles; } QList<Particle>& getParticles() { return *_particles; }
bool hasParticles() const { return _particles.size() > 0; } bool hasParticles() const { return _particles->size() > 0; }
void update(ParticleTreeUpdateArgs& args); void update(ParticleTreeUpdateArgs& args);
void setTree(ParticleTree* tree) { _myTree = tree; } void setTree(ParticleTree* tree) { _myTree = tree; }
@ -93,10 +93,12 @@ public:
protected: protected:
virtual void init(unsigned char * octalCode);
void storeParticle(const Particle& particle, Node* senderNode = NULL); void storeParticle(const Particle& particle, Node* senderNode = NULL);
ParticleTree* _myTree; ParticleTree* _myTree;
std::vector<Particle> _particles; QList<Particle>* _particles;
}; };
#endif /* defined(__hifi__ParticleTreeElement__) */ #endif /* defined(__hifi__ParticleTreeElement__) */