5476 lines · 222.9 KB · C++
RawDownload
        QScreen* hmdScreen = hmdTools->getHMDScreen();        QWindow* appWindow = getWindow()->windowHandle();        QScreen* appScreen = appWindow->screen();         // if our app's screen is the hmd screen, we don't want to place the        // running scripts widget on it. So we need to pick a better screen.        // we will use the screen for the HMDTools since it's a guaranteed        // better screen.        if (appScreen == hmdScreen) {            QScreen* betterScreen = hmdTools->windowHandle()->screen();            applicationGeometry = betterScreen->geometry();        }    }    return applicationGeometry;} /////////////////////////////////////////////////////////////////////////////////////// loadViewFrustum()//// Description: this will load the view frustum bounds for EITHER the head//                 or the "myCamera".//void Application::loadViewFrustum(Camera& camera, ViewFrustum& viewFrustum) {    PerformanceTimer perfTimer("loadViewFrustum");    PROFILE_RANGE(__FUNCTION__);    // We will use these below, from either the camera or head vectors calculated above    viewFrustum.setProjection(camera.getProjection());     // Set the viewFrustum up with the correct position and orientation of the camera    viewFrustum.setPosition(camera.getPosition());    viewFrustum.setOrientation(camera.getOrientation());     // Ask the ViewFrustum class to calculate our corners    viewFrustum.calculate();} glm::vec3 Application::getSunDirection() const {    // Sun direction is in fact just the location of the sun relative to the origin    auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();    return skyStage->getSunLight()->getDirection();} // FIXME, preprocessor guard this check to occur only in DEBUG buildsstatic QThread * activeRenderingThread = nullptr; PickRay Application::computePickRay(float x, float y) const {    vec2 pickPoint { x, y };    PickRay result;    if (isHMDMode()) {        getApplicationCompositor().computeHmdPickRay(pickPoint, result.origin, result.direction);    } else {        pickPoint /= getCanvasSize();        QMutexLocker viewLocker(&_viewMutex);        _viewFrustum.computePickRay(pickPoint.x, pickPoint.y, result.origin, result.direction);    }    return result;} MyAvatar* Application::getMyAvatar() const {    return DependencyManager::get<AvatarManager>()->getMyAvatar();} glm::vec3 Application::getAvatarPosition() const {    return getMyAvatar()->getPosition();} void Application::copyViewFrustum(ViewFrustum& viewOut) const {    QMutexLocker viewLocker(&_viewMutex);    viewOut = _viewFrustum;} void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const {    QMutexLocker viewLocker(&_viewMutex);    viewOut = _displayViewFrustum;} void Application::copyShadowViewFrustum(ViewFrustum& viewOut) const {    QMutexLocker viewLocker(&_viewMutex);    viewOut = _shadowViewFrustum;} // WorldBox Render Data & rendering functions class WorldBoxRenderData {public:    typedef render::Payload<WorldBoxRenderData> Payload;    typedef Payload::DataPointer Pointer;     int _val = 0;    static render::ItemID _item; // unique WorldBoxRenderData}; render::ItemID WorldBoxRenderData::_item { render::Item::INVALID_ITEM_ID }; namespace render {    template <> const ItemKey payloadGetKey(const WorldBoxRenderData::Pointer& stuff) { return ItemKey::Builder::opaqueShape(); }    template <> const Item::Bound payloadGetBound(const WorldBoxRenderData::Pointer& stuff) { return Item::Bound(); }    template <> void payloadRender(const WorldBoxRenderData::Pointer& stuff, RenderArgs* args) {        if (args->_renderMode != RenderArgs::MIRROR_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::WorldAxes)) {            PerformanceTimer perfTimer("worldBox");             auto& batch = *args->_batch;            DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch);            renderWorldBox(batch);        }    }} // Background Render Data & rendering functionsclass BackgroundRenderData {public:    typedef render::Payload<BackgroundRenderData> Payload;    typedef Payload::DataPointer Pointer;     Stars _stars;     static render::ItemID _item; // unique WorldBoxRenderData}; render::ItemID BackgroundRenderData::_item = 0; namespace render {    template <> const ItemKey payloadGetKey(const BackgroundRenderData::Pointer& stuff) {        return ItemKey::Builder::background();    }     template <> const Item::Bound payloadGetBound(const BackgroundRenderData::Pointer& stuff) {        return Item::Bound();    }     template <> void payloadRender(const BackgroundRenderData::Pointer& background, RenderArgs* args) {        Q_ASSERT(args->_batch);        gpu::Batch& batch = *args->_batch;         // Background rendering decision        auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();        auto backgroundMode = skyStage->getBackgroundMode();         switch (backgroundMode) {            case model::SunSkyStage::SKY_BOX: {                auto skybox = skyStage->getSkybox();                if (skybox) {                    PerformanceTimer perfTimer("skybox");                    skybox->render(batch, args->getViewFrustum());                    break;                }            }             // Fall through: if no skybox is available, render the SKY_DOME            case model::SunSkyStage::SKY_DOME:  {                if (Menu::getInstance()->isOptionChecked(MenuOption::Stars)) {                    PerformanceTimer perfTimer("stars");                    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),                        "Application::payloadRender<BackgroundRenderData>() ... My god, it's full of stars...");                    // should be the first rendering pass - w/o depth buffer / lighting                     static const float alpha = 1.0f;                    background->_stars.render(args, alpha);                }            }                break;             case model::SunSkyStage::NO_BACKGROUND:            default:                // this line intentionally left blank                break;        }    }}  void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool selfAvatarOnly) {     // FIXME: This preDisplayRender call is temporary until we create a separate render::scene for the mirror rendering.    // Then we can move this logic into the Avatar::simulate call.    auto myAvatar = getMyAvatar();    myAvatar->preDisplaySide(renderArgs);     activeRenderingThread = QThread::currentThread();    PROFILE_RANGE(__FUNCTION__);    PerformanceTimer perfTimer("display");    PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide()");     // load the view frustum    {        QMutexLocker viewLocker(&_viewMutex);        loadViewFrustum(theCamera, _displayViewFrustum);    }     // TODO fix shadows and make them use the GPU library     // The pending changes collecting the changes here    render::PendingChanges pendingChanges;     // FIXME: Move this out of here!, Background / skybox should be driven by the enityt content just like the other entities    // Background rendering decision    if (!render::Item::isValidID(BackgroundRenderData::_item)) {        auto backgroundRenderData = make_shared<BackgroundRenderData>();        auto backgroundRenderPayload = make_shared<BackgroundRenderData::Payload>(backgroundRenderData);        BackgroundRenderData::_item = _main3DScene->allocateID();        pendingChanges.resetItem(BackgroundRenderData::_item, backgroundRenderPayload);    }     // Assuming nothing get's rendered through that    if (!selfAvatarOnly) {        if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {            // render models...            PerformanceTimer perfTimer("entities");            PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),                "Application::displaySide() ... entities...");             RenderArgs::DebugFlags renderDebugFlags = RenderArgs::RENDER_DEBUG_NONE;             if (Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls)) {                renderDebugFlags = static_cast<RenderArgs::DebugFlags>(renderDebugFlags |                    static_cast<int>(RenderArgs::RENDER_DEBUG_HULLS));            }            renderArgs->_debugFlags = renderDebugFlags;            //ViveControllerManager::getInstance().updateRendering(renderArgs, _main3DScene, pendingChanges);        }    }     // FIXME: Move this out of here!, WorldBox should be driven by the entity content just like the other entities    // Make sure the WorldBox is in the scene    if (!render::Item::isValidID(WorldBoxRenderData::_item)) {        auto worldBoxRenderData = make_shared<WorldBoxRenderData>();        auto worldBoxRenderPayload = make_shared<WorldBoxRenderData::Payload>(worldBoxRenderData);         WorldBoxRenderData::_item = _main3DScene->allocateID();         pendingChanges.resetItem(WorldBoxRenderData::_item, worldBoxRenderPayload);    } else {        pendingChanges.updateItem<WorldBoxRenderData>(WorldBoxRenderData::_item,            [](WorldBoxRenderData& payload) {            payload._val++;        });    }     // Setup the current Zone Entity lighting    {        auto stage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();        DependencyManager::get<DeferredLightingEffect>()->setGlobalLight(stage->getSunLight());    }     {        PerformanceTimer perfTimer("SceneProcessPendingChanges");        _main3DScene->enqueuePendingChanges(pendingChanges);         _main3DScene->processPendingChangesQueue();    }     // For now every frame pass the renderContext    {        PerformanceTimer perfTimer("EngineRun");         {            QMutexLocker viewLocker(&_viewMutex);            renderArgs->setViewFrustum(_displayViewFrustum);        }        _renderEngine->getRenderContext()->args = renderArgs;         // Before the deferred pass, let's try to use the render engine        _renderEngine->run();    }     activeRenderingThread = nullptr;} void Application::renderRearViewMirror(RenderArgs* renderArgs, const QRect& region, bool isZoomed) {    auto originalViewport = renderArgs->_viewport;    // Grab current viewport to reset it at the end     float aspect = (float)region.width() / region.height();    float fov = MIRROR_FIELD_OF_VIEW;     auto myAvatar = getMyAvatar();     // bool eyeRelativeCamera = false;    if (!isZoomed) {        _mirrorCamera.setPosition(myAvatar->getChestPosition() +                                  myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_BODY_DISTANCE * myAvatar->getScale());     } else { // HEAD zoom level        // FIXME note that the positioning of the camera relative to the avatar can suffer limited        // precision as the user's position moves further away from the origin.  Thus at        // /1e7,1e7,1e7 (well outside the buildable volume) the mirror camera veers and sways        // wildly as you rotate your avatar because the floating point values are becoming        // larger, squeezing out the available digits of precision you have available at the        // human scale for camera positioning.         // Previously there was a hack to correct this using the mechanism of repositioning        // the avatar at the origin of the world for the purposes of rendering the mirror,        // but it resulted in failing to render the avatar's head model in the mirror view        // when in first person mode.  Presumably this was because of some missed culling logic        // that was not accounted for in the hack.         // This was removed in commit 71e59cfa88c6563749594e25494102fe01db38e9 but could be further        // investigated in order to adapt the technique while fixing the head rendering issue,        // but the complexity of the hack suggests that a better approach        _mirrorCamera.setPosition(myAvatar->getDefaultEyePosition() +                                    myAvatar->getOrientation() * glm::vec3(0.0f, 0.0f, -1.0f) * MIRROR_REARVIEW_DISTANCE * myAvatar->getScale());    }    _mirrorCamera.setProjection(glm::perspective(glm::radians(fov), aspect, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));    _mirrorCamera.setOrientation(myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI, 0.0f)));      // set the bounds of rear mirror view    // the region is in device independent coordinates; must convert to device    float ratio = (float)QApplication::desktop()->windowHandle()->devicePixelRatio() * getRenderResolutionScale();    int width = region.width() * ratio;    int height = region.height() * ratio;    gpu::Vec4i viewport = gpu::Vec4i(0, 0, width, height);    renderArgs->_viewport = viewport;     // render rear mirror view    displaySide(renderArgs, _mirrorCamera, true);     renderArgs->_viewport =  originalViewport;} void Application::resetSensors(bool andReload) {    DependencyManager::get<Faceshift>()->reset();    DependencyManager::get<DdeFaceTracker>()->reset();    DependencyManager::get<EyeTracker>()->reset();    getActiveDisplayPlugin()->resetSensors();    _overlayConductor.centerUI();    getMyAvatar()->reset(andReload);    QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "reset", Qt::QueuedConnection);} void Application::updateWindowTitle() const {     QString buildVersion = " (build " + applicationVersion() + ")";    auto nodeList = DependencyManager::get<NodeList>();     QString connectionStatus = nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED) ";    QString username = DependencyManager::get<AccountManager>()->getAccountInfo().getUsername();    QString currentPlaceName = DependencyManager::get<AddressManager>()->getHost();     if (currentPlaceName.isEmpty()) {        currentPlaceName = nodeList->getDomainHandler().getHostname();    }     QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())        + currentPlaceName + connectionStatus + buildVersion; #ifndef WIN32    // crashes with vs2013/win32    qCDebug(interfaceapp, "Application title set to: %s", title.toStdString().c_str());#endif    _window->setWindowTitle(title);} void Application::clearDomainOctreeDetails() {     // if we're about to quit, we really don't need to do any of these things...    if (_aboutToQuit) {        return;    }     qCDebug(interfaceapp) << "Clearing domain octree details...";     resetPhysicsReadyInformation();    getMyAvatar()->setAvatarEntityDataChanged(true); // to recreate worn entities     // reset our node to stats and node to jurisdiction maps... since these must be changing...    _entityServerJurisdictions.withWriteLock([&] {        _entityServerJurisdictions.clear();    });     _octreeServerSceneStats.withWriteLock([&] {        _octreeServerSceneStats.clear();    });     // reset the model renderer    getEntities()->clear();     auto skyStage = DependencyManager::get<SceneScriptingInterface>()->getSkyStage();    skyStage->setBackgroundMode(model::SunSkyStage::SKY_DOME);     _recentlyClearedDomain = true;} void Application::domainChanged(const QString& domainHostname) {    updateWindowTitle();    // disable physics until we have enough information about our new location to not cause craziness.    resetPhysicsReadyInformation();}  void Application::resettingDomain() {    _notifiedPacketVersionMismatchThisDomain = false;} void Application::nodeAdded(SharedNodePointer node) const {    if (node->getType() == NodeType::AvatarMixer) {        // new avatar mixer, send off our identity packet right away        getMyAvatar()->sendIdentityPacket();    }} void Application::nodeActivated(SharedNodePointer node) {    if (node->getType() == NodeType::AssetServer) {        // asset server just connected - check if we have the asset browser showing         auto offscreenUi = DependencyManager::get<OffscreenUi>();        auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");         if (assetDialog) {            auto nodeList = DependencyManager::get<NodeList>();             if (nodeList->getThisNodeCanWriteAssets()) {                // call reload on the shown asset browser dialog to get the mappings (if permissions allow)                QMetaObject::invokeMethod(assetDialog, "reload");            } else {                // we switched to an Asset Server that we can't modify, hide the Asset Browser                assetDialog->setVisible(false);            }        }    }     // If we get a new EntityServer activated, do a "forceRedraw" query. This will send a degenerate    // query so that the server will think our next non-degenerate query is "different enough" to send    // us a full scene    if (_recentlyClearedDomain && node->getType() == NodeType::EntityServer) {        _recentlyClearedDomain = false;        if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {            queryOctree(NodeType::EntityServer, PacketType::EntityQuery, _entityServerJurisdictions, true);        }    } } void Application::nodeKilled(SharedNodePointer node) {    // These are here because connecting NodeList::nodeKilled to OctreePacketProcessor::nodeKilled doesn't work:    // OctreePacketProcessor::nodeKilled is not being called when NodeList::nodeKilled is emitted.    // This may have to do with GenericThread::threadRoutine() blocking the QThread event loop     _octreeProcessor.nodeKilled(node);     _entityEditSender.nodeKilled(node);     if (node->getType() == NodeType::AudioMixer) {        QMetaObject::invokeMethod(DependencyManager::get<AudioClient>().data(), "audioMixerKilled");    } else if (node->getType() == NodeType::EntityServer) {        QUuid nodeUUID = node->getUUID();        // see if this is the first we've heard of this node...        _entityServerJurisdictions.withReadLock([&] {            if (_entityServerJurisdictions.find(nodeUUID) == _entityServerJurisdictions.end()) {                return;            }             auto rootCode = _entityServerJurisdictions[nodeUUID].getRootOctalCode();            VoxelPositionSize rootDetails;            voxelDetailsForCode(rootCode.get(), rootDetails);             qCDebug(interfaceapp, "model server going away...... v[%f, %f, %f, %f]",                (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);         });         // If the model server is going away, remove it from our jurisdiction map so we don't send voxels to a dead server        _entityServerJurisdictions.withWriteLock([&] {            _entityServerJurisdictions.erase(_entityServerJurisdictions.find(nodeUUID));        });         // also clean up scene stats for that server        _octreeServerSceneStats.withWriteLock([&] {            if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {                _octreeServerSceneStats.erase(nodeUUID);            }        });    } else if (node->getType() == NodeType::AvatarMixer) {        // our avatar mixer has gone away - clear the hash of avatars        DependencyManager::get<AvatarManager>()->clearOtherAvatars();    } else if (node->getType() == NodeType::AssetServer) {        // asset server going away - check if we have the asset browser showing         auto offscreenUi = DependencyManager::get<OffscreenUi>();        auto assetDialog = offscreenUi->getRootItem()->findChild<QQuickItem*>("AssetServer");         if (assetDialog) {            // call reload on the shown asset browser dialog            QMetaObject::invokeMethod(assetDialog, "clear");        }    }} void Application::trackIncomingOctreePacket(ReceivedMessage& message, SharedNodePointer sendingNode, bool wasStatsPacket) {    // Attempt to identify the sender from its address.    if (sendingNode) {        const QUuid& nodeUUID = sendingNode->getUUID();         // now that we know the node ID, let's add these stats to the stats for that node...        _octreeServerSceneStats.withWriteLock([&] {            if (_octreeServerSceneStats.find(nodeUUID) != _octreeServerSceneStats.end()) {                OctreeSceneStats& stats = _octreeServerSceneStats[nodeUUID];                stats.trackIncomingOctreePacket(message, wasStatsPacket, sendingNode->getClockSkewUsec());            }        });

Lines 4001–4500 of 5476. Previous lines