diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 4bc2c27938..1f7e447e5f 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -78,60 +78,62 @@ const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(const EntityItem& en Model* EntityTreeRenderer::getModel(const EntityItem& entityItem) { Model* model = NULL; - + + if (!entityItem.getModelURL().isEmpty()) { #ifdef HIDE_SUBCLASS_METHODS - if (entityItem.isKnownID()) { - if (_knownEntityItemModels.find(entityItem.getID()) != _knownEntityItemModels.end()) { - model = _knownEntityItemModels[entityItem.getID()]; - if (QUrl(entityItem.getModelURL()) != model->getURL()) { - delete model; // delete the old model... - model = NULL; - _knownEntityItemModels.remove(entityItem.getID()); - } - } - - // if we don't have a model... but our item does have a model URL - if (!model && !entityItem.getModelURL().isEmpty()) { - // Make sure we only create new models on the thread that owns the EntityTreeRenderer - if (QThread::currentThread() != thread()) { - qDebug() << "about to call QMetaObject::invokeMethod(this, 'getModel', Qt::BlockingQueuedConnection,..."; - QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(Model*, model), Q_ARG(const EntityItem&, entityItem)); - qDebug() << "got it... model=" << model; - return model; + if (entityItem.isKnownID()) { + if (_knownEntityItemModels.find(entityItem.getID()) != _knownEntityItemModels.end()) { + model = _knownEntityItemModels[entityItem.getID()]; + if (QUrl(entityItem.getModelURL()) != model->getURL()) { + delete model; // delete the old model... + model = NULL; + _knownEntityItemModels.remove(entityItem.getID()); + } } - model = new Model(); - model->init(); - model->setURL(QUrl(entityItem.getModelURL())); - _knownEntityItemModels[entityItem.getID()] = model; - } + // if we don't have a model... but our item does have a model URL + if (!model) { + // Make sure we only create new models on the thread that owns the EntityTreeRenderer + if (QThread::currentThread() != thread()) { + qDebug() << "about to call QMetaObject::invokeMethod(this, 'getModel', Qt::BlockingQueuedConnection,..."; + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const EntityItem&, entityItem)); + qDebug() << "got it... model=" << model; + return model; + } + + model = new Model(); + model->init(); + model->setURL(QUrl(entityItem.getModelURL())); + _knownEntityItemModels[entityItem.getID()] = model; + } + + } else { + if (_unknownEntityItemModels.find(entityItem.getCreatorTokenID()) != _unknownEntityItemModels.end()) { + model = _unknownEntityItemModels[entityItem.getCreatorTokenID()]; + if (QUrl(entityItem.getModelURL()) != model->getURL()) { + delete model; // delete the old model... + model = NULL; + _unknownEntityItemModels.remove(entityItem.getID()); + } + } + + if (!model) { + // Make sure we only create new models on the thread that owns the EntityTreeRenderer + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(Model*, model), Q_ARG(const EntityItem&, entityItem)); + return model; + } - } else { - if (_unknownEntityItemModels.find(entityItem.getCreatorTokenID()) != _unknownEntityItemModels.end()) { - model = _unknownEntityItemModels[entityItem.getCreatorTokenID()]; - if (QUrl(entityItem.getModelURL()) != model->getURL()) { - delete model; // delete the old model... - model = NULL; - _unknownEntityItemModels.remove(entityItem.getID()); + model = new Model(); + model->init(); + model->setURL(QUrl(entityItem.getModelURL())); + _unknownEntityItemModels[entityItem.getCreatorTokenID()] = model; } } - - if (!model) { - // Make sure we only create new models on the thread that owns the EntityTreeRenderer - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "getModel", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(Model*, model), Q_ARG(const EntityItem&, entityItem)); - return model; - } - - model = new Model(); - model->init(); - model->setURL(QUrl(entityItem.getModelURL())); - _unknownEntityItemModels[entityItem.getCreatorTokenID()] = model; - } - } #endif + } return model; } diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 27d60f6c33..e0b2d1f0f6 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -43,7 +43,7 @@ void EntityEditPacketSender::adjustEditPacketForClockSkew(unsigned char* codeCol } -void EntityEditPacketSender::queueEntityEditMessage(PacketType type, EntityItemID modelID, +void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties) { if (!_shouldSend) { return; // bail early diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 99cf251bbe..b727f74519 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -28,7 +28,7 @@ public: /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in /// which case up to MaxPendingMessages will be buffered and processed when voxel servers are known. /// NOTE: EntityItemProperties assumes that all distances are in meter units - void queueEntityEditMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties); + void queueEditEntityMessage(PacketType type, EntityItemID modelID, const EntityItemProperties& properties); // My server type is the model server virtual char getMyNodeType() const { return NodeType::EntityServer; } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4ccf7951f2..99ca107903 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -87,6 +87,13 @@ EntityItem* EntityTypes::constructEntityItem(const unsigned char* data, int byte return NULL; // TODO Implement this for real! } +bool EntityTypes::decodEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + const EntityItemID& entityID, const EntityItemProperties& properties) { + bool valid = false; + return valid; +} + + bool registered = EntityTypes::registerEntityType(EntityTypes::Base, "Base") && EntityTypes::registerEntityType(EntityTypes::Model, "Model"); // TODO: move this to model subclass @@ -1018,7 +1025,7 @@ EntityItem* EntityItem::fromEditPacket(const unsigned char* data, int length, in } void EntityItem::debugDump() const { - qDebug("EntityItem id :%u", _id); + qDebug() << "EntityItem id:" << getEntityItemID(); qDebug(" edited ago:%f", getEditedAgo()); qDebug(" should die:%s", debug::valueOf(getShouldBeDeleted())); qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z); @@ -1026,10 +1033,401 @@ void EntityItem::debugDump() const { #ifdef HIDE_SUBCLASS_METHODS qDebug(" color:%d,%d,%d", _color[0], _color[1], _color[2]); - qDebug() << " modelURL:" << qPrintable(getModelURL()); + if (!getModelURL().isEmpty()) { + qDebug() << " modelURL:" << qPrintable(getModelURL()); + } else { + qDebug() << " modelURL: NONE"; + } #endif } + + + + + +////////////////////////////////////////////////////// +////////////////////////////////////////////////////// +////////////////////////////////////////////////////// +//Change this to use property flags... +//How do we also change this to support spanning multiple MTUs... +//Need to output the encode structure like handling packets over the wire... +////////////////////////////////////////////////////// +////////////////////////////////////////////////////// +////////////////////////////////////////////////////// + +bool EntityItem::encodeEntityEditMessageDetails(PacketType command, EntityItemID id, const EntityItemProperties& properties, + unsigned char* bufferOut, int sizeIn, int& sizeOut) { + + OctreePacketData packetData(false, sizeIn); // create a packetData object to add out packet details too. + + bool success = true; // assume the best + OctreeElement::AppendState appendState = OctreeElement::COMPLETED; // assume the best + sizeOut = 0; + + // TODO: We need to review how jurisdictions should be handled for entities. (The old Models and Particles code + // didn't do anything special for jurisdictions, so we're keeping that same behavior here.) + // + // Always include the root octcode. This is only because the OctreeEditPacketSender will check these octcodes + // to determine which server to send the changes to in the case of multiple jurisdictions. The root will be sent + // to all servers. + glm::vec3 rootPosition(0); + float rootScale = 0.5f; + unsigned char* octcode = pointToOctalCode(rootPosition.x, rootPosition.y, rootPosition.z, rootScale); + + success = packetData.startSubTree(octcode); + delete[] octcode; + + // assuming we have rome to fit our octalCode, proceed... + if (success) { + + // Now add our edit content details... + bool isNewEntityItem = (id.id == NEW_ENTITY); + + // id + // encode our ID as a byte count coded byte stream + ByteCountCoded idCoder = id.id; + QByteArray encodedID = idCoder; + + // encode our ID as a byte count coded byte stream + ByteCountCoded tokenCoder; + QByteArray encodedToken; + + // special case for handling "new" modelItems + if (isNewEntityItem) { + // encode our creator token as a byte count coded byte stream + tokenCoder = id.creatorTokenID; + encodedToken = tokenCoder; + } + + // encode our type as a byte count coded byte stream + ByteCountCoded typeCoder = (quint32)properties.getType(); + QByteArray encodedType = typeCoder; + + quint64 updateDelta = 0; // this is an edit so by definition, it's update is in sync + ByteCountCoded updateDeltaCoder = updateDelta; + QByteArray encodedUpdateDelta = updateDeltaCoder; + EntityPropertyFlags propertyFlags(PROP_LAST_ITEM); + EntityPropertyFlags requestedProperties = properties.getChangedProperties(); + EntityPropertyFlags propertiesDidntFit = requestedProperties; + + // TODO: we need to handle the multi-pass form of this, similar to how we handle entity data + // + // If we are being called for a subsequent pass at appendEntityData() that failed to completely encode this item, + // then our modelTreeElementExtraEncodeData should include data about which properties we need to append. + //if (modelTreeElementExtraEncodeData && modelTreeElementExtraEncodeData->includedItems.contains(getEntityItemID())) { + // requestedProperties = modelTreeElementExtraEncodeData->includedItems.value(getEntityItemID()); + //} + + //qDebug() << "requestedProperties="; + //requestedProperties.debugDumpBits(); + + LevelDetails entityLevel = packetData.startLevel(); + + bool successIDFits = packetData.appendValue(encodedID); + if (isNewEntityItem && successIDFits) { + successIDFits = packetData.appendValue(encodedToken); + } + bool successTypeFits = packetData.appendValue(encodedType); + bool successLastEditedFits = packetData.appendValue(properties.getLastEdited()); + bool successLastUpdatedFits = packetData.appendValue(encodedUpdateDelta); + + int propertyFlagsOffset = packetData.getUncompressedByteOffset(); + QByteArray encodedPropertyFlags = propertyFlags; + int oldPropertyFlagsLength = encodedPropertyFlags.length(); + bool successPropertyFlagsFits = packetData.appendValue(encodedPropertyFlags); + int propertyCount = 0; + + bool headerFits = successIDFits && successTypeFits && successLastEditedFits + && successLastUpdatedFits && successPropertyFlagsFits; + + int startOfEntityItemData = packetData.getUncompressedByteOffset(); + + if (headerFits) { + bool successPropertyFits; + + propertyFlags -= PROP_LAST_ITEM; // clear the last item for now, we may or may not set it as the actual item + + // These items would go here once supported.... + // PROP_PAGED_PROPERTY, + // PROP_CUSTOM_PROPERTIES_INCLUDED, + // PROP_VISIBLE, + + // PROP_POSITION + if (requestedProperties.getHasProperty(PROP_POSITION)) { + //qDebug() << "PROP_POSITION requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendPosition(properties.getPosition()); + if (successPropertyFits) { + propertyFlags |= PROP_POSITION; + propertiesDidntFit -= PROP_POSITION; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_POSITION didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_POSITION NOT requested..."; + propertiesDidntFit -= PROP_POSITION; + } + + // PROP_RADIUS + if (requestedProperties.getHasProperty(PROP_RADIUS)) { + //qDebug() << "PROP_RADIUS requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getRadius()); + if (successPropertyFits) { + propertyFlags |= PROP_RADIUS; + propertiesDidntFit -= PROP_RADIUS; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_RADIUS didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_RADIUS NOT requested..."; + propertiesDidntFit -= PROP_RADIUS; + } + + // PROP_ROTATION + if (requestedProperties.getHasProperty(PROP_ROTATION)) { + //qDebug() << "PROP_ROTATION requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getRotation()); + if (successPropertyFits) { + propertyFlags |= PROP_ROTATION; + propertiesDidntFit -= PROP_ROTATION; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_ROTATION didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_ROTATION NOT requested..."; + propertiesDidntFit -= PROP_ROTATION; + } + + // PROP_SHOULD_BE_DELETED + if (requestedProperties.getHasProperty(PROP_SHOULD_BE_DELETED)) { + //qDebug() << "PROP_SHOULD_BE_DELETED requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getShouldBeDeleted()); + if (successPropertyFits) { + propertyFlags |= PROP_SHOULD_BE_DELETED; + propertiesDidntFit -= PROP_SHOULD_BE_DELETED; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_SHOULD_BE_DELETED didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_SHOULD_BE_DELETED NOT requested..."; + propertiesDidntFit -= PROP_SHOULD_BE_DELETED; + } + + // PROP_SCRIPT + // script would go here... + + +#if 0 // def HIDE_SUBCLASS_METHODS + // PROP_COLOR + if (requestedProperties.getHasProperty(PROP_COLOR)) { + //qDebug() << "PROP_COLOR requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendColor(properties.getColor()); + if (successPropertyFits) { + propertyFlags |= PROP_COLOR; + propertiesDidntFit -= PROP_COLOR; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_COLOR didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_COLOR NOT requested..."; + propertiesDidntFit -= PROP_COLOR; + } + + // PROP_MODEL_URL + if (requestedProperties.getHasProperty(PROP_MODEL_URL)) { + //qDebug() << "PROP_MODEL_URL requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getModelURL()); + if (successPropertyFits) { + propertyFlags |= PROP_MODEL_URL; + propertiesDidntFit -= PROP_MODEL_URL; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_MODEL_URL didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_MODEL_URL NOT requested..."; + propertiesDidntFit -= PROP_MODEL_URL; + } + + // PROP_ANIMATION_URL + if (requestedProperties.getHasProperty(PROP_ANIMATION_URL)) { + //qDebug() << "PROP_ANIMATION_URL requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getAnimationURL()); + if (successPropertyFits) { + propertyFlags |= PROP_ANIMATION_URL; + propertiesDidntFit -= PROP_ANIMATION_URL; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_ANIMATION_URL didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_ANIMATION_URL NOT requested..."; + propertiesDidntFit -= PROP_ANIMATION_URL; + } + + // PROP_ANIMATION_FPS + if (requestedProperties.getHasProperty(PROP_ANIMATION_FPS)) { + //qDebug() << "PROP_ANIMATION_FPS requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getAnimationFPS()); + if (successPropertyFits) { + propertyFlags |= PROP_ANIMATION_FPS; + propertiesDidntFit -= PROP_ANIMATION_FPS; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_ANIMATION_FPS didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_ANIMATION_FPS NOT requested..."; + propertiesDidntFit -= PROP_ANIMATION_FPS; + } + + // PROP_ANIMATION_FRAME_INDEX + if (requestedProperties.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { + //qDebug() << "PROP_ANIMATION_FRAME_INDEX requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getAnimationFrameIndex()); + if (successPropertyFits) { + propertyFlags |= PROP_ANIMATION_FRAME_INDEX; + propertiesDidntFit -= PROP_ANIMATION_FRAME_INDEX; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_ANIMATION_FRAME_INDEX didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_ANIMATION_FRAME_INDEX NOT requested..."; + propertiesDidntFit -= PROP_ANIMATION_FRAME_INDEX; + } + + // PROP_ANIMATION_PLAYING + if (requestedProperties.getHasProperty(PROP_ANIMATION_PLAYING)) { + //qDebug() << "PROP_ANIMATION_PLAYING requested..."; + LevelDetails propertyLevel = packetData.startLevel(); + successPropertyFits = packetData.appendValue(properties.getAnimationIsPlaying()); + if (successPropertyFits) { + propertyFlags |= PROP_ANIMATION_PLAYING; + propertiesDidntFit -= PROP_ANIMATION_PLAYING; + propertyCount++; + packetData.endLevel(propertyLevel); + } else { + //qDebug() << "PROP_ANIMATION_PLAYING didn't fit..."; + packetData.discardLevel(propertyLevel); + appendState = OctreeElement::PARTIAL; + } + } else { + //qDebug() << "PROP_ANIMATION_PLAYING NOT requested..."; + propertiesDidntFit -= PROP_ANIMATION_PLAYING; + } + +#endif //def HIDE_SUBCLASS_METHODS + + } + if (propertyCount > 0) { + int endOfEntityItemData = packetData.getUncompressedByteOffset(); + + encodedPropertyFlags = propertyFlags; + int newPropertyFlagsLength = encodedPropertyFlags.length(); + packetData.updatePriorBytes(propertyFlagsOffset, + (const unsigned char*)encodedPropertyFlags.constData(), encodedPropertyFlags.length()); + + // if the size of the PropertyFlags shrunk, we need to shift everything down to front of packet. + if (newPropertyFlagsLength < oldPropertyFlagsLength) { + int oldSize = packetData.getUncompressedSize(); + + const unsigned char* modelItemData = packetData.getUncompressedData(propertyFlagsOffset + oldPropertyFlagsLength); + int modelItemDataLength = endOfEntityItemData - startOfEntityItemData; + int newEntityItemDataStart = propertyFlagsOffset + newPropertyFlagsLength; + packetData.updatePriorBytes(newEntityItemDataStart, modelItemData, modelItemDataLength); + + int newSize = oldSize - (oldPropertyFlagsLength - newPropertyFlagsLength); + packetData.setUncompressedSize(newSize); + + } else { + assert(newPropertyFlagsLength == oldPropertyFlagsLength); // should not have grown + } + + packetData.endLevel(entityLevel); + } else { + packetData.discardLevel(entityLevel); + appendState = OctreeElement::NONE; // if we got here, then we didn't include the item + } + + //qDebug() << "propertyFlags="; + //propertyFlags.debugDumpBits(); + + //qDebug() << "propertiesDidntFit="; + //propertiesDidntFit.debugDumpBits(); + + // If any part of the model items didn't fit, then the element is considered partial + if (appendState != OctreeElement::COMPLETED) { + + + // TODO: handle mechanism for handling partial fitting data! + // add this item into our list for the next appendElementData() pass + //modelTreeElementExtraEncodeData->includedItems.insert(getEntityItemID(), propertiesDidntFit); + + // for now, if it's not complete, it's not successful + success = false; + } + } + + if (success) { + packetData.endSubTree(); + const unsigned char* finalizedData = packetData.getFinalizedData(); + int finalizedSize = packetData.getFinalizedSize(); + memcpy(bufferOut, finalizedData, finalizedSize); + sizeOut = finalizedSize; + + qDebug() << "encodeEntityEditMessageDetails().... "; + outputBufferBits(finalizedData, finalizedSize); + + } else { + packetData.discardSubTree(); + sizeOut = 0; + } + return success; +} + +#if 0 /// OLD VERSION bool EntityItem::encodeEntityEditMessageDetails(PacketType command, EntityItemID id, const EntityItemProperties& properties, unsigned char* bufferOut, int sizeIn, int& sizeOut) { @@ -1196,6 +1594,7 @@ bool EntityItem::encodeEntityEditMessageDetails(PacketType command, EntityItemID return success; } +#endif // adjust any internal timestamps to fix clock skew for this server void EntityItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssize_t length, int clockSkew) { @@ -1554,13 +1953,61 @@ uint16_t EntityItemProperties::getChangedBits() const { return changedBits; } +EntityPropertyFlags EntityItemProperties::getChangedProperties() const { + EntityPropertyFlags changedProperties; + if (_radiusChanged) { + changedProperties += PROP_RADIUS; + } + + if (_positionChanged) { + changedProperties += PROP_POSITION; + } + + if (_rotationChanged) { + changedProperties += PROP_ROTATION; + } + + if (_shouldBeDeletedChanged) { + changedProperties += PROP_SHOULD_BE_DELETED; + } + +#if 0 //def HIDE_SUBCLASS_METHODS + if (_colorChanged) { + changedProperties += PROP_COLOR; + } + + if (_modelURLChanged) { + changedProperties += PROP_MODEL_URL; + } + + if (_animationURLChanged) { + changedProperties += PROP_ANIMATION_URL; + } + + if (_animationIsPlayingChanged) { + changedProperties += PROP_ANIMATION_PLAYING; + } + + if (_animationFrameIndexChanged) { + changedProperties += PROP_ANIMATION_FRAME_INDEX; + } + + if (_animationFPSChanged) { + changedProperties += PROP_ANIMATION_FPS; + } +#endif + + return changedProperties; +} QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) const { QScriptValue properties = engine->newObject(); if (_idSet) { properties.setProperty("id", _id); - properties.setProperty("isKnownID", (_id != UNKNOWN_ENTITY_ID)); + bool isKnownID = (_id != UNKNOWN_ENTITY_ID); + properties.setProperty("isKnownID", isKnownID); +qDebug() << "EntityItemProperties::copyToScriptValue()... isKnownID=" << isKnownID << "id=" << _id; } QScriptValue position = vec3toScriptValue(engine, _position); @@ -1752,6 +2199,7 @@ QScriptValue EntityItemIDtoScriptValue(QScriptEngine* engine, const EntityItemID obj.setProperty("id", id.id); obj.setProperty("creatorTokenID", id.creatorTokenID); obj.setProperty("isKnownID", id.isKnownID); +qDebug() << "EntityItemIDtoScriptValue()... isKnownID=" << id.isKnownID << "id=" << id.id << "creatorTokenID=" << id.creatorTokenID; return obj; } diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 3e94d70a45..e11214325b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -83,6 +83,8 @@ public: static EntityItem* constructEntityItem(EntityType_t entityType, const EntityItemID& entityID, const EntityItemProperties& properties); static EntityItem* constructEntityItem(const unsigned char* data, int bytesToRead); + static bool decodEntityEditPacket(const unsigned char* data, int bytesToRead, int& processedBytes, + const EntityItemID& entityID, const EntityItemProperties& properties); private: static QHash _typeNameHash; }; @@ -125,6 +127,7 @@ public: // editing related features supported by all entities quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; + EntityPropertyFlags getChangedProperties() const; /// used by EntityScriptingInterface to return EntityItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_ENTITY_ID; _idSet = true; } @@ -224,13 +227,19 @@ void EntityItemPropertiesFromScriptValue(const QScriptValue &object, EntityItemP class EntityItemID { public: EntityItemID() : - id(NEW_ENTITY), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(false) { }; + id(NEW_ENTITY), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(false) { + //qDebug() << "EntityItemID::EntityItemID()... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID) : - id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { }; + id(id), creatorTokenID(creatorTokenID), isKnownID(isKnownID) { + //qDebug() << "EntityItemID::EntityItemID(uint32_t id, uint32_t creatorTokenID, bool isKnownID)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; EntityItemID(uint32_t id) : - id(id), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(true) { }; + id(id), creatorTokenID(UNKNOWN_ENTITY_TOKEN), isKnownID(true) { + //qDebug() << "EntityItemID::EntityItemID(uint32_t id)... isKnownID=" << isKnownID << "id=" << id << "creatorTokenID=" << creatorTokenID; + }; uint32_t id; uint32_t creatorTokenID; @@ -259,7 +268,7 @@ inline uint qHash(const EntityItemID& a, uint seed) { } inline QDebug operator<<(QDebug debug, const EntityItemID& id) { - debug << "[ id:" << id.id << ", creatorTokenID:" << id.creatorTokenID << "]"; + debug << "[ id:" << id.id << ", creatorTokenID:" << id.creatorTokenID << ", isKnownID:" << id.isKnownID << "]"; return debug; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 13ea3316da..4c951f06fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -21,7 +21,7 @@ EntityScriptingInterface::EntityScriptingInterface() : void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { - getEntityPacketSender()->queueEntityEditMessage(packetType, entityID, properties); + getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties); } EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { @@ -56,6 +56,7 @@ EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { // found it! entityID.id = actualID; entityID.isKnownID = true; + qDebug() << "EntityScriptingInterface::identifyEntity() ...found it... isKnownID=" << entityID.isKnownID << "id=" << entityID.id << "creatorTokenID=" << entityID.creatorTokenID; } return entityID; } @@ -95,6 +96,7 @@ EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const E if (actualID != UNKNOWN_ENTITY_ID) { entityID.id = actualID; entityID.isKnownID = true; + qDebug() << "EntityScriptingInterface::editEntity()... isKnownID=" << entityID.isKnownID << "id=" << entityID.id << "creatorTokenID=" << entityID.creatorTokenID; queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties); } @@ -130,6 +132,7 @@ void EntityScriptingInterface::deleteEntity(EntityItemID entityID) { if (actualID != UNKNOWN_ENTITY_ID) { entityID.id = actualID; entityID.isKnownID = true; + qDebug() << "EntityScriptingInterface::deleteEntity()... isKnownID=" << entityID.isKnownID << "id=" << entityID.id << "creatorTokenID=" << entityID.creatorTokenID; queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, properties); } @@ -151,12 +154,21 @@ EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center if (closestEntity) { result.id = closestEntity->getID(); result.isKnownID = true; + qDebug() << "EntityScriptingInterface::findClosestEntity()... isKnownID=" << result.isKnownID << "id=" << result.id << "creatorTokenID=" << result.creatorTokenID; } } return result; } +void EntityScriptingInterface::dumpTree() const { + if (_entityTree) { + _entityTree->lockForRead(); + _entityTree->dumpTree(); + _entityTree->unlock(); + } +} + QVector EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const { QVector result; if (_entityTree) { @@ -186,10 +198,14 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke RayToEntityIntersectionResult result; if (_entityTree) { OctreeElement* element; - EntityItem* intersectedEntity; + EntityItem* intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, (void**)&intersectedEntity, lockType, &result.accurate); if (result.intersects && intersectedEntity) { + +qDebug() << "findRayIntersectionWorker().... intersectedEntity=" << intersectedEntity; +intersectedEntity->debugDump(); + result.entityID = intersectedEntity->getEntityItemID(); result.properties = intersectedEntity->getProperties(); result.intersection = ray.origin + (ray.direction * result.distance); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index af4537c834..bd3f3856af 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -54,30 +54,30 @@ public: public slots: /// adds a model with the specific properties - EntityItemID addEntity(const EntityItemProperties& properties); + Q_INVOKABLE EntityItemID addEntity(const EntityItemProperties& properties); /// identify a recently created model to determine its true ID - EntityItemID identifyEntity(EntityItemID entityID); + Q_INVOKABLE EntityItemID identifyEntity(EntityItemID entityID); /// gets the current model properties for a specific model /// this function will not find return results in script engine contexts which don't have access to models - EntityItemProperties getEntityProperties(EntityItemID entityID); + Q_INVOKABLE EntityItemProperties getEntityProperties(EntityItemID entityID); /// edits a model updating only the included properties, will return the identified EntityItemID in case of /// successful edit, if the input entityID is for an unknown model this function will have no effect - EntityItemID editEntity(EntityItemID entityID, const EntityItemProperties& properties); + Q_INVOKABLE EntityItemID editEntity(EntityItemID entityID, const EntityItemProperties& properties); /// deletes a model - void deleteEntity(EntityItemID entityID); + Q_INVOKABLE void deleteEntity(EntityItemID entityID); /// finds the closest model to the center point, within the radius /// will return a EntityItemID.isKnownID = false if no models are in the radius /// this function will not find any models in script engine contexts which don't have access to models - EntityItemID findClosestEntity(const glm::vec3& center, float radius) const; + Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const; /// finds models within the search sphere specified by the center point and radius /// this function will not find any models in script engine contexts which don't have access to models - QVector findEntities(const glm::vec3& center, float radius) const; + Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; /// If the scripting context has visible voxels, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible voxels, in which case result.accurate @@ -89,6 +89,8 @@ public slots: Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray); + Q_INVOKABLE void dumpTree() const; + signals: void modelCollisionWithVoxel(const EntityItemID& entityID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const CollisionInfo& collision); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f8075264de..c56be6dfcb 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -349,6 +349,9 @@ bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProp } EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItemProperties& properties) { + +qDebug() << "EntityTree::addEntity()... entityID=" << entityID; + EntityItem* result = NULL; // You should not call this on existing entities that are already part of the tree! Call updateEntity() EntityTreeElement* containingElement = getContainingElement(entityID); @@ -360,6 +363,9 @@ EntityItem* EntityTree::addEntity(const EntityItemID& entityID, const EntityItem // construct the instance of the entity EntityTypes::EntityType_t type = properties.getType(); result = EntityTypes::constructEntityItem(type, entityID, properties); + +qDebug() << "EntityTree::addEntity()... result = EntityTypes::constructEntityItem(type, entityID, properties)... result->getEntityItemID()=" << result->getEntityItemID(); + if (result) { // this does the actual adding of the entity addEntityItem(result); @@ -760,10 +766,33 @@ int EntityTree::processEditPacketData(PacketType packetType, const unsigned char // we handle these types of "edit" packets switch (packetType) { case PacketTypeEntityAddOrEdit: { - // TODO: need to do this + qDebug() << "EntityTree::processEditPacketData()...."; + + EntityItemID entityItemID; + EntityItemProperties properties; + + bool validEditPacket = EntityTypes::decodEntityEditPacket(editData, maxLength, + processedBytes, entityItemID, properties); + + // If we got a valid edit packet, then it could be a new entity or it could be an update to + // an existing entity... handle appropriately + if (validEditPacket) { + // search for the entity by EntityItemID + EntityItem* existingEntity = findEntityByEntityItemID(entityItemID); + + // if the entityItem exists, then update it + if (existingEntity) { + updateEntity(entityItemID, properties); + } else { + EntityItem* newEntity = addEntity(entityItemID, properties); + if (newEntity) { + notifyNewlyCreatedEntity(*newEntity, senderNode); + } + } + } + - qDebug() << "EntityTree::processEditPacketData().... NOT YET IMPLEMENTED!!!"; -#if 0 +#if 0 ////// OLD CODE... bool isValid = false; EntityItem* newEntity = NULL; // EntityItem::fromEditPacket(editData, maxLength, processedBytes, this, isValid); if (isValid) { @@ -1062,11 +1091,32 @@ void EntityTree::debugDumpMap() { } } +class DebugOperator : public RecurseOctreeOperator { +public: + virtual bool PreRecursion(OctreeElement* element); + virtual bool PostRecursion(OctreeElement* element) { return true; }; + virtual OctreeElement* PossiblyCreateChildAt(OctreeElement* element, int childIndex) { return NULL; } +}; + +bool DebugOperator::PreRecursion(OctreeElement* element) { + EntityTreeElement* entityTreeElement = static_cast(element); + entityTreeElement->debugDump(); + return true; +} + +void EntityTree::dumpTree() { + // First, look for the existing model in the tree.. + DebugOperator theOperator; + recurseTreeWithOperator(&theOperator); +} + void EntityTree::rememberDirtyCube(const AACube& cube) { // TODO: do something here + qDebug() << "void EntityTree::rememberDirtyCube(const AACube& cube) CALLED BUT NOT IMPLEMENTED!"; } void EntityTree::rememberEntityToMove(const EntityItem* entity) { // TODO: do something here + qDebug() << "void EntityTree::rememberEntityToMove() CALLED BUT NOT IMPLEMENTED!"; } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 8fe0bcf6e1..e12fa04da5 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -99,7 +99,7 @@ public: EntityTreeElement* getContainingElement(const EntityItemID& entityItemID) const; void setContainingElement(const EntityItemID& entityItemID, EntityTreeElement* element); void debugDumpMap(); - + void dumpTree(); void rememberDirtyCube(const AACube& cube); void rememberEntityToMove(const EntityItem* entity); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index c761ea1249..4ea0a70ec9 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -333,7 +333,11 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con if (localDistance < distance) { distance = localDistance; face = localFace; - *intersectedObject = (void*)(&entity); + + qDebug() << "about to set intersectedObject=" << entity; + entity->debugDump(); + + *intersectedObject = (void*)entity; somethingIntersected = true; } } @@ -341,7 +345,11 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con } else if (localDistance < distance) { distance = localDistance; face = localFace; - *intersectedObject = (void*)(&entity); + + qDebug() << "about to set intersectedObject=" << entity; + entity->debugDump(); + + *intersectedObject = (void*)entity; somethingIntersected = true; } } @@ -672,3 +680,12 @@ bool EntityTreeElement::collapseChildren() { // nothing to do here yet... return false; } + + +void EntityTreeElement::debugDump() { + for (uint16_t i = 0; i < _entityItems->size(); i++) { + EntityItem* entity = (*_entityItems)[i]; + entity->debugDump(); + } +} + \ No newline at end of file diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d049ffc668..4c446123a8 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -161,6 +161,8 @@ public: 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 + void debugDump(); + protected: virtual void init(unsigned char * octalCode); EntityTree* _myTree;