HifiExperimentsavatar fading properties5a22d061 year agoHistory
1098 lines · 48.7 KB · C++
RawDownload
////  AvatarManager.cpp//  interface/src/avatar////  Created by Stephen Birarda on 1/23/2014.//  Copyright 2014 High Fidelity, Inc.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "AvatarManager.h" #include <string> #include <ScriptEngine.h>#include <ScriptValue.h> #include "AvatarLogging.h" #if defined(__GNUC__) && !defined(__clang__)#pragma GCC diagnostic push#pragma GCC diagnostic ignored "-Wdouble-promotion"#endif #include <glm/gtx/string_cast.hpp> #if defined(__GNUC__) && !defined(__clang__)#pragma GCC diagnostic pop#endif #include <shared/QtHelpers.h>#include <AvatarData.h>#include <PerfStat.h>#include <PrioritySortUtil.h>#include <RegisteredMetaTypes.h>#include <Rig.h>#include <SettingHandle.h>#include <UsersScriptingInterface.h>#include <UUID.h>#include <shared/ConicalViewFrustum.h>#include <ui/AvatarInputs.h> #include "Application.h"#include "InterfaceLogging.h"#include "Menu.h"#include "MyAvatar.h"#include "DebugDraw.h"#include "SceneScriptingInterface.h"#include "ui/AvatarCertifyBanner.h" // 50 times per second - target is 45hz, but this helps account for any small deviations// in the update loop - this also results in ~30hz when in desktop mode which is essentially// what we want // We add _myAvatar into the hash with all the other AvatarData, and we use the default NULL QUid as the key.const QUuid MY_AVATAR_KEY;  // NULL key AvatarManager::AvatarManager(QObject* parent) :    _myAvatar(new MyAvatar(qApp->thread()), [](MyAvatar* ptr) { ptr->deleteLater(); }){    // register a meta type for the weak pointer we'll use for the owning avatar mixer for each avatar    qRegisterMetaType<QWeakPointer<Node> >("NodeWeakPointer");     auto nodeList = DependencyManager::get<NodeList>();     // when we hear that the user has ignored an avatar by session UUID    // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer    connect(nodeList.data(), &NodeList::ignoredNode, this, [this](const QUuid& nodeID, bool enabled) {        if (enabled) {            removeAvatar(nodeID, KillAvatarReason::AvatarIgnored);        } else {            auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(nodeID));            if (avatar) {                avatar->createOrb();            }        }    });     _transitConfig._totalFrames = AVATAR_TRANSIT_FRAME_COUNT;    _transitConfig._minTriggerDistance = AVATAR_TRANSIT_MIN_TRIGGER_DISTANCE;    _transitConfig._maxTriggerDistance = AVATAR_TRANSIT_MAX_TRIGGER_DISTANCE;    _transitConfig._framesPerMeter = AVATAR_TRANSIT_FRAMES_PER_METER;    _transitConfig._isDistanceBased = AVATAR_TRANSIT_DISTANCE_BASED;    _transitConfig._abortDistance = AVATAR_TRANSIT_ABORT_DISTANCE;} AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer<Node>& mixerWeakPointer) {    AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer);    const auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);    if (otherAvatar && _space) {        std::unique_lock<std::mutex> lock(_spaceLock);        auto spaceIndex = _space->allocateID();        otherAvatar->setSpaceIndex(spaceIndex);        workload::Sphere sphere(otherAvatar->getWorldPosition(), otherAvatar->getBoundingRadius());        workload::Transaction transaction;        SpatiallyNestablePointer nestable = std::static_pointer_cast<SpatiallyNestable>(otherAvatar);        transaction.reset(spaceIndex, sphere, workload::Owner(nestable));        _space->enqueueTransaction(transaction);    }    return avatar;} AvatarManager::~AvatarManager() {    assert(_otherAvatarsToChangeInPhysics.empty());} void AvatarManager::init() {    _myAvatar->init();    {        QWriteLocker locker(&_hashLock);        _avatarHash.insert(MY_AVATAR_KEY, _myAvatar);    }     _shouldRender = DependencyManager::get<SceneScriptingInterface>()->shouldRenderAvatars();    connect(DependencyManager::get<SceneScriptingInterface>().data(), &SceneScriptingInterface::shouldRenderAvatarsChanged,            this, &AvatarManager::updateAvatarRenderStatus, Qt::QueuedConnection);     if (_shouldRender) {        const render::ScenePointer& scene = qApp->getMain3DScene();        render::Transaction transaction;        _myAvatar->addToScene(_myAvatar, scene, transaction);        scene->enqueueTransaction(transaction);    }     setEnableDebugDrawOtherSkeletons(Menu::getInstance()->isOptionChecked(MenuOption::AnimDebugDrawOtherSkeletons));} void AvatarManager::setSpace(workload::SpacePointer& space ) {    assert(!_space);    _space = space;} void AvatarManager::handleTransitAnimations(AvatarTransit::Status status) {    switch (status) {        case AvatarTransit::Status::STARTED:            _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("preTransitAnim");            break;        case AvatarTransit::Status::START_TRANSIT:            _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("transitAnim");            break;        case AvatarTransit::Status::END_TRANSIT:            _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("postTransitAnim");            break;        case AvatarTransit::Status::ENDED:            _myAvatar->getSkeletonModel()->getRig().triggerNetworkRole("idleAnim");            break;        case AvatarTransit::Status::PRE_TRANSIT:            break;        case AvatarTransit::Status::POST_TRANSIT:            break;        case AvatarTransit::Status::IDLE:            break;        case AvatarTransit::Status::TRANSITING:            break;        case AvatarTransit::Status::ABORT_TRANSIT:            break;    }} void AvatarManager::updateMyAvatar(float deltaTime) {    bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);    PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");     AvatarTransit::Status status = _myAvatar->updateTransit(deltaTime, _myAvatar->getNextPosition(), _myAvatar->getSensorToWorldScale(), _transitConfig);    handleTransitAnimations(status);     _myAvatar->update(deltaTime);    render::Transaction transaction;    _myAvatar->updateRenderItem(transaction);    qApp->getMain3DScene()->enqueueTransaction(transaction);     quint64 now = usecTimestampNow();    quint64 dt = now - _lastSendAvatarDataTime;     if (dt > MIN_TIME_BETWEEN_MY_AVATAR_DATA_SENDS && !_myAvatarDataPacketsPaused) {        // send head/hand data to the avatar mixer and voxel server        PerformanceTimer perfTimer("send");         _myAvatar->sendAvatarDataPacket();        _lastSendAvatarDataTime = now;        _myAvatarSendRate.increment();    }     static AvatarCertifyBanner theftBanner;    if (_myAvatar->isCertifyFailed()) {        theftBanner.show(_myAvatar->getSessionUUID());    } else {        theftBanner.clear();    }}  Q_LOGGING_CATEGORY(trace_simulation_avatar, "trace.simulation.avatar"); float AvatarManager::getAvatarDataRate(const QUuid& sessionID, const QString& rateName) const {    auto avatar = getAvatarBySessionID(sessionID);    return avatar ? avatar->getDataRate(rateName) : 0.0f;} void AvatarManager::setMyAvatarDataPacketsPaused(bool pause) {    if (_myAvatarDataPacketsPaused != pause) {        _myAvatarDataPacketsPaused = pause;         if (!_myAvatarDataPacketsPaused) {            _myAvatar->sendAvatarDataPacket(true);        }    }} float AvatarManager::getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName) const {    auto avatar = getAvatarBySessionID(sessionID);    return avatar ? avatar->getUpdateRate(rateName) : 0.0f;} float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName) const {    auto avatar = std::static_pointer_cast<Avatar>(getAvatarBySessionID(sessionID));    return avatar ? avatar->getSimulationRate(rateName) : 0.0f;} void AvatarManager::updateOtherAvatars(float deltaTime) {    {        // lock the hash for read to check the size        QReadLocker lock(&_hashLock);        if (_avatarHash.size() < 2) {            return;        }    }     PerformanceTimer perfTimer("otherAvatars");     class SortableAvatar: public PrioritySortUtil::Sortable {    public:        SortableAvatar() = delete;        SortableAvatar(const std::shared_ptr<Avatar>& avatar) : _avatar(avatar) {}        glm::vec3 getPosition() const override { return _avatar->getWorldPosition(); }        float getRadius() const override { return _avatar->getBoundingRadius(); }        uint64_t getTimestamp() const override { return _avatar->getLastRenderUpdateTime(); }        std::shared_ptr<Avatar> getAvatar() const { return _avatar; }    private:        std::shared_ptr<Avatar> _avatar;    };     auto avatarMap = getHashCopy();     const auto& views = qApp->getConicalViews();    // Prepare 2 queues for heros and for crowd avatars    using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue<SortableAvatar>;    // Keep two independent queues, one for heroes and one for the riff-raff.    enum PriorityVariants    {        kHero = 0,        kNonHero,        NumVariants    };    AvatarPriorityQueue avatarPriorityQueues[NumVariants] = {             { views,               AvatarData::_avatarSortCoefficientSize,               AvatarData::_avatarSortCoefficientCenter,               AvatarData::_avatarSortCoefficientAge },             { views,               AvatarData::_avatarSortCoefficientSize,               AvatarData::_avatarSortCoefficientCenter,               AvatarData::_avatarSortCoefficientAge } };    // Reserve space    //avatarPriorityQueues[kHero].reserve(10);  // just few    avatarPriorityQueues[kNonHero].reserve(avatarMap.size() - 1);  // don't include MyAvatar     // Build vector and compute priorities    auto nodeList = DependencyManager::get<NodeList>();    AvatarHash::iterator itr = avatarMap.begin();    while (itr != avatarMap.end()) {        auto avatar = std::static_pointer_cast<Avatar>(*itr);        // DO NOT update _myAvatar!  Its update has already been done earlier in the main loop.        // DO NOT update or fade out uninitialized Avatars        if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) {            if (avatar->getHasPriority()) {                avatarPriorityQueues[kHero].push(SortableAvatar(avatar));            } else {                avatarPriorityQueues[kNonHero].push(SortableAvatar(avatar));            }        }        ++itr;    }     _numHeroAvatars = (int)avatarPriorityQueues[kHero].size();     // process in sorted order    uint64_t startTime = usecTimestampNow();     const uint64_t MAX_UPDATE_HEROS_TIME_BUDGET = uint64_t(0.8 * MAX_UPDATE_AVATARS_TIME_BUDGET);     uint64_t updatePriorityExpiries[NumVariants] = { startTime + MAX_UPDATE_HEROS_TIME_BUDGET, startTime + MAX_UPDATE_AVATARS_TIME_BUDGET };    int numHerosUpdated = 0;    int numAvatarsUpdated = 0;    int numAvatarsNotUpdated = 0;     render::Transaction renderTransaction;    workload::Transaction workloadTransaction;     for (int p = kHero; p < NumVariants; p++) {        auto& priorityQueue = avatarPriorityQueues[p];        // Sorting the current queue HERE as part of the measured timing.        const auto& sortedAvatarVector = priorityQueue.getSortedVector();         auto passExpiry = updatePriorityExpiries[p];         for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) {            const SortableAvatar& sortData = *it;            const auto avatar = std::static_pointer_cast<OtherAvatar>(sortData.getAvatar());            if (!avatar->_isClientAvatar) {                avatar->setIsClientAvatar(true);            }            // TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update            if (avatar->getSkeletonModel()->isLoaded()) {                // remove the orb if it is there                avatar->removeOrb();                if (avatar->needsPhysicsUpdate()) {                    _otherAvatarsToChangeInPhysics.insert(avatar);                }            } else {                avatar->updateOrbPosition();            }             // for ALL avatars...            if (_shouldRender) {                avatar->ensureInScene(avatar, qApp->getMain3DScene());            }             avatar->animateScaleChanges(deltaTime);             uint64_t now = usecTimestampNow();            if (now < passExpiry) {                // we're within budget                bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD;                if (inView && avatar->hasNewJointData()) {                    numAvatarsUpdated++;                }                auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig);                if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT ||                                                 transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {                    avatar->_transit.reset();                    avatar->setIsNewAvatar(false);                }                avatar->simulate(deltaTime, inView);                if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) {                    _myAvatar->addAvatarHandsToFlow(avatar);                }                if (_drawOtherAvatarSkeletons) {                    avatar->debugJointData();                }                avatar->setEnableMeshVisible(!_drawOtherAvatarSkeletons);                avatar->updateRenderItem(renderTransaction);                avatar->updateSpaceProxy(workloadTransaction);                avatar->setLastRenderUpdateTime(startTime);             } else {                // we've spent our time budget for this priority bucket                // let's deal with the reminding avatars if this pass and BREAK from the for loop                 if (p == kHero) {                    // Hero,                    // --> put them back in the non hero queue                     auto& crowdQueue = avatarPriorityQueues[kNonHero];                    while (it != sortedAvatarVector.end()) {                        crowdQueue.push(SortableAvatar((*it).getAvatar()));                        ++it;                    }                } else {                    // Non Hero                    // --> bail on the rest of the avatar updates                    // --> more avatars may freeze until their priority trickles up                    // --> some scale animations may glitch                    // --> some avatar velocity measurements may be a little off                     // no time to simulate, but we take the time to count how many were tragically missed                    numAvatarsNotUpdated = sortedAvatarVector.end() - it;                }                 // We had to cut short this pass, we must break out of the for loop here                break;            }        }         if (p == kHero) {            numHerosUpdated = numAvatarsUpdated;        }    }     if (_shouldRender) {        qApp->getMain3DScene()->enqueueTransaction(renderTransaction);    }     _space->enqueueTransaction(workloadTransaction);     _numAvatarsUpdated = numAvatarsUpdated;    _numAvatarsNotUpdated = numAvatarsNotUpdated;    _numHeroAvatarsUpdated = numHerosUpdated;     _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC;} void AvatarManager::postUpdate(float deltaTime, const render::ScenePointer& scene) {    auto hashCopy = getHashCopy();    AvatarHash::iterator avatarIterator = hashCopy.begin();    for (avatarIterator = hashCopy.begin(); avatarIterator != hashCopy.end(); avatarIterator++) {        auto avatar = std::static_pointer_cast<Avatar>(avatarIterator.value());        avatar->postUpdate(deltaTime, scene);    }} AvatarSharedPointer AvatarManager::newSharedAvatar(const QUuid& sessionUUID) {    auto otherAvatar = new OtherAvatar(qApp->thread());    otherAvatar->setSessionUUID(sessionUUID);    auto nodeList = DependencyManager::get<NodeList>();    if (nodeList && !nodeList->isIgnoringNode(sessionUUID)) {        otherAvatar->createOrb();    }    return AvatarSharedPointer(otherAvatar, [](OtherAvatar* ptr) { ptr->deleteLater(); });} void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) {    _otherAvatarsToChangeInPhysics.insert(avatar);} DetailedMotionState* AvatarManager::createDetailedMotionState(OtherAvatarPointer avatar, int32_t jointIndex) {    bool isBound = false;    std::vector<int32_t> boundJoints;    const btCollisionShape* shape = avatar->createCollisionShape(jointIndex, isBound, boundJoints);    if (shape) {        DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);        motionState->setMass(0.0f); // DetailedMotionState has KINEMATIC MotionType, so zero mass is ok        motionState->setIsBound(isBound, boundJoints);        return motionState;    }    return nullptr;} void AvatarManager::rebuildAvatarPhysics(PhysicsEngine::Transaction& transaction, const OtherAvatarPointer& avatar) {    if (!avatar->_motionState) {        avatar->_motionState = new AvatarMotionState(avatar, nullptr);    }    AvatarMotionState* motionState = avatar->_motionState;    ShapeInfo shapeInfo;    avatar->computeShapeInfo(shapeInfo);    const btCollisionShape* shape = ObjectMotionState::getShapeManager()->getShape(shapeInfo);    assert(shape);    motionState->setShape(shape);    motionState->setMass(avatar->computeMass());    if (motionState->getRigidBody()) {        transaction.objectsToReinsert.push_back(motionState);    } else {        transaction.objectsToAdd.push_back(motionState);    }    motionState->clearIncomingDirtyFlags();} void AvatarManager::removeDetailedAvatarPhysics(PhysicsEngine::Transaction& transaction, const OtherAvatarPointer& avatar) {    // delete old detailedMotionStates    auto& detailedMotionStates = avatar->getDetailedMotionStates();    if (detailedMotionStates.size() != 0) {        for (auto& detailedMotionState : detailedMotionStates) {            transaction.objectsToRemove.push_back(detailedMotionState);        }        avatar->forgetDetailedMotionStates();    }} void AvatarManager::rebuildDetailedAvatarPhysics(PhysicsEngine::Transaction& transaction, const OtherAvatarPointer& avatar) {    // Rather than reconcile numbers of joints after change to model or LOD    // we blow away old detailedMotionStates and create anew all around.    removeDetailedAvatarPhysics(transaction, avatar);    auto& detailedMotionStates = avatar->getDetailedMotionStates();    OtherAvatar::BodyLOD lod = avatar->getBodyLOD();    if (lod == OtherAvatar::BodyLOD::Sphere) {        auto dMotionState = createDetailedMotionState(avatar, -1);        if (dMotionState) {            detailedMotionStates.push_back(dMotionState);            transaction.objectsToAdd.push_back(dMotionState);        }    } else {        int32_t numJoints = avatar->getJointCount();        for (int32_t i = 0; i < numJoints; i++) {            auto dMotionState = createDetailedMotionState(avatar, i);            if (dMotionState) {                detailedMotionStates.push_back(dMotionState);                transaction.objectsToAdd.push_back(dMotionState);            }        }    }    avatar->_needsDetailedRebuild = false;} void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {    _myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);    for (auto avatar : _otherAvatarsToChangeInPhysics) {        bool isInPhysics = avatar->isInPhysicsSimulation();        if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) {

Lines 1–500 of 1098.