more work on virtual entities, unit tests passing woot

This commit is contained in:
ZappoMan 2014-07-07 12:56:32 -07:00
parent 29e02f2681
commit 3145d150f3
9 changed files with 332 additions and 394 deletions

View file

@ -215,17 +215,17 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
}
for (uint16_t i = 0; i < numberOfEntities; i++) {
EntityItem& entityItem = entityItems[i];
EntityItem* entityItem = entityItems[i];
// render entityItem aspoints
AACube entityCube = entityItem.getAACube();
AACube entityCube = entityItem->getAACube();
entityCube.scale(TREE_SCALE);
if (args->_viewFrustum->cubeInFrustum(entityCube) != ViewFrustum::OUTSIDE) {
glm::vec3 position = entityItem.getPosition() * (float)TREE_SCALE;
float radius = entityItem.getRadius() * (float)TREE_SCALE;
float size = entityItem.getSize() * (float)TREE_SCALE;
glm::vec3 position = entityItem->getPosition() * (float)TREE_SCALE;
float radius = entityItem->getRadius() * (float)TREE_SCALE;
float size = entityItem->getSize() * (float)TREE_SCALE;
#ifdef HIDE_SUBCLASS_METHODS
bool drawAsModel = entityItem.hasModel();
bool drawAsModel = entityItem->hasModel();
#else
bool drawAsModel = false;
#endif
@ -238,27 +238,27 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
{
const float alpha = 1.0f;
Model* model = getModel(entityItem);
Model* model = getModel(*entityItem);
if (model) {
model->setScaleToFit(true, radius * 2.0f);
model->setSnapModelToCenter(true);
// set the rotation
glm::quat rotation = entityItem.getRotation();
glm::quat rotation = entityItem->getRotation();
model->setRotation(rotation);
// set the position
model->setTranslation(position);
// handle animations..
if (entityItem.hasAnimation()) {
if (!entityItem.jointsMapped()) {
if (entityItem->hasAnimation()) {
if (!entityItem->jointsMapped()) {
QStringList modelJointNames = model->getJointNames();
entityItem.mapJoints(modelJointNames);
entityItem->mapJoints(modelJointNames);
}
QVector<glm::quat> frameData = entityItem.getAnimationFrame();
QVector<glm::quat> frameData = entityItem->getAnimationFrame();
for (int i = 0; i < frameData.size(); i++) {
model->setJointState(i, true, frameData[i]);
}
@ -271,14 +271,14 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE
? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE;
if (entityItem.getGlowLevel() > 0.0f) {
Glower glower(entityItem.getGlowLevel());
if (entityItem->getGlowLevel() > 0.0f) {
Glower glower(entityItem->getGlowLevel());
if (model->isActive()) {
model->render(alpha, modelRenderMode);
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(entityItem.getColor()[RED_INDEX],entityItem.getColor()[GREEN_INDEX],entityItem.getColor()[BLUE_INDEX]);
glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
@ -289,7 +289,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
model->render(alpha, modelRenderMode);
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(entityItem.getColor()[RED_INDEX],entityItem.getColor()[GREEN_INDEX],entityItem.getColor()[BLUE_INDEX]);
glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
@ -340,7 +340,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
}
} else {
// if we couldn't get a model, then just draw a sphere
glColor3ub(entityItem.getColor()[RED_INDEX],entityItem.getColor()[GREEN_INDEX],entityItem.getColor()[BLUE_INDEX]);
glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem->getColor()[BLUE_INDEX]);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);
glutSolidSphere(radius, 15, 15);
@ -350,7 +350,7 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
glPopMatrix();
#endif
} else {
//glColor3ub(entityItem.getColor()[RED_INDEX],entityItem.getColor()[GREEN_INDEX],entityItem.getColor()[BLUE_INDEX]);
//glColor3ub(entityItem->getColor()[RED_INDEX],entityItem->getColor()[GREEN_INDEX],entityItem.getColor()[BLUE_INDEX]);
glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix();
glTranslatef(position.x, position.y, position.z);

View file

@ -43,7 +43,44 @@ bool EntityTypes::registerEntityType(EntityType_t entityType, const QString& nam
}
EntityItem* EntityTypes::constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties) {
return NULL; // new EntityItem(entityID, properties); // for now, needs to support registration of constructor
EntityItem* newEntityItem = NULL;
// switch statement for now, needs to support registration of constructor
switch (entityType) {
// Base, // ??? not supported?
case Model:
newEntityItem = new ModelEntityItem(entityID, properties);
break;
case Particle:
newEntityItem = new ParticleEntityItem(entityID, properties);
break;
case Box:
newEntityItem = new BoxEntityItem(entityID, properties);
break;
case Sphere:
newEntityItem = new SphereEntityItem(entityID, properties);
break;
case Plane:
newEntityItem = new PlaneEntityItem(entityID, properties);
break;
case Cylinder:
newEntityItem = new CylinderEntityItem(entityID, properties);
break;
case Pyramid:
newEntityItem = new PyramidEntityItem(entityID, properties);
break;
default:
newEntityItem = new ModelEntityItem(entityID, properties);
break;
}
return newEntityItem;
}
EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int bytesToRead) {

View file

@ -74,7 +74,8 @@ public:
Box,
Sphere,
Plane,
Cylinder
Cylinder,
Pyramid
} EntityType_t;
static const QString& getEntityTypeName(EntityType_t entityType);
@ -128,9 +129,13 @@ public:
/// used by EntityScriptingInterface to return EntityItemProperties for unknown models
void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; }
glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); }
AACube getAACube() const { return AACube(getMinimumPoint(), getMaxDimension()); } /// AACube in domain scale units (0.0 - 1.0)
glm::vec3 getMinimumPointMeters() const { return _position - glm::vec3(_radius, _radius, _radius); }
glm::vec3 getMaximumPointMeters() const { return _position + glm::vec3(_radius, _radius, _radius); }
AACube getAACubeMeters() const { return AACube(getMinimumPointMeters(), getMaxDimension()); } /// AACube in meter units
glm::vec3 getMinimumPointTreeUnits() const { return getMinimumPointMeters() / (float)TREE_SCALE; }
glm::vec3 getMaximumPointTreeUnits() const { return getMaximumPointMeters() / (float)TREE_SCALE; }
AACube getAACubeTreeUnits() const { return AACube(getMinimumPointMeters()/(float)TREE_SCALE, getMaxDimension()/(float)TREE_SCALE); } /// AACube in domain scale units (0.0 - 1.0)
void debugDump() const;
// properties of all entities
@ -426,4 +431,61 @@ protected:
};
// our non-pure virtual subclass for now...
class ModelEntityItem : public EntityItem {
public:
ModelEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class ParticleEntityItem : public EntityItem {
public:
ParticleEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class BoxEntityItem : public EntityItem {
public:
BoxEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class SphereEntityItem : public EntityItem {
public:
SphereEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class PlaneEntityItem : public EntityItem {
public:
PlaneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class CylinderEntityItem : public EntityItem {
public:
CylinderEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
class PyramidEntityItem : public EntityItem {
public:
PyramidEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID, properties) { };
virtual void somePureVirtualFunction() { }; // allow this class to be constructed
};
#endif // hifi_EntityItem_h

View file

@ -37,190 +37,12 @@ bool EntityTree::handlesEditPacketType(PacketType packetType) const {
}
}
class StoreEntityOperator : public RecurseOctreeOperator {
public:
StoreEntityOperator(EntityTree* tree, const EntityItem& searchEntity);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex);
private:
EntityTree* _tree;
const EntityItem& _newEntity;
const EntityItem* _oldEntity;
EntityTreeElement* _containingElement;
bool _foundOld;
bool _foundNew;
quint64 _changeTime;
bool subTreeContainsOldEntity(OctreeElement* element);
bool subTreeContainsNewEntity(OctreeElement* element);
};
StoreEntityOperator::StoreEntityOperator(EntityTree* tree, const EntityItem& searchEntity) :
_tree(tree),
_newEntity(searchEntity),
_oldEntity(NULL),
_containingElement(NULL),
_foundOld(false),
_foundNew(false),
_changeTime(usecTimestampNow())
{
// check our tree, to determine if this model is known
_containingElement = _tree->getContainingElement(searchEntity.getEntityItemID());
if (_containingElement) {
_oldEntity = _containingElement->getEntityWithEntityItemID(searchEntity.getEntityItemID());
if (!_oldEntity) {
//assert(_oldEntity);
qDebug() << "that's UNEXPECTED, we got a _containingElement, but couldn't find the oldEntity!";
}
// If this containing element would be the best fit for our new model, then just do the new
// portion of the store pass, since the change path will be the same for both parts of the update
if (_containingElement->bestFitEntityBounds(&_newEntity)) {
_foundOld = true;
}
} else {
// if the old model is not known, then we can consider if found, and
// we'll only be searching for the new location
_foundOld = true;
}
}
// does this model tree element contain the old model
bool StoreEntityOperator::subTreeContainsOldEntity(OctreeElement* element) {
bool containsEntity = false;
// If we don't have an old model, then we don't contain the model, otherwise
// check the bounds
if (_oldEntity) {
AACube elementCube = element->getAACube();
AACube modelCube = _oldEntity->getAACube();
containsEntity = elementCube.contains(modelCube);
}
return containsEntity;
}
bool StoreEntityOperator::subTreeContainsNewEntity(OctreeElement* element) {
AACube elementCube = element->getAACube();
AACube modelCube = _newEntity.getAACube();
return elementCube.contains(modelCube);
}
bool StoreEntityOperator::PreRecursion(OctreeElement* element) {
EntityTreeElement* modelTreeElement = 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
//
// Note: it's often the case that the branch in question contains both the old model
// and the new model.
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 (!_foundOld && subTreeContainsOldEntity(element)) {
// If this is the element we're looking for, then ask it to remove the old model
// and we can stop searching.
if (modelTreeElement == _containingElement) {
// If the containgElement IS NOT the best fit for the new model properties
// then we need to remove it, and the updateEntity below will store it in the
// correct element.
if (!_containingElement->bestFitEntityBounds(&_newEntity)) {
modelTreeElement->removeEntityWithEntityItemID(_newEntity.getEntityItemID());
// If we haven't yet found the new location, then we need to
// make sure to remove our model to element map, because for
// now we're not in that map
if (!_foundNew) {
_tree->setContainingElement(_newEntity.getEntityItemID(), NULL);
}
}
_foundOld = true;
} else {
// if this isn't the element we're looking for, then keep searching
keepSearching = true;
}
}
// If we haven't yet found the new model, and this subTreeContains our new
// model, then we need to keep searching.
if (!_foundNew && subTreeContainsNewEntity(element)) {
// Note: updateEntity() will only operate on correctly found models and/or add them
// to the element if they SHOULD be stored there.
if (modelTreeElement->updateEntity(_newEntity)) {
//qDebug() << "StoreEntityOperator::PreRecursion()... model 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.
} else {
keepSearching = true;
}
}
return keepSearching; // if we haven't yet found it, keep looking
}
bool StoreEntityOperator::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.
bool keepSearching = !_foundOld || !_foundNew;
// As we unwind, if we're in either of these two paths, we mark our element
// as dirty.
if ((_foundOld && subTreeContainsOldEntity(element)) ||
(_foundNew && subTreeContainsNewEntity(element))) {
element->markWithChangedTime();
}
return keepSearching; // if we haven't yet found it, keep looking
}
OctreeElement* StoreEntityOperator::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.
// Check to see if
if (!_foundNew) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntity.getAACube());
if (childIndex == indexOfChildContainingNewEntity) {
return element->addChildAtIndex(childIndex);
}
}
return NULL;
}
void EntityTree::storeEntity(const EntityItem& model, const SharedNodePointer& senderNode) {
// NOTE: callers must lock the tree before using this method
// First, look for the existing model in the tree..
StoreEntityOperator theOperator(this, model);
recurseTreeWithOperator(&theOperator);
_isDirty = true;
bool wantDebug = false;
if (wantDebug) {
EntityTreeElement* containingElement = getContainingElement(model.getEntityItemID());
qDebug() << "EntityTree::storeEntity().... after store... containingElement=" << containingElement;
}
}
/// Give an EntityItemID and EntityItemProperties, this will either find the correct entity that already exists
/// in the tree or it will create a new entity of the type specified by the properties and return that item.
/// In the case that it creates a new item, the item will be properly added to the tree and all appropriate lookup hashes.
EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = NULL;
@ -232,12 +54,111 @@ EntityItem* EntityTree::getOrCreateEntityItem(const EntityItemID& entityID, cons
// if the element does not exist, then create a new one of the specified type...
if (!result) {
EntityTypes::EntityType_t type = properties.getType();
result = EntityTypes::constructEntityItem(type, entityID, properties);
result = addEntity(entityID, properties);
}
return result;
}
class AddEntityOperator : public RecurseOctreeOperator {
public:
AddEntityOperator(EntityTree* tree, EntityItem* newEntity);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex);
private:
EntityTree* _tree;
EntityItem* _newEntity;
bool _foundNew;
quint64 _changeTime;
AACube _newEntityCube;
};
AddEntityOperator::AddEntityOperator(EntityTree* tree,
EntityItem* newEntity) :
_tree(tree),
_newEntity(newEntity),
_foundNew(false),
_changeTime(usecTimestampNow()),
_newEntityCube()
{
// caller must have verified existence of newEntity
assert(_newEntity);
_newEntityCube = _newEntity->getAACube();
}
bool AddEntityOperator::PreRecursion(OctreeElement* 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 location for the new entity, and this branch contains the bounds of the new entity
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 this element is the best fit for the new entity properties, then add/or update it
if (entityTreeElement->bestFitBounds(_newEntityCube)) {
entityTreeElement->addEntityItem(_newEntity);
_tree->setContainingElement(_newEntity->getEntityItemID(), entityTreeElement);
_foundNew = true;
keepSearching = false;
} else {
keepSearching = true;
}
}
return keepSearching; // if we haven't yet found it, keep looking
}
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.
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))) {
element->markWithChangedTime();
}
return keepSearching; // if we haven't yet found it, keep looking
}
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.
// Check to see if
if (!_foundNew) {
int indexOfChildContainingNewEntity = element->getMyChildContaining(_newEntityCube);
if (childIndex == indexOfChildContainingNewEntity) {
return element->addChildAtIndex(childIndex);
}
}
return NULL;
}
/// Adds a new entity item to the tree
void EntityTree::addEntityItem(EntityItem* entityItem) {
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityItemID entityID = entityItem->getEntityItemID();
EntityTreeElement* containingElement = getContainingElement(entityID);
assert(containingElement == NULL); // don't call addEntityItem() on existing entity items
// Recurse the tree and store the entity in the correct tree element
AddEntityOperator theOperator(this, entityItem);
recurseTreeWithOperator(&theOperator);
_isDirty = true;
}
class UpdateEntityOperator : public RecurseOctreeOperator {
public:
@ -293,7 +214,7 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTree* tree,
_foundOld = true;
_newEntityCube = _oldEntityCube;
} else {
_newEntityCube = properties.getAACube();
_newEntityCube = properties.getAACubeTreeUnits();
_removeOld = true; // our properties are going to move us, so remember this for later processing
}
@ -334,7 +255,7 @@ bool UpdateEntityOperator::PreRecursion(OctreeElement* element) {
// If the containgElement IS NOT the best fit for the new model properties
// then we need to remove it, and the updateEntity below will store it in the
// correct element.
if (!_removeOld) {
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
@ -403,69 +324,47 @@ OctreeElement* UpdateEntityOperator::PossiblyCreateChildAt(OctreeElement* elemen
return NULL;
}
// NOTE: This should only be called on entities that are known to exist. If the entity can not be found, the method
// will exit without any changes to tree state
//
// NOTE: If the properties contain position/scale/rotation, then there is a potential that the entity could need to
// move to a different EntityTreeElement, otherwise it will not move. If the entity can not move, then the dirty path
// can be determined to just be the path to the entity
void EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
#if 0
EntityItem* updateItem = NULL;
bool entityMightMove = properties.containsBoundsProperties();
// since the properties might not be complete, they may only contain some values,
// we need to first see if we already have the entity in our tree, and make a copy of
// its existing properties first
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
bool updated = false;
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityTreeElement* containingElement = getContainingElement(entityID);
if (wantDebug) {
qDebug() << "EntityTree::updateEntity(entityID, properties) containingElement=" << containingElement;
if (!containingElement) {
assert(containingElement); // don't call updateEntity() on entity items that don't exist
return false;
}
if (containingElement) {
const EntityItem* oldEntity = containingElement->getEntityWithEntityItemID(entityID);
if (oldEntity) {
EntityItemProperties oldProps = oldEntity->getProperties();
if (wantDebug) {
qDebug() << "EntityTree::updateEntity(entityID, properties) ********** COPY PROPERTIES FROM oldEntity=" << oldEntity << "*******************";
qDebug() << "EntityTree::updateEntity(entityID, properties) oldEntity=" << oldEntity;
oldProps.debugDump();
qDebug() << "EntityTree::updateEntity(entityID, properties) line:" << __LINE__ << "about to call updateItem.setProperties(oldProps);";
}
updateItem.setProperties(oldProps, true); // force copy
if (wantDebug) {
qDebug() << "EntityTree::updateEntity(entityID, properties) line:" << __LINE__ << "updateItem:";
updateItem.debugDump();
}
} else {
if (wantDebug) {
qDebug() << "EntityTree::updateEntity(entityID, properties) WAIT WHAT!!! COULDN'T FIND oldEntity=" << oldEntity;
}
}
}
if (updateItem) {
updateItem.setProperties(properties);
if (wantDebug) {
qDebug() << "EntityTree::updateEntity(entityID, properties) line:" << __LINE__ << "updateItem:";
updateItem.debugDump();
EntityItem* existingEntity = containingElement->getEntityWithEntityItemID(entityID);
if (!existingEntity) {
assert(existingEntity); // don't call updateEntity() on entity items that don't exist
return false;
}
storeEntity(updateItem);
#endif
UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties);
recurseTreeWithOperator(&theOperator);
_isDirty = true;
return true;
}
void EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
#if 0
EntityItem updateItem(entityID, properties);
storeEntity(updateItem);
#endif
EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = NULL;
// You should not call this on existing entities that are already part of the tree! Call updateEntity()
EntityTreeElement* containingElement = getContainingElement(entityID);
if (containingElement) {
assert(containingElement == NULL); // don't call addEntity() on existing entity items
return result;
}
// construct the instance of the entity
EntityTypes::EntityType_t type = properties.getType();
result = EntityTypes::constructEntityItem(type, entityID, properties);
if (result) {
// this does the actual adding of the entity
addEntityItem(result);
}
return result;
}
class EntityToDeleteDetails {
@ -487,7 +386,7 @@ class DeleteEntityOperator : public RecurseOctreeOperator {
public:
DeleteEntityOperator(EntityTree* tree);
DeleteEntityOperator(EntityTree* tree, const EntityItemID& searchEntityID);
void modelToDelete(const EntityItemID& searchEntityID);
void addEntityIDToDeleteList(const EntityItemID& searchEntityID);
virtual bool PreRecursion(OctreeElement* element);
virtual bool PostRecursion(OctreeElement* element);
private:
@ -505,7 +404,7 @@ DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree, const EntityItemID&
_foundCount(0),
_lookingCount(0)
{
modelToDelete(searchEntityID);
addEntityIDToDeleteList(searchEntityID);
}
DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree) :
@ -516,7 +415,7 @@ DeleteEntityOperator::DeleteEntityOperator(EntityTree* tree) :
{
}
void DeleteEntityOperator::modelToDelete(const EntityItemID& searchEntityID) {
void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEntityID) {
// check our tree, to determine if this model is known
EntityToDeleteDetails details;
details.containingElement = _tree->getContainingElement(searchEntityID);
@ -629,7 +528,7 @@ void EntityTree::deleteEntitys(QSet<EntityItemID> modelIDs) {
DeleteEntityOperator theOperator(this);
foreach(const EntityItemID& entityID, modelIDs) {
// First, look for the existing model in the tree..
theOperator.modelToDelete(entityID);
theOperator.addEntityIDToDeleteList(entityID);
}
recurseTreeWithOperator(&theOperator);
@ -854,7 +753,6 @@ EntityItem* EntityTree::findEntityByEntityItemID(const EntityItemID& entityID) c
return foundEntity;
}
int EntityTree::processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) {
@ -941,6 +839,9 @@ void EntityTree::update() {
int movingEntitys = args._movingEntities.size();
for (int i = 0; i < movingEntitys; i++) {
// XXXBHG: replace storeEntity with new API!!
#if 0 //////////////////////////////////////////////////////
bool shouldDie = args._movingEntities[i]->getShouldBeDeleted();
// if the particle is still inside our total bounds, then re-add it
@ -955,6 +856,7 @@ void EntityTree::update() {
_recentlyDeletedEntityItemIDs.insert(deletedAt, entityItemID);
_recentlyDeletedEntitysLock.unlock();
}
#endif // 0 //////////////////////////////////////////////////////
}
// prune the tree...

View file

@ -50,11 +50,16 @@ public:
virtual bool rootElementHasData() const { return true; }
virtual void update();
// The newer API...
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);
// the old API
void storeEntity(const EntityItem& model, const SharedNodePointer& senderNode = SharedNodePointer());
void updateEntity(const EntityItemID& modelID, const EntityItemProperties& properties);
void addEntity(const EntityItemID& modelID, const EntityItemProperties& properties);
//void updateEntity(const EntityItemID& modelID, const EntityItemProperties& properties);
void deleteEntity(const EntityItemID& modelID);
void deleteEntitys(QSet<EntityItemID> modelIDs);
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);

View file

@ -183,11 +183,11 @@ bool EntityTreeElement::bestFitEntityBounds(const EntityItem* entity) const {
}
bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {
return containsBounds(properties.getMinimumPoint(), properties.getMaximumPoint());
return containsBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits());
}
bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {
return bestFitBounds(properties.getMinimumPoint(), properties.getMaximumPoint());
return bestFitBounds(properties.getMinimumPointTreeUnits(), properties.getMaximumPointTreeUnits());
}
bool EntityTreeElement::containsBounds(const AACube& bounds) const {
@ -403,6 +403,7 @@ bool EntityTreeElement::addOrUpdateEntity(EntityItem* entity, const EntityItemPr
// If we didn't find the entity here, then let's check to see if we should add it...
_entityItems->push_back(entity);
entity->setProperties(properties); // still need to update the properties!
markWithChangedTime();
// Since we're adding this item to this element, we need to let the tree know about it
_myTree->setContainingElement(entity->getEntityItemID(), this);
@ -410,75 +411,6 @@ bool EntityTreeElement::addOrUpdateEntity(EntityItem* entity, const EntityItemPr
return true;
}
// TODO: the old entity code has support for sittingPoints... need to determine how to handle this...
// for local editors, the old updateModels(id, properties) had this code...
// if (found) {
// thisModel.setProperties(properties);
// if (_myTree->getGeometryForModel(thisModel)) {
// thisModel.setSittingPoints(_myTree->getGeometryForModel(thisModel)->sittingPoints);
// }
// ...
#if 0
bool EntityTreeElement::updateEntity(const EntityItem& entity) {
const bool wantDebug = false;
if (wantDebug) {
EntityItemID entityItemID = entity.getEntityItemID();
qDebug() << "EntityTreeElement::updateEntity(entity) entityID.id="
<< entityItemID.id << "creatorTokenID=" << entityItemID.creatorTokenID;
}
// NOTE: this method must first lookup the entity by ID, hence it is O(N)
// and "entity is not found" is worst-case (full N) but maybe we don't care?
// (guaranteed that num entities per elemen is small?)
uint16_t numberOfEntities = _entityItems->size();
for (uint16_t i = 0; i < numberOfEntities; i++) {
EntityItem* thisEntity = (*_entityItems)[i];
if (thisEntity->getID() == entity.getID()) {
if (wantDebug) {
qDebug() << "found entity with id";
}
int difference = thisEntity->getLastUpdated() - entity.getLastUpdated();
bool changedOnServer = thisEntity->getLastEdited() <= entity.getLastEdited();
bool localOlder = thisEntity->getLastUpdated() < entity.getLastUpdated();
if (changedOnServer || localOlder) {
if (wantDebug) {
qDebug("local entity [id:%d] %s and %s than server entity by %d, entity.isNewlyCreated()=%s",
entity.getID(), (changedOnServer ? "CHANGED" : "same"),
(localOlder ? "OLDER" : "NEWER"),
difference, debug::valueOf(entity.isNewlyCreated()) );
}
thisEntity->copyChangedProperties(entity);
markWithChangedTime();
// seems like we shouldn't need this
_myTree->setContainingElement(entity.getEntityItemID(), this);
} else {
if (wantDebug) {
qDebug(">>> IGNORING SERVER!!! Would've caused jutter! <<< "
"local entity [id:%d] %s and %s than server entity by %d, entity.isNewlyCreated()=%s",
entity.getID(), (changedOnServer ? "CHANGED" : "same"),
(localOlder ? "OLDER" : "NEWER"),
difference, debug::valueOf(entity.isNewlyCreated()) );
}
}
return true;
}
}
// If we didn't find the entity here, then let's check to see if we should add it...
if (bestFitEntityBounds(entity)) {
_entityItems->push_back(entity);
markWithChangedTime();
// Since we're adding this item to this element, we need to let the tree know about it
_myTree->setContainingElement(entity.getEntityItemID(), this);
return true;
}
return false;
}
#endif
void EntityTreeElement::updateEntityItemID(FindAndUpdateEntityItemIDArgs* args) {
bool wantDebug = false;
uint16_t numberOfEntities = _entityItems->size();
@ -519,8 +451,6 @@ void EntityTreeElement::updateEntityItemID(FindAndUpdateEntityItemIDArgs* args)
}
}
const EntityItem* EntityTreeElement::getClosestEntity(glm::vec3 position) const {
const EntityItem* closestEntity = NULL;
float closestEntityDistance = FLT_MAX;

View file

@ -152,14 +152,14 @@ public:
bool containsEntityBounds(const EntityItem* entity) const;
bool bestFitEntityBounds(const EntityItem* entity) const;
bool containsBounds(const EntityItemProperties& properties) const;
bool bestFitBounds(const EntityItemProperties& properties) const;
bool containsBounds(const EntityItemProperties& properties) const; // NOTE: property units in meters
bool bestFitBounds(const EntityItemProperties& properties) const; // NOTE: property units in meters
bool containsBounds(const AACube& bounds) const;
bool bestFitBounds(const AACube& bounds) const;
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 glm::vec3& minPoint, const glm::vec3& maxPoint) const;
bool bestFitBounds(const glm::vec3& minPoint, const glm::vec3& maxPoint) const;
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
protected:
virtual void init(unsigned char * octalCode);

View file

@ -25,8 +25,9 @@
//#include "EntityTests.h"
#include "ModelTests.h" // needs to be EntityTests.h soon
void EntityTests::modelTreeTests(bool verbose) {
#ifdef HIDE_SUBCLASS_METHODS
void EntityTests::entityTreeTests(bool verbose) {
//#ifdef HIDE_SUBCLASS_METHODS
bool extraVerbose = false;
int testsTaken = 0;
int testsPassed = 0;
@ -36,13 +37,13 @@ void EntityTests::modelTreeTests(bool verbose) {
qDebug() << "******************************************************************************************";
}
qDebug() << "EntityTests::modelTreeTests()";
qDebug() << "EntityTests::entityTreeTests()";
// Tree, id, and model properties used in many tests below...
// Tree, id, and entity properties used in many tests below...
EntityTree tree;
uint32_t id = 1;
EntityItemID modelID(id);
modelID.isKnownID = false; // this is a temporary workaround to allow local tree models to be added with known IDs
EntityItemID entityID(id);
entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs
EntityItemProperties properties;
float oneMeter = 1.0f;
float halfMeter = oneMeter / 2.0f;
@ -54,21 +55,21 @@ void EntityTests::modelTreeTests(bool verbose) {
{
testsTaken++;
QString testName = "add model to tree and search";
QString testName = "add entity to tree and search";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
properties.setPosition(positionAtCenterInMeters);
properties.setRadius(halfMeter);
//properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx");
//properties.setModelURL("https://s3-us-west-1.amazonaws.com/highfidelity-public/ozan/theater.fbx");
tree.addEntity(modelID, properties);
tree.addEntity(entityID, properties);
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
const EntityItem* foundEntityByID = tree.findEntityByID(id);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
if (verbose) {
@ -93,11 +94,11 @@ void EntityTests::modelTreeTests(bool verbose) {
}
}
modelID.isKnownID = true; // this is a temporary workaround to allow local tree models to be added with known IDs
entityID.isKnownID = true; // this is a temporary workaround to allow local tree entities to be added with known IDs
{
testsTaken++;
QString testName = "change position of model in tree";
QString testName = "change position of entity in tree";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
@ -106,12 +107,12 @@ void EntityTests::modelTreeTests(bool verbose) {
properties.setPosition(newPosition);
tree.updateEntity(modelID, properties);
tree.updateEntity(entityID, properties);
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionNearOriginInTreeUnits, targetRadius);
const EntityItem* foundEntityByID = tree.findEntityByID(id);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
if (verbose) {
@ -137,7 +138,7 @@ void EntityTests::modelTreeTests(bool verbose) {
{
testsTaken++;
QString testName = "change position of model in tree back to center";
QString testName = "change position of entity in tree back to center";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
@ -146,12 +147,12 @@ void EntityTests::modelTreeTests(bool verbose) {
properties.setPosition(newPosition);
tree.updateEntity(modelID, properties);
tree.updateEntity(entityID, properties);
float targetRadius = oneMeter * 2.0 / (float)TREE_SCALE; // in tree units
const EntityItem* foundEntityByRadius = tree.findClosestEntity(positionAtCenterInTreeUnits, targetRadius);
const EntityItem* foundEntityByID = tree.findEntityByID(id);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
if (verbose) {
@ -244,7 +245,7 @@ void EntityTests::modelTreeTests(bool verbose) {
testsTaken++;
const int TEST_ITERATIONS = 1000;
QString testName = "Performance - add model to tree " + QString::number(TEST_ITERATIONS) + " times";
QString testName = "Performance - add entity to tree " + QString::number(TEST_ITERATIONS) + " times";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
@ -253,9 +254,9 @@ void EntityTests::modelTreeTests(bool verbose) {
quint64 totalElapsedAdd = 0;
quint64 totalElapsedFind = 0;
for (int i = 0; i < TEST_ITERATIONS; i++) {
uint32_t id = i + 2; // make sure it doesn't collide with previous model ids
EntityItemID modelID(id);
modelID.isKnownID = false; // this is a temporary workaround to allow local tree models to be added with known IDs
uint32_t id = i + 2; // make sure it doesn't collide with previous entity ids
EntityItemID entityID(id);
entityID.isKnownID = false; // this is a temporary workaround to allow local tree entities to be added with known IDs
float randomX = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
float randomY = randFloatInRange(1.0f ,(float)TREE_SCALE - 1.0f);
@ -269,12 +270,12 @@ void EntityTests::modelTreeTests(bool verbose) {
if (extraVerbose) {
qDebug() << "iteration:" << i
<< "ading model at x/y/z=" << randomX << "," << randomY << "," << randomZ;
<< "ading entity at x/y/z=" << randomX << "," << randomY << "," << randomZ;
qDebug() << "before:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
}
quint64 startAdd = usecTimestampNow();
tree.addEntity(modelID, properties);
tree.addEntity(entityID, properties);
quint64 endAdd = usecTimestampNow();
totalElapsedAdd += (endAdd - startAdd);
@ -289,7 +290,7 @@ void EntityTests::modelTreeTests(bool verbose) {
quint64 endFind = usecTimestampNow();
totalElapsedFind += (endFind - startFind);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
AACube elementCube = containingElement ? containingElement->getAACube() : AACube();
bool elementIsBestFit = containingElement->bestFitEntityBounds(foundEntityByID);
@ -348,7 +349,7 @@ void EntityTests::modelTreeTests(bool verbose) {
{
testsTaken++;
const int TEST_ITERATIONS = 1000;
QString testName = "Performance - delete model from tree " + QString::number(TEST_ITERATIONS) + " times";
QString testName = "Performance - delete entity from tree " + QString::number(TEST_ITERATIONS) + " times";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
@ -357,16 +358,16 @@ void EntityTests::modelTreeTests(bool verbose) {
quint64 totalElapsedDelete = 0;
quint64 totalElapsedFind = 0;
for (int i = 0; i < TEST_ITERATIONS; i++) {
uint32_t id = i + 2; // These are the models we added above
EntityItemID modelID(id);
modelID.isKnownID = true; // this is a temporary workaround to allow local tree models to be added with known IDs
uint32_t id = i + 2; // These are the entities we added above
EntityItemID entityID(id);
entityID.isKnownID = true; // this is a temporary workaround to allow local tree entities to be added with known IDs
if (extraVerbose) {
qDebug() << "before:" << i << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
}
quint64 startDelete = usecTimestampNow();
tree.deleteEntity(modelID);
tree.deleteEntity(entityID);
quint64 endDelete = usecTimestampNow();
totalElapsedDelete += (endDelete - startDelete);
@ -379,7 +380,7 @@ void EntityTests::modelTreeTests(bool verbose) {
quint64 endFind = usecTimestampNow();
totalElapsedFind += (endFind - startFind);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
if (extraVerbose) {
qDebug() << "foundEntityByID=" << foundEntityByID;
@ -426,9 +427,9 @@ void EntityTests::modelTreeTests(bool verbose) {
{
testsTaken++;
const int TEST_ITERATIONS = 100;
const int MODELS_PER_ITERATION = 10;
QString testName = "Performance - delete " + QString::number(MODELS_PER_ITERATION)
+ " models from tree " + QString::number(TEST_ITERATIONS) + " times";
const int ENTITIES_PER_ITERATION = 10;
QString testName = "Performance - delete " + QString::number(ENTITIES_PER_ITERATION)
+ " entities from tree " + QString::number(TEST_ITERATIONS) + " times";
if (verbose) {
qDebug() << "Test" << testsTaken <<":" << qPrintable(testName);
}
@ -438,11 +439,11 @@ void EntityTests::modelTreeTests(bool verbose) {
quint64 totalElapsedFind = 0;
for (int i = 0; i < TEST_ITERATIONS; i++) {
QSet<EntityItemID> modelsToDelete;
for (int j = 0; j < MODELS_PER_ITERATION; j++) {
uint32_t id = 2 + (i * MODELS_PER_ITERATION) + j; // These are the models we added above
EntityItemID modelID(id);
modelsToDelete << modelID;
QSet<EntityItemID> entitiesToDelete;
for (int j = 0; j < ENTITIES_PER_ITERATION; j++) {
uint32_t id = 2 + (i * ENTITIES_PER_ITERATION) + j; // These are the entities we added above
EntityItemID entityID(id);
entitiesToDelete << entityID;
}
if (extraVerbose) {
@ -450,7 +451,7 @@ void EntityTests::modelTreeTests(bool verbose) {
}
quint64 startDelete = usecTimestampNow();
tree.deleteEntitys(modelsToDelete);
tree.deleteEntitys(entitiesToDelete);
quint64 endDelete = usecTimestampNow();
totalElapsedDelete += (endDelete - startDelete);
@ -459,11 +460,11 @@ void EntityTests::modelTreeTests(bool verbose) {
}
quint64 startFind = usecTimestampNow();
for (int j = 0; j < MODELS_PER_ITERATION; j++) {
uint32_t id = 2 + (i * MODELS_PER_ITERATION) + j; // These are the models we added above
EntityItemID modelID(id);
for (int j = 0; j < ENTITIES_PER_ITERATION; j++) {
uint32_t id = 2 + (i * ENTITIES_PER_ITERATION) + j; // These are the entities we added above
EntityItemID entityID(id);
const EntityItem* foundEntityByID = tree.findEntityByID(id);
EntityTreeElement* containingElement = tree.getContainingElement(modelID);
EntityTreeElement* containingElement = tree.getContainingElement(entityID);
if (extraVerbose) {
qDebug() << "foundEntityByID=" << foundEntityByID;
@ -490,7 +491,7 @@ void EntityTests::modelTreeTests(bool verbose) {
qDebug() << "getOctreeElementsCount()=" << tree.getOctreeElementsCount();
}
bool passed = iterationsPassed == (TEST_ITERATIONS * MODELS_PER_ITERATION);
bool passed = iterationsPassed == (TEST_ITERATIONS * ENTITIES_PER_ITERATION);
if (passed) {
testsPassed++;
} else {
@ -509,11 +510,12 @@ void EntityTests::modelTreeTests(bool verbose) {
if (verbose) {
qDebug() << "******************************************************************************************";
}
#endif
//#endif
//#endif // 0
}
void EntityTests::runAllTests(bool verbose) {
modelTreeTests(verbose);
entityTreeTests(verbose);
}

View file

@ -13,7 +13,7 @@
#define hifi_EntityTests_h
namespace EntityTests {
void modelTreeTests(bool verbose = false);
void entityTreeTests(bool verbose = false);
void runAllTests(bool verbose = false);
}