From 9ea6968e351081aeb13884f50fe7d793da3824f4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 9 Jan 2019 17:26:23 -0800 Subject: [PATCH] fix bug where attached AvatarEntities do not update in timely fashion --- .../src/avatars/AvatarMixerClientData.cpp | 2 +- interface/src/avatar/MyAvatar.cpp | 10 +- interface/src/avatar/MyAvatar.h | 4 +- interface/src/avatar/OtherAvatar.cpp | 301 +++++++++++++++++- interface/src/avatar/OtherAvatar.h | 9 + .../src/avatars-renderer/Avatar.cpp | 240 -------------- .../src/avatars-renderer/Avatar.h | 3 +- libraries/avatars/src/AvatarData.cpp | 12 +- libraries/avatars/src/AvatarData.h | 6 +- 9 files changed, 326 insertions(+), 261 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 7f1b40142e..bf90a4e727 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -157,7 +157,7 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) { _avatar->processDeletedTraitInstance(traitType, instanceID); // Mixer doesn't need deleted IDs. - _avatar->getAndClearRecentlyDetachedIDs(); + _avatar->getAndClearRecentlyRemovedIDs(); // to track a deleted instance but keep version information // the avatar mixer uses the negative value of the sent version diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 03c69076aa..91ea8f0291 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -674,7 +674,7 @@ void MyAvatar::update(float deltaTime) { _clientTraitsHandler->sendChangedTraitsToMixer(); - simulate(deltaTime); + simulate(deltaTime, true); currentEnergy += energyChargeRate; currentEnergy -= getAccelerationEnergy(); @@ -746,7 +746,7 @@ void MyAvatar::updateChildCauterization(SpatiallyNestablePointer object, bool ca } } -void MyAvatar::simulate(float deltaTime) { +void MyAvatar::simulate(float deltaTime, bool inView) { PerformanceTimer perfTimer("simulate"); animateScaleChanges(deltaTime); @@ -890,7 +890,7 @@ void MyAvatar::simulate(float deltaTime) { _characterController.setCollisionlessAllowed(collisionlessAllowed); } - updateAvatarEntities(); + handleChangedAvatarEntityData(); updateFadingStatus(); } @@ -1480,7 +1480,7 @@ void MyAvatar::sanitizeAvatarEntityProperties(EntityItemProperties& properties) properties.markAllChanged(); } -void MyAvatar::updateAvatarEntities() { +void MyAvatar::handleChangedAvatarEntityData() { // NOTE: this is a per-frame update if (getID().isNull() || getID() == AVATAR_SELF_ID || @@ -1499,7 +1499,7 @@ void MyAvatar::updateAvatarEntities() { return; } - // We collect changes to AvatarEntities and then handle them all in one spot per frame: updateAvatarEntities(). + // We collect changes to AvatarEntities and then handle them all in one spot per frame: handleChangedAvatarEntityData(). // Basically this is a "transaction pattern" with an extra complication: these changes can come from two // "directions" and the "authoritative source" of each direction is different, so we maintain two distinct sets // of transaction lists: diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 38cef264a7..67a449b274 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1186,7 +1186,6 @@ public: virtual void setAttachmentsVariant(const QVariantList& variant) override; glm::vec3 getNextPosition() { return _goToPending ? _goToPosition : getWorldPosition(); } - void updateAvatarEntities() override; void prepareAvatarEntityDataForReload(); /**jsdoc @@ -1619,6 +1618,7 @@ private slots: void updateCollisionCapsuleCache(); protected: + void handleChangedAvatarEntityData(); virtual void beParentOfChild(SpatiallyNestablePointer newChild) const override; virtual void forgetChild(SpatiallyNestablePointer newChild) const override; virtual void recalculateChildCauterization() const override; @@ -1630,7 +1630,7 @@ private: virtual QByteArray toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) override; - void simulate(float deltaTime); + void simulate(float deltaTime, bool inView) override; void updateFromTrackers(float deltaTime); void saveAvatarUrl(); virtual void render(RenderArgs* renderArgs) override; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index a71d2478ad..569f92ac4d 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -7,10 +7,18 @@ // #include "OtherAvatar.h" -#include "Application.h" +#include +#include + +#include + +#include "Application.h" #include "AvatarMotionState.h" +const float DISPLAYNAME_FADE_TIME = 0.5f; +const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME); + static glm::u8vec3 getLoadingOrbColor(Avatar::LoadingStatus loadingStatus) { const glm::u8vec3 NO_MODEL_COLOR(0xe3, 0xe3, 0xe3); @@ -142,4 +150,293 @@ void OtherAvatar::updateCollisionGroup(bool myAvatarCollide) { _motionState->addDirtyFlags(Simulation::DIRTY_COLLISION_GROUP); } } -} \ No newline at end of file +} + +void OtherAvatar::simulate(float deltaTime, bool inView) { + PROFILE_RANGE(simulation, "simulate"); + + _globalPosition = _transit.isActive() ? _transit.getCurrentPosition() : _serverPosition; + if (!hasParent()) { + setLocalPosition(_globalPosition); + } + + _simulationRate.increment(); + if (inView) { + _simulationInViewRate.increment(); + } + + PerformanceTimer perfTimer("simulate"); + { + PROFILE_RANGE(simulation, "updateJoints"); + if (inView) { + Head* head = getHead(); + if (_hasNewJointData || _transit.isActive()) { + _skeletonModel->getRig().copyJointsFromJointData(_jointData); + glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); + _skeletonModel->getRig().computeExternalPoses(rootTransform); + _jointDataSimulationRate.increment(); + + _skeletonModel->simulate(deltaTime, true); + + locationChanged(); // joints changed, so if there are any children, update them. + _hasNewJointData = false; + + glm::vec3 headPosition = getWorldPosition(); + if (!_skeletonModel->getHeadPosition(headPosition)) { + headPosition = getWorldPosition(); + } + head->setPosition(headPosition); + } + head->setScale(getModelScale()); + head->simulate(deltaTime); + relayJointDataToChildren(); + } else { + // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. + _skeletonModel->simulate(deltaTime, false); + } + _skeletonModelSimulationRate.increment(); + } + + // update animation for display name fade in/out + if ( _displayNameTargetAlpha != _displayNameAlpha) { + // the alpha function is + // Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt) + // Fade in => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt) + // factor^(dt) = coef + float coef = pow(DISPLAYNAME_FADE_FACTOR, deltaTime); + if (_displayNameTargetAlpha < _displayNameAlpha) { + // Fading out + _displayNameAlpha *= coef; + } else { + // Fading in + _displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef; + } + _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha; + } + + { + PROFILE_RANGE(simulation, "misc"); + measureMotionDerivatives(deltaTime); + simulateAttachments(deltaTime); + updatePalms(); + } + { + PROFILE_RANGE(simulation, "entities"); + handleChangedAvatarEntityData(); + updateAttachedAvatarEntities(); + } + + { + PROFILE_RANGE(simulation, "grabs"); + updateGrabs(); + } + + updateFadingStatus(); +} + +void OtherAvatar::handleChangedAvatarEntityData() { + PerformanceTimer perfTimer("attachments"); + + // AVATAR ENTITY UPDATE FLOW + // - if queueEditEntityMessage() sees "AvatarEntity" HostType it calls _myAvatar->storeAvatarEntityDataPayload() + // - storeAvatarEntityDataPayload() saves the payload and flags the trait instance for the entity as updated, + // - ClientTraitsHandler::sendChangedTraitsToMixea() sends the entity bytes to the mixer which relays them to other interfaces + // - AvatarHashMap::processBulkAvatarTraits() on other interfaces calls avatar->processTraitInstance() + // - AvatarData::processTraitInstance() calls storeAvatarEntityDataPayload(), which sets _avatarEntityDataChanged = true + // - (My)Avatar::simulate() calls handleChangedAvatarEntityData() every frame which checks _avatarEntityDataChanged + // and here we are... + + // AVATAR ENTITY DELETE FLOW + // - EntityScriptingInterface::deleteEntity() calls _myAvatar->clearAvatarEntity() for deleted avatar entities + // - clearAvatarEntity() removes the avatar entity and flags the trait instance for the entity as deleted + // - ClientTraitsHandler::sendChangedTraitsToMixer() sends a deletion to the mixer which relays to other interfaces + // - AvatarHashMap::processBulkAvatarTraits() on other interfaces calls avatar->processDeletedTraitInstace() + // - AvatarData::processDeletedTraitInstance() calls clearAvatarEntity() + // - AvatarData::clearAvatarEntity() sets _avatarEntityDataChanged = true and adds the ID to the detached list + // - (My)Avatar::simulate() calls handleChangedAvatarEntityData() every frame which checks _avatarEntityDataChanged + // and here we are... + + if (!_avatarEntityDataChanged) { + return; + } + + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (!entityTree) { + return; + } + + PackedAvatarEntityMap packedAvatarEntityData; + _avatarEntitiesLock.withReadLock([&] { + packedAvatarEntityData = _packedAvatarEntityData; + }); + entityTree->withWriteLock([&] { + AvatarEntityMap::const_iterator dataItr = packedAvatarEntityData.begin(); + while (dataItr != packedAvatarEntityData.end()) { + // compute hash of data. TODO? cache this? + QByteArray data = dataItr.value(); + uint32_t newHash = qHash(data); + + // check to see if we recognize this hash and whether it was already successfully processed + QUuid entityID = dataItr.key(); + MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); + if (stateItr != _avatarEntityDataHashes.end()) { + if (stateItr.value().success) { + if (newHash == stateItr.value().hash) { + // data hasn't changed --> nothing to do + ++dataItr; + continue; + } + } else { + // NOTE: if the data was unsuccessful in producing an entity in the past + // we will try again just in case something changed (unlikely). + // Unfortunately constantly trying to build the entity for this data costs + // CPU cycles that we'd rather not spend. + // TODO? put a maximum number of tries on this? + } + } else { + // sanity check data + QUuid id; + EntityTypes::EntityType type; + EntityTypes::extractEntityTypeAndID((unsigned char*)(data.data()), data.size(), type, id); + if (id != entityID || !EntityTypes::typeIsValid(type)) { + // skip for corrupt + ++dataItr; + continue; + } + // remember this hash for the future + stateItr = _avatarEntityDataHashes.insert(entityID, AvatarEntityDataHash(newHash)); + } + ++dataItr; + + EntityItemProperties properties; + int32_t bytesLeftToRead = data.size(); + unsigned char* dataAt = (unsigned char*)(data.data()); + if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) { + // properties are corrupt + continue; + } + + properties.setEntityHostType(entity::HostType::AVATAR); + properties.setOwningAvatarID(getID()); + + // there's no entity-server to tell us we're the simulation owner, so always set the + // simulationOwner to the owningAvatarID and a high priority. + properties.setSimulationOwner(getID(), AVATAR_ENTITY_SIMULATION_PRIORITY); + + if (properties.getParentID() == AVATAR_SELF_ID) { + properties.setParentID(getID()); + } + + // NOTE: if this avatar entity is not attached to us, strip its entity script completely... + auto attachedScript = properties.getScript(); + if (!isMyAvatar() && !attachedScript.isEmpty()) { + QString noScript; + properties.setScript(noScript); + } + + auto specifiedHref = properties.getHref(); + if (!isMyAvatar() && !specifiedHref.isEmpty()) { + qCDebug(avatars) << "removing entity href from avatar attached entity:" << entityID << "old href:" << specifiedHref; + QString noHref; + properties.setHref(noHref); + } + + // When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed + // they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear. + // The thinking here is the local position was noticed as changing, but not the parentID (since it is now + // back to the default), and the entity flew off somewhere. Marking all changed definitely fixes this, + // and seems safe (per Seth). + properties.markAllChanged(); + + // try to build the entity + EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); + bool success = true; + if (entity) { + QUuid oldParentID = entity->getParentID(); + if (entityTree->updateEntity(entityID, properties)) { + entity->updateLastEditedFromRemote(); + } else { + success = false; + } + if (oldParentID != entity->getParentID()) { + if (entity->getParentID() == getID()) { + onAddAttachedAvatarEntity(entityID); + } else if (oldParentID == getID()) { + onRemoveAttachedAvatarEntity(entityID); + } + } + } else { + entity = entityTree->addEntity(entityID, properties); + if (!entity) { + success = false; + } else if (entity->getParentID() == getID()) { + onAddAttachedAvatarEntity(entityID); + } + } + stateItr.value().success = success; + } + + AvatarEntityIDs recentlyRemovedAvatarEntities = getAndClearRecentlyRemovedIDs(); + if (!recentlyRemovedAvatarEntities.empty()) { + // only lock this thread when absolutely necessary + AvatarEntityMap packedAvatarEntityData; + _avatarEntitiesLock.withReadLock([&] { + packedAvatarEntityData = _packedAvatarEntityData; + }); + foreach (auto entityID, recentlyRemovedAvatarEntities) { + if (!packedAvatarEntityData.contains(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } + } + + // TODO: move this outside of tree lock + // remove stale data hashes + foreach (auto entityID, recentlyRemovedAvatarEntities) { + MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); + if (stateItr != _avatarEntityDataHashes.end()) { + _avatarEntityDataHashes.erase(stateItr); + } + onRemoveAttachedAvatarEntity(entityID); + } + } + if (packedAvatarEntityData.size() != _avatarEntityForRecording.size()) { + createRecordingIDs(); + } + }); + + setAvatarEntityDataChanged(false); +} + +void OtherAvatar::onAddAttachedAvatarEntity(const QUuid& id) { + for (uint32_t i = 0; i < _attachedAvatarEntities.size(); ++i) { + if (_attachedAvatarEntities[i] == id) { + return; + } + } + _attachedAvatarEntities.push_back(id); +} + +void OtherAvatar::onRemoveAttachedAvatarEntity(const QUuid& id) { + for (uint32_t i = 0; i < _attachedAvatarEntities.size(); ++i) { + if (_attachedAvatarEntities[i] == id) { + if (i != _attachedAvatarEntities.size() - 1) { + _attachedAvatarEntities[i] = _attachedAvatarEntities.back(); + } + _attachedAvatarEntities.pop_back(); + break; + } + } +} + +void OtherAvatar::updateAttachedAvatarEntities() { + if (!_attachedAvatarEntities.empty()) { + auto treeRenderer = DependencyManager::get(); + if (!treeRenderer) { + return; + } + for (const QUuid& id : _attachedAvatarEntities) { + treeRenderer->onEntityChanged(id); + } + } +} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 48402fe55c..a1dc5724a9 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -10,6 +10,7 @@ #define hifi_OtherAvatar_h #include +#include #include #include @@ -47,9 +48,17 @@ public: void updateCollisionGroup(bool myAvatarCollide); + void simulate(float deltaTime, bool inView) override; + friend AvatarManager; protected: + void handleChangedAvatarEntityData(); + void updateAttachedAvatarEntities(); + void onAddAttachedAvatarEntity(const QUuid& id); + void onRemoveAttachedAvatarEntity(const QUuid& id); + + std::vector _attachedAvatarEntities; std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; AvatarMotionState* _motionState { nullptr }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index feb95f1dd0..a6185d7e79 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -308,164 +308,6 @@ void Avatar::setAvatarEntityDataChanged(bool value) { _avatarEntityDataHashes.clear(); } -void Avatar::updateAvatarEntities() { - PerformanceTimer perfTimer("attachments"); - - // AVATAR ENTITY UPDATE FLOW - // - if queueEditEntityMessage sees "AvatarEntity" HostType it calls _myAvatar->storeAvatarEntityDataPayload - // - storeAvatarEntityDataPayload saves the payload and flags the trait instance for the entity as updated, - // - ClientTraitsHandler::sendChangedTraitsToMixer sends the entity bytes to the mixer which relays them to other interfaces - // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processTraitInstace - // - AvatarData::processTraitInstance calls storeAvatarEntityDataPayload, which sets _avatarEntityDataChanged = true - // - (My)Avatar::simulate calls updateAvatarEntities every frame which checks _avatarEntityDataChanged - // and here we are... - - // AVATAR ENTITY DELETE FLOW - // - EntityScriptingInterface::deleteEntity calls _myAvatar->clearAvatarEntity() for deleted avatar entities - // - clearAvatarEntity removes the avatar entity and flags the trait instance for the entity as deleted - // - ClientTraitsHandler::sendChangedTraitsToMixer sends a deletion to the mixer which relays to other interfaces - // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processDeletedTraitInstace - // - AvatarData::processDeletedTraitInstance calls clearAvatarEntity - // - AvatarData::clearAvatarEntity sets _avatarEntityDataChanged = true and adds the ID to the detached list - // - (My)Avatar::simulate calls updateAvatarEntities every frame which checks _avatarEntityDataChanged - // and here we are... - - if (!_avatarEntityDataChanged) { - return; - } - - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (!entityTree) { - return; - } - - PackedAvatarEntityMap packedAvatarEntityData; - _avatarEntitiesLock.withReadLock([&] { - packedAvatarEntityData = _packedAvatarEntityData; - }); - entityTree->withWriteLock([&] { - AvatarEntityMap::const_iterator dataItr = packedAvatarEntityData.begin(); - while (dataItr != packedAvatarEntityData.end()) { - // compute hash of data. TODO? cache this? - QByteArray data = dataItr.value(); - uint32_t newHash = qHash(data); - - // check to see if we recognize this hash and whether it was already successfully processed - QUuid entityID = dataItr.key(); - MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); - if (stateItr != _avatarEntityDataHashes.end()) { - if (stateItr.value().success) { - if (newHash == stateItr.value().hash) { - // data hasn't changed --> nothing to do - ++dataItr; - continue; - } - } else { - // NOTE: if the data was unsuccessful in producing an entity in the past - // we will try again just in case something changed (unlikely). - // Unfortunately constantly trying to build the entity for this data costs - // CPU cycles that we'd rather not spend. - // TODO? put a maximum number of tries on this? - } - } else { - // sanity check data - QUuid id; - EntityTypes::EntityType type; - EntityTypes::extractEntityTypeAndID((unsigned char*)(data.data()), data.size(), type, id); - if (id != entityID || !EntityTypes::typeIsValid(type)) { - // skip for corrupt - ++dataItr; - continue; - } - // remember this hash for the future - stateItr = _avatarEntityDataHashes.insert(entityID, AvatarEntityDataHash(newHash)); - } - ++dataItr; - - EntityItemProperties properties; - int32_t bytesLeftToRead = data.size(); - unsigned char* dataAt = (unsigned char*)(data.data()); - if (!properties.constructFromBuffer(dataAt, bytesLeftToRead)) { - // properties are corrupt - continue; - } - - properties.setEntityHostType(entity::HostType::AVATAR); - properties.setOwningAvatarID(getID()); - - if (properties.getParentID() == AVATAR_SELF_ID) { - properties.setParentID(getID()); - } - - // NOTE: if this avatar entity is not attached to us, strip its entity script completely... - auto attachedScript = properties.getScript(); - if (!isMyAvatar() && !attachedScript.isEmpty()) { - QString noScript; - properties.setScript(noScript); - } - - auto specifiedHref = properties.getHref(); - if (!isMyAvatar() && !specifiedHref.isEmpty()) { - qCDebug(avatars_renderer) << "removing entity href from avatar attached entity:" << entityID << "old href:" << specifiedHref; - QString noHref; - properties.setHref(noHref); - } - - // When grabbing avatar entities, they are parented to the joint moving them, then when un-grabbed - // they go back to the default parent (null uuid). When un-gripped, others saw the entity disappear. - // The thinking here is the local position was noticed as changing, but not the parentID (since it is now - // back to the default), and the entity flew off somewhere. Marking all changed definitely fixes this, - // and seems safe (per Seth). - properties.markAllChanged(); - - // try to build the entity - EntityItemPointer entity = entityTree->findEntityByEntityItemID(EntityItemID(entityID)); - bool success = true; - if (entity) { - if (entityTree->updateEntity(entityID, properties)) { - entity->updateLastEditedFromRemote(); - } else { - success = false; - } - } else { - entity = entityTree->addEntity(entityID, properties); - if (!entity) { - success = false; - } - } - stateItr.value().success = success; - } - - AvatarEntityIDs recentlyDetachedAvatarEntities = getAndClearRecentlyDetachedIDs(); - if (!recentlyDetachedAvatarEntities.empty()) { - // only lock this thread when absolutely necessary - AvatarEntityMap packedAvatarEntityData; - _avatarEntitiesLock.withReadLock([&] { - packedAvatarEntityData = _packedAvatarEntityData; - }); - foreach (auto entityID, recentlyDetachedAvatarEntities) { - if (!packedAvatarEntityData.contains(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } - - // remove stale data hashes - foreach (auto entityID, recentlyDetachedAvatarEntities) { - MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); - if (stateItr != _avatarEntityDataHashes.end()) { - _avatarEntityDataHashes.erase(stateItr); - } - } - } - if (packedAvatarEntityData.size() != _avatarEntityForRecording.size()) { - createRecordingIDs(); - } - }); - - setAvatarEntityDataChanged(false); -} - void Avatar::removeAvatarEntitiesFromTree() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; @@ -650,87 +492,6 @@ void Avatar::relayJointDataToChildren() { _reconstructSoftEntitiesJointMap = false; } -void Avatar::simulate(float deltaTime, bool inView) { - PROFILE_RANGE(simulation, "simulate"); - - _globalPosition = _transit.isActive() ? _transit.getCurrentPosition() : _serverPosition; - if (!hasParent()) { - setLocalPosition(_globalPosition); - } - - _simulationRate.increment(); - if (inView) { - _simulationInViewRate.increment(); - } - - PerformanceTimer perfTimer("simulate"); - { - PROFILE_RANGE(simulation, "updateJoints"); - if (inView) { - Head* head = getHead(); - if (_hasNewJointData || _transit.isActive()) { - _skeletonModel->getRig().copyJointsFromJointData(_jointData); - glm::mat4 rootTransform = glm::scale(_skeletonModel->getScale()) * glm::translate(_skeletonModel->getOffset()); - _skeletonModel->getRig().computeExternalPoses(rootTransform); - _jointDataSimulationRate.increment(); - - _skeletonModel->simulate(deltaTime, true); - - locationChanged(); // joints changed, so if there are any children, update them. - _hasNewJointData = false; - - glm::vec3 headPosition = getWorldPosition(); - if (!_skeletonModel->getHeadPosition(headPosition)) { - headPosition = getWorldPosition(); - } - head->setPosition(headPosition); - } - head->setScale(getModelScale()); - head->simulate(deltaTime); - relayJointDataToChildren(); - } else { - // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. - _skeletonModel->simulate(deltaTime, false); - } - _skeletonModelSimulationRate.increment(); - } - - // update animation for display name fade in/out - if ( _displayNameTargetAlpha != _displayNameAlpha) { - // the alpha function is - // Fade out => alpha(t) = factor ^ t => alpha(t+dt) = alpha(t) * factor^(dt) - // Fade in => alpha(t) = 1 - factor^t => alpha(t+dt) = 1-(1-alpha(t))*coef^(dt) - // factor^(dt) = coef - float coef = pow(DISPLAYNAME_FADE_FACTOR, deltaTime); - if (_displayNameTargetAlpha < _displayNameAlpha) { - // Fading out - _displayNameAlpha *= coef; - } else { - // Fading in - _displayNameAlpha = 1 - (1 - _displayNameAlpha) * coef; - } - _displayNameAlpha = abs(_displayNameAlpha - _displayNameTargetAlpha) < 0.01f ? _displayNameTargetAlpha : _displayNameAlpha; - } - - { - PROFILE_RANGE(simulation, "misc"); - measureMotionDerivatives(deltaTime); - simulateAttachments(deltaTime); - updatePalms(); - } - { - PROFILE_RANGE(simulation, "entities"); - updateAvatarEntities(); - } - - { - PROFILE_RANGE(simulation, "grabs"); - updateGrabs(); - } - - updateFadingStatus(); -} - float Avatar::getSimulationRate(const QString& rateName) const { if (rateName == "") { return _simulationRate.rate(); @@ -1045,7 +806,6 @@ void Avatar::render(RenderArgs* renderArgs) { } } - void Avatar::setEnableMeshVisible(bool isEnabled) { if (_isMeshVisible != isEnabled) { _isMeshVisible = isEnabled; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 97218ac3c1..4ff3e9cc13 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -139,9 +139,8 @@ public: typedef render::Payload Payload; void init(); - virtual void updateAvatarEntities(); void removeAvatarEntitiesFromTree(); - void simulate(float deltaTime, bool inView); + virtual void simulate(float deltaTime, bool inView) = 0; virtual void simulateAttachments(float deltaTime); virtual void render(RenderArgs* renderArgs); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5eda53329b..21e0a6aba2 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2777,7 +2777,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFr removedEntity = _packedAvatarEntityData.remove(entityID); }); - insertDetachedEntityID(entityID); + insertRemovedEntityID(entityID); if (removedEntity && _clientTraitsHandler) { // we have a client traits handler, so we need to mark this removed instance trait as deleted @@ -2798,18 +2798,18 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { // each QByteArray represents an EntityItemProperties object from JavaScript } -void AvatarData::insertDetachedEntityID(const QUuid entityID) { +void AvatarData::insertRemovedEntityID(const QUuid entityID) { _avatarEntitiesLock.withWriteLock([&] { - _avatarEntityDetached.insert(entityID); + _avatarEntityRemoved.insert(entityID); }); _avatarEntityDataChanged = true; } -AvatarEntityIDs AvatarData::getAndClearRecentlyDetachedIDs() { +AvatarEntityIDs AvatarData::getAndClearRecentlyRemovedIDs() { AvatarEntityIDs result; _avatarEntitiesLock.withWriteLock([&] { - result = _avatarEntityDetached; - _avatarEntityDetached.clear(); + result = _avatarEntityRemoved; + _avatarEntityRemoved.clear(); }); return result; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0fea5a541e..ec5ea4a5d0 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1150,8 +1150,7 @@ public: Q_INVOKABLE virtual void setAvatarEntityData(const AvatarEntityMap& avatarEntityData); virtual void setAvatarEntityDataChanged(bool value) { _avatarEntityDataChanged = value; } - void insertDetachedEntityID(const QUuid entityID); - AvatarEntityIDs getAndClearRecentlyDetachedIDs(); + AvatarEntityIDs getAndClearRecentlyRemovedIDs(); /**jsdoc * @function MyAvatar.getSensorToWorldMatrix @@ -1338,6 +1337,7 @@ public slots: void resetLastSent() { _lastToByteArray = 0; } protected: + void insertRemovedEntityID(const QUuid entityID); void lazyInitHeadData() const; float getDistanceBasedMinRotationDOT(glm::vec3 viewerPosition) const; @@ -1466,7 +1466,7 @@ protected: AABox _defaultBubbleBox; mutable ReadWriteLockable _avatarEntitiesLock; - AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar + AvatarEntityIDs _avatarEntityRemoved; // recently removed AvatarEntity ids AvatarEntityIDs _avatarEntityForRecording; // create new entities id for avatar recording PackedAvatarEntityMap _packedAvatarEntityData; bool _avatarEntityDataChanged { false };