int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { int bytesRead = 0; const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) { setCompoundShapeURL(""); } else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) { READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); } else { READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); } READ_ENTITY_PROPERTY(PROP_ANIMATION_URL, QString, setAnimationURL); // Because we're using AnimationLoop which will reset the frame index if you change it's running state // we want to read these values in the order they appear in the buffer, but call our setters in an // order that allows AnimationLoop to preserve the correct frame rate. float animationFPS = getAnimationFPS(); float animationFrameIndex = getAnimationFrameIndex(); bool animationIsPlaying = getAnimationIsPlaying(); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { if (animationIsPlaying != getAnimationIsPlaying()) { setAnimationIsPlaying(animationIsPlaying); } } if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { setAnimationFPS(animationFPS); } if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { setAnimationFrameIndex(animationFrameIndex); } READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); return bytesRead; }
EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties) { EntityItemProperties results; if (_entityTree) { _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(identity)); if (entity) { if (desiredProperties.getHasProperty(PROP_POSITION) || desiredProperties.getHasProperty(PROP_ROTATION) || desiredProperties.getHasProperty(PROP_LOCAL_POSITION) || desiredProperties.getHasProperty(PROP_LOCAL_ROTATION)) { // if we are explicitly getting position or rotation, we need parent information to make sense of them. desiredProperties.setHasProperty(PROP_PARENT_ID); desiredProperties.setHasProperty(PROP_PARENT_JOINT_INDEX); } if (desiredProperties.isEmpty()) { // these are left out of EntityItem::getEntityProperties so that localPosition and localRotation // don't end up in json saves, etc. We still want them here, though. EncodeBitstreamParams params; // unknown desiredProperties = entity->getEntityProperties(params); desiredProperties.setHasProperty(PROP_LOCAL_POSITION); desiredProperties.setHasProperty(PROP_LOCAL_ROTATION); } results = entity->getProperties(desiredProperties); // TODO: improve sitting points and naturalDimensions in the future, // for now we've included the old sitting points model behavior for entity types that are models // we've also added this hack for setting natural dimensions of models if (entity->getType() == EntityTypes::Model) { const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); if (geometry) { results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum); } } } }); } return convertLocationToScriptSemantics(results); }
int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData) { int bytesRead = 0; const unsigned char* dataAt = data; READ_ENTITY_PROPERTY(PROP_COLOR, rgbColor, setColor); // Because we're using AnimationLoop which will reset the frame index if you change it's running state // we want to read these values in the order they appear in the buffer, but call our setters in an // order that allows AnimationLoop to preserve the correct frame rate. float animationFPS = getAnimationFPS(); float animationFrameIndex = getAnimationFrameIndex(); bool animationIsPlaying = getAnimationIsPlaying(); READ_ENTITY_PROPERTY(PROP_ANIMATION_FPS, float, setAnimationFPS); READ_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex); READ_ENTITY_PROPERTY(PROP_ANIMATION_PLAYING, bool, setAnimationIsPlaying); if (propertyFlags.getHasProperty(PROP_ANIMATION_PLAYING)) { if (animationIsPlaying != getAnimationIsPlaying()) { setAnimationIsPlaying(animationIsPlaying); } } if (propertyFlags.getHasProperty(PROP_ANIMATION_FPS)) { setAnimationFPS(animationFPS); } if (propertyFlags.getHasProperty(PROP_ANIMATION_FRAME_INDEX)) { setAnimationFrameIndex(animationFrameIndex); } READ_ENTITY_PROPERTY(PROP_ANIMATION_SETTINGS, QString, setAnimationSettings); READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, updateShapeType); READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles); READ_ENTITY_PROPERTY(PROP_LIFESPAN, float, setLifespan); READ_ENTITY_PROPERTY(PROP_EMIT_RATE, float, setEmitRate); READ_ENTITY_PROPERTY(PROP_EMIT_DIRECTION, glm::vec3, setEmitDirection); READ_ENTITY_PROPERTY(PROP_EMIT_STRENGTH, float, setEmitStrength); READ_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, float, setLocalGravity); READ_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, float, setParticleRadius); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); return bytesRead; }
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { bool wantDebug = false; if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU) { // NOTE: This shouldn't happen. The only versions of the bit stream that didn't support split mtu buffers should // be handled by the model subclass and shouldn't call this routine. qDebug() << "EntityItem::readEntityDataFromBuffer()... " "ERROR CASE...args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_SPLIT_MTU"; return 0; } // Header bytes // object ID [16 bytes] // ByteCountCoded(type code) [~1 byte] // last edited [8 bytes] // ByteCountCoded(last_edited to last_updated delta) [~1-8 bytes] // PropertyFlags<>( everything ) [1-2 bytes] // ~27-35 bytes... const int MINIMUM_HEADER_BYTES = 27; int bytesRead = 0; if (bytesLeftToRead >= MINIMUM_HEADER_BYTES) { int originalLength = bytesLeftToRead; QByteArray originalDataBuffer((const char*)data, originalLength); int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; const unsigned char* dataAt = data; // id QByteArray encodedID = originalDataBuffer.mid(bytesRead, NUM_BYTES_RFC4122_UUID); // maximum possible size _id = QUuid::fromRfc4122(encodedID); _creatorTokenID = UNKNOWN_ENTITY_TOKEN; // if we know the id, then we don't care about the creator token _newlyCreated = false; dataAt += encodedID.size(); bytesRead += encodedID.size(); // type QByteArray encodedType = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded<quint32> typeCoder = encodedType; encodedType = typeCoder; // determine true length dataAt += encodedType.size(); bytesRead += encodedType.size(); quint32 type = typeCoder; _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??? if (wantDebug) { quint64 lastEdited = getLastEdited(); float editedAgo = getEditedAgo(); QString agoAsString = formatSecondsElapsed(editedAgo); QString ageAsString = formatSecondsElapsed(getAge()); qDebug() << "Loading entity " << getEntityItemID() << " from buffer..."; qDebug() << " _created =" << _created; qDebug() << " age=" << getAge() << "seconds - " << ageAsString; qDebug() << " lastEdited =" << lastEdited; qDebug() << " ago=" << editedAgo << "seconds - " << agoAsString; } quint64 now = usecTimestampNow(); quint64 lastEditedFromBuffer = 0; quint64 lastEditedFromBufferAdjusted = 0; // TODO: we could make this encoded as a delta from _created // _lastEdited memcpy(&lastEditedFromBuffer, dataAt, sizeof(lastEditedFromBuffer)); dataAt += sizeof(lastEditedFromBuffer); bytesRead += sizeof(lastEditedFromBuffer); lastEditedFromBufferAdjusted = lastEditedFromBuffer - clockSkew; bool fromSameServerEdit = (lastEditedFromBuffer == _lastEditedFromRemoteInRemoteTime); if (wantDebug) { qDebug() << "data from server **************** "; qDebug() << " entityItemID=" << getEntityItemID(); qDebug() << " now=" << now; qDebug() << " getLastEdited()=" << getLastEdited(); qDebug() << " lastEditedFromBuffer=" << lastEditedFromBuffer << " (BEFORE clockskew adjust)"; qDebug() << " clockSkew=" << clockSkew; qDebug() << " lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted << " (AFTER clockskew adjust)"; qDebug() << " _lastEditedFromRemote=" << _lastEditedFromRemote << " (our local time the last server edit we accepted)"; qDebug() << " _lastEditedFromRemoteInRemoteTime=" << _lastEditedFromRemoteInRemoteTime << " (remote time the last server edit we accepted)"; qDebug() << " fromSameServerEdit=" << fromSameServerEdit; } bool ignoreServerPacket = false; // assume we're use this server packet // If this packet is from the same server edit as the last packet we accepted from the server // we probably want to use it. if (fromSameServerEdit) { // If this is from the same sever packet, then check against any local changes since we got // the most recent packet from this server time if (_lastEdited > _lastEditedFromRemote) { ignoreServerPacket = true; } } else { // If this isn't from the same sever packet, then honor our skew adjusted times... // If we've changed our local tree more recently than the new data from this packet // then we will not be changing our values, instead we just read and skip the data if (_lastEdited > lastEditedFromBufferAdjusted) { ignoreServerPacket = true; } } if (ignoreServerPacket) { overwriteLocalData = false; if (wantDebug) { qDebug() << "IGNORING old data from server!!! ****************"; } } else { if (wantDebug) { qDebug() << "USING NEW data from server!!! ****************"; } _lastEdited = lastEditedFromBufferAdjusted; _lastEditedFromRemote = now; _lastEditedFromRemoteInRemoteTime = lastEditedFromBuffer; somethingChangedNotification(); // notify derived classes that something has changed } // last updated is stored as ByteCountCoded delta from lastEdited QByteArray encodedUpdateDelta = originalDataBuffer.mid(bytesRead); // maximum possible size ByteCountCoded<quint64> updateDeltaCoder = encodedUpdateDelta; quint64 updateDelta = updateDeltaCoder; if (overwriteLocalData) { _lastUpdated = lastEditedFromBufferAdjusted + updateDelta; // don't adjust for clock skew since we already did that for _lastEdited if (wantDebug) { qDebug() << "_lastUpdated=" << _lastUpdated; qDebug() << "_lastEdited=" << _lastEdited; qDebug() << "lastEditedFromBufferAdjusted=" << lastEditedFromBufferAdjusted; } } encodedUpdateDelta = updateDeltaCoder; // determine true length dataAt += encodedUpdateDelta.size(); bytesRead += encodedUpdateDelta.size(); // Property Flags QByteArray encodedPropertyFlags = originalDataBuffer.mid(bytesRead); // maximum possible size EntityPropertyFlags propertyFlags = encodedPropertyFlags; dataAt += propertyFlags.getEncodedLength(); bytesRead += propertyFlags.getEncodedLength(); READ_ENTITY_PROPERTY(PROP_POSITION, glm::vec3, _position); // Old bitstreams had PROP_RADIUS, new bitstreams have PROP_DIMENSIONS if (args.bitstreamVersion < VERSION_ENTITIES_SUPPORT_DIMENSIONS) { if (propertyFlags.getHasProperty(PROP_RADIUS)) { float fromBuffer; memcpy(&fromBuffer, dataAt, sizeof(fromBuffer)); dataAt += sizeof(fromBuffer); bytesRead += sizeof(fromBuffer); if (overwriteLocalData) { setRadius(fromBuffer); } if (wantDebug) { qDebug() << " readEntityDataFromBuffer() OLD FORMAT... found PROP_RADIUS"; } } } else { READ_ENTITY_PROPERTY(PROP_DIMENSIONS, glm::vec3, _dimensions); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() NEW FORMAT... look for PROP_DIMENSIONS"; } } if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _dimensions:" << getDimensionsInMeters() << " in meters"; } READ_ENTITY_PROPERTY_QUAT(PROP_ROTATION, _rotation); READ_ENTITY_PROPERTY(PROP_MASS, float, _mass); READ_ENTITY_PROPERTY(PROP_VELOCITY, glm::vec3, _velocity); READ_ENTITY_PROPERTY(PROP_GRAVITY, glm::vec3, _gravity); READ_ENTITY_PROPERTY(PROP_DAMPING, float, _damping); READ_ENTITY_PROPERTY(PROP_LIFETIME, float, _lifetime); READ_ENTITY_PROPERTY_STRING(PROP_SCRIPT,setScript); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, _registrationPoint); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, _angularVelocity); READ_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, float, _angularDamping); READ_ENTITY_PROPERTY(PROP_VISIBLE, bool, _visible); READ_ENTITY_PROPERTY(PROP_IGNORE_FOR_COLLISIONS, bool, _ignoreForCollisions); READ_ENTITY_PROPERTY(PROP_COLLISIONS_WILL_MOVE, bool, _collisionsWillMove); if (wantDebug) { qDebug() << " readEntityDataFromBuffer() _registrationPoint:" << _registrationPoint; qDebug() << " readEntityDataFromBuffer() _visible:" << _visible; qDebug() << " readEntityDataFromBuffer() _ignoreForCollisions:" << _ignoreForCollisions; qDebug() << " readEntityDataFromBuffer() _collisionsWillMove:" << _collisionsWillMove; } bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData); recalculateCollisionShape(); } return bytesRead; }