fixed some problems in UpdateEntityOperator in case where original containing element isn't best fit

This commit is contained in:
ZappoMan 2014-08-29 17:48:14 -07:00
parent f654fac851
commit 2fab662e8c
11 changed files with 532 additions and 124 deletions

View file

@ -66,8 +66,21 @@ void EntityTreeRenderer::update() {
}
void EntityTreeRenderer::render(RenderMode renderMode) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "------------ EntityTreeRenderer::render() -- BEFORE WE BEGIN: the tree[" << _tree << "] -------------";
static_cast<EntityTree*>(_tree)->dumpTree();
qDebug() << "------------ EntityTreeRenderer::render() -- END the tree-------------";
}
OctreeRenderer::render(renderMode);
deleteReleasedModels(); // seems like as good as any other place to do some memory cleanup
if (wantDebug) {
qDebug() << "------------ DONE EntityTreeRenderer::render() -------------";
}
}
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem* entityItem) {
@ -98,6 +111,14 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI
}
void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "EntityTreeRenderer::renderElement()...";
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
}
//PerformanceTimer perfTimer("renderElement");
args->_elementsTouched++;
// actually render it here...
@ -180,8 +201,6 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
for (uint16_t i = 0; i < numberOfEntities; i++) {
EntityItem* entityItem = entityItems[i];
bool wantDebug = false;
if (wantDebug) {
bool isBestFit = entityTreeElement->bestFitEntityBounds(entityItem);
qDebug() << "EntityTreeRenderer::renderElement() "
@ -202,6 +221,12 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
}
if (wantDebug) {
qDebug() << " EntityTreeRenderer about to render entity...";
qDebug() << " element=" << element;
qDebug() << " entityItem=" << entityItem;
qDebug() << " entityItem->getEntityItemID()=" << entityItem->getEntityItemID();
}
entityItem->render(args);
if (glower) {
delete glower;

View file

@ -21,36 +21,95 @@ DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree, const EntityItemID&
_foundCount(0),
_lookingCount(0)
{
bool wantDebug = false;
if (wantDebug) {
qDebug() << "DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID).... ";
qDebug() << " tree=" << tree;
qDebug() << " _tree=" << tree;
qDebug() << "------------ DeleteEntityOperator -- BEFORE WE BEGIN: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ DeleteEntityOperator -- END the tree-------------";
}
addEntityIDToDeleteList(searchEntityID);
}
DeleteEntityOperator::~DeleteEntityOperator() {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "~DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID).... ";
qDebug() << "------------ ~DeleteEntityOperator -- AFTER WE'RE DONE: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ ~DeleteEntityOperator -- END the tree-------------";
}
}
DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree) :
_tree(tree),
_changeTime(usecTimestampNow()),
_foundCount(0),
_lookingCount(0)
{
bool wantDebug = false;
if (wantDebug) {
qDebug() << "DeleteEntityOperator(EntityTree* tree).... ";
qDebug() << " tree=" << tree;
qDebug() << " _tree=" << _tree;
qDebug() << "------------ DeleteEntityOperator -- BEFORE WE BEGIN: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ DeleteEntityOperator -- END the tree-------------";
}
}
void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID).... ";
qDebug() << " _tree=" << _tree;
qDebug() << " searchEntityID=" << searchEntityID;
}
// check our tree, to determine if this entity is known
EntityToDeleteDetails details;
details.containingElement = _tree->getContainingElement(searchEntityID);
if (wantDebug) {
qDebug() << " details.containingElement=" << details.containingElement;
}
if (details.containingElement) {
details.entity = details.containingElement->getEntityWithEntityItemID(searchEntityID);
if (wantDebug) {
qDebug() << " details.entity=" << details.entity;
}
if (!details.entity) {
//assert(false);
qDebug() << "that's UNEXPECTED, we got a _containingElement, but couldn't find the oldEntity!";
} else {
details.cube = details.containingElement->getAACube();
if (wantDebug) {
qDebug() << " details.cube=" << details.cube;
}
_entitiesToDelete << details;
_lookingCount++;
_tree->trackDeletedEntity(searchEntityID);
// before deleting any entity make sure to remove it from our Mortal, Changing, and Moving lists
_tree->removeEntityFromSimulationLists(searchEntityID);
}
}
if (wantDebug) {
qDebug() << " _entitiesToDelete.size():" << _entitiesToDelete.size();
}
}
@ -73,6 +132,29 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElement* el
}
bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "DeleteEntityOperator::PreRecursion().... ";
qDebug() << " element=" << element;
qDebug() << " element.AACube=" << element->getAACube();
qDebug() << " _lookingCount=" << _lookingCount;
qDebug() << " _foundCount=" << _foundCount;
qDebug() << " --------- list of deleting entities -----------";
foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
qDebug() << " ENTITY TO DELETE";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " cube=" << details.cube;
qDebug() << " containingElement=" << details.containingElement;
qDebug() << " containingElement->getAACube()=" << details.containingElement->getAACube();
}
qDebug() << " --------- list of deleting entities -----------";
}
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -98,10 +180,20 @@ bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
if (entityTreeElement == details.containingElement) {
EntityItemID entityItemID = details.entity->getEntityItemID();
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
bool removed = entityTreeElement->removeEntityItem(theEntity); // remove it from the element
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
delete theEntity; // now actually delete the entity!
_foundCount++;
if (wantDebug) {
qDebug() << "DeleteEntityOperator::PreRecursion().... deleting entity:" << entityItemID;
qDebug() << " details.entity=" << details.entity;
qDebug() << " theEntity=" << theEntity;
qDebug() << " called entityTreeElement->removeEntityItem(theEntity)";
qDebug() << " removed=" << removed;
qDebug() << " called _tree->setContainingElement(entityItemID, NULL)";
qDebug() << " called delete theEntity";
}
}
}
@ -113,6 +205,27 @@ bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
}
bool DeleteEntityOperator::PostRecursion(OctreeElement* element) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "DeleteEntityOperator::PostRecursion().... ";
qDebug() << " element=" << element;
qDebug() << " element.AACube=" << element->getAACube();
qDebug() << " _lookingCount=" << _lookingCount;
qDebug() << " _foundCount=" << _foundCount;
qDebug() << " --------- list of deleting entities -----------";
foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
qDebug() << " DELETING ENTITY";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " cube=" << details.cube;
qDebug() << " containingElement=" << details.containingElement;
}
qDebug() << " --------- list of deleting entities -----------";
}
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -124,9 +237,9 @@ bool DeleteEntityOperator::PostRecursion(OctreeElement* element) {
element->markWithChangedTime();
}
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
bool somethingPruned = entityTreeElement->pruneChildren(); // take this opportunity to prune any empty leaves
bool wantDebug = false;
if (somethingPruned && wantDebug) {
qDebug() << "DeleteEntityOperator::PostRecursion() something pruned!!!";
}

View file

@ -31,6 +31,8 @@ class DeleteEntityOperator : public RecurseOctreeOperator {
public:
DeleteEntityOperator(EntityTree* tree);
DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID);
~DeleteEntityOperator();
void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);

View file

@ -694,16 +694,23 @@ void EntityTree::changeEntityState(EntityItem* const entity,
void EntityTree::update() {
// our new strategy should be to segregate entities into three classes:
// 1) stationary things that are not changing - most models
// 2) stationary things that are animating - they can be touched linearly and they don't change the tree
// 2) mortal things - these are stationary but have a lifetime - then need to be checked,
// can be touched linearly, and won't change the tree
// 2) changing things - like things animating they can be touched linearly and they don't change the tree
// 3) moving things - these need to scan the tree and update accordingly
// finally - all things that need to be deleted, can be handled on a single delete pass.
//
// TODO: theoretically we could combine the move and delete tree passes...
lockForWrite();
quint64 now = usecTimestampNow();
QSet<EntityItemID> entitiesToDelete;
updateChangingEntities(now, entitiesToDelete);
updateMovingEntities(now, entitiesToDelete);
updateMortalEntities(now, entitiesToDelete);
deleteEntities(entitiesToDelete);
if (entitiesToDelete.size() > 0) {
deleteEntities(entitiesToDelete);
}
unlock();
}
@ -749,66 +756,76 @@ void EntityTree::updateChangingEntities(quint64 now, QSet<EntityItemID>& entitie
void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
bool wantDebug = false;
MovingEntitiesOperator moveOperator(this);
QSet<EntityItem*> entitiesBecomingStatic;
QSet<EntityItem*> entitiesBecomingMortal;
QSet<EntityItem*> entitiesBecomingChanging;
if (_movingEntities.size() > 0) {
MovingEntitiesOperator moveOperator(this);
// TODO: switch these to iterators so we can remove items that get deleted
for (int i = 0; i < _movingEntities.size(); i++) {
EntityItem* thisEntity = _movingEntities[i];
QSet<EntityItem*> entitiesBecomingStatic;
QSet<EntityItem*> entitiesBecomingMortal;
QSet<EntityItem*> entitiesBecomingChanging;
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity;
} else {
AACube oldCube = thisEntity->getAACube();
thisEntity->update(now);
AACube newCube = thisEntity->getAACube();
// check to see if this movement has sent the entity outside of the domain.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) {
if (wantDebug) {
qDebug() << "The entity " << thisEntity->getEntityItemID() << " moved outside of the domain. Delete it.";
}
// TODO: switch these to iterators so we can remove items that get deleted
for (int i = 0; i < _movingEntities.size(); i++) {
EntityItem* thisEntity = _movingEntities[i];
// always check to see if the lifetime has expired, for immortal entities this is always false
if (thisEntity->lifetimeHasExpired()) {
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity;
} else {
AACube oldCube = thisEntity->getAACube();
thisEntity->update(now);
AACube newCube = thisEntity->getAACube();
if (wantDebug) {
qDebug() << "EntityTree::update() thisEntity=" << thisEntity;
qDebug() << " oldCube=" << oldCube;
qDebug() << " newCube=" << newCube;
qDebug() << "MOVING entity " << thisEntity->getEntityItemID();
qDebug() << " oldCube:" << oldCube;
qDebug() << " newCube:" << newCube;
}
// check to see if this movement has sent the entity outside of the domain.
AACube domainBounds(glm::vec3(0.0f,0.0f,0.0f), 1.0f);
if (!domainBounds.touches(newCube)) {
if (wantDebug) {
qDebug() << "The entity moved outside of the domain. Delete it.";
qDebug() << " entity=" << thisEntity;
qDebug() << " entityID=" << thisEntity->getEntityItemID();
}
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
// check to see if this entity is no longer moving
EntityItem::SimulationState newState = thisEntity->getSimulationState();
if (newState == EntityItem::Changing) {
entitiesBecomingChanging << thisEntity;
} else if (newState == EntityItem::Mortal) {
entitiesBecomingMortal << thisEntity;
} else if (newState == EntityItem::Static) {
entitiesToDelete << thisEntity->getEntityItemID();
entitiesBecomingStatic << thisEntity;
} else {
if (wantDebug) {
qDebug() << " ACTUALLY MOVING IT";
}
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
// check to see if this entity is no longer moving
EntityItem::SimulationState newState = thisEntity->getSimulationState();
if (newState == EntityItem::Changing) {
entitiesBecomingChanging << thisEntity;
} else if (newState == EntityItem::Mortal) {
entitiesBecomingMortal << thisEntity;
} else if (newState == EntityItem::Static) {
entitiesBecomingStatic << thisEntity;
}
}
}
}
}
recurseTreeWithOperator(&moveOperator);
if (moveOperator.hasMovingEntities()) {
recurseTreeWithOperator(&moveOperator);
}
// change state for any entities that were moving but are now either static, mortal, or changing
foreach(EntityItem* entity, entitiesBecomingStatic) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Static);
}
foreach(EntityItem* entity, entitiesBecomingMortal) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Mortal);
}
foreach(EntityItem* entity, entitiesBecomingChanging) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Changing);
// change state for any entities that were moving but are now either static, mortal, or changing
foreach(EntityItem* entity, entitiesBecomingStatic) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Static);
}
foreach(EntityItem* entity, entitiesBecomingMortal) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Mortal);
}
foreach(EntityItem* entity, entitiesBecomingChanging) {
changeEntityState(entity, EntityItem::Moving, EntityItem::Changing);
}
}
}
@ -1196,6 +1213,7 @@ public:
bool DebugOperator::PreRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
qDebug() << "EntityTreeElement [" << entityTreeElement << "]";
entityTreeElement->debugDump();
return true;
}

View file

@ -54,8 +54,6 @@ EntityTreeElement* EntityTreeElement::addChildAtIndex(int index) {
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData,
EncodeBitstreamParams& params) const {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "EntityTreeElement::appendElementData()";
@ -600,17 +598,8 @@ bool EntityTreeElement::removeEntityWithEntityItemID(const EntityItemID& id) {
return foundEntity;
}
bool EntityTreeElement::removeEntityItem(const EntityItem* entity) {
bool foundEntity = false;
uint16_t numberOfEntities = _entityItems->size();
for (uint16_t i = 0; i < numberOfEntities; i++) {
if ((*_entityItems)[i] == entity) {
foundEntity = true;
_entityItems->removeAt(i);
break;
}
}
return foundEntity;
bool EntityTreeElement::removeEntityItem(EntityItem* entity) {
return _entityItems->removeAll(entity) > 0;
}
@ -666,7 +655,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
EntityItem::SimulationState oldState = entityItem->getSimulationState();
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
EntityItem::SimulationState newState = entityItem->getSimulationState();
_myTree->changeEntityState(entityItem, oldState, newState);
if (oldState != newState) {
_myTree->changeEntityState(entityItem, oldState, newState);
}
bool bestFitAfter = bestFitEntityBounds(entityItem);
if (bestFitBefore != bestFitAfter) {
@ -675,19 +666,20 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
// This is the case where the entity existed, and is in some element in our tree...
if (currentContainingElement != this) {
currentContainingElement->removeEntityItem(entityItem);
this->addEntityItem(entityItem);
addEntityItem(entityItem);
_myTree->setContainingElement(entityItemID, this);
}
}
}
} else {
entityItem = EntityTypes::constructEntityItem(dataAt, bytesLeftToRead, args);
if (entityItem) {
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
addEntityItem(entityItem); // add this new entity to this elements entities
_myTree->setContainingElement(entityItem->getEntityItemID(), this);
_myTree->setContainingElement(entityItemID, this);
newEntity = true;
EntityItem::SimulationState newState = entityItem->getSimulationState();
_myTree->changeEntityState(entityItem, EntityItem::Static, newState);
}
}
// Move the buffer forward to read more entities

View file

@ -143,7 +143,7 @@ public:
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
bool removeEntityWithEntityItemID(const EntityItemID& id);
bool removeEntityItem(const EntityItem* entity);
bool removeEntityItem(EntityItem* entity);
bool containsEntityBounds(const EntityItem* entity) const;
bool bestFitEntityBounds(const EntityItem* entity) const;

View file

@ -22,8 +22,31 @@ MovingEntitiesOperator::MovingEntitiesOperator(EntityTree* tree) :
_foundNewCount(0),
_lookingCount(0)
{
bool wantDebug = false;
if (wantDebug) {
qDebug() << "MovingEntitiesOperator..... *********** start here!";
qDebug() << " tree=" << tree;
qDebug() << " _tree=" << _tree;
qDebug() << "------------ MovingEntitiesOperator -- BEFORE WE BEGIN: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ MovingEntitiesOperator -- END the tree-------------";
}
}
MovingEntitiesOperator::~MovingEntitiesOperator() {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "~MovingEntitiesOperator().... ";
qDebug() << "------------ ~MovingEntitiesOperator -- AFTER WE'RE DONE: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ ~MovingEntitiesOperator -- END the tree-------------";
}
}
void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube) {
// check our tree, to determine if this entity is known
EntityToMoveDetails details;
@ -33,6 +56,7 @@ void MovingEntitiesOperator::addEntityToMoveList(EntityItem* entity, const AACub
details.newFound = false;
details.oldCube = oldCube;
details.newCube = newCube;
details.newBox = newCube.clamp(0.0f, 1.0f);
_entitiesToMove << details;
_lookingCount++;
}
@ -56,6 +80,32 @@ bool MovingEntitiesOperator::shouldRecurseSubTree(OctreeElement* element) {
}
bool MovingEntitiesOperator::PreRecursion(OctreeElement* element) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "MovingEntitiesOperator::PreRecursion().... ";
qDebug() << " element=" << element;
qDebug() << " element.AACube=" << element->getAACube();
qDebug() << " _lookingCount=" << _lookingCount;
qDebug() << " _foundNewCount=" << _foundNewCount;
qDebug() << " _foundOldCount=" << _foundOldCount;
qDebug() << " --------- list of moving entities -----------";
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
qDebug() << " MOVING ENTITY";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " oldCube=" << details.oldCube;
qDebug() << " newCube=" << details.newCube;
qDebug() << " newBox=" << details.newBox;
qDebug() << " oldContainingElement=" << details.oldContainingElement;
qDebug() << " oldFound=" << details.oldFound;
qDebug() << " newFound=" << details.newFound;
}
qDebug() << " --------- list of moving entities -----------";
}
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
@ -68,34 +118,97 @@ bool MovingEntitiesOperator::PreRecursion(OctreeElement* element) {
// and the new entity.
bool keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);
if (wantDebug) {
qDebug() << " keepSearching=" << keepSearching;
qDebug() << " shouldRecurseSubTree(element)=" << shouldRecurseSubTree(element);
}
// If we haven't yet found all the entities, and this sub tree contains at least one of our
// entities, then we need to keep searching.
if (keepSearching && shouldRecurseSubTree(element)) {
if (wantDebug) {
qDebug() << " PROCESSING MOVE ON THIS ELEMENT";
}
// check against each of our search entities
qDebug() << " --------- PROCESSING list of moving entities -----------";
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
qDebug() << " PROCESSING --- MOVING ENTITY";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " oldCube=" << details.oldCube;
qDebug() << " newCube=" << details.newCube;
qDebug() << " newBox=" << details.newBox;
qDebug() << " oldContainingElement=" << details.oldContainingElement;
qDebug() << " oldFound=" << details.oldFound;
qDebug() << " newFound=" << details.newFound;
if (!details.oldFound) {
qDebug() << " THIS ENTITY'S OLD LOCATION HAS NOT BEEN FOUND... ";
}
if (entityTreeElement == details.oldContainingElement) {
qDebug() << " THIS ELEMENT IS THE ENTITY'S OLD LOCATION... ";
}
// If this is one of the old elements we're looking for, then ask it to remove the old entity
if (!details.oldFound && entityTreeElement == details.oldContainingElement) {
EntityItemID entityItemID = details.entity->getEntityItemID();
entityTreeElement->removeEntityWithEntityItemID(entityItemID);
//qDebug() << "removing entityItem from element... entityItemID=" << entityItemID << "entityTreeElement=" << entityTreeElement;
qDebug() << " PROCESSING REMOVE ENTITY FROM OLD ELEMENT <<<<<<<<<<<<<";
entityTreeElement->removeEntityItem(details.entity);
qDebug() << "removing entityItem from element... ";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " entityTreeElement=" << entityTreeElement;
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
_foundOldCount++;
//details.oldFound = true; // TODO: would be nice to add this optimization
}
// If this element is the best fit for the new bounds of this entity then add the entity to the element
bool bestFitCube = entityTreeElement->bestFitBounds(details.newCube);
bool bestFitBox = entityTreeElement->bestFitBounds(details.newBox);
qDebug() << " bestFitCube=" << bestFitCube;
qDebug() << " bestFitBox=" << bestFitBox;
if (bestFitCube != bestFitBox) {
qDebug() << " WHOA!!!! bestFitCube != bestFitBox!!!!";
}
if (!details.newFound) {
qDebug() << " THIS ENTITY'S NEW LOCATION HAS NOT BEEN FOUND... ";
}
if (entityTreeElement->bestFitBounds(details.newCube)) {
qDebug() << " THIS ELEMENT IS THE ENTITY'S BEST FIT NEW LOCATION... ";
}
if (!details.newFound && entityTreeElement->bestFitBounds(details.newCube)) {
qDebug() << " PROCESSING ADD ENTITY TO NEW ELEMENT <<<<<<<<<<<<<";
EntityItemID entityItemID = details.entity->getEntityItemID();
qDebug() << "adding entityItem to element...";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << entityItemID;
qDebug() << " entityTreeElement=" << entityTreeElement;
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
entityTreeElement->addEntityItem(details.entity);
_tree->setContainingElement(entityItemID, entityTreeElement);
//qDebug() << "adding entityItem to element... entityItemID=" << entityItemID << "entityTreeElement=" << entityTreeElement;
_foundNewCount++;
//details.newFound = true; // TODO: would be nice to add this optimization
}
}
qDebug() << " --------- DONE PROCESSING list of moving entities -----------";
// if we haven't found all of our search for entities, then keep looking
keepSearching = (_foundOldCount < _lookingCount) || (_foundNewCount < _lookingCount);
@ -105,6 +218,33 @@ bool MovingEntitiesOperator::PreRecursion(OctreeElement* element) {
}
bool MovingEntitiesOperator::PostRecursion(OctreeElement* element) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "MovingEntitiesOperator::PostRecursion().... ";
qDebug() << " element=" << element;
qDebug() << " element.AACube=" << element->getAACube();
qDebug() << " _lookingCount=" << _lookingCount;
qDebug() << " _foundNewCount=" << _foundNewCount;
qDebug() << " _foundOldCount=" << _foundOldCount;
qDebug() << " --------- list of moving entities -----------";
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
qDebug() << " MOVING ENTITY";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " oldCube=" << details.oldCube;
qDebug() << " newCube=" << details.newCube;
qDebug() << " newBox=" << details.newBox;
qDebug() << " oldContainingElement=" << details.oldContainingElement;
qDebug() << " oldFound=" << details.oldFound;
qDebug() << " newFound=" << details.newFound;
}
qDebug() << " --------- list of moving entities -----------";
}
// Post-recursion is the unwinding process. For this operation, while we
// unwind we want to mark the path as being dirty if we changed it below.
// We might have two paths, one for the old entity and one for the new entity.
@ -118,7 +258,6 @@ bool MovingEntitiesOperator::PostRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
bool somethingPruned = entityTreeElement->pruneChildren(); // take this opportunity to prune any empty leaves
bool wantDebug = false;
if (somethingPruned && wantDebug) {
qDebug() << "MovingEntitiesOperator::PostRecursion() something pruned!!!";
}
@ -131,8 +270,27 @@ OctreeElement* MovingEntitiesOperator::PossiblyCreateChildAt(OctreeElement* elem
if (wantDebug) {
qDebug() << "MovingEntitiesOperator::PossiblyCreateChildAt().... ";
qDebug() << " _foundNewCount=" << _foundNewCount;
qDebug() << " element=" << element;
qDebug() << " element.AACube=" << element->getAACube();
qDebug() << " childIndex=" << childIndex;
qDebug() << " _lookingCount=" << _lookingCount;
qDebug() << " _foundNewCount=" << _foundNewCount;
qDebug() << " _foundOldCount=" << _foundOldCount;
qDebug() << " --------- list of moving entities -----------";
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
qDebug() << " MOVING ENTITY";
qDebug() << " entity=" << details.entity;
qDebug() << " entityItemID=" << details.entity->getEntityItemID();
qDebug() << " oldCube=" << details.oldCube;
qDebug() << " newCube=" << details.newCube;
qDebug() << " newBox=" << details.newBox;
qDebug() << " oldContainingElement=" << details.oldContainingElement;
qDebug() << " oldFound=" << details.oldFound;
qDebug() << " newFound=" << details.newFound;
}
qDebug() << " --------- list of moving entities -----------";
}
// If we're getting called, it's because there was no child element at this index while recursing.
@ -145,25 +303,37 @@ OctreeElement* MovingEntitiesOperator::PossiblyCreateChildAt(OctreeElement* elem
foreach(const EntityToMoveDetails& details, _entitiesToMove) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
bool thisElementIsBestFit = entityTreeElement->bestFitBounds(details.newCube);
if (wantDebug) {
qDebug() << " thisElementIsBestFit=" << thisElementIsBestFit;
bool thisElementIsBestFitCube = entityTreeElement->bestFitBounds(details.newCube);
bool thisElementIsBestFitBox = entityTreeElement->bestFitBounds(details.newBox);
qDebug() << " thisElementIsBestFitCube=" << thisElementIsBestFitCube;
qDebug() << " thisElementIsBestFitBox=" << thisElementIsBestFitBox;
qDebug() << " details.newCube=" << details.newCube;
qDebug() << " details.newBox=" << details.newBox;
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
}
// if the scale of our desired cube is smaller than our children, then consider making a child
if (details.newCube.getScale() <= childElementScale) {
//qDebug() << " calling element->getMyChildContaining(details.newCube); ---------";
int indexOfChildContainingNewEntity = element->getMyChildContaining(details.newCube);
//qDebug() << " called element->getMyChildContaining(details.newCube); ---------";
if (details.newBox.getLargestDimension() <= childElementScale) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(details.newBox);
if (wantDebug) {
qDebug() << " called element->getMyChildContaining(details.newBox); ---------";
qDebug() << " childIndex=" << childIndex;
qDebug() << " indexOfChildContainingNewEntity=" << indexOfChildContainingNewEntity;
}
// If the childIndex we were asked if we wanted to create contains this newCube,
// then we will create this branch and continue. We can exit this loop immediately
// because if we need this branch for any one entity then it doesn't matter if it's
// needed for more entities.
if (childIndex == indexOfChildContainingNewEntity) {
return element->addChildAtIndex(childIndex);
OctreeElement* newChild = element->addChildAtIndex(childIndex);
if (wantDebug) {
qDebug() << " CREATING NEW CHILD --- childIndex=" << childIndex << "newChild=" << newChild;
}
return newChild;
}
}
}

View file

@ -17,6 +17,7 @@ public:
EntityItem* entity;
AACube oldCube;
AACube newCube;
AABox newBox;
EntityTreeElement* oldContainingElement;
bool oldFound;
bool newFound;
@ -33,10 +34,13 @@ inline bool operator==(const EntityToMoveDetails& a, const EntityToMoveDetails&
class MovingEntitiesOperator : public RecurseOctreeOperator {
public:
MovingEntitiesOperator(EntityTree* tree);
~MovingEntitiesOperator();
void addEntityToMoveList(EntityItem* entity, const AACube& oldCube, const AACube& newCube);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex);
bool hasMovingEntities() const { return _entitiesToMove.size() > 0; }
private:
EntityTree* _tree;
QSet<EntityToMoveDetails> _entitiesToMove;

View file

@ -28,6 +28,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_foundOld(false),
_foundNew(false),
_removeOld(false),
_dontMove(false), // assume we'll be moving
_changeTime(usecTimestampNow()),
_oldEntityCube(),
_newEntityCube()
@ -42,6 +43,10 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
if (wantDebug) {
qDebug() << "UpdateEntityOperator....";
qDebug() << " _existingEntity=" << _existingEntity;
qDebug() << " _entityItemID=" << _entityItemID;
qDebug() << " _containingElement=" << _containingElement;
qDebug() << " _containingElement->getAACube()=" << _containingElement->getAACube();
qDebug() << " _oldEntityCube=" << _oldEntityCube;
qDebug() << " _oldEntityBox=" << _oldEntityBox;
qDebug() << " _existingEntity->getPosition()=" << _existingEntity->getPosition() * (float)TREE_SCALE << "in meters";
@ -64,16 +69,30 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
// If our new properties don't have bounds details (no change to position, etc) or if this containing element would
// be the best fit for our new properties, then just do the new portion of the store pass, since the change path will
// be the same for both parts of the update
bool oldElementBestFit = _containingElement->bestFitBounds(_properties);
// if we don't have bounds properties, then use our old clamped box to determine best fit
if (!_properties.containsBoundsProperties()) {
oldElementBestFit = _containingElement->bestFitBounds(_oldEntityBox);
}
if (wantDebug) {
qDebug() << " _properties.containsBoundsProperties()=" << _properties.containsBoundsProperties();
qDebug() << " oldElementBestFit=" << oldElementBestFit;
}
if (!_properties.containsBoundsProperties() || oldElementBestFit) {
// For some reason we've seen a case where the original containing element isn't a best fit for the old properties
// in this case we want to move it, even if the properties haven't changed.
if (!_properties.containsBoundsProperties() && !oldElementBestFit) {
_newEntityCube = _oldEntityCube;
_removeOld = true; // our properties are going to move us, so remember this for later processing
if (wantDebug) {
qDebug() << " old element is NOT the best element even though props not changing <<<<<";
}
} else if (!_properties.containsBoundsProperties() || oldElementBestFit) {
_foundOld = true;
_newEntityCube = _oldEntityCube;
_dontMove = true;
if (wantDebug) {
qDebug() << " old element is best element <<<<<";
}
@ -94,12 +113,37 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
qDebug() << " _newEntityCube=" << _newEntityCube;
qDebug() << " _newEntityBox=" << _newEntityBox;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " _containingElement->bestFitBounds(_properties)=" << _containingElement->bestFitBounds(_properties);
qDebug() << " _containingElement->bestFitBounds(_oldEntityCube)=" << _containingElement->bestFitBounds(_oldEntityCube);
qDebug() << " _containingElement->bestFitBounds(_oldEntityBox)=" << _containingElement->bestFitBounds(_oldEntityBox);
qDebug() << " _containingElement->bestFitBounds(_newEntityCube)=" << _containingElement->bestFitBounds(_newEntityCube);
qDebug() << " _containingElement->bestFitBounds(_newEntityBox)=" << _containingElement->bestFitBounds(_newEntityBox);
qDebug() << "------------ UpdateEntityOperator -- BEFORE WE BEGIN: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ UpdateEntityOperator -- END the tree-------------";
}
}
UpdateEntityOperator::~UpdateEntityOperator() {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "~UpdateEntityOperator().... ";
qDebug() << "------------ ~UpdateEntityOperator -- AFTER WE'RE DONE: the tree[" << _tree << "] -------------";
_tree->dumpTree();
qDebug() << "------------ ~UpdateEntityOperator -- END the tree-------------";
}
}
// does this entity tree element contain the old entity
bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElement* element) {
bool elementContainsOldBox = element->getAACube().contains(_oldEntityBox);
// We've found cases where the old entity might be placed in an element that is not actually the best fit
// so when we're searching the tree for the old element, we use the known cube for the known containing element
bool elementContainsOldBox = element->getAACube().contains(_containingElementCube);
/*
bool elementContainsOldCube = element->getAACube().contains(_oldEntityCube);
@ -177,6 +221,11 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
if (wantDebug) {
qDebug() << " OLD BRANCH....";
qDebug() << " _dontMove=" << _dontMove;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " entityTreeElement == _containingElement=" << (entityTreeElement == _containingElement);
qDebug() << " entityTreeElement->bestFitBounds(_newEntityBox)=" << entityTreeElement->bestFitBounds(_newEntityBox);
qDebug() << " _containingElement->bestFitBounds(_newEntityBox)=" << _containingElement->bestFitBounds(_newEntityBox);
}
@ -223,6 +272,11 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
if (wantDebug) {
qDebug() << " NEW BRANCH....";
qDebug() << " _dontMove=" << _dontMove;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " entityTreeElement == _containingElement=" << (entityTreeElement == _containingElement);
qDebug() << " entityTreeElement->bestFitBounds(_newEntityBox)=" << entityTreeElement->bestFitBounds(_newEntityBox);
qDebug() << " _containingElement->bestFitBounds(_newEntityBox)=" << _containingElement->bestFitBounds(_newEntityBox);
}
@ -231,9 +285,13 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
if (wantDebug) {
qDebug() << " NEW BRANCH.... BEST FIT";
qDebug() << " _dontMove=" << _dontMove;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " entityTreeElement == _containingElement=" << (entityTreeElement == _containingElement);
}
// if we are the existing containing element, then we can just do the update of the entity properties
if (entityTreeElement == _containingElement) {
assert(!_removeOld); // We shouldn't be in a remove old case and also be the new best fit
@ -246,6 +304,15 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
} else {
// otherwise, this is an add case.
if (wantDebug) {
qDebug() << "UpdateEntityOperator()... UPDATING EXISTING ENTITY -- PLACE IN NEW ELEMENT";
qDebug() << " element=" << entityTreeElement;
qDebug() << " element cube=" << entityTreeElement->getAACube();
qDebug() << " existingEntity=" << _existingEntity;
qDebug() << " entityItemID=" << _entityItemID;
}
entityTreeElement->addEntityItem(_existingEntity);
_existingEntity->setProperties(_properties); // still need to update the properties!
_tree->setContainingElement(_entityItemID, entityTreeElement);
@ -324,37 +391,38 @@ bool UpdateEntityOperator::PostRecursion(OctreeElement* element) {
}
OctreeElement* UpdateEntityOperator::PossiblyCreateChildAt(OctreeElement* element, int childIndex) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "UpdateEntityOperator::PossiblyCreateChildAt()...";
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
qDebug() << " childIndex=" << childIndex;
bool subtreeContainsOld = subTreeContainsOldEntity(element);
bool subtreeContainsNew = subTreeContainsNewEntity(element);
qDebug() << " subtreeContainsOld=" << subtreeContainsOld;
qDebug() << " subtreeContainsNew=" << subtreeContainsNew;
qDebug() << " _foundOld=" << _foundOld;
qDebug() << " _foundNew=" << _foundNew;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " _containingElement=" << _containingElement;
qDebug() << " _containingElementCube=" << _containingElementCube;
qDebug() << " _properties.getPosition()=" << _properties.getPosition() << "in meters";
qDebug() << " _properties.getRadius()=" << _properties.getRadius() << "in meters";
qDebug() << " _newEntityCube=" << _newEntityCube;
qDebug() << " _newEntityBox=" << _newEntityBox;
}
// If we're getting called, it's because there was no child element at this index while recursing.
// We only care if this happens while still searching for the new entity location.
// Check to see if
if (!_foundNew) {
bool wantDebug = false;
if (wantDebug) {
qDebug() << "UpdateEntityOperator::PossiblyCreateChildAt()...";
qDebug() << " element=" << element;
qDebug() << " element->getAACube()=" << element->getAACube();
qDebug() << " childIndex=" << childIndex;
bool subtreeContainsOld = subTreeContainsOldEntity(element);
bool subtreeContainsNew = subTreeContainsNewEntity(element);
qDebug() << " subtreeContainsOld=" << subtreeContainsOld;
qDebug() << " subtreeContainsNew=" << subtreeContainsNew;
qDebug() << " _foundOld=" << _foundOld;
qDebug() << " _foundNew=" << _foundNew;
qDebug() << " _removeOld=" << _removeOld;
qDebug() << " _containingElement=" << _containingElement;
qDebug() << " _containingElementCube=" << _containingElementCube;
qDebug() << " _properties.getPosition()=" << _properties.getPosition() << "in meters";
qDebug() << " _properties.getRadius()=" << _properties.getRadius() << "in meters";
qDebug() << " _newEntityCube=" << _newEntityCube;
qDebug() << " _newEntityBox=" << _newEntityBox;
}
float childElementScale = element->getScale() / 2.0f; // all of our children will be half our scale
// Note: because the entity's bounds might have been clamped to the domain. We want to check if the

View file

@ -16,6 +16,7 @@ class UpdateEntityOperator : public RecurseOctreeOperator {
public:
UpdateEntityOperator(EntityTree* tree, EntityTreeElement* containingElement,
EntityItem* existingEntity, const EntityItemProperties& properties);
~UpdateEntityOperator();
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
@ -30,6 +31,7 @@ private:
bool _foundOld;
bool _foundNew;
bool _removeOld;
bool _dontMove;
quint64 _changeTime;
AACube _oldEntityCube;

View file

@ -1,7 +1,5 @@
// REQUIRED:
1) crash on rendering of element that flies off the domain???
2) Test file save load for case where two siblings have more than MTU amount of data. I wonder if the fact that file save
doesn't include the extra exists bits will break something.
@ -9,15 +7,12 @@
4) test animation again...
5) PROP_VISIBLE
6) PROP_SCRIPT
7) some jutter with moving entities
-- I think this might only happen with lots of models in an element or in view
this may be related to issue 13 below
8) Look into why non-changed octree cells are being resent when editing an entity
8) Look into why non-changed octree cells are being resent when editing an entity --
this is probably because we're marking the trees as dirty -- but we probably can not send entitys that haven't changed
9) Verify pruning logic...
The old code used the update loop to handle pruning elements from the tree - do we need this?
@ -27,11 +22,23 @@
11) quickly do some edits... then change domains... watch the entities continue to exist in new domain and move around.
-- verify this happens in old code, if so... move to "nice to have"
12) Double check operators for these problems:
MovingEntitiesOperator has these issues:
- does the same pruning/reallocating issue as the old UpdateEntityOperator
- doesn't used clamped boxes for best fit tests... so could be problematic
// NICE TO HAVE:
why is _entityItems a pointer? why not just make it a member of EntityTreeElement....
1) EnterEntity/LeaveEntity JS messages
2) PROP_VISIBLE
3) PROP_SCRIPT
2) update and verify all particle examples to use new entity features
3) implement support for requestedProperties in appendEntityData() that only include CHANGED properties for the viewer...
@ -200,6 +207,13 @@
// -- 3) the containing element got pruned
// -- 4) the NEW branch is traversed and PossiblyCreateChildAt creates a new element -- THAT HAS SAME POINTER AS CONTAINING ELEMENT!!!!
// -- 5) the logic for recursedelement == _containingElement gets confused
// SOLVED -- 47) crash on rendering of element that flies off the domain
// -- root cause was an UpdateEntityOperator that left two copies of the entity in the tree
// this was caused by the original element that the entity was in (according to the server)
// was not actually the best fit. it's not clear how this happened, but we can protect
// against it in the update operator if it does happen again. This made update operator more robust.
// SOLVED -- 48) server and client definitley not adding entities to proper update lists on load... only on change...
---------------- properties -----------------
Base properties...