mirror of
https://github.com/overte-org/overte.git
synced 2025-07-03 19:49:24 +02:00
Working on thread safety for the entity tree
This commit is contained in:
parent
92eeb564fe
commit
b7ffb96adf
6 changed files with 225 additions and 239 deletions
|
@ -642,22 +642,14 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
// we need to iterate the actual entityItems of the element
|
// we need to iterate the actual entityItems of the element
|
||||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||||
|
|
||||||
EntityItems& entityItems = entityTreeElement->getEntities();
|
|
||||||
|
|
||||||
|
|
||||||
uint16_t numberOfEntities = entityItems.size();
|
|
||||||
|
|
||||||
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
|
bool isShadowMode = args->_renderMode == RenderArgs::SHADOW_RENDER_MODE;
|
||||||
|
|
||||||
if (!isShadowMode && _displayModelElementProxy && numberOfEntities > 0) {
|
if (!isShadowMode && _displayModelElementProxy && entityTreeElement->size() > 0) {
|
||||||
renderElementProxy(entityTreeElement, args);
|
renderElementProxy(entityTreeElement, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
|
||||||
EntityItemPointer entityItem = entityItems[i];
|
|
||||||
|
|
||||||
if (entityItem->isVisible()) {
|
if (entityItem->isVisible()) {
|
||||||
|
|
||||||
// NOTE: Zone Entities are a special case we handle here...
|
// NOTE: Zone Entities are a special case we handle here...
|
||||||
if (entityItem->getType() == EntityTypes::Zone) {
|
if (entityItem->getType() == EntityTypes::Zone) {
|
||||||
if (entityItem->contains(_viewState->getAvatarPosition())) {
|
if (entityItem->contains(_viewState->getAvatarPosition())) {
|
||||||
|
@ -681,7 +673,8 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float EntityTreeRenderer::getSizeScale() const {
|
float EntityTreeRenderer::getSizeScale() const {
|
||||||
|
|
|
@ -1512,7 +1512,7 @@ bool EntityItem::addAction(EntitySimulation* simulation, EntityActionPointer act
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
checkWaitingToRemove(simulation);
|
checkWaitingToRemove(simulation);
|
||||||
|
|
||||||
bool result = addActionInternal(simulation, action);
|
result = addActionInternal(simulation, action);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
removeActionInternal(action->getID());
|
removeActionInternal(action->getID());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1012,12 +1012,10 @@ QVector<EntityItemID> EntityTree::sendEntities(EntityEditPacketSender* packetSen
|
||||||
bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) {
|
bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData) {
|
||||||
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
|
SendEntitiesOperationArgs* args = static_cast<SendEntitiesOperationArgs*>(extraData);
|
||||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||||
|
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
|
||||||
const EntityItems& entities = entityTreeElement->getEntities();
|
|
||||||
for (int i = 0; i < entities.size(); i++) {
|
|
||||||
EntityItemID newID(QUuid::createUuid());
|
EntityItemID newID(QUuid::createUuid());
|
||||||
args->newEntityIDs->append(newID);
|
args->newEntityIDs->append(newID);
|
||||||
EntityItemProperties properties = entities[i]->getProperties();
|
EntityItemProperties properties = entityItem->getProperties();
|
||||||
properties.setPosition(properties.getPosition() + args->root);
|
properties.setPosition(properties.getPosition() + args->root);
|
||||||
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
|
properties.markAllChanged(); // so the entire property set is considered new, since we're making a new entity
|
||||||
|
|
||||||
|
@ -1030,8 +1028,7 @@ bool EntityTree::sendEntitiesOperation(OctreeElement* element, void* extraData)
|
||||||
args->localTree->addEntity(newID, properties);
|
args->localTree->addEntity(newID, properties);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,6 @@ EntityTreeElement::EntityTreeElement(unsigned char* octalCode) : OctreeElement()
|
||||||
|
|
||||||
EntityTreeElement::~EntityTreeElement() {
|
EntityTreeElement::~EntityTreeElement() {
|
||||||
_octreeMemoryUsage -= sizeof(EntityTreeElement);
|
_octreeMemoryUsage -= sizeof(EntityTreeElement);
|
||||||
delete _entityItems;
|
|
||||||
_entityItems = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be called primarily on addChildAt(), which means we're adding a child of our
|
// This will be called primarily on addChildAt(), which means we're adding a child of our
|
||||||
|
@ -39,7 +37,6 @@ OctreeElement* EntityTreeElement::createNewElement(unsigned char* octalCode) {
|
||||||
|
|
||||||
void EntityTreeElement::init(unsigned char* octalCode) {
|
void EntityTreeElement::init(unsigned char* octalCode) {
|
||||||
OctreeElement::init(octalCode);
|
OctreeElement::init(octalCode);
|
||||||
_entityItems = new EntityItems;
|
|
||||||
_octreeMemoryUsage += sizeof(EntityTreeElement);
|
_octreeMemoryUsage += sizeof(EntityTreeElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +68,7 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
|
||||||
// Check to see if this element yet has encode data... if it doesn't create it
|
// Check to see if this element yet has encode data... if it doesn't create it
|
||||||
if (!extraEncodeData->contains(this)) {
|
if (!extraEncodeData->contains(this)) {
|
||||||
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
|
EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
|
||||||
entityTreeElementExtraEncodeData->elementCompleted = (_entityItems->size() == 0);
|
entityTreeElementExtraEncodeData->elementCompleted = (_entityItems.size() == 0);
|
||||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
EntityTreeElement* child = getChildAtIndex(i);
|
EntityTreeElement* child = getChildAtIndex(i);
|
||||||
if (!child) {
|
if (!child) {
|
||||||
|
@ -84,10 +81,9 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
forEachEntity([&](EntityItemPointer entity) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
|
||||||
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
||||||
}
|
});
|
||||||
|
|
||||||
// TODO: some of these inserts might be redundant!!!
|
// TODO: some of these inserts might be redundant!!!
|
||||||
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
|
extraEncodeData->insert(this, entityTreeElementExtraEncodeData);
|
||||||
|
@ -248,7 +244,7 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
||||||
} else {
|
} else {
|
||||||
// if there wasn't one already, then create one
|
// if there wasn't one already, then create one
|
||||||
entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
|
entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData();
|
||||||
entityTreeElementExtraEncodeData->elementCompleted = (_entityItems->size() == 0);
|
entityTreeElementExtraEncodeData->elementCompleted = !hasContent();
|
||||||
|
|
||||||
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
for (int i = 0; i < NUMBER_OF_CHILDREN; i++) {
|
||||||
EntityTreeElement* child = getChildAtIndex(i);
|
EntityTreeElement* child = getChildAtIndex(i);
|
||||||
|
@ -262,10 +258,9 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
forEachEntity([&](EntityItemPointer entity) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
|
||||||
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//assert(extraEncodeData);
|
//assert(extraEncodeData);
|
||||||
|
@ -277,81 +272,84 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
|
||||||
// write our entities out... first determine which of the entities are in view based on our params
|
// write our entities out... first determine which of the entities are in view based on our params
|
||||||
uint16_t numberOfEntities = 0;
|
uint16_t numberOfEntities = 0;
|
||||||
uint16_t actualNumberOfEntities = 0;
|
uint16_t actualNumberOfEntities = 0;
|
||||||
QVector<uint16_t> indexesOfEntitiesToInclude;
|
int numberOfEntitiesOffset = 0;
|
||||||
|
withReadLock([&] {
|
||||||
|
QVector<uint16_t> indexesOfEntitiesToInclude;
|
||||||
|
|
||||||
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
|
// It's possible that our element has been previous completed. In this case we'll simply not include any of our
|
||||||
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
|
// entities for encoding. This is needed because we encode the element data at the "parent" level, and so we
|
||||||
// need to handle the case where our sibling elements need encoding but we don't.
|
// need to handle the case where our sibling elements need encoding but we don't.
|
||||||
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
if (!entityTreeElementExtraEncodeData->elementCompleted) {
|
||||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
for (uint16_t i = 0; i < _entityItems.size(); i++) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
EntityItemPointer entity = _entityItems[i];
|
||||||
bool includeThisEntity = true;
|
bool includeThisEntity = true;
|
||||||
|
|
||||||
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {
|
if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) {
|
||||||
includeThisEntity = false;
|
includeThisEntity = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hadElementExtraData) {
|
if (hadElementExtraData) {
|
||||||
includeThisEntity = includeThisEntity &&
|
includeThisEntity = includeThisEntity &&
|
||||||
entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
|
entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeThisEntity && params.viewFrustum) {
|
if (includeThisEntity && params.viewFrustum) {
|
||||||
|
|
||||||
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
|
// we want to use the maximum possible box for this, so that we don't have to worry about the nuance of
|
||||||
// simulation changing what's visible. consider the case where the entity contains an angular velocity
|
// simulation changing what's visible. consider the case where the entity contains an angular velocity
|
||||||
// the entity may not be in view and then in view a frame later, let the client side handle it's view
|
// the entity may not be in view and then in view a frame later, let the client side handle it's view
|
||||||
// frustum culling on rendering.
|
// frustum culling on rendering.
|
||||||
AACube entityCube = entity->getMaximumAACube();
|
AACube entityCube = entity->getMaximumAACube();
|
||||||
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
|
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
|
||||||
includeThisEntity = false; // out of view, don't include it
|
includeThisEntity = false; // out of view, don't include it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeThisEntity) {
|
||||||
|
indexesOfEntitiesToInclude << i;
|
||||||
|
numberOfEntities++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeThisEntity) {
|
|
||||||
indexesOfEntitiesToInclude << i;
|
|
||||||
numberOfEntities++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
|
numberOfEntitiesOffset = packetData->getUncompressedByteOffset();
|
||||||
bool successAppendEntityCount = packetData->appendValue(numberOfEntities);
|
bool successAppendEntityCount = packetData->appendValue(numberOfEntities);
|
||||||
|
|
||||||
if (successAppendEntityCount) {
|
if (successAppendEntityCount) {
|
||||||
foreach (uint16_t i, indexesOfEntitiesToInclude) {
|
foreach(uint16_t i, indexesOfEntitiesToInclude) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
EntityItemPointer entity = _entityItems[i];
|
||||||
LevelDetails entityLevel = packetData->startLevel();
|
LevelDetails entityLevel = packetData->startLevel();
|
||||||
OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
|
OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData,
|
||||||
params, entityTreeElementExtraEncodeData);
|
params, entityTreeElementExtraEncodeData);
|
||||||
|
|
||||||
// If none of this entity data was able to be appended, then discard it
|
// If none of this entity data was able to be appended, then discard it
|
||||||
// and don't include it in our entity count
|
// and don't include it in our entity count
|
||||||
if (appendEntityState == OctreeElement::NONE) {
|
if (appendEntityState == OctreeElement::NONE) {
|
||||||
packetData->discardLevel(entityLevel);
|
packetData->discardLevel(entityLevel);
|
||||||
} else {
|
} else {
|
||||||
// If either ALL or some of it got appended, then end the level (commit it)
|
// If either ALL or some of it got appended, then end the level (commit it)
|
||||||
// and include the entity in our final count of entities
|
// and include the entity in our final count of entities
|
||||||
packetData->endLevel(entityLevel);
|
packetData->endLevel(entityLevel);
|
||||||
actualNumberOfEntities++;
|
actualNumberOfEntities++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the entity item got completely appended, then we can remove it from the extra encode data
|
// If the entity item got completely appended, then we can remove it from the extra encode data
|
||||||
if (appendEntityState == OctreeElement::COMPLETED) {
|
if (appendEntityState == OctreeElement::COMPLETED) {
|
||||||
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
|
entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any part of the entity items didn't fit, then the element is considered partial
|
// If any part of the entity items didn't fit, then the element is considered partial
|
||||||
// NOTE: if the entity item didn't fit or only partially fit, then the entity item should have
|
// NOTE: if the entity item didn't fit or only partially fit, then the entity item should have
|
||||||
// added itself to the extra encode data.
|
// added itself to the extra encode data.
|
||||||
if (appendEntityState != OctreeElement::COMPLETED) {
|
if (appendEntityState != OctreeElement::COMPLETED) {
|
||||||
appendElementState = OctreeElement::PARTIAL;
|
appendElementState = OctreeElement::PARTIAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state
|
||||||
|
appendElementState = OctreeElement::NONE;
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
// we we couldn't add the entity count, then we couldn't add anything for this element and we're in a NONE state
|
|
||||||
appendElementState = OctreeElement::NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were provided with extraEncodeData, and we allocated and/or got entityTreeElementExtraEncodeData
|
// If we were provided with extraEncodeData, and we allocated and/or got entityTreeElementExtraEncodeData
|
||||||
// then we need to do some additional processing, namely make sure our extraEncodeData is up to date for
|
// then we need to do some additional processing, namely make sure our extraEncodeData is up to date for
|
||||||
|
@ -478,56 +476,41 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
||||||
|
|
||||||
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
// only called if we do intersect our bounding cube, but find if we actually intersect with entities...
|
||||||
int entityNumber = 0;
|
int entityNumber = 0;
|
||||||
|
|
||||||
EntityItems::iterator entityItr = _entityItems->begin();
|
|
||||||
EntityItems::const_iterator entityEnd = _entityItems->end();
|
|
||||||
bool somethingIntersected = false;
|
bool somethingIntersected = false;
|
||||||
|
forEachEntity([&](EntityItemPointer entity) {
|
||||||
//float bestEntityDistance = distance;
|
|
||||||
|
|
||||||
while(entityItr != entityEnd) {
|
|
||||||
EntityItemPointer entity = (*entityItr);
|
|
||||||
|
|
||||||
AABox entityBox = entity->getAABox();
|
AABox entityBox = entity->getAABox();
|
||||||
float localDistance;
|
float localDistance;
|
||||||
BoxFace localFace;
|
BoxFace localFace;
|
||||||
|
|
||||||
// if the ray doesn't intersect with our cube, we can stop searching!
|
// if the ray doesn't intersect with our cube, we can stop searching!
|
||||||
if (entityBox.findRayIntersection(origin, direction, localDistance, localFace)) {
|
if (!entityBox.findRayIntersection(origin, direction, localDistance, localFace)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// extents is the entity relative, scaled, centered extents of the entity
|
// extents is the entity relative, scaled, centered extents of the entity
|
||||||
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
|
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
|
||||||
glm::mat4 translation = glm::translate(entity->getPosition());
|
glm::mat4 translation = glm::translate(entity->getPosition());
|
||||||
glm::mat4 entityToWorldMatrix = translation * rotation;
|
glm::mat4 entityToWorldMatrix = translation * rotation;
|
||||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
||||||
|
|
||||||
glm::vec3 dimensions = entity->getDimensions();
|
glm::vec3 dimensions = entity->getDimensions();
|
||||||
glm::vec3 registrationPoint = entity->getRegistrationPoint();
|
glm::vec3 registrationPoint = entity->getRegistrationPoint();
|
||||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
glm::vec3 corner = -(dimensions * registrationPoint);
|
||||||
|
|
||||||
AABox entityFrameBox(corner, dimensions);
|
AABox entityFrameBox(corner, dimensions);
|
||||||
|
|
||||||
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
|
||||||
glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
|
glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f));
|
||||||
|
|
||||||
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
|
// we can use the AABox's ray intersection by mapping our origin and direction into the entity frame
|
||||||
// and testing intersection there.
|
// and testing intersection there.
|
||||||
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
|
if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) {
|
||||||
if (localDistance < distance) {
|
if (localDistance < distance) {
|
||||||
// now ask the entity if we actually intersect
|
// now ask the entity if we actually intersect
|
||||||
if (entity->supportsDetailedRayIntersection()) {
|
if (entity->supportsDetailedRayIntersection()) {
|
||||||
if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance,
|
if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance,
|
||||||
localFace, intersectedObject, precisionPicking)) {
|
localFace, intersectedObject, precisionPicking)) {
|
||||||
|
|
||||||
if (localDistance < distance) {
|
|
||||||
distance = localDistance;
|
|
||||||
face = localFace;
|
|
||||||
*intersectedObject = (void*)entity.get();
|
|
||||||
somethingIntersected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
|
|
||||||
if (localDistance < distance) {
|
if (localDistance < distance) {
|
||||||
distance = localDistance;
|
distance = localDistance;
|
||||||
face = localFace;
|
face = localFace;
|
||||||
|
@ -535,75 +518,79 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
|
||||||
somethingIntersected = true;
|
somethingIntersected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// if the entity type doesn't support a detailed intersection, then just return the non-AABox results
|
||||||
|
if (localDistance < distance) {
|
||||||
|
distance = localDistance;
|
||||||
|
face = localFace;
|
||||||
|
*intersectedObject = (void*)entity.get();
|
||||||
|
somethingIntersected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
++entityItr;
|
|
||||||
entityNumber++;
|
entityNumber++;
|
||||||
}
|
});
|
||||||
return somethingIntersected;
|
return somethingIntersected;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change this to use better bounding shape for entity than sphere
|
// TODO: change this to use better bounding shape for entity than sphere
|
||||||
bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float radius,
|
||||||
glm::vec3& penetration, void** penetratedObject) const {
|
glm::vec3& penetration, void** penetratedObject) const {
|
||||||
EntityItems::iterator entityItr = _entityItems->begin();
|
bool result = false;
|
||||||
EntityItems::const_iterator entityEnd = _entityItems->end();
|
withReadLock([&] {
|
||||||
while(entityItr != entityEnd) {
|
foreach(EntityItemPointer entity, _entityItems) {
|
||||||
EntityItemPointer entity = (*entityItr);
|
glm::vec3 entityCenter = entity->getPosition();
|
||||||
glm::vec3 entityCenter = entity->getPosition();
|
float entityRadius = entity->getRadius();
|
||||||
float entityRadius = entity->getRadius();
|
|
||||||
|
|
||||||
// don't penetrate yourself
|
// don't penetrate yourself
|
||||||
if (entityCenter == center && entityRadius == radius) {
|
if (entityCenter == center && entityRadius == radius) {
|
||||||
return false;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findSphereSpherePenetration(center, radius, entityCenter, entityRadius, penetration)) {
|
||||||
|
// return true on first valid entity penetration
|
||||||
|
*penetratedObject = (void*)(entity.get());
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if (findSphereSpherePenetration(center, radius, entityCenter, entityRadius, penetration)) {
|
return result;
|
||||||
// return true on first valid entity penetration
|
|
||||||
|
|
||||||
*penetratedObject = (void*)(entity.get());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
++entityItr;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const {
|
||||||
EntityItemPointer closestEntity = NULL;
|
EntityItemPointer closestEntity = NULL;
|
||||||
float closestEntityDistance = FLT_MAX;
|
float closestEntityDistance = FLT_MAX;
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
withReadLock([&] {
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
foreach(EntityItemPointer entity, _entityItems) {
|
||||||
float distanceToEntity = glm::distance(position, (*_entityItems)[i]->getPosition());
|
float distanceToEntity = glm::distance2(position, entity->getPosition());
|
||||||
if (distanceToEntity < closestEntityDistance) {
|
if (distanceToEntity < closestEntityDistance) {
|
||||||
closestEntity = (*_entityItems)[i];
|
closestEntity = entity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return closestEntity;
|
return closestEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change this to use better bounding shape for entity than sphere
|
// TODO: change this to use better bounding shape for entity than sphere
|
||||||
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<EntityItemPointer>& foundEntities) const {
|
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<EntityItemPointer>& foundEntities) const {
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
float compareRadius = searchRadius * searchRadius;
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
forEachEntity([&](EntityItemPointer entity) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
// For iteration like this, avoid the use of square roots by comparing distances squared
|
||||||
float distance = glm::length(entity->getPosition() - searchPosition);
|
float distanceSquared = glm::length2(entity->getPosition() - searchPosition);
|
||||||
if (distance < searchRadius + entity->getRadius()) {
|
float otherRadius = entity->getRadius();
|
||||||
|
if (distanceSquared < (compareRadius + (otherRadius * otherRadius))) {
|
||||||
foundEntities.push_back(entity);
|
foundEntities.push_back(entity);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change this to use better bounding shape for entity than sphere
|
// TODO: change this to use better bounding shape for entity than sphere
|
||||||
void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItemPointer>& foundEntities) {
|
void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItemPointer>& foundEntities) {
|
||||||
EntityItems::iterator entityItr = _entityItems->begin();
|
|
||||||
EntityItems::iterator entityEnd = _entityItems->end();
|
|
||||||
AACube entityCube;
|
AACube entityCube;
|
||||||
while(entityItr != entityEnd) {
|
forEachEntity([&](EntityItemPointer entity) {
|
||||||
EntityItemPointer entity = (*entityItr);
|
|
||||||
float radius = entity->getRadius();
|
float radius = entity->getRadius();
|
||||||
// NOTE: we actually do cube-cube collision queries here, which is sloppy but good enough for now
|
// NOTE: we actually do cube-cube collision queries here, which is sloppy but good enough for now
|
||||||
// TODO: decide whether to replace entityCube-cube query with sphere-cube (requires a square root
|
// TODO: decide whether to replace entityCube-cube query with sphere-cube (requires a square root
|
||||||
|
@ -612,64 +599,57 @@ void EntityTreeElement::getEntities(const AACube& box, QVector<EntityItemPointer
|
||||||
if (entityCube.touches(box)) {
|
if (entityCube.touches(box)) {
|
||||||
foundEntities.push_back(entity);
|
foundEntities.push_back(entity);
|
||||||
}
|
}
|
||||||
++entityItr;
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
|
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const {
|
||||||
EntityItemPointer foundEntity = NULL;
|
EntityItemPointer foundEntity = NULL;
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
withReadLock([&] {
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
foreach(EntityItemPointer entity, _entityItems) {
|
||||||
if ((*_entityItems)[i]->getEntityItemID() == id) {
|
if (entity->getEntityItemID() == id) {
|
||||||
foundEntity = (*_entityItems)[i];
|
foundEntity = entity;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return foundEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) {
|
|
||||||
EntityItemPointer foundEntity = NULL;
|
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
|
||||||
if ((*_entityItems)[i]->getEntityItemID() == id) {
|
|
||||||
foundEntity = (*_entityItems)[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundEntity;
|
return foundEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeElement::cleanupEntities() {
|
void EntityTreeElement::cleanupEntities() {
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
withWriteLock([&] {
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
foreach(EntityItemPointer entity, _entityItems) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
// NOTE: We explicitly don't delete the EntityItem here because since we only
|
||||||
entity->_element = NULL;
|
// access it by smart pointers, when we remove it from the _entityItems
|
||||||
|
// we know that it will be deleted.
|
||||||
// NOTE: We explicitly don't delete the EntityItem here because since we only
|
//delete entity;
|
||||||
// access it by smart pointers, when we remove it from the _entityItems
|
entity->_element = NULL;
|
||||||
// we know that it will be deleted.
|
}
|
||||||
//delete entity;
|
_entityItems.clear();
|
||||||
}
|
});
|
||||||
_entityItems->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTreeElement::removeEntityWithEntityItemID(const EntityItemID& id) {
|
bool EntityTreeElement::removeEntityWithEntityItemID(const EntityItemID& id) {
|
||||||
bool foundEntity = false;
|
bool foundEntity = false;
|
||||||
uint16_t numberOfEntities = _entityItems->size();
|
withWriteLock([&] {
|
||||||
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
uint16_t numberOfEntities = _entityItems.size();
|
||||||
if ((*_entityItems)[i]->getEntityItemID() == id) {
|
for (uint16_t i = 0; i < numberOfEntities; i++) {
|
||||||
foundEntity = true;
|
EntityItemPointer& entity = _entityItems[i];
|
||||||
(*_entityItems)[i]->_element = NULL;
|
if (entity->getEntityItemID() == id) {
|
||||||
_entityItems->removeAt(i);
|
foundEntity = true;
|
||||||
break;
|
entity->_element = NULL;
|
||||||
|
_entityItems.removeAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return foundEntity;
|
return foundEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTreeElement::removeEntityItem(EntityItemPointer entity) {
|
bool EntityTreeElement::removeEntityItem(EntityItemPointer entity) {
|
||||||
int numEntries = _entityItems->removeAll(entity);
|
int numEntries = 0;
|
||||||
|
withWriteLock([&] {
|
||||||
|
numEntries = _entityItems.removeAll(entity);
|
||||||
|
});
|
||||||
if (numEntries > 0) {
|
if (numEntries > 0) {
|
||||||
assert(entity->_element == this);
|
assert(entity->_element == this);
|
||||||
entity->_element = NULL;
|
entity->_element = NULL;
|
||||||
|
@ -791,7 +771,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
||||||
void EntityTreeElement::addEntityItem(EntityItemPointer entity) {
|
void EntityTreeElement::addEntityItem(EntityItemPointer entity) {
|
||||||
assert(entity);
|
assert(entity);
|
||||||
assert(entity->_element == NULL);
|
assert(entity->_element == NULL);
|
||||||
_entityItems->push_back(entity);
|
withWriteLock([&] {
|
||||||
|
_entityItems.push_back(entity);
|
||||||
|
});
|
||||||
entity->_element = this;
|
entity->_element = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -824,30 +806,39 @@ bool EntityTreeElement::pruneChildren() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeElement::expandExtentsToContents(Extents& extents) {
|
void EntityTreeElement::expandExtentsToContents(Extents& extents) {
|
||||||
if (_entityItems->size()) {
|
withReadLock([&] {
|
||||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
foreach(EntityItemPointer entity, _entityItems) {
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
|
||||||
extents.add(entity->getAABox());
|
extents.add(entity->getAABox());
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t EntityTreeElement::size() const {
|
||||||
|
uint16_t result = 0;
|
||||||
|
withReadLock([&] {
|
||||||
|
result = _entityItems.size();
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EntityTreeElement::debugDump() {
|
void EntityTreeElement::debugDump() {
|
||||||
qCDebug(entities) << "EntityTreeElement...";
|
qCDebug(entities) << "EntityTreeElement...";
|
||||||
qCDebug(entities) << " cube:" << _cube;
|
qCDebug(entities) << " cube:" << _cube;
|
||||||
qCDebug(entities) << " has child elements:" << getChildCount();
|
qCDebug(entities) << " has child elements:" << getChildCount();
|
||||||
if (_entityItems->size()) {
|
|
||||||
qCDebug(entities) << " has entities:" << _entityItems->size();
|
withReadLock([&] {
|
||||||
qCDebug(entities) << "--------------------------------------------------";
|
if (_entityItems.size()) {
|
||||||
for (uint16_t i = 0; i < _entityItems->size(); i++) {
|
qCDebug(entities) << " has entities:" << _entityItems.size();
|
||||||
EntityItemPointer entity = (*_entityItems)[i];
|
qCDebug(entities) << "--------------------------------------------------";
|
||||||
entity->debugDump();
|
for (uint16_t i = 0; i < _entityItems.size(); i++) {
|
||||||
|
EntityItemPointer entity = _entityItems[i];
|
||||||
|
entity->debugDump();
|
||||||
|
}
|
||||||
|
qCDebug(entities) << "--------------------------------------------------";
|
||||||
|
} else {
|
||||||
|
qCDebug(entities) << " NO entities!";
|
||||||
}
|
}
|
||||||
qCDebug(entities) << "--------------------------------------------------";
|
});
|
||||||
} else {
|
|
||||||
qCDebug(entities) << " NO entities!";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class EntityTreeElement : public OctreeElement {
|
class EntityTreeElement : public OctreeElement, ReadWriteLockable {
|
||||||
friend class EntityTree; // to allow createElement to new us...
|
friend class EntityTree; // to allow createElement to new us...
|
||||||
|
|
||||||
EntityTreeElement(unsigned char* octalCode = NULL);
|
EntityTreeElement(unsigned char* octalCode = NULL);
|
||||||
|
@ -146,10 +146,18 @@ public:
|
||||||
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
virtual bool findSpherePenetration(const glm::vec3& center, float radius,
|
||||||
glm::vec3& penetration, void** penetratedObject) const;
|
glm::vec3& penetration, void** penetratedObject) const;
|
||||||
|
|
||||||
const EntityItems& getEntities() const { return *_entityItems; }
|
|
||||||
EntityItems& getEntities() { return *_entityItems; }
|
|
||||||
|
|
||||||
bool hasEntities() const { return _entityItems ? _entityItems->size() > 0 : false; }
|
template <typename F>
|
||||||
|
void forEachEntity(F f) const {
|
||||||
|
withReadLock([&] {
|
||||||
|
foreach(EntityItemPointer entityItem, _entityItems) {
|
||||||
|
f(entityItem);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual uint16_t size() const;
|
||||||
|
bool hasEntities() const { return size() > 0; }
|
||||||
|
|
||||||
void setTree(EntityTree* tree) { _myTree = tree; }
|
void setTree(EntityTree* tree) { _myTree = tree; }
|
||||||
EntityTree* getTree() const { return _myTree; }
|
EntityTree* getTree() const { return _myTree; }
|
||||||
|
@ -174,8 +182,6 @@ public:
|
||||||
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
|
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const;
|
||||||
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
|
void getEntitiesInside(const AACube& box, QVector<EntityItemPointer>& foundEntities);
|
||||||
|
|
||||||
EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id);
|
|
||||||
|
|
||||||
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
|
void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities
|
||||||
bool removeEntityWithEntityItemID(const EntityItemID& id);
|
bool removeEntityWithEntityItemID(const EntityItemID& id);
|
||||||
bool removeEntityItem(EntityItemPointer entity);
|
bool removeEntityItem(EntityItemPointer entity);
|
||||||
|
@ -204,7 +210,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
virtual void init(unsigned char * octalCode);
|
virtual void init(unsigned char * octalCode);
|
||||||
EntityTree* _myTree;
|
EntityTree* _myTree;
|
||||||
EntityItems* _entityItems;
|
EntityItems _entityItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityTreeElement_h
|
#endif // hifi_EntityTreeElement_h
|
||||||
|
|
|
@ -43,11 +43,9 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) {
|
||||||
EntityItemProperties defaultProperties;
|
EntityItemProperties defaultProperties;
|
||||||
|
|
||||||
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
|
||||||
const EntityItems& entities = entityTreeElement->getEntities();
|
|
||||||
|
|
||||||
QVariantList entitiesQList = qvariant_cast<QVariantList>(_map["Entities"]);
|
QVariantList entitiesQList = qvariant_cast<QVariantList>(_map["Entities"]);
|
||||||
|
|
||||||
foreach (EntityItemPointer entityItem, entities) {
|
entityTreeElement->forEachEntity([&](EntityItemPointer entityItem) {
|
||||||
EntityItemProperties properties = entityItem->getProperties();
|
EntityItemProperties properties = entityItem->getProperties();
|
||||||
QScriptValue qScriptValues;
|
QScriptValue qScriptValues;
|
||||||
if (_skipDefaultValues) {
|
if (_skipDefaultValues) {
|
||||||
|
@ -56,7 +54,8 @@ bool RecurseOctreeToMapOperator::postRecursion(OctreeElement* element) {
|
||||||
qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
|
qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
|
||||||
}
|
}
|
||||||
entitiesQList << qScriptValues.toVariant();
|
entitiesQList << qScriptValues.toVariant();
|
||||||
}
|
});
|
||||||
|
|
||||||
_map["Entities"] = entitiesQList;
|
_map["Entities"] = entitiesQList;
|
||||||
if (element == _top) {
|
if (element == _top) {
|
||||||
_withinTop = false;
|
_withinTop = false;
|
||||||
|
|
Loading…
Reference in a new issue