void RenderableParticleEffectEntityItem::update(const quint64& now) {
    ParticleEffectEntityItem::update(now);

    if (_texturesChangedFlag) {
        if (_textures.isEmpty()) {
            _texture.clear();
        } else {
            // for now use the textures string directly.
            // Eventually we'll want multiple textures in a map or array.
            _texture = DependencyManager::get<TextureCache>()->getTexture(_textures);
        }
        _texturesChangedFlag = false;
    }

    updateRenderItem();
}
Example #2
0
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;
}