mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-12 20:24:38 +02:00
first cut at properly working lifetime
This commit is contained in:
parent
2f90df04ee
commit
76c77d6994
16 changed files with 359 additions and 76 deletions
|
@ -1200,17 +1200,23 @@ function Tooltip() {
|
|||
text += "Roll: " + angles.z.toFixed(this.decimals) + "\n"
|
||||
text += "Scale: " + 2 * properties.radius.toFixed(this.decimals) + "\n"
|
||||
text += "ID: " + properties.id + "\n"
|
||||
text += "Model URL: " + properties.modelURL + "\n"
|
||||
text += "Animation URL: " + properties.animationURL + "\n"
|
||||
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
|
||||
if (properties.sittingPoints && properties.sittingPoints.length > 0) {
|
||||
text += properties.sittingPoints.length + " Sitting points: "
|
||||
for (var i = 0; i < properties.sittingPoints.length; ++i) {
|
||||
text += properties.sittingPoints[i].name + " "
|
||||
if (properties.type == "Model") {
|
||||
text += "Model URL: " + properties.modelURL + "\n"
|
||||
text += "Animation URL: " + properties.animationURL + "\n"
|
||||
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
|
||||
if (properties.sittingPoints && properties.sittingPoints.length > 0) {
|
||||
text += properties.sittingPoints.length + " Sitting points: "
|
||||
for (var i = 0; i < properties.sittingPoints.length; ++i) {
|
||||
text += properties.sittingPoints[i].name + " "
|
||||
}
|
||||
} else {
|
||||
text += "No sitting points" + "\n"
|
||||
}
|
||||
} else {
|
||||
text += "No sitting points"
|
||||
}
|
||||
if (properties.lifetime > -1) {
|
||||
text += "Lifetime: " + properties.lifetime + "\n"
|
||||
}
|
||||
text += "Age: " + properties.ageAsText + "\n"
|
||||
|
||||
|
||||
Overlays.editOverlay(this.textOverlay, { text: text });
|
||||
|
@ -1684,6 +1690,8 @@ function handeMenuEvent(menuItem){
|
|||
array.push({ label: "Gravity X:", value: properties.gravity.x.toFixed(decimals) });
|
||||
array.push({ label: "Gravity Y:", value: properties.gravity.y.toFixed(decimals) });
|
||||
array.push({ label: "Gravity Z:", value: properties.gravity.z.toFixed(decimals) });
|
||||
|
||||
array.push({ label: "Lifetime:", value: properties.lifetime.toFixed(decimals) });
|
||||
|
||||
if (properties.type == "Box") {
|
||||
array.push({ label: "Red:", value: properties.color.red });
|
||||
|
@ -1718,6 +1726,7 @@ function handeMenuEvent(menuItem){
|
|||
properties.gravity.x = array[index++].value;
|
||||
properties.gravity.y = array[index++].value;
|
||||
properties.gravity.z = array[index++].value;
|
||||
properties.lifetime = array[index++].value; // give ourselves that many more seconds
|
||||
|
||||
if (properties.type == "Box") {
|
||||
properties.color.red = array[index++].value;
|
||||
|
|
|
@ -29,7 +29,9 @@ EntityItem* RenderableModelEntityItem::factory(const EntityItemID& entityID, con
|
|||
}
|
||||
|
||||
RenderableModelEntityItem::~RenderableModelEntityItem() {
|
||||
delete _model;
|
||||
|
||||
// TODO: we can't just delete this because we may be on the wrong thread.
|
||||
//delete _model;
|
||||
_model = NULL;
|
||||
};
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ void DeleteEntityOperator::addEntityIDToDeleteList(const EntityItemID& searchEnt
|
|||
_entitiesToDelete << details;
|
||||
_lookingCount++;
|
||||
_tree->trackDeletedEntity(searchEntityID);
|
||||
// before deleting any entity make sure to remove it from our Mortal, Changing, and Moving lists
|
||||
_tree->removeEntityFromSimulationLists(searchEntityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,15 +96,11 @@ bool DeleteEntityOperator::PreRecursion(OctreeElement* element) {
|
|||
// If this is the element we're looking for, then ask it to remove the old entity
|
||||
// and we can stop searching.
|
||||
if (entityTreeElement == details.containingElement) {
|
||||
|
||||
EntityTreeElement* containingElement = _tree->getContainingElement(details.entity->getEntityItemID());
|
||||
|
||||
// This is a good place to delete it!!!
|
||||
EntityItemID entityItemID = details.entity->getEntityItemID();
|
||||
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID);
|
||||
entityTreeElement->removeEntityItem(theEntity);
|
||||
_tree->setContainingElement(entityItemID, NULL);
|
||||
delete theEntity; // now actually delete it!
|
||||
EntityItem* theEntity = entityTreeElement->getEntityWithEntityItemID(entityItemID); // find the actual entity
|
||||
entityTreeElement->removeEntityItem(theEntity); // remove it from the element
|
||||
_tree->setContainingElement(entityItemID, NULL); // update or id to element lookup
|
||||
delete theEntity; // now actually delete the entity!
|
||||
_foundCount++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ void EntityItem::initFromEntityItemID(const EntityItemID& entityItemID) {
|
|||
//uint64_t now = usecTimestampNow();
|
||||
_lastEdited = 0;
|
||||
_lastUpdated = 0;
|
||||
_created = 0; // TODO: when do we actually want to make this "now"
|
||||
|
||||
_position = glm::vec3(0,0,0);
|
||||
_radius = 0;
|
||||
|
@ -61,6 +62,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID, const EntityItemPropert
|
|||
_type = EntityTypes::Unknown;
|
||||
_lastEdited = 0;
|
||||
_lastUpdated = 0;
|
||||
_created = properties.getCreated();
|
||||
initFromEntityItemID(entityItemID);
|
||||
setProperties(properties, true); // force copy
|
||||
}
|
||||
|
@ -120,6 +122,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
|
||||
bool successIDFits = false;
|
||||
bool successTypeFits = false;
|
||||
bool successCreatedFits = false;
|
||||
bool successLastEditedFits = false;
|
||||
bool successLastUpdatedFits = false;
|
||||
bool successPropertyFlagsFits = false;
|
||||
|
@ -132,8 +135,22 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
if (successIDFits) {
|
||||
successTypeFits = packetData->appendValue(encodedType);
|
||||
}
|
||||
|
||||
if (successTypeFits) {
|
||||
successCreatedFits = packetData->appendValue(_created);
|
||||
|
||||
qDebug() << "EntityItem::appendEntityData()...";
|
||||
qDebug() << " entityID=" << getEntityItemID();
|
||||
if (_created == USE_EXISTING_CREATED_TIME) {
|
||||
qDebug() << " _created = USE_EXISTING_CREATED_TIME";
|
||||
} else if (_created == UNKNOWN_CREATED_TIME) {
|
||||
qDebug() << " _created = UNKNOWN_CREATED_TIME";
|
||||
} else {
|
||||
qDebug() << " _created=" << _created;
|
||||
qDebug() << " getAge()=" << getAge() << " - " << formatSecondsElapsed(getAge());
|
||||
}
|
||||
|
||||
}
|
||||
if (successCreatedFits) {
|
||||
successLastEditedFits = packetData->appendValue(lastEdited);
|
||||
}
|
||||
if (successLastEditedFits) {
|
||||
|
@ -147,7 +164,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
|
|||
successPropertyFlagsFits = packetData->appendValue(encodedPropertyFlags);
|
||||
}
|
||||
|
||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||
bool headerFits = successIDFits && successTypeFits && successCreatedFits && successLastEditedFits
|
||||
&& successLastUpdatedFits && successPropertyFlagsFits;
|
||||
|
||||
int startOfEntityItemData = packetData->getUncompressedByteOffset();
|
||||
|
@ -426,9 +443,35 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
|||
_type = (EntityTypes::EntityType)type;
|
||||
|
||||
bool overwriteLocalData = true; // assume the new content overwrites our local data
|
||||
|
||||
// _created
|
||||
quint64 createdFromBuffer = 0;
|
||||
memcpy(&createdFromBuffer, dataAt, sizeof(createdFromBuffer));
|
||||
dataAt += sizeof(createdFromBuffer);
|
||||
bytesRead += sizeof(createdFromBuffer);
|
||||
createdFromBuffer -= clockSkew;
|
||||
|
||||
_created = createdFromBuffer; // TODO: do we ever want to discard this???
|
||||
qDebug() << "EntityItem::readEntityDataFromBuffer()...";
|
||||
qDebug() << " entityID=" << getEntityItemID();
|
||||
if (_created == USE_EXISTING_CREATED_TIME) {
|
||||
qDebug() << " _created = USE_EXISTING_CREATED_TIME";
|
||||
} else if (_created == UNKNOWN_CREATED_TIME) {
|
||||
qDebug() << " _created = UNKNOWN_CREATED_TIME";
|
||||
} else {
|
||||
qDebug() << " _created=" << _created;
|
||||
qDebug() << " getAge()=" << getAge() << " - " << formatSecondsElapsed(getAge());
|
||||
}
|
||||
|
||||
/*
|
||||
QString ageAsString = formatSecondsElapsed(getAge());
|
||||
qDebug() << "Loading entity " << getEntityItemID() << " from buffer, _created =" << _created
|
||||
<< " age=" << getAge() << "seconds - " << ageAsString;
|
||||
*/
|
||||
|
||||
quint64 lastEditedFromBuffer = 0;
|
||||
|
||||
// TODO: we could make this encoded as a delta from _created
|
||||
// _lastEdited
|
||||
memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer));
|
||||
dataAt += sizeof(lastEditedFromBuffer);
|
||||
|
@ -621,7 +664,6 @@ bool EntityItem::isRestingOnSurface() const {
|
|||
&& _gravity.y < 0.0f;
|
||||
}
|
||||
|
||||
|
||||
void EntityItem::update(const quint64& updateTime) {
|
||||
bool wantDebug = false;
|
||||
float timeElapsed = (float)(updateTime - _lastUpdated) / (float)(USECS_PER_SECOND);
|
||||
|
@ -631,13 +673,6 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
qDebug() << "********** EntityItem::update() .... SETTING _lastUpdated=" << _lastUpdated;
|
||||
}
|
||||
|
||||
if (isMortal()) {
|
||||
if (getAge() > getLifetime()) {
|
||||
qDebug() << "Lifetime has expired... WHAT TO DO??? getAge()=" << getAge() << "getLifetime()=" << getLifetime();
|
||||
//setShouldDie(true); // TODO get rid of this stuff!!
|
||||
}
|
||||
}
|
||||
|
||||
if (hasVelocity() || hasGravity()) {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::vec3 velocity = getVelocity();
|
||||
|
@ -706,7 +741,7 @@ void EntityItem::update(const quint64& updateTime) {
|
|||
}
|
||||
}
|
||||
|
||||
EntityItem::SimuationState EntityItem::getSimulationState() const {
|
||||
EntityItem::SimulationState EntityItem::getSimulationState() const {
|
||||
bool wantDebug = false;
|
||||
|
||||
if (wantDebug) {
|
||||
|
@ -725,9 +760,9 @@ EntityItem::SimuationState EntityItem::getSimulationState() const {
|
|||
}
|
||||
if (isMortal()) {
|
||||
if (wantDebug) {
|
||||
qDebug() << " return EntityItem::Changing;";
|
||||
qDebug() << " return EntityItem::Mortal;";
|
||||
}
|
||||
return EntityItem::Changing;
|
||||
return EntityItem::Mortal;
|
||||
}
|
||||
if (wantDebug) {
|
||||
qDebug() << " return EntityItem::Static;";
|
||||
|
@ -735,6 +770,18 @@ EntityItem::SimuationState EntityItem::getSimulationState() const {
|
|||
return EntityItem::Static;
|
||||
}
|
||||
|
||||
bool EntityItem::lifetimeHasExpired() const {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityItem::lifetimeHasExpired()...";
|
||||
qDebug() << " isMortal()=" << isMortal();
|
||||
qDebug() << " getAge()=" << getAge();
|
||||
qDebug() << " getLifetime()=" << getLifetime();
|
||||
}
|
||||
return isMortal() && (getAge() > getLifetime());
|
||||
}
|
||||
|
||||
|
||||
void EntityItem::copyChangedProperties(const EntityItem& other) {
|
||||
*this = other;
|
||||
}
|
||||
|
@ -775,6 +822,16 @@ EntityItemProperties EntityItem::getProperties() const {
|
|||
|
||||
bool EntityItem::setProperties(const EntityItemProperties& properties, bool forceCopy) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
// handle the setting of created timestamps for the basic new entity case
|
||||
if (forceCopy) {
|
||||
if (properties.getCreated() == UNKNOWN_CREATED_TIME) {
|
||||
_created = usecTimestampNow();
|
||||
} else if (properties.getCreated() != USE_EXISTING_CREATED_TIME) {
|
||||
_created = properties.getCreated();
|
||||
}
|
||||
}
|
||||
|
||||
if (properties._positionChanged || forceCopy) {
|
||||
setPosition(properties._position / (float) TREE_SCALE);
|
||||
somethingChanged = true;
|
||||
|
@ -814,6 +871,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties, bool forc
|
|||
somethingChanged = true;
|
||||
}
|
||||
if (properties._lifetimeChanged || forceCopy) {
|
||||
qDebug() << "setLifetime().... properties._lifetime=" << properties._lifetime;
|
||||
setLifetime(properties._lifetime);
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
|
|
@ -101,13 +101,14 @@ public:
|
|||
static void adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, size_t length, int clockSkew);
|
||||
virtual void update(const quint64& now);
|
||||
|
||||
typedef enum SimuationState_t {
|
||||
typedef enum SimulationState_t {
|
||||
Static,
|
||||
Mortal,
|
||||
Changing,
|
||||
Moving
|
||||
} SimuationState;
|
||||
} SimulationState;
|
||||
|
||||
virtual SimuationState getSimulationState() const;
|
||||
virtual SimulationState getSimulationState() const;
|
||||
virtual void debugDump() const;
|
||||
|
||||
// similar to assignment/copy, but it handles keeping lifetime accurate
|
||||
|
@ -167,6 +168,7 @@ public:
|
|||
|
||||
/// age of this entity in seconds
|
||||
float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
|
||||
bool lifetimeHasExpired() const;
|
||||
|
||||
// position, size, and bounds related helpers
|
||||
float getSize() const { return _radius * 2.0f; } /// get maximum dimension in domain scale units (0.0 - 1.0)
|
||||
|
|
|
@ -24,6 +24,7 @@ EntityItemProperties::EntityItemProperties() :
|
|||
_id(UNKNOWN_ENTITY_ID),
|
||||
_idSet(false),
|
||||
_lastEdited(0),
|
||||
_created(UNKNOWN_CREATED_TIME),
|
||||
_type(EntityTypes::Unknown),
|
||||
|
||||
_position(0),
|
||||
|
@ -167,6 +168,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
|
|||
properties.setProperty("gravity", gravity);
|
||||
properties.setProperty("damping", _damping);
|
||||
properties.setProperty("lifetime", _lifetime);
|
||||
properties.setProperty("age", getAge()); // gettable, but not settable
|
||||
properties.setProperty("ageAsText", formatSecondsElapsed(getAge())); // gettable, but not settable
|
||||
properties.setProperty("script", _script);
|
||||
|
||||
QScriptValue color = xColorToScriptValue(engine, _color);
|
||||
|
@ -496,15 +499,18 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
// Last Edited quint64 always first, before any other details, which allows us easy access to adjusting this
|
||||
// timestamp for clock skew
|
||||
quint64 lastEdited = properties.getLastEdited();
|
||||
|
||||
bool successLastEditedFits = packetData.appendValue(lastEdited);
|
||||
|
||||
|
||||
bool successIDFits = packetData.appendValue(encodedID);
|
||||
if (isNewEntityItem && successIDFits) {
|
||||
successIDFits = packetData.appendValue(encodedToken);
|
||||
}
|
||||
bool successTypeFits = packetData.appendValue(encodedType);
|
||||
|
||||
// NOTE: We intentionally do not send "created" times in edit messages. This is because:
|
||||
// 1) if the edit is to an existing entity, the created time can not be changed
|
||||
// 2) if the edit is to a new entity, the created time is the last edited time
|
||||
|
||||
// TODO: Should we get rid of this in this in edit packets, since this has to always be 0?
|
||||
bool successLastUpdatedFits = packetData.appendValue(encodedUpdateDelta);
|
||||
|
||||
|
@ -514,7 +520,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
|
|||
bool successPropertyFlagsFits = packetData.appendValue(encodedPropertyFlags);
|
||||
int propertyCount = 0;
|
||||
|
||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||
bool headerFits = successIDFits && successTypeFits && successLastEditedFits
|
||||
&& successLastUpdatedFits && successPropertyFlagsFits;
|
||||
|
||||
int startOfEntityItemData = packetData.getUncompressedByteOffset();
|
||||
|
@ -883,6 +889,10 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
processedBytes += sizeof(lastEdited);
|
||||
properties.setLastEdited(lastEdited);
|
||||
|
||||
// NOTE: We intentionally do not send "created" times in edit messages. This is because:
|
||||
// 1) if the edit is to an existing entity, the created time can not be changed
|
||||
// 2) if the edit is to a new entity, the created time is the last edited time
|
||||
|
||||
// encoded id
|
||||
QByteArray encodedID((const char*)dataAt, NUM_BYTES_RFC4122_UUID); // maximum possible size
|
||||
QUuid editID = QUuid::fromRfc4122(encodedID);
|
||||
|
@ -917,11 +927,16 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
|
||||
valid = true;
|
||||
|
||||
// created time is lastEdited time
|
||||
properties.setCreated(lastEdited);
|
||||
} else {
|
||||
entityID.id = editID;
|
||||
entityID.creatorTokenID = UNKNOWN_ENTITY_TOKEN;
|
||||
entityID.isKnownID = true;
|
||||
valid = true;
|
||||
|
||||
// created time is lastEdited time
|
||||
properties.setCreated(USE_EXISTING_CREATED_TIME);
|
||||
}
|
||||
|
||||
// Entity Type...
|
||||
|
@ -932,7 +947,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
encodedType = typeCoder; // determine true bytesToRead
|
||||
dataAt += encodedType.size();
|
||||
processedBytes += encodedType.size();
|
||||
|
||||
|
||||
// Update Delta - when was this item updated relative to last edit... this really should be 0
|
||||
// TODO: Should we get rid of this in this in edit packets, since this has to always be 0?
|
||||
// TODO: do properties need to handle lastupdated???
|
||||
|
|
|
@ -39,6 +39,9 @@ const glm::quat ENTITY_DEFAULT_ROTATION;
|
|||
const QString ENTITY_DEFAULT_ANIMATION_URL("");
|
||||
const float ENTITY_DEFAULT_ANIMATION_FPS = 30.0f;
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = (quint64)(-1);
|
||||
const quint64 USE_EXISTING_CREATED_TIME = (quint64)(-2);
|
||||
|
||||
// PropertyFlags support
|
||||
enum EntityPropertyList {
|
||||
PROP_PAGED_PROPERTY,
|
||||
|
@ -138,6 +141,11 @@ public:
|
|||
|
||||
float getLifetime() const { return _lifetime; } /// get the lifetime in seconds for the entity
|
||||
void setLifetime(float value) { _lifetime = value; _lifetimeChanged = true; } /// set the lifetime in seconds for the entity
|
||||
float getAge() const { return (float)(usecTimestampNow() - _created) / (float)USECS_PER_SECOND; }
|
||||
quint64 getCreated() const { return _created; }
|
||||
void setCreated(quint64 usecTime) { _created = usecTime; }
|
||||
bool hasCreatedTime() const { return (_created != UNKNOWN_CREATED_TIME); }
|
||||
|
||||
|
||||
// NOTE: how do we handle _defaultSettings???
|
||||
bool containsBoundsProperties() const { return (_positionChanged || _radiusChanged); }
|
||||
|
@ -196,11 +204,12 @@ public:
|
|||
void markAllChanged();
|
||||
|
||||
private:
|
||||
void setLastEdited(quint64 lastEdited) { _lastEdited = lastEdited; }
|
||||
void setLastEdited(quint64 usecTime) { _lastEdited = usecTime; }
|
||||
|
||||
QUuid _id;
|
||||
bool _idSet;
|
||||
quint64 _lastEdited;
|
||||
quint64 _created;
|
||||
|
||||
EntityTypes::EntityType _type;
|
||||
glm::vec3 _position;
|
||||
|
|
|
@ -102,13 +102,13 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp
|
|||
}
|
||||
|
||||
// check to see if we need to simulate this entity...
|
||||
EntityItem::SimuationState oldState = existingEntity->getSimulationState();
|
||||
EntityItem::SimulationState oldState = existingEntity->getSimulationState();
|
||||
|
||||
UpdateEntityOperator theOperator(this, containingElement, existingEntity, properties);
|
||||
recurseTreeWithOperator(&theOperator);
|
||||
_isDirty = true;
|
||||
|
||||
EntityItem::SimuationState newState = existingEntity->getSimulationState();
|
||||
EntityItem::SimulationState newState = existingEntity->getSimulationState();
|
||||
changeEntityState(existingEntity, oldState, newState);
|
||||
|
||||
containingElement = getContainingElement(entityID);
|
||||
|
@ -196,7 +196,7 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
|
|||
|
||||
DeleteEntityOperator theOperator(this);
|
||||
foreach(const EntityItemID& entityID, entityIDs) {
|
||||
// First, look for the existing entity in the tree..
|
||||
// tell our delete operator about this entityID
|
||||
theOperator.addEntityIDToDeleteList(entityID);
|
||||
}
|
||||
|
||||
|
@ -215,6 +215,32 @@ void EntityTree::deleteEntities(QSet<EntityItemID> entityIDs) {
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTree::removeEntityFromSimulationLists(const EntityItemID& entityID) {
|
||||
EntityItem* theEntity = findEntityByEntityItemID(entityID);
|
||||
|
||||
if (theEntity) {
|
||||
// make sure to remove it from any of our simulation lists
|
||||
EntityItem::SimulationState theState = theEntity->getSimulationState();
|
||||
switch (theState) {
|
||||
case EntityItem::Changing:
|
||||
_changingEntities.removeAll(theEntity);
|
||||
break;
|
||||
|
||||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(theEntity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(theEntity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This method is used to find and fix entity IDs that are shifting from creator token based to known ID based entity IDs.
|
||||
/// This should only be used on a client side (viewing) tree. The typical usage is that a local editor has been creating
|
||||
/// entities in the local tree, those entities have creatorToken based entity IDs. But those entity edits are also sent up to
|
||||
|
@ -521,7 +547,6 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
|
||||
EntityItemID entityItemID;
|
||||
EntityItemProperties properties;
|
||||
|
||||
bool validEditPacket = EntityItemProperties::decodeEntityEditPacket(editData, maxLength,
|
||||
processedBytes, entityItemID, properties);
|
||||
|
||||
|
@ -560,7 +585,6 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char
|
|||
qDebug() << "EntityTree::processEditPacketData() ... "
|
||||
"AFTER assignEntityID()... entityItemID=" << entityItemID;
|
||||
}
|
||||
|
||||
EntityItem* newEntity = addEntity(entityItemID, properties);
|
||||
if (newEntity) {
|
||||
notifyNewlyCreatedEntity(*newEntity, senderNode);
|
||||
|
@ -605,7 +629,8 @@ void EntityTree::removeNewlyCreatedHook(NewlyCreatedEntityHook* hook) {
|
|||
}
|
||||
|
||||
|
||||
void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::SimuationState oldState, EntityItem::SimuationState newState) {
|
||||
void EntityTree::changeEntityState(EntityItem* const entity,
|
||||
EntityItem::SimulationState oldState, EntityItem::SimulationState newState) {
|
||||
|
||||
bool wantDebug = false;
|
||||
|
||||
|
@ -617,6 +642,7 @@ void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::Simuati
|
|||
qDebug() << "EntityTree::changeEntityState() BEFORE....";
|
||||
qDebug() << " _changingEntities:" << _changingEntities;
|
||||
qDebug() << " _movingEntities:" << _movingEntities;
|
||||
qDebug() << " _mortalEntities:" << _mortalEntities;
|
||||
}
|
||||
|
||||
// TODO: can we short circuit this if the state isn't changing?
|
||||
|
@ -628,6 +654,11 @@ void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::Simuati
|
|||
case EntityItem::Moving:
|
||||
_movingEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.removeAll(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -641,6 +672,11 @@ void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::Simuati
|
|||
case EntityItem::Moving:
|
||||
_movingEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
case EntityItem::Mortal:
|
||||
_mortalEntities.push_back(entity);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -650,14 +686,12 @@ void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::Simuati
|
|||
qDebug() << "EntityTree::changeEntityState() AFTER....";
|
||||
qDebug() << " _changingEntities:" << _changingEntities;
|
||||
qDebug() << " _movingEntities:" << _movingEntities;
|
||||
qDebug() << " _mortalEntities:" << _mortalEntities;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void EntityTree::update() {
|
||||
|
||||
bool wantDebug = false;
|
||||
|
||||
// our new strategy should be to segregate entities into three classes:
|
||||
// 1) stationary things that are not changing - most models
|
||||
// 2) stationary things that are animating - they can be touched linearly and they don't change the tree
|
||||
|
@ -665,51 +699,147 @@ void EntityTree::update() {
|
|||
|
||||
lockForWrite();
|
||||
quint64 now = usecTimestampNow();
|
||||
QSet<EntityItemID> entitiesToDelete;
|
||||
updateChangingEntities(now, entitiesToDelete);
|
||||
updateMovingEntities(now, entitiesToDelete);
|
||||
updateMortalEntities(now, entitiesToDelete);
|
||||
deleteEntities(entitiesToDelete);
|
||||
unlock();
|
||||
}
|
||||
|
||||
void EntityTree::updateChangingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
QSet<EntityItem*> entitiesBecomingStatic;
|
||||
QSet<EntityItem*> entitiesBecomingMortal;
|
||||
QSet<EntityItem*> entitiesBecomingMoving;
|
||||
|
||||
// TODO: switch these to iterators so we can remove items that get deleted
|
||||
for (int i = 0; i < _changingEntities.size(); i++) {
|
||||
EntityItem* thisEntity = _changingEntities[i];
|
||||
thisEntity->update(now);
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
} else {
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimulationState newState = thisEntity->getSimulationState();
|
||||
|
||||
if (newState == EntityItem::Static) {
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
} else if (newState == EntityItem::Mortal) {
|
||||
entitiesBecomingMortal << thisEntity;
|
||||
} else if (newState == EntityItem::Moving) {
|
||||
entitiesBecomingMoving << thisEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// change state for any entities that were changing but are now either static, mortal, or moving
|
||||
foreach(EntityItem* entity, entitiesBecomingStatic) {
|
||||
changeEntityState(entity, EntityItem::Changing, EntityItem::Static);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingMortal) {
|
||||
changeEntityState(entity, EntityItem::Changing, EntityItem::Mortal);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingMoving) {
|
||||
changeEntityState(entity, EntityItem::Changing, EntityItem::Moving);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityTree::updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
bool wantDebug = false;
|
||||
MovingEntitiesOperator moveOperator(this);
|
||||
|
||||
QSet<EntityItem*> entitiesBecomingStatic;
|
||||
QSet<EntityItem*> entitiesBecomingMortal;
|
||||
QSet<EntityItem*> entitiesBecomingChanging;
|
||||
|
||||
// TODO: switch these to iterators so we can remove items that get deleted
|
||||
for (int i = 0; i < _movingEntities.size(); i++) {
|
||||
EntityItem* thisEntity = _movingEntities[i];
|
||||
AACube oldCube = thisEntity->getAACube();
|
||||
thisEntity->update(now);
|
||||
AACube newCube = thisEntity->getAACube();
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityTree::update() thisEntity=" << thisEntity;
|
||||
qDebug() << " oldCube=" << oldCube;
|
||||
qDebug() << " newCube=" << newCube;
|
||||
}
|
||||
|
||||
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
|
||||
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimuationState newState = thisEntity->getSimulationState();
|
||||
if (newState == EntityItem::Changing) {
|
||||
entitiesBecomingChanging << thisEntity;
|
||||
} else if (newState == EntityItem::Static) {
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
} else {
|
||||
AACube oldCube = thisEntity->getAACube();
|
||||
thisEntity->update(now);
|
||||
AACube newCube = thisEntity->getAACube();
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityTree::update() thisEntity=" << thisEntity;
|
||||
qDebug() << " oldCube=" << oldCube;
|
||||
qDebug() << " newCube=" << newCube;
|
||||
}
|
||||
|
||||
moveOperator.addEntityToMoveList(thisEntity, oldCube, newCube);
|
||||
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimulationState newState = thisEntity->getSimulationState();
|
||||
if (newState == EntityItem::Changing) {
|
||||
entitiesBecomingChanging << thisEntity;
|
||||
} else if (newState == EntityItem::Mortal) {
|
||||
entitiesBecomingMortal << thisEntity;
|
||||
} else if (newState == EntityItem::Static) {
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
recurseTreeWithOperator(&moveOperator);
|
||||
|
||||
// for any and all entities that were moving but are now either static or changing
|
||||
// change their state accordingly
|
||||
// change state for any entities that were moving but are now either static, mortal, or changing
|
||||
foreach(EntityItem* entity, entitiesBecomingStatic) {
|
||||
changeEntityState(entity, EntityItem::Moving, EntityItem::Static);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingMortal) {
|
||||
changeEntityState(entity, EntityItem::Moving, EntityItem::Mortal);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingChanging) {
|
||||
changeEntityState(entity, EntityItem::Moving, EntityItem::Changing);
|
||||
}
|
||||
}
|
||||
|
||||
unlock();
|
||||
void EntityTree::updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete) {
|
||||
QSet<EntityItem*> entitiesBecomingStatic;
|
||||
QSet<EntityItem*> entitiesBecomingChanging;
|
||||
QSet<EntityItem*> entitiesBecomingMoving;
|
||||
|
||||
// TODO: switch these to iterators so we can remove items that get deleted
|
||||
for (int i = 0; i < _mortalEntities.size(); i++) {
|
||||
EntityItem* thisEntity = _mortalEntities[i];
|
||||
thisEntity->update(now);
|
||||
// always check to see if the lifetime has expired, for immortal entities this is always false
|
||||
if (thisEntity->lifetimeHasExpired()) {
|
||||
qDebug() << "Lifetime has expired for entity:" << thisEntity->getEntityItemID();
|
||||
entitiesToDelete << thisEntity->getEntityItemID();
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
} else {
|
||||
// check to see if this entity is no longer moving
|
||||
EntityItem::SimulationState newState = thisEntity->getSimulationState();
|
||||
|
||||
if (newState == EntityItem::Static) {
|
||||
entitiesBecomingStatic << thisEntity;
|
||||
} else if (newState == EntityItem::Changing) {
|
||||
entitiesBecomingChanging << thisEntity;
|
||||
} else if (newState == EntityItem::Moving) {
|
||||
entitiesBecomingMoving << thisEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// change state for any entities that were mortal but are now either static, changing, or moving
|
||||
foreach(EntityItem* entity, entitiesBecomingStatic) {
|
||||
changeEntityState(entity, EntityItem::Mortal, EntityItem::Static);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingChanging) {
|
||||
changeEntityState(entity, EntityItem::Mortal, EntityItem::Changing);
|
||||
}
|
||||
foreach(EntityItem* entity, entitiesBecomingMoving) {
|
||||
changeEntityState(entity, EntityItem::Mortal, EntityItem::Moving);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ public:
|
|||
bool updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
void deleteEntity(const EntityItemID& entityID);
|
||||
void deleteEntities(QSet<EntityItemID> entityIDs);
|
||||
void removeEntityFromSimulationLists(const EntityItemID& entityID);
|
||||
|
||||
const EntityItem* findClosestEntity(glm::vec3 position, float targetRadius);
|
||||
EntityItem* findEntityByID(const QUuid& id);
|
||||
|
@ -123,12 +124,17 @@ public:
|
|||
|
||||
void sendEntities(EntityEditPacketSender* packetSender, EntityTree* localTree, float x, float y, float z);
|
||||
|
||||
void changeEntityState(EntityItem* const entity, EntityItem::SimuationState oldState, EntityItem::SimuationState newState);
|
||||
void changeEntityState(EntityItem* const entity,
|
||||
EntityItem::SimulationState oldState, EntityItem::SimulationState newState);
|
||||
|
||||
void trackDeletedEntity(const EntityItemID& entityID);
|
||||
|
||||
private:
|
||||
|
||||
void updateChangingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
void updateMovingEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
void updateMortalEntities(quint64 now, QSet<EntityItemID>& entitiesToDelete);
|
||||
|
||||
static bool findNearPointOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInSphereOperation(OctreeElement* element, void* extraData);
|
||||
static bool findInCubeOperation(OctreeElement* element, void* extraData);
|
||||
|
@ -147,6 +153,7 @@ private:
|
|||
|
||||
QList<EntityItem*> _movingEntities; // entities that are moving as part of update
|
||||
QList<EntityItem*> _changingEntities; // entities that are changing (like animating), but not moving
|
||||
QList<EntityItem*> _mortalEntities; // entities that are mortal (have lifetime), but not moving or changing
|
||||
};
|
||||
|
||||
#endif // hifi_EntityTree_h
|
||||
|
|
|
@ -663,9 +663,9 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int
|
|||
if (entityItem) {
|
||||
bool bestFitBefore = bestFitEntityBounds(entityItem);
|
||||
EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID);
|
||||
EntityItem::SimuationState oldState = entityItem->getSimulationState();
|
||||
EntityItem::SimulationState oldState = entityItem->getSimulationState();
|
||||
bytesForThisEntity = entityItem->readEntityDataFromBuffer(dataAt, bytesLeftToRead, args);
|
||||
EntityItem::SimuationState newState = entityItem->getSimulationState();
|
||||
EntityItem::SimulationState newState = entityItem->getSimulationState();
|
||||
_myTree->changeEntityState(entityItem, oldState, newState);
|
||||
bool bestFitAfter = bestFitEntityBounds(entityItem);
|
||||
|
||||
|
|
|
@ -84,7 +84,15 @@ EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const Entity
|
|||
factory = _factories[entityType];
|
||||
}
|
||||
if (factory) {
|
||||
newEntityItem = factory(entityID, properties);
|
||||
// NOTE: if someone attempts to create an entity with properties that do not include a proper "created" time
|
||||
// then set the created time to now
|
||||
if (!properties.hasCreatedTime()) {
|
||||
EntityItemProperties mutableProperties = properties;
|
||||
mutableProperties.setCreated(usecTimestampNow());
|
||||
newEntityItem = factory(entityID, mutableProperties);
|
||||
} else {
|
||||
newEntityItem = factory(entityID, properties);
|
||||
}
|
||||
}
|
||||
return newEntityItem;
|
||||
}
|
||||
|
@ -139,6 +147,7 @@ EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int byte
|
|||
|
||||
EntityItemID tempEntityID(actualID);
|
||||
EntityItemProperties tempProperties;
|
||||
tempProperties.setCreated(usecTimestampNow()); // this is temporary...
|
||||
|
||||
if (wantDebug) {
|
||||
qDebug() << "EntityTypes::constructEntityItem(data, bytesToRead).... NEW BITSTREAM!!! entityType=" << entityType;
|
||||
|
|
|
@ -232,6 +232,11 @@ int ModelEntityItem::oldVersionReadEntityDataFromBuffer(const unsigned char* dat
|
|||
dataAt += sizeof(_lastEdited);
|
||||
bytesRead += sizeof(_lastEdited);
|
||||
_lastEdited -= clockSkew;
|
||||
_created = _lastEdited; // NOTE: old models didn't have age or created time, assume their last edit was a create
|
||||
|
||||
QString ageAsString = formatSecondsElapsed(getAge());
|
||||
qDebug() << "Loading old model file, _created = _lastEdited =" << _created
|
||||
<< " age=" << getAge() << "seconds - " << ageAsString;
|
||||
|
||||
// radius
|
||||
memcpy(&_radius, dataAt, sizeof(_radius));
|
||||
|
@ -532,8 +537,8 @@ bool ModelEntityItem::isAnimatingSomething() const {
|
|||
!getAnimationURL().isEmpty();
|
||||
}
|
||||
|
||||
EntityItem::SimuationState ModelEntityItem::getSimulationState() const {
|
||||
EntityItem::SimuationState baseClassState = EntityItem::getSimulationState();
|
||||
EntityItem::SimulationState ModelEntityItem::getSimulationState() const {
|
||||
EntityItem::SimulationState baseClassState = EntityItem::getSimulationState();
|
||||
|
||||
// if the base class is static, then consider our animation state, and upgrade to changing if
|
||||
// we are animating. If the base class has a higher simulation state than static, then
|
||||
|
|
|
@ -44,7 +44,7 @@ public:
|
|||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
virtual void update(const quint64& now);
|
||||
virtual SimuationState getSimulationState() const;
|
||||
virtual SimulationState getSimulationState() const;
|
||||
virtual void debugDump() const;
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
// REQUIRED:
|
||||
|
||||
|
||||
10) Lifetime??
|
||||
1) Lifetime, Age, Created
|
||||
* support created timestamp in all entities
|
||||
* handle old model files with no created time stamp
|
||||
* add age, ageAsText properties for scripting
|
||||
|
||||
* make sure that entities that expire are correctly deleted safely
|
||||
* make sure that deleting a mortal but not yet dead entity works
|
||||
|
||||
|
||||
* make sure that newly "viewed" entities are correctly added to our simulation lists: moral, changing, moving
|
||||
|
||||
|
||||
2) Crash in delete model - deleting the geometry...
|
||||
I think this is a cross threading issue... delete Model needs to be on same thread that created it.
|
||||
|
||||
-- change Model* RenderableModelEntityItem::getModel(); to call a new method on EntityTreeRenderer::newModel();
|
||||
-- change RenderableModelEntityItem::~RenderableModelEntityItem() to call a new method on EntityTreeRenderer::deleteModel(Model*);
|
||||
|
||||
3) Look into why non-changed octree cells are being resent when editing an entity
|
||||
|
||||
|
||||
7) Test file save load for case where two siblings have more than MTU amount of data. I wonder if the fact that file save
|
||||
|
|
|
@ -571,3 +571,23 @@ QString formatUsecTime(float usecs, int prec) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString formatSecondsElapsed(float seconds) {
|
||||
QString result;
|
||||
|
||||
const float SECONDS_IN_DAY = 60.0f * 60.0f * 24.0f;
|
||||
if (seconds > SECONDS_IN_DAY) {
|
||||
float days = floor(seconds / SECONDS_IN_DAY);
|
||||
float rest = seconds - (days * SECONDS_IN_DAY);
|
||||
result = QString::number((int)days);
|
||||
if (days > 1.0f) {
|
||||
result += " days ";
|
||||
} else {
|
||||
result += " day ";
|
||||
}
|
||||
result += QDateTime::fromTime_t(rest).toUTC().toString("h 'hours' m 'minutes' s 'seconds'");
|
||||
} else {
|
||||
result = QDateTime::fromTime_t(seconds).toUTC().toString("h 'hours' m 'minutes' s 'seconds'");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ bool isBetween(int64_t value, int64_t max, int64_t min);
|
|||
bool isNaN(float value);
|
||||
|
||||
QString formatUsecTime(float usecs, int prec = 3);
|
||||
QString formatSecondsElapsed(float seconds);
|
||||
|
||||
|
||||
#endif // hifi_SharedUtil_h
|
||||
|
|
Loading…
Reference in a new issue