more model to entity renaming, adding AABox:clamp() and AACube::clamp() fix AddEntity to support adding an entity that extends over bounds of universe

This commit is contained in:
ZappoMan 2014-07-10 13:42:49 -07:00
parent 3c4f5f88d0
commit 780532bfd8
15 changed files with 365 additions and 209 deletions

View file

@ -1,6 +1,6 @@
//
// EntityNodeData.h
// assignment-client/src/models
// assignment-client/src/entities
//
// Created by Brad Hefta-Gaub on 4/29/14
// Copyright 2014 High Fidelity, Inc.
@ -20,15 +20,15 @@ class EntityNodeData : public OctreeQueryNode {
public:
EntityNodeData() :
OctreeQueryNode(),
_lastDeletedEntitysSentAt(0) { };
_lastDeletedEntitiesSentAt(0) { };
virtual PacketType getMyPacketType() const { return PacketTypeEntityData; }
quint64 getLastDeletedEntitysSentAt() const { return _lastDeletedEntitysSentAt; }
void setLastDeletedEntitysSentAt(quint64 sentAt) { _lastDeletedEntitysSentAt = sentAt; }
quint64 getLastDeletedEntitiesSentAt() const { return _lastDeletedEntitiesSentAt; }
void setLastDeletedEntitiesSentAt(quint64 sentAt) { _lastDeletedEntitiesSentAt = sentAt; }
private:
quint64 _lastDeletedEntitysSentAt;
quint64 _lastDeletedEntitiesSentAt;
};
#endif // hifi_EntityNodeData_h

View file

@ -1,6 +1,6 @@
//
// EntityServer.cpp
// assignment-client/src/models
// assignment-client/src/entities
//
// Created by Brad Hefta-Gaub on 4/29/14
// Copyright 2014 High Fidelity, Inc.
@ -40,10 +40,10 @@ Octree* EntityServer::createTree() {
}
void EntityServer::beforeRun() {
QTimer* pruneDeletedEntitysTimer = new QTimer(this);
connect(pruneDeletedEntitysTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntitys()));
QTimer* pruneDeletedEntitiesTimer = new QTimer(this);
connect(pruneDeletedEntitiesTimer, SIGNAL(timeout()), this, SLOT(pruneDeletedEntities()));
const int PRUNE_DELETED_MODELS_INTERVAL_MSECS = 1 * 1000; // once every second
pruneDeletedEntitysTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
pruneDeletedEntitiesTimer->start(PRUNE_DELETED_MODELS_INTERVAL_MSECS);
}
void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode) {
@ -60,30 +60,30 @@ void EntityServer::entityCreated(const EntityItem& newEntity, const SharedNodePo
copyAt += sizeof(creatorTokenID);
packetLength += sizeof(creatorTokenID);
// encode the model ID
uint32_t modelID = newEntity.getID();
memcpy(copyAt, &modelID, sizeof(modelID));
copyAt += sizeof(modelID);
packetLength += sizeof(modelID);
// encode the entity ID
uint32_t entityID = newEntity.getID();
memcpy(copyAt, &entityID, sizeof(entityID));
copyAt += sizeof(entityID);
packetLength += sizeof(entityID);
NodeList::getInstance()->writeDatagram((char*) outputBuffer, packetLength, senderNode);
}
// EntityServer will use the "special packets" to send list of recently deleted models
// EntityServer will use the "special packets" to send list of recently deleted entities
bool EntityServer::hasSpecialPacketToSend(const SharedNodePointer& node) {
bool shouldSendDeletedEntitys = false;
bool shouldSendDeletedEntities = false;
// check to see if any new models have been added since we last sent to this node...
// check to see if any new entities have been added since we last sent to this node...
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
if (nodeData) {
quint64 deletedEntitysSentAt = nodeData->getLastDeletedEntitysSentAt();
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
EntityTree* tree = static_cast<EntityTree*>(_tree);
shouldSendDeletedEntitys = tree->hasEntitysDeletedSince(deletedEntitysSentAt);
shouldSendDeletedEntities = tree->hasEntitiesDeletedSince(deletedEntitiesSentAt);
}
return shouldSendDeletedEntitys;
return shouldSendDeletedEntities;
}
int EntityServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNode* queryNode, int& packetsSent) {
@ -92,16 +92,16 @@ int EntityServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNo
EntityNodeData* nodeData = static_cast<EntityNodeData*>(node->getLinkedData());
if (nodeData) {
quint64 deletedEntitysSentAt = nodeData->getLastDeletedEntitysSentAt();
quint64 deletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
quint64 deletePacketSentAt = usecTimestampNow();
EntityTree* tree = static_cast<EntityTree*>(_tree);
bool hasMoreToSend = true;
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 models?
// TODO: is it possible to send too many of these packets? what if you deleted 1,000,000 entities?
packetsSent = 0;
while (hasMoreToSend) {
hasMoreToSend = tree->encodeEntitysDeletedSince(queryNode->getSequenceNumber(), deletedEntitysSentAt,
hasMoreToSend = tree->encodeEntitiesDeletedSince(queryNode->getSequenceNumber(), deletedEntitiesSentAt,
outputBuffer, MAX_PACKET_SIZE, packetLength);
//qDebug() << "sending PacketType_MODEL_ERASE packetLength:" << packetLength;
@ -111,30 +111,30 @@ int EntityServer::sendSpecialPacket(const SharedNodePointer& node, OctreeQueryNo
packetsSent++;
}
nodeData->setLastDeletedEntitysSentAt(deletePacketSentAt);
nodeData->setLastDeletedEntitiesSentAt(deletePacketSentAt);
}
// TODO: caller is expecting a packetLength, what if we send more than one packet??
return packetLength;
}
void EntityServer::pruneDeletedEntitys() {
void EntityServer::pruneDeletedEntities() {
EntityTree* tree = static_cast<EntityTree*>(_tree);
if (tree->hasAnyDeletedEntitys()) {
if (tree->hasAnyDeletedEntities()) {
//qDebug() << "there are some deleted models to consider...";
quint64 earliestLastDeletedEntitysSent = usecTimestampNow() + 1; // in the future
//qDebug() << "there are some deleted entities to consider...";
quint64 earliestLastDeletedEntitiesSent = usecTimestampNow() + 1; // in the future
foreach (const SharedNodePointer& otherNode, NodeList::getInstance()->getNodeHash()) {
if (otherNode->getLinkedData()) {
EntityNodeData* nodeData = static_cast<EntityNodeData*>(otherNode->getLinkedData());
quint64 nodeLastDeletedEntitysSentAt = nodeData->getLastDeletedEntitysSentAt();
if (nodeLastDeletedEntitysSentAt < earliestLastDeletedEntitysSent) {
earliestLastDeletedEntitysSent = nodeLastDeletedEntitysSentAt;
quint64 nodeLastDeletedEntitiesSentAt = nodeData->getLastDeletedEntitiesSentAt();
if (nodeLastDeletedEntitiesSentAt < earliestLastDeletedEntitiesSent) {
earliestLastDeletedEntitiesSent = nodeLastDeletedEntitiesSentAt;
}
}
}
//qDebug() << "earliestLastDeletedEntitysSent=" << earliestLastDeletedEntitysSent;
tree->forgetEntitysDeletedBefore(earliestLastDeletedEntitysSent);
//qDebug() << "earliestLastDeletedEntitiesSent=" << earliestLastDeletedEntitiesSent;
tree->forgetEntitiesDeletedBefore(earliestLastDeletedEntitiesSent);
}
}

View file

@ -1,6 +1,6 @@
//
// EntityServer.h
// assignment-client/src/models
// assignment-client/src/entities
//
// Created by Brad Hefta-Gaub on 4/29/14
// Copyright 2014 High Fidelity, Inc.
@ -18,7 +18,7 @@
#include "EntityServerConsts.h"
#include "EntityTree.h"
/// Handles assignments of type EntityServer - sending models to various clients.
/// Handles assignments of type EntityServer - sending entities to various clients.
class EntityServer : public OctreeServer, public NewlyCreatedEntityHook {
Q_OBJECT
public:
@ -43,7 +43,7 @@ public:
virtual void entityCreated(const EntityItem& newEntity, const SharedNodePointer& senderNode);
public slots:
void pruneDeletedEntitys();
void pruneDeletedEntities();
private:
};

View file

@ -44,15 +44,15 @@ var originalProperties = {
var positionDelta = { x: 0, y: 0, z: 0 };
var modelID = Models.addModel(originalProperties);
var entityID = Entities.addEntity(originalProperties);
function moveModel(deltaTime) {
function moveEntity(deltaTime) {
if (count >= moveUntil) {
// delete it...
if (count == moveUntil) {
print("calling Models.deleteModel()");
Models.deleteModel(modelID);
print("calling Entities.deleteEntity()");
Entities.deleteEntity(entityID);
}
// stop it...
@ -68,7 +68,7 @@ function moveModel(deltaTime) {
//print("count =" + count);
count++;
//print("modelID.creatorTokenID = " + modelID.creatorTokenID);
//print("entityID.creatorTokenID = " + entityID.creatorTokenID);
var newProperties = {
position: {
@ -81,13 +81,13 @@ function moveModel(deltaTime) {
};
//print("modelID = " + modelID);
//print("entityID = " + entityID);
//print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z);
Models.editModel(modelID, newProperties);
Entities.editEntity(entityID, newProperties);
}
// register the call back so it fires before each data send
Script.update.connect(moveModel);
Script.update.connect(moveEntity);

View file

@ -1,6 +1,6 @@
//
// EntityTree.cpp
// libraries/models/src
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
@ -22,7 +22,7 @@ EntityTreeElement* EntityTree::createNewElement(unsigned char * octalCode) {
}
void EntityTree::eraseAllOctreeElements() {
_modelToElementMap.clear();
_entityToElementMap.clear();
Octree::eraseAllOctreeElements();
}
@ -72,7 +72,7 @@ private:
bool _foundNew;
quint64 _changeTime;
AACube _newEntityCube;
AABox _newEntityBox;
};
AddEntityOperator::AddEntityOperator(EntityTree* tree,
@ -81,16 +81,22 @@ AddEntityOperator::AddEntityOperator(EntityTree* tree,
_newEntity(newEntity),
_foundNew(false),
_changeTime(usecTimestampNow()),
_newEntityCube()
_newEntityBox()
{
// caller must have verified existence of newEntity
assert(_newEntity);
_newEntityCube = _newEntity->getAACube();
_newEntityBox = _newEntity->getAACube().clamp(0.0f, 1.0f);
qDebug() << "AddEntityOperator::AddEntityOperator() newEntity=" << newEntity;
qDebug() << " _newEntityBox=" << _newEntityBox;
}
bool AddEntityOperator::PreRecursion(OctreeElement* element) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
qDebug() << "AddEntityOperator::PreRecursion() entityTreeElement=" << entityTreeElement;
entityTreeElement->debugDump();
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
// path of the tree. For this operation, we want to recurse the branch of the tree if
@ -99,14 +105,25 @@ bool AddEntityOperator::PreRecursion(OctreeElement* element) {
bool keepSearching = false; // assume we don't need to search any more
// If we haven't yet found the new model, and this subTreeContains our new
// model, then we need to keep searching.
if (!_foundNew && element->getAACube().contains(_newEntityCube)) {
// If we haven't yet found the new entity, and this subTreeContains our new
// entity, then we need to keep searching.
if (!_foundNew && element->getAACube().contains(_newEntityBox)) {
qDebug() << "this element contains the _newEntityBox..." << _newEntityBox;
// If this element is the best fit for the new entity properties, then add/or update it
if (entityTreeElement->bestFitBounds(_newEntityCube)) {
if (entityTreeElement->bestFitBounds(_newEntityBox)) {
qDebug() << "this element is the best fit for _newEntityBox..." << _newEntityBox;
qDebug() << "calling entityTreeElement->addEntityItem(_newEntity);";
entityTreeElement->addEntityItem(_newEntity);
qDebug() << "calling setContainingElement();";
_tree->setContainingElement(_newEntity->getEntityItemID(), entityTreeElement);
qDebug() << "AddEntityOperator calling setContainingElement... new entityID=" << _newEntity->getEntityItemID();
_tree->debugDumpMap();
_foundNew = true;
keepSearching = false;
} else {
@ -120,12 +137,12 @@ bool AddEntityOperator::PreRecursion(OctreeElement* element) {
bool AddEntityOperator::PostRecursion(OctreeElement* element) {
// 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 model and one for the new model.
// We might have two paths, one for the old entity and one for the new entity.
bool keepSearching = !_foundNew;
// As we unwind, if we're in either of these two paths, we mark our element
// as dirty.
if ((_foundNew && element->getAACube().contains(_newEntityCube))) {
if ((_foundNew && element->getAACube().contains(_newEntityBox))) {
element->markWithChangedTime();
}
return keepSearching; // if we haven't yet found it, keep looking
@ -133,10 +150,10 @@ bool AddEntityOperator::PostRecursion(OctreeElement* element) {
OctreeElement* AddEntityOperator::PossiblyCreateChildAt(OctreeElement* element, int childIndex) {
// 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 model location.
// We only care if this happens while still searching for the new entity location.
// Check to see if
if (!_foundNew) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityCube);
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityBox);
if (childIndex == indexOfChildContainingNewEntity) {
return element->addChildAtIndex(childIndex);
@ -153,9 +170,12 @@ void EntityTree::addEntityItem(EntityItem* entityItem) {
assert(containingElement == NULL); // don't call addEntityItem() on existing entity items
// Recurse the tree and store the entity in the correct tree element
qDebug() << "about to call recurseTreeWithOperator(AddEntityOperator)...";
AddEntityOperator theOperator(this, entityItem);
recurseTreeWithOperator(&theOperator);
qDebug() << "AFTER... about to call recurseTreeWithOperator(AddEntityOperator)...";
debugDumpMap();
_isDirty = true;
}
@ -220,7 +240,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
}
// does this model tree element contain the old model
// does this entity tree element contain the old entity
bool UpdateEntityOperator::subTreeContainsOldEntity(OctreeElement* element) {
return element->getAACube().contains(_oldEntityCube);
}
@ -236,33 +256,35 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
// path of the tree. For this operation, we want to recurse the branch of the tree if
// and of the following are true:
// * We have not yet found the old model, and this branch contains our old model
// * We have not yet found the new model, and this branch contains our new model
// * We have not yet found the old entity, and this branch contains our old entity
// * We have not yet found the new entity, and this branch contains our new entity
//
// Note: it's often the case that the branch in question contains both the old model
// and the new model.
// Note: it's often the case that the branch in question contains both the old entity
// and the new entity.
bool keepSearching = false; // assume we don't need to search any more
// If we haven't yet found the old model, and this subTreeContains our old
// model, then we need to keep searching.
// If we haven't yet found the old entity, and this subTreeContains our old
// entity, then we need to keep searching.
if (!_foundOld && subTreeContainsOldEntity(element)) {
// If this is the element we're looking for, then ask it to remove the old model
// If this is the element we're looking for, then ask it to remove the old entity
// and we can stop searching.
if (entityTreeElement == _containingElement) {
// If the containgElement IS NOT the best fit for the new model properties
// If the containgElement IS NOT the best fit for the new entity properties
// then we need to remove it, and the updateEntity below will store it in the
// correct element.
if (_removeOld) {
entityTreeElement->removeEntityItem(_existingEntity); // NOTE: only removes the entity, doesn't delete it
// If we haven't yet found the new location, then we need to
// make sure to remove our model to element map, because for
// make sure to remove our entity to element map, because for
// now we're not in that map
if (!_foundNew) {
_tree->setContainingElement(_entityItemID, NULL);
qDebug() << "UpdateEntityOperator calling setContainingElement(NULL)... entityID=" << _entityItemID;
_tree->debugDumpMap();
}
}
_foundOld = true;
@ -272,20 +294,20 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
}
}
// If we haven't yet found the new model, and this subTreeContains our new
// model, then we need to keep searching.
// If we haven't yet found the new entity, and this subTreeContains our new
// entity, then we need to keep searching.
if (!_foundNew && subTreeContainsNewEntity(element)) {
// If this element is the best fit for the new entity properties, then add/or update it
if (entityTreeElement->bestFitBounds(_newEntityCube)) {
if (entityTreeElement->addOrUpdateEntity(_existingEntity, _properties)) {
//qDebug() << "UpdateEntityOperator::PreRecursion()... model was updated!";
//qDebug() << "UpdateEntityOperator::PreRecursion()... entity was updated!";
_foundNew = true;
// NOTE: don't change the keepSearching here, if it came in here
// false then we stay false, if it came in here true, then it
// means we're still searching for our old model and this branch
// contains our old model. In which case we want to keep searching.
// means we're still searching for our old entity and this branch
// contains our old entity. In which case we want to keep searching.
}
} else {
keepSearching = true;
@ -298,7 +320,7 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
bool UpdateEntityOperator::PostRecursion(OctreeElement* element) {
// 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 model and one for the new model.
// We might have two paths, one for the old entity and one for the new entity.
bool keepSearching = !_foundOld || !_foundNew;
// As we unwind, if we're in either of these two paths, we mark our element
@ -312,7 +334,7 @@ bool UpdateEntityOperator::PostRecursion(OctreeElement* element) {
OctreeElement* UpdateEntityOperator::PossiblyCreateChildAt(OctreeElement* element, int childIndex) {
// 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 model location.
// We only care if this happens while still searching for the new entity location.
// Check to see if
if (!_foundNew) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityCube);
@ -375,17 +397,17 @@ qDebug() << "EntityTree::addEntity()... result = EntityTypes::constructEntityIte
class EntityToDeleteDetails {
public:
const EntityItem* model;
const EntityItem* entity;
AACube cube;
EntityTreeElement* containingElement;
};
inline uint qHash(const EntityToDeleteDetails& a, uint seed) {
return qHash(a.model->getEntityItemID(), seed);
return qHash(a.entity->getEntityItemID(), seed);
}
inline bool operator==(const EntityToDeleteDetails& a, const EntityToDeleteDetails& b) {
return a.model->getEntityItemID() == b.model->getEntityItemID();
return a.entity->getEntityItemID() == b.entity->getEntityItemID();
}
class DeleteEntityOperator : public RecurseOctreeOperator {
@ -397,11 +419,11 @@ public:
virtual bool PostRecursion(OctreeElement* element);
private:
EntityTree* _tree;
QSet<EntityToDeleteDetails> _modelsToDelete;
QSet<EntityToDeleteDetails> _entitiesToDelete;
quint64 _changeTime;
int _foundCount;
int _lookingCount;
bool subTreeContainsSomeEntitysToDelete(OctreeElement* element);
bool subTreeContainsSomeEntitiesToDelete(OctreeElement* element);
};
DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID) :
@ -422,33 +444,33 @@ DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree) :
}
void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID) {
// check our tree, to determine if this model is known
// check our tree, to determine if this entity is known
EntityToDeleteDetails details;
details.containingElement = _tree->getContainingElement(searchEntityID);
if (details.containingElement) {
details.model = details.containingElement->getEntityWithEntityItemID(searchEntityID);
if (!details.model) {
details.entity = details.containingElement->getEntityWithEntityItemID(searchEntityID);
if (!details.entity) {
//assert(false);
qDebug() << "that's UNEXPECTED, we got a _containingElement, but couldn't find the oldEntity!";
} else {
details.cube = details.model->getAACube();
_modelsToDelete << details;
details.cube = details.entity->getAACube();
_entitiesToDelete << details;
_lookingCount++;
}
}
}
// does this model tree element contain the old model
bool DeleteEntityOperator::subTreeContainsSomeEntitysToDelete(OctreeElement* element) {
// does this entity tree element contain the old entity
bool DeleteEntityOperator::subTreeContainsSomeEntitiesToDelete(OctreeElement* element) {
bool containsEntity = false;
// If we don't have an old model, then we don't contain the model, otherwise
// If we don't have an old entity, then we don't contain the entity, otherwise
// check the bounds
if (_modelsToDelete.size() > 0) {
if (_entitiesToDelete.size() > 0) {
AACube elementCube = element->getAACube();
foreach(const EntityToDeleteDetails& details, _modelsToDelete) {
foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
if (elementCube.contains(details.cube)) {
containsEntity = true;
break; // if it contains at least one, we're good to go
@ -459,39 +481,43 @@ bool DeleteEntityOperator::subTreeContainsSomeEntitysToDelete(OctreeElement* ele
}
bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// In Pre-recursion, we're generally deciding whether or not we want to recurse this
// path of the tree. For this operation, we want to recurse the branch of the tree if
// and of the following are true:
// * We have not yet found the old model, and this branch contains our old model
// * We have not yet found the new model, and this branch contains our new model
// * We have not yet found the old entity, and this branch contains our old entity
// * We have not yet found the new entity, and this branch contains our new entity
//
// Note: it's often the case that the branch in question contains both the old model
// and the new model.
// Note: it's often the case that the branch in question contains both the old entity
// and the new entity.
bool keepSearching = false; // assume we don't need to search any more
// If we haven't yet found all the models, and this sub tree contains at least one of our
// models, then we need to keep searching.
if ((_foundCount < _lookingCount) && subTreeContainsSomeEntitysToDelete(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 ((_foundCount < _lookingCount) && subTreeContainsSomeEntitiesToDelete(element)) {
// check against each of our search models
foreach(const EntityToDeleteDetails& details, _modelsToDelete) {
// check against each of our search entities
foreach(const EntityToDeleteDetails& details, _entitiesToDelete) {
// If this is the element we're looking for, then ask it to remove the old model
// If this is the element we're looking for, then ask it to remove the old entity
// and we can stop searching.
if (modelTreeElement == details.containingElement) {
if (entityTreeElement == details.containingElement) {
// This is a good place to delete it!!!
EntityItemID entityItemID = details.model->getEntityItemID();
modelTreeElement->removeEntityWithEntityItemID(entityItemID);
EntityItemID entityItemID = details.entity->getEntityItemID();
entityTreeElement->removeEntityWithEntityItemID(entityItemID);
_tree->setContainingElement(entityItemID, NULL);
qDebug() << "DeleteEntityOperator calling setContainingElement(NULL)... entityID=" << entityItemID;
_tree->debugDumpMap();
_foundCount++;
}
}
// if we haven't found all of our search for models, then keep looking
// if we haven't found all of our search for entities, then keep looking
keepSearching = (_foundCount < _lookingCount);
}
@ -501,12 +527,12 @@ bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
bool DeleteEntityOperator::PostRecursion(OctreeElement* element) {
// 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 model and one for the new model.
// We might have two paths, one for the old entity and one for the new entity.
bool keepSearching = (_foundCount < _lookingCount);
// As we unwind, if we're in either of these two paths, we mark our element
// as dirty.
if ((subTreeContainsSomeEntitysToDelete(element))) {
if ((subTreeContainsSomeEntitiesToDelete(element))) {
element->markWithChangedTime();
}
return keepSearching; // if we haven't yet found it, keep looking
@ -515,7 +541,7 @@ bool DeleteEntityOperator::PostRecursion(OctreeElement* element) {
void EntityTree::deleteEntity(const EntityItemID& entityID) {
// NOTE: callers must lock the tree before using this method
// First, look for the existing model in the tree..
// First, look for the existing entity in the tree..
DeleteEntityOperator theOperator(this, entityID);
recurseTreeWithOperator(&theOperator);
@ -528,12 +554,12 @@ void EntityTree::deleteEntity(const EntityItemID& entityID) {
}
}
void EntityTree::deleteEntitys(QSet<EntityItemID> modelIDs) {
void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
// NOTE: callers must lock the tree before using this method
DeleteEntityOperator theOperator(this);
foreach(const EntityItemID& entityID, modelIDs) {
// First, look for the existing model in the tree..
foreach(const EntityItemID& entityID, entityIDs) {
// First, look for the existing entity in the tree..
theOperator.addEntityIDToDeleteList(entityID);
}
@ -542,25 +568,25 @@ void EntityTree::deleteEntitys(QSet<EntityItemID> modelIDs) {
bool wantDebug = false;
if (wantDebug) {
foreach(const EntityItemID& entityID, modelIDs) {
foreach(const EntityItemID& entityID, entityIDs) {
EntityTreeElement* containingElement = getContainingElement(entityID);
qDebug() << "EntityTree::storeEntity().... after store... containingElement=" << containingElement;
}
}
}
// scans the tree and handles mapping locally created models to know IDs.
// scans the tree and handles mapping locally created entities to know IDs.
// in the event that this tree is also viewing the scene, then we need to also
// search the tree to make sure we don't have a duplicate model from the viewing
// search the tree to make sure we don't have a duplicate entity from the viewing
// operation.
bool EntityTree::findAndUpdateEntityItemIDOperation(OctreeElement* element, void* extraData) {
bool keepSearching = true;
FindAndUpdateEntityItemIDArgs* args = static_cast<FindAndUpdateEntityItemIDArgs*>(extraData);
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
// Note: updateEntityItemID() will only operate on correctly found models
modelTreeElement->updateEntityItemID(args);
// Note: updateEntityItemID() will only operate on correctly found entities
entityTreeElement->updateEntityItemID(args);
// if we've found and replaced both the creatorTokenID and the viewedEntity, then we
// can stop looking, otherwise we will keep looking
@ -595,8 +621,8 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) {
qDebug() << " entityID=" << entityID;
}
// update models in our tree
bool assumeEntityFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual model
// update entities in our tree
bool assumeEntityFound = !getIsViewing(); // if we're not a viewing tree, then we don't have to find the actual entity
FindAndUpdateEntityItemIDArgs args = {
entityID,
creatorTokenID,
@ -610,7 +636,7 @@ void EntityTree::handleAddEntityResponse(const QByteArray& packet) {
<< " getIsViewing()=" << getIsViewing();
}
lockForWrite();
// TODO: Switch this to use list of known model IDs....
// TODO: Switch this to use list of known entity IDs....
recurseTreeWithOperation(findAndUpdateEntityItemIDOperation, &args);
unlock();
}
@ -628,20 +654,20 @@ public:
bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData) {
FindNearPointArgs* args = static_cast<FindNearPointArgs*>(extraData);
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
glm::vec3 penetration;
bool sphereIntersection = modelTreeElement->getAACube().findSpherePenetration(args->position,
bool sphereIntersection = entityTreeElement->getAACube().findSpherePenetration(args->position,
args->targetRadius, penetration);
// If this modelTreeElement contains the point, then search it...
// If this entityTreeElement contains the point, then search it...
if (sphereIntersection) {
const EntityItem* thisClosestEntity = modelTreeElement->getClosestEntity(args->position);
const EntityItem* thisClosestEntity = entityTreeElement->getClosestEntity(args->position);
// we may have gotten NULL back, meaning no model was available
// we may have gotten NULL back, meaning no entity was available
if (thisClosestEntity) {
glm::vec3 modelPosition = thisClosestEntity->getPosition();
float distanceFromPointToEntity = glm::distance(modelPosition, args->position);
glm::vec3 entityPosition = thisClosestEntity->getPosition();
float distanceFromPointToEntity = glm::distance(entityPosition, args->position);
// If we're within our target radius
if (distanceFromPointToEntity <= args->targetRadius) {
@ -655,7 +681,7 @@ bool EntityTree::findNearPointOperation(OctreeElement* element, void* extraData)
}
// we should be able to optimize this...
return true; // keep searching in case children have closer models
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
@ -675,7 +701,7 @@ class FindAllNearPointArgs {
public:
glm::vec3 position;
float targetRadius;
QVector<const EntityItem*> models;
QVector<const EntityItem*> entities;
};
@ -687,9 +713,9 @@ bool EntityTree::findInSphereOperation(OctreeElement* element, void* extraData)
// If this element contains the point, then search it...
if (sphereIntersection) {
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
modelTreeElement->getEntities(args->position, args->targetRadius, args->models);
return true; // keep searching in case children have closer models
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
entityTreeElement->getEntities(args->position, args->targetRadius, args->entities);
return true; // keep searching in case children have closer entities
}
// if this element doesn't contain the point, then none of it's children can contain the point, so stop searching
@ -697,46 +723,46 @@ bool EntityTree::findInSphereOperation(OctreeElement* element, void* extraData)
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntitys) {
void EntityTree::findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities) {
FindAllNearPointArgs args = { center, radius };
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInSphereOperation, &args);
// swap the two lists of model pointers instead of copy
foundEntitys.swap(args.models);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args.entities);
}
class FindEntitysInCubeArgs {
class FindEntitiesInCubeArgs {
public:
FindEntitysInCubeArgs(const AACube& cube)
: _cube(cube), _foundEntitys() {
FindEntitiesInCubeArgs(const AACube& cube)
: _cube(cube), _foundEntities() {
}
AACube _cube;
QVector<EntityItem*> _foundEntitys;
QVector<EntityItem*> _foundEntities;
};
bool EntityTree::findInCubeOperation(OctreeElement* element, void* extraData) {
FindEntitysInCubeArgs* args = static_cast< FindEntitysInCubeArgs*>(extraData);
FindEntitiesInCubeArgs* args = static_cast<FindEntitiesInCubeArgs*>(extraData);
const AACube& elementCube = element->getAACube();
if (elementCube.touches(args->_cube)) {
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
modelTreeElement->getEntities(args->_cube, args->_foundEntitys);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
entityTreeElement->getEntities(args->_cube, args->_foundEntities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*> foundEntitys) {
FindEntitysInCubeArgs args(cube);
void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*> foundEntities) {
FindEntitiesInCubeArgs args(cube);
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInCubeOperation, &args);
// swap the two lists of model pointers instead of copy
foundEntitys.swap(args._foundEntitys);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args._foundEntities);
}
EntityItem* EntityTree::findEntityByID(uint32_t id, bool alreadyLocked) const {
EntityItem* EntityTree::findEntityByID(uint32_t id, bool alreadyLocked) /*const*/ {
EntityItemID entityID(id);
bool wantDebug = false;
@ -744,13 +770,13 @@ EntityItem* EntityTree::findEntityByID(uint32_t id, bool alreadyLocked) const {
qDebug() << "EntityTree::findEntityByID()...";
qDebug() << " id=" << id;
qDebug() << " entityID=" << entityID;
qDebug() << "_modelToElementMap=" << _modelToElementMap;
qDebug() << "_entityToElementMap=" << _entityToElementMap;
}
return findEntityByEntityItemID(entityID);
}
EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) const {
EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) /*const*/ {
EntityItem* foundEntity = NULL;
EntityTreeElement* containingElement = getContainingElement(entityID);
if (containingElement) {
@ -841,17 +867,17 @@ void EntityTree::removeNewlyCreatedHook(NewlyCreatedEntityHook* hook) {
bool EntityTree::updateOperation(OctreeElement* element, void* extraData) {
EntityTreeUpdateArgs* args = static_cast<EntityTreeUpdateArgs*>(extraData);
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
modelTreeElement->update(*args);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
entityTreeElement->update(*args);
return true;
}
bool EntityTree::pruneOperation(OctreeElement* element, void* extraData) {
EntityTreeElement* modelTreeElement = static_cast<EntityTreeElement*>(element);
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
EntityTreeElement* childAt = modelTreeElement->getChildAtIndex(i);
EntityTreeElement* childAt = entityTreeElement->getChildAtIndex(i);
if (childAt && childAt->isLeaf() && !childAt->hasEntities()) {
modelTreeElement->deleteChildAtIndex(i);
entityTreeElement->deleteChildAtIndex(i);
}
}
return true;
@ -861,14 +887,14 @@ void EntityTree::update() {
lockForWrite();
_isDirty = true;
// TODO: could we manage this by iterating the known models map/hash? Would that be faster?
// TODO: could we manage this by iterating the known entities map/hash? Would that be faster?
EntityTreeUpdateArgs args;
recurseTreeWithOperation(updateOperation, &args);
// now add back any of the particles that moved elements....
int movingEntitys = args._movingEntities.size();
int movingEntities = args._movingEntities.size();
for (int i = 0; i < movingEntitys; i++) {
for (int i = 0; i < movingEntities; i++) {
// XXXBHG: replace storeEntity with new API!!
qDebug() << "EntityTree::update().... NOT YET IMPLEMENTED!!!";
@ -883,9 +909,9 @@ void EntityTree::update() {
} else {
uint32_t entityItemID = args._movingEntities[i]->getID();
quint64 deletedAt = usecTimestampNow();
_recentlyDeletedEntitysLock.lockForWrite();
_recentlyDeletedEntitiesLock.lockForWrite();
_recentlyDeletedEntityItemIDs.insert(deletedAt, entityItemID);
_recentlyDeletedEntitysLock.unlock();
_recentlyDeletedEntitiesLock.unlock();
}
#endif // 0 //////////////////////////////////////////////////////
}
@ -896,11 +922,11 @@ void EntityTree::update() {
}
bool EntityTree::hasEntitysDeletedSince(quint64 sinceTime) {
bool EntityTree::hasEntitiesDeletedSince(quint64 sinceTime) {
// we can probably leverage the ordered nature of QMultiMap to do this quickly...
bool hasSomethingNewer = false;
_recentlyDeletedEntitysLock.lockForRead();
_recentlyDeletedEntitiesLock.lockForRead();
QMultiMap<quint64, uint32_t>::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin();
while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) {
//qDebug() << "considering... time/key:" << iterator.key();
@ -910,12 +936,12 @@ bool EntityTree::hasEntitysDeletedSince(quint64 sinceTime) {
}
++iterator;
}
_recentlyDeletedEntitysLock.unlock();
_recentlyDeletedEntitiesLock.unlock();
return hasSomethingNewer;
}
// sinceTime is an in/out parameter - it will be side effected with the last time sent out
bool EntityTree::encodeEntitysDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* outputBuffer,
bool EntityTree::encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime, unsigned char* outputBuffer,
size_t maxLength, size_t& outputLength) {
bool hasMoreToSend = true;
@ -951,9 +977,9 @@ bool EntityTree::encodeEntitysDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber
copyAt += sizeof(numberOfIds);
outputLength += sizeof(numberOfIds);
// we keep a multi map of model IDs to timestamps, we only want to include the model IDs that have been
// we keep a multi map of entity IDs to timestamps, we only want to include the entity IDs that have been
// deleted since we last sent to this node
_recentlyDeletedEntitysLock.lockForRead();
_recentlyDeletedEntitiesLock.lockForRead();
QMultiMap<quint64, uint32_t>::const_iterator iterator = _recentlyDeletedEntityItemIDs.constBegin();
while (iterator != _recentlyDeletedEntityItemIDs.constEnd()) {
QList<uint32_t> values = _recentlyDeletedEntityItemIDs.values(iterator.key());
@ -988,7 +1014,7 @@ bool EntityTree::encodeEntitysDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber
if (iterator == _recentlyDeletedEntityItemIDs.constEnd()) {
hasMoreToSend = false;
}
_recentlyDeletedEntitysLock.unlock();
_recentlyDeletedEntitiesLock.unlock();
// replace the correct count for ids included
memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds));
@ -997,11 +1023,11 @@ bool EntityTree::encodeEntitysDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber
// called by the server when it knows all nodes have been sent deleted packets
void EntityTree::forgetEntitysDeletedBefore(quint64 sinceTime) {
//qDebug() << "forgetEntitysDeletedBefore()";
void EntityTree::forgetEntitiesDeletedBefore(quint64 sinceTime) {
//qDebug() << "forgetEntitiesDeletedBefore()";
QSet<quint64> keysToRemove;
_recentlyDeletedEntitysLock.lockForWrite();
_recentlyDeletedEntitiesLock.lockForWrite();
QMultiMap<quint64, uint32_t>::iterator iterator = _recentlyDeletedEntityItemIDs.begin();
// First find all the keys in the map that are older and need to be deleted
@ -1018,7 +1044,7 @@ void EntityTree::forgetEntitysDeletedBefore(quint64 sinceTime) {
_recentlyDeletedEntityItemIDs.remove(value);
}
_recentlyDeletedEntitysLock.unlock();
_recentlyDeletedEntitiesLock.unlock();
}
@ -1041,7 +1067,7 @@ void EntityTree::processEraseMessage(const QByteArray& dataByteArray, const Shar
processedBytes += sizeof(numberOfIds);
if (numberOfIds > 0) {
QSet<EntityItemID> modelItemIDsToDelete;
QSet<EntityItemID> entityItemIDsToDelete;
for (size_t i = 0; i < numberOfIds; i++) {
if (processedBytes + sizeof(uint32_t) > packetLength) {
@ -1054,19 +1080,22 @@ void EntityTree::processEraseMessage(const QByteArray& dataByteArray, const Shar
processedBytes += sizeof(entityID);
EntityItemID entityItemID(entityID);
modelItemIDsToDelete << entityItemID;
entityItemIDsToDelete << entityItemID;
}
deleteEntitys(modelItemIDsToDelete);
deleteEntities(entityItemIDsToDelete);
}
}
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) const {
//qDebug() << "_modelToElementMap=" << _modelToElementMap;
EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityItemID) /*const*/ {
//qDebug() << "_entityToElementMap=" << _entityToElementMap;
qDebug() << "EntityTree::getContainingElement() entityItemID=" << entityItemID;
debugDumpMap();
// TODO: do we need to make this thread safe? Or is it acceptable as is
if (_modelToElementMap.contains(entityItemID)) {
return _modelToElementMap.value(entityItemID);
if (_entityToElementMap.contains(entityItemID)) {
return _entityToElementMap.value(entityItemID);
}
return NULL;
}
@ -1074,21 +1103,23 @@ EntityTreeElement* EntityTree::getContainingElement(const EntityItemID& entityIt
void EntityTree::setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element) {
// TODO: do we need to make this thread safe? Or is it acceptable as is
if (element) {
_modelToElementMap[entityItemID] = element;
_entityToElementMap[entityItemID] = element;
} else {
_modelToElementMap.remove(entityItemID);
_entityToElementMap.remove(entityItemID);
}
//qDebug() << "setContainingElement() entityItemID=" << entityItemID << "element=" << element;
//qDebug() << "AFTER _modelToElementMap=" << _modelToElementMap;
//qDebug() << "AFTER _entityToElementMap=" << _entityToElementMap;
}
void EntityTree::debugDumpMap() {
QHashIterator<EntityItemID, EntityTreeElement*> i(_modelToElementMap);
qDebug() << "EntityTree::debugDumpMap() --------------------------";
QHashIterator<EntityItemID, EntityTreeElement*> i(_entityToElementMap);
while (i.hasNext()) {
i.next();
qDebug() << i.key() << ": " << i.value();
}
qDebug() << "-----------------------------------------------------";
}
class DebugOperator : public RecurseOctreeOperator {
@ -1105,7 +1136,7 @@ bool DebugOperator::PreRecursion(OctreeElement* element) {
}
void EntityTree::dumpTree() {
// First, look for the existing model in the tree..
// First, look for the existing entity in the tree..
DebugOperator theOperator;
recurseTreeWithOperator(&theOperator);
}

View file

@ -1,6 +1,6 @@
//
// EntityTree.h
// libraries/models/src
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
@ -22,7 +22,7 @@ public:
class EntityItemFBXService {
public:
virtual const FBXGeometry* getGeometryForEntity(const EntityItem& modelItem) = 0;
virtual const FBXGeometry* getGeometryForEntity(const EntityItem& entityItem) = 0;
};
class EntityTree : public Octree {
@ -54,49 +54,49 @@ public:
EntityItem* getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties);
void addEntityItem(EntityItem* entityItem);
EntityItem* addEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
bool updateEntity(const EntityItemID& modelID, const EntityItemProperties& properties);
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
// the old API
void storeEntity(const EntityItem& model, const SharedNodePointer& senderNode = SharedNodePointer());
//void updateEntity(const EntityItemID& modelID, const EntityItemProperties& properties);
void deleteEntity(const EntityItemID& modelID);
void deleteEntitys(QSet<EntityItemID> modelIDs);
void storeEntity(const EntityItem& entity, const SharedNodePointer& senderNode = SharedNodePointer());
//void updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
void deleteEntity(const EntityItemID& entityID);
void deleteEntities(QSet<EntityItemID> entityIDs);
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
EntityItem* findEntityByID(uint32_t id, bool alreadyLocked = false) const;
EntityItem* findEntityByEntityItemID(const EntityItemID& modelID) const;
EntityItem* findEntityByID(uint32_t id, bool alreadyLocked = false) /*const*/;
EntityItem* findEntityByEntityItemID(const EntityItemID& entityID) /*const*/;
/// finds all models that touch a sphere
/// finds all entities that touch a sphere
/// \param center the center of the sphere
/// \param radius the radius of the sphere
/// \param foundEntitys[out] vector of const EntityItem*
/// \remark Side effect: any initial contents in foundEntitys will be lost
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntitys);
/// \param foundEntities[out] vector of const EntityItem*
/// \remark Side effect: any initial contents in foundEntities will be lost
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
/// finds all models that touch a cube
/// finds all entities that touch a cube
/// \param cube the query cube
/// \param foundEntitys[out] vector of non-const EntityItem*
/// \remark Side effect: any initial contents in models will be lost
void findEntities(const AACube& cube, QVector<EntityItem*> foundEntitys);
/// \param foundEntities[out] vector of non-const EntityItem*
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AACube& cube, QVector<EntityItem*> foundEntities);
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
bool hasAnyDeletedEntitys() const { return _recentlyDeletedEntityItemIDs.size() > 0; }
bool hasEntitysDeletedSince(quint64 sinceTime);
bool encodeEntitysDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
bool hasAnyDeletedEntities() const { return _recentlyDeletedEntityItemIDs.size() > 0; }
bool hasEntitiesDeletedSince(quint64 sinceTime);
bool encodeEntitiesDeletedSince(OCTREE_PACKET_SEQUENCE sequenceNumber, quint64& sinceTime,
unsigned char* packetData, size_t maxLength, size_t& outputLength);
void forgetEntitysDeletedBefore(quint64 sinceTime);
void forgetEntitiesDeletedBefore(quint64 sinceTime);
void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode);
void handleAddEntityResponse(const QByteArray& packet);
void setFBXService(EntityItemFBXService* service) { _fbxService = service; }
const FBXGeometry* getGeometryForEntity(const EntityItem& modelItem) {
return _fbxService ? _fbxService->getGeometryForEntity(modelItem) : NULL;
const FBXGeometry* getGeometryForEntity(const EntityItem& entityItem) {
return _fbxService ? _fbxService->getGeometryForEntity(entityItem) : NULL;
}
EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) const;
EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) /*const*/;
void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element);
void debugDumpMap();
void dumpTree();
@ -123,11 +123,11 @@ private:
QReadWriteLock _newlyCreatedHooksLock;
QVector<NewlyCreatedEntityHook*> _newlyCreatedHooks;
QReadWriteLock _recentlyDeletedEntitysLock;
QReadWriteLock _recentlyDeletedEntitiesLock;
QMultiMap<quint64, uint32_t> _recentlyDeletedEntityItemIDs;
EntityItemFBXService* _fbxService;
QHash<EntityItemID, EntityTreeElement*> _modelToElementMap;
QHash<EntityItemID, EntityTreeElement*> _entityToElementMap;
};
#endif // hifi_EntityTree_h

View file

@ -198,6 +198,14 @@ bool EntityTreeElement::bestFitBounds(const AACube& bounds) const {
return bestFitBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());
}
bool EntityTreeElement::containsBounds(const AABox& bounds) const {
return containsBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());
}
bool EntityTreeElement::bestFitBounds(const AABox& bounds) const {
return bestFitBounds(bounds.getMinimumPoint(), bounds.getMaximumPoint());
}
bool EntityTreeElement::containsBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const {
glm::vec3 clampedMin = glm::clamp(minPoint, 0.0f, 1.0f);
glm::vec3 clampedMax = glm::clamp(maxPoint, 0.0f, 1.0f);
@ -683,6 +691,9 @@ bool EntityTreeElement::collapseChildren() {
void EntityTreeElement::debugDump() {
qDebug() << "EntityTreeElement...";
qDebug() << "entity count:" << _entityItems->size();
qDebug() << "cube:" << getAACube();
for (uint16_t i = 0; i < _entityItems->size(); i++) {
EntityItem* entity = (*_entityItems)[i];
entity->debugDump();

View file

@ -158,6 +158,9 @@ public:
bool containsBounds(const AACube& bounds) const; // NOTE: units in tree units
bool bestFitBounds(const AACube& bounds) const; // NOTE: units in tree units
bool containsBounds(const AABox& bounds) const; // NOTE: units in tree units
bool bestFitBounds(const AABox& bounds) const; // NOTE: units in tree units
bool containsBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const; // NOTE: units in tree units
bool bestFitBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const; // NOTE: units in tree units

View file

@ -1458,6 +1458,32 @@ OctreeElement* OctreeElement::getOrCreateChildElementContaining(const AACube& cu
return child->getOrCreateChildElementContaining(cube);
}
OctreeElement* OctreeElement::getOrCreateChildElementContaining(const AABox& box) {
OctreeElement* child = NULL;
int childIndex = getMyChildContaining(box);
// If getMyChildContaining() returns CHILD_UNKNOWN then it means that our level
// is the correct level for this cube
if (childIndex == CHILD_UNKNOWN) {
return this;
}
// Now, check if we have a child at that location
child = getChildAtIndex(childIndex);
if (!child) {
child = addChildAtIndex(childIndex);
}
// if we've made a really small child, then go ahead and use that one.
if (child->getScale() <= SMALLEST_REASONABLE_OCTREE_ELEMENT_SCALE) {
return child;
}
// Now that we have the child to recurse down, let it answer the original question...
return child->getOrCreateChildElementContaining(box);
}
int OctreeElement::getMyChildContaining(const AACube& cube) const {
float ourScale = getScale();
float cubeScale = cube.getScale();
@ -1483,6 +1509,31 @@ int OctreeElement::getMyChildContaining(const AACube& cube) const {
return childIndexCubeMinimum; // either would do, they are the same
}
int OctreeElement::getMyChildContaining(const AABox& box) const {
float ourScale = getScale();
float boxLargestScale = box.getLargestDimension();
// TODO: consider changing this to assert()
if(boxLargestScale > ourScale) {
qDebug("UNEXPECTED -- OctreeElement::getMyChildContaining() "
"boxLargestScale=[%f] > ourScale=[%f] ", boxLargestScale, ourScale);
}
// Determine which of our children the minimum and maximum corners of the cube live in...
glm::vec3 cubeCornerMinimum = box.getCorner();
glm::vec3 cubeCornerMaximum = box.calcTopFarLeft();
int childIndexCubeMinimum = getMyChildContainingPoint(cubeCornerMinimum);
int childIndexCubeMaximum = getMyChildContainingPoint(cubeCornerMaximum);
// If the minimum and maximum corners of the cube are in two different children's cubes, then we are the containing element
if (childIndexCubeMinimum != childIndexCubeMaximum) {
return CHILD_UNKNOWN;
}
return childIndexCubeMinimum; // either would do, they are the same
}
int OctreeElement::getMyChildContainingPoint(const glm::vec3& point) const {
glm::vec3 ourCenter = _cube.calcCenter();
int childIndex = CHILD_UNKNOWN;

View file

@ -237,7 +237,9 @@ public:
OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s);
OctreeElement* getOrCreateChildElementContaining(const AACube& box);
OctreeElement* getOrCreateChildElementContaining(const AABox& box);
int getMyChildContaining(const AACube& cube) const;
int getMyChildContaining(const AABox& box) const;
int getMyChildContainingPoint(const glm::vec3& point) const;
protected:

View file

@ -14,6 +14,11 @@
#include "GeometryUtil.h"
#include "SharedUtil.h"
AABox::AABox(const AACube& other) :
_corner(other.getCorner()), _scale(other.getScale(), other.getScale(), other.getScale()) {
}
AABox::AABox(const glm::vec3& corner, float size) :
_corner(corner), _scale(size, size, size) {
};
@ -443,3 +448,19 @@ BoxFace AABox::getOppositeFace(BoxFace face) {
case MAX_Z_FACE: return MIN_Z_FACE;
}
}
AABox AABox::clamp(const glm::vec3& min, const glm::vec3& max) const {
glm::vec3 clampedCorner = glm::clamp(_corner, min, max);
glm::vec3 clampedTopFarLeft = glm::clamp(calcTopFarLeft(), min, max);
glm::vec3 clampedScale = clampedTopFarLeft - clampedCorner;
return AABox(clampedCorner, clampedScale);
}
AABox AABox::clamp(float min, float max) const {
glm::vec3 clampedCorner = glm::clamp(_corner, min, max);
glm::vec3 clampedTopFarLeft = glm::clamp(calcTopFarLeft(), min, max);
glm::vec3 clampedScale = clampedTopFarLeft - clampedCorner;
return AABox(clampedCorner, clampedScale);
}

View file

@ -17,13 +17,17 @@
#include <glm/glm.hpp>
#include <QDebug>
#include "BoxBase.h"
#include "StreamUtils.h"
class AACube;
class AABox {
public:
AABox(const AACube& other);
AABox(const glm::vec3& corner, float size);
AABox(const glm::vec3& corner, const glm::vec3& dimensions);
AABox();
@ -38,6 +42,7 @@ public:
const glm::vec3& getCorner() const { return _corner; }
const glm::vec3& getScale() const { return _scale; }
const glm::vec3& getDimensions() const { return _scale; }
float getLargestDimension() const { return glm::max(_scale.x, glm::max(_scale.y, _scale.z)); }
glm::vec3 calcCenter() const;
glm::vec3 calcTopFarLeft() const;
@ -62,6 +67,9 @@ public:
bool isNull() const { return _scale == glm::vec3(0.0f, 0.0f, 0.0f); }
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
AABox clamp(float min, float max) const;
private:
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
@ -77,4 +85,10 @@ inline bool operator==(const AABox& a, const AABox& b) {
return a.getCorner() == b.getCorner() && a.getDimensions() == b.getDimensions();
}
inline QDebug operator<<(QDebug debug, const AABox& box) {
debug << "AABox[ (" << box.getCorner().x << "," << box.getCorner().y << "," << box.getCorner().z << " ) to ("
<< box.calcTopFarLeft().x << "," << box.calcTopFarLeft().y << "," << box.calcTopFarLeft().z << ")]";
return debug;
}
#endif // hifi_AABox_h

View file

@ -433,3 +433,14 @@ BoxFace AACube::getOppositeFace(BoxFace face) {
case MAX_Z_FACE: return MIN_Z_FACE;
}
}
AABox AACube::clamp(const glm::vec3& min, const glm::vec3& max) const {
AABox temp(*this);
return temp.clamp(min, max);
}
AABox AACube::clamp(float min, float max) const {
AABox temp(*this);
return temp.clamp(min, max);
}

View file

@ -17,6 +17,8 @@
#include <glm/glm.hpp>
#include <QDebug>
#include "BoxBase.h"
class AABox;
@ -54,6 +56,9 @@ public:
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
AABox clamp(float min, float max) const;
private:
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;
@ -69,4 +74,11 @@ inline bool operator==(const AACube& a, const AACube& b) {
return a.getCorner() == b.getCorner() && a.getScale() == b.getScale();
}
inline QDebug operator<<(QDebug debug, const AACube& cube) {
debug << "AACube[ (" << cube.getCorner().x << "," << cube.getCorner().y << "," << cube.getCorner().z << " ) to ("
<< cube.calcTopFarLeft().x << "," << cube.calcTopFarLeft().y << "," << cube.calcTopFarLeft().z << ")]";
return debug;
}
#endif // hifi_AACube_h

View file

@ -451,7 +451,7 @@ void EntityTests::entityTreeTests(bool verbose) {
}
quint64 startDelete = usecTimestampNow();
tree.deleteEntitys(entitiesToDelete);
tree.deleteEntities(entitiesToDelete);
quint64 endDelete = usecTimestampNow();
totalElapsedDelete += (endDelete - startDelete);