5476 lines · 222.9 KB · C++
RawDownload
    }} bool Application::nearbyEntitiesAreReadyForPhysics() {    // this is used to avoid the following scenario:    // A table has some items sitting on top of it.  The items are at rest, meaning they aren't active in bullet.    // Someone logs in close to the table.  They receive information about the items on the table before they    // receive information about the table.  The items are very close to the avatar's capsule, so they become    // activated in bullet.  This causes them to fall to the floor, because the table's shape isn't yet in bullet.    EntityTreePointer entityTree = getEntities()->getTree();    if (!entityTree) {        return false;    }     QVector<EntityItemPointer> entities;    entityTree->withReadLock([&] {        AABox box(getMyAvatar()->getPosition() - glm::vec3(PHYSICS_READY_RANGE), glm::vec3(2 * PHYSICS_READY_RANGE));        entityTree->findEntities(box, entities);    });     // For reasons I haven't found, we don't necessarily have the full scene when we receive a stats packet.  Apply    // a heuristic to try to decide when we actually know about all of the nearby entities.    uint32_t nearbyCount = entities.size();    if (nearbyCount == _nearbyEntitiesCountAtLastPhysicsCheck) {        _nearbyEntitiesStabilityCount++;    } else {        _nearbyEntitiesStabilityCount = 0;    }    _nearbyEntitiesCountAtLastPhysicsCheck = nearbyCount;     const uint32_t MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT = 3;    if (_nearbyEntitiesStabilityCount >= MINIMUM_NEARBY_ENTITIES_STABILITY_COUNT) {        // We've seen the same number of nearby entities for several stats packets in a row.  assume we've got all        // the local entities.        foreach (EntityItemPointer entity, entities) {            if (entity->shouldBePhysical() && !entity->isReadyToComputeShape()) {                static QString repeatedMessage =                    LogHandler::getInstance().addRepeatedMessageRegex("Physics disabled until entity loads: .*");                qCDebug(interfaceapp) << "Physics disabled until entity loads: " << entity->getID() << entity->getName();                return false;            }        }        return true;    }    return false;} int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer sendingNode) {    // But, also identify the sender, and keep track of the contained jurisdiction root for this server     // parse the incoming stats datas stick it in a temporary object for now, while we    // determine which server it belongs to    int statsMessageLength = 0;     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([&] {        OctreeSceneStats& octreeStats = _octreeServerSceneStats[nodeUUID];        statsMessageLength = octreeStats.unpackFromPacket(message);         if (octreeStats.isFullScene()) {            _fullSceneReceivedCounter++;        }         // see if this is the first we've heard of this node...        NodeToJurisdictionMap* jurisdiction = nullptr;        QString serverType;        if (sendingNode->getType() == NodeType::EntityServer) {            jurisdiction = &_entityServerJurisdictions;            serverType = "Entity";        }         bool found = false;         jurisdiction->withReadLock([&] {            if (jurisdiction->find(nodeUUID) != jurisdiction->end()) {                found = true;                return;            }             VoxelPositionSize rootDetails;            voxelDetailsForCode(octreeStats.getJurisdictionRoot().get(), rootDetails);             qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",                qPrintable(serverType),                (double)rootDetails.x, (double)rootDetails.y, (double)rootDetails.z, (double)rootDetails.s);        });         if (!found) {            // store jurisdiction details for later use            // This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it            // but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the            // details from the OctreeSceneStats to construct the JurisdictionMap            JurisdictionMap jurisdictionMap;            jurisdictionMap.copyContents(octreeStats.getJurisdictionRoot(), octreeStats.getJurisdictionEndNodes());            jurisdiction->withWriteLock([&] {                (*jurisdiction)[nodeUUID] = jurisdictionMap;            });        }    });     return statsMessageLength;} void Application::packetSent(quint64 length) {} void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scriptEngine) {    // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so    // we can use the same ones from the application.    auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();    entityScriptingInterface->setPacketSender(&_entityEditSender);    entityScriptingInterface->setEntityTree(getEntities()->getTree());     // AvatarManager has some custom types    AvatarManager::registerMetaTypes(scriptEngine);     scriptEngine->registerGlobalObject("Rates", new RatesScriptingInterface(this));     // hook our avatar and avatar hash map object into this script engine    scriptEngine->registerGlobalObject("MyAvatar", getMyAvatar());    qScriptRegisterMetaType(scriptEngine, audioListenModeToScriptValue, audioListenModeFromScriptValue);     scriptEngine->registerGlobalObject("AvatarList", DependencyManager::get<AvatarManager>().data());     scriptEngine->registerGlobalObject("Camera", &_myCamera); #if defined(Q_OS_MAC) || defined(Q_OS_WIN)    scriptEngine->registerGlobalObject("SpeechRecognizer", DependencyManager::get<SpeechRecognizer>().data());#endif     ClipboardScriptingInterface* clipboardScriptable = new ClipboardScriptingInterface();    scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);    connect(scriptEngine, &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater);     scriptEngine->registerGlobalObject("Overlays", &_overlays);    qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue);    qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue,                            RayToOverlayIntersectionResultFromScriptValue);     scriptEngine->registerGlobalObject("Desktop", DependencyManager::get<DesktopScriptingInterface>().data());    scriptEngine->registerGlobalObject("Toolbars", DependencyManager::get<ToolbarScriptingInterface>().data());     scriptEngine->registerGlobalObject("Window", DependencyManager::get<WindowScriptingInterface>().data());    scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,                        LocationScriptingInterface::locationSetter, "Window");    // register `location` on the global object.    scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter,                                       LocationScriptingInterface::locationSetter);     scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);    scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);    scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor);     scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());    scriptEngine->registerGlobalObject("Stats", Stats::getInstance());    scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());    scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());     // Caches    scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());    scriptEngine->registerGlobalObject("TextureCache", DependencyManager::get<TextureCache>().data());    scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get<ModelCache>().data());    scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get<SoundCache>().data());     scriptEngine->registerGlobalObject("Account", AccountScriptingInterface::getInstance());    scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface);     scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance());    qScriptRegisterMetaType(scriptEngine, DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue);     scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get<DdeFaceTracker>().data());     scriptEngine->registerGlobalObject("AvatarManager", DependencyManager::get<AvatarManager>().data());     scriptEngine->registerGlobalObject("UndoStack", &_undoStackScriptingInterface);     scriptEngine->registerGlobalObject("LODManager", DependencyManager::get<LODManager>().data());     scriptEngine->registerGlobalObject("Paths", DependencyManager::get<PathUtils>().data());     scriptEngine->registerGlobalObject("HMD", DependencyManager::get<HMDScriptingInterface>().data());    scriptEngine->registerFunction("HMD", "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0);    scriptEngine->registerFunction("HMD", "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0);     scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());    scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());     scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());    scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());     scriptEngine->registerGlobalObject("UserActivityLogger", DependencyManager::get<UserActivityLoggerScriptingInterface>().data());} bool Application::canAcceptURL(const QString& urlString) const {    QUrl url(urlString);    if (urlString.startsWith(HIFI_URL_SCHEME)) {        return true;    }    QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);    QString lowerPath = url.path().toLower();    while (i.hasNext()) {        i.next();        if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {            return true;        }    }    return false;} bool Application::acceptURL(const QString& urlString, bool defaultUpload) {    if (urlString.startsWith(HIFI_URL_SCHEME)) {        // this is a hifi URL - have the AddressManager handle it        QMetaObject::invokeMethod(DependencyManager::get<AddressManager>().data(), "handleLookupString",                                  Qt::AutoConnection, Q_ARG(const QString&, urlString));        return true;    }     QUrl url(urlString);    QHashIterator<QString, AcceptURLMethod> i(_acceptedExtensions);    QString lowerPath = url.path().toLower();    while (i.hasNext()) {        i.next();        if (lowerPath.endsWith(i.key(), Qt::CaseInsensitive)) {            AcceptURLMethod method = i.value();            return (this->*method)(urlString);        }    }     if (defaultUpload) {        toggleAssetServerWidget(urlString);    }    return defaultUpload;} void Application::setSessionUUID(const QUuid& sessionUUID) const {    Physics::setSessionUUID(sessionUUID);} bool Application::askToSetAvatarUrl(const QString& url) {    QUrl realUrl(url);    if (realUrl.isLocalFile()) {        OffscreenUi::warning("", "You can not use local files for avatar components.");        return false;    }     // Download the FST file, to attempt to determine its model type    QVariantHash fstMapping = FSTReader::downloadMapping(url);     FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping);     QString modelName = fstMapping["name"].toString();    QString modelLicense = fstMapping["license"].toString();     bool agreeToLicence = true; // assume true    if (!modelLicense.isEmpty()) {        // word wrap the licence text to fit in a reasonable shaped message box.        const int MAX_CHARACTERS_PER_LINE = 90;        modelLicense = simpleWordWrap(modelLicense, MAX_CHARACTERS_PER_LINE);         agreeToLicence = QMessageBox::Yes == OffscreenUi::question("Avatar Usage License",            modelLicense + "\nDo you argee to these terms?",            QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);    }     bool ok = false;     if (!agreeToLicence) {        qCDebug(interfaceapp) << "Declined to agree to avatar license: " << url;    } else {        switch (modelType) {             case FSTReader::HEAD_AND_BODY_MODEL:                 ok = QMessageBox::Ok == OffscreenUi::question("Set Avatar",                                   "Would you like to use '" + modelName + "' for your avatar?",                                   QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok);            break;             default:                OffscreenUi::warning("", modelName + "Does not support a head and body as required.");            break;        }    }     if (ok) {        getMyAvatar()->useFullAvatarURL(url, modelName);        emit fullAvatarURLChanged(url, modelName);    } else {        qCDebug(interfaceapp) << "Declined to use the avatar: " << url;    }     return true;}  bool Application::askToLoadScript(const QString& scriptFilenameOrURL) {    QMessageBox::StandardButton reply;    QString message = "Would you like to run this script:\n" + scriptFilenameOrURL;    reply = OffscreenUi::question(getWindow(), "Run Script", message, QMessageBox::Yes | QMessageBox::No);     if (reply == QMessageBox::Yes) {        qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL;        DependencyManager::get<ScriptEngines>()->loadScript(scriptFilenameOrURL);    } else {        qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL;    }    return true;} bool Application::askToWearAvatarAttachmentUrl(const QString& url) {     QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();    QNetworkRequest networkRequest = QNetworkRequest(url);    networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);    QNetworkReply* reply = networkAccessManager.get(networkRequest);    int requestNumber = ++_avatarAttachmentRequest;    connect(reply, &QNetworkReply::finished, [this, reply, url, requestNumber]() {         if (requestNumber != _avatarAttachmentRequest) {            // this request has been superseded by another more recent request            reply->deleteLater();            return;        }         QNetworkReply::NetworkError networkError = reply->error();        if (networkError == QNetworkReply::NoError) {            // download success            QByteArray contents = reply->readAll();             QJsonParseError jsonError;            auto doc = QJsonDocument::fromJson(contents, &jsonError);            if (jsonError.error == QJsonParseError::NoError) {                 auto jsonObject = doc.object();                 // retrieve optional name field from JSON                QString name = tr("Unnamed Attachment");                auto nameValue = jsonObject.value("name");                if (nameValue.isString()) {                    name = nameValue.toString();                }                 // display confirmation dialog                if (displayAvatarAttachmentConfirmationDialog(name)) {                     // add attachment to avatar                    auto myAvatar = getMyAvatar();                    assert(myAvatar);                    auto attachmentDataVec = myAvatar->getAttachmentData();                    AttachmentData attachmentData;                    attachmentData.fromJson(jsonObject);                    attachmentDataVec.push_back(attachmentData);                    myAvatar->setAttachmentData(attachmentDataVec);                 } else {                    qCDebug(interfaceapp) << "User declined to wear the avatar attachment: " << url;                }             } else {                // json parse error                auto avatarAttachmentParseErrorString = tr("Error parsing attachment JSON from url: \"%1\"");                displayAvatarAttachmentWarning(avatarAttachmentParseErrorString.arg(url));            }        } else {            // download failure            auto avatarAttachmentDownloadErrorString = tr("Error downloading attachment JSON from url: \"%1\"");            displayAvatarAttachmentWarning(avatarAttachmentDownloadErrorString.arg(url));        }        reply->deleteLater();    });    return true;} void Application::displayAvatarAttachmentWarning(const QString& message) const {    auto avatarAttachmentWarningTitle = tr("Avatar Attachment Failure");    OffscreenUi::warning(avatarAttachmentWarningTitle, message);} bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) const {    auto avatarAttachmentConfirmationTitle = tr("Avatar Attachment Confirmation");    auto avatarAttachmentConfirmationMessage = tr("Would you like to wear '%1' on your avatar?").arg(name);    auto reply = OffscreenUi::question(avatarAttachmentConfirmationTitle,                                       avatarAttachmentConfirmationMessage,                                       QMessageBox::Ok | QMessageBox::Cancel);    if (QMessageBox::Ok == reply) {        return true;    } else {        return false;    }} void Application::toggleRunningScriptsWidget() const {    static const QUrl url("hifi/dialogs/RunningScripts.qml");    DependencyManager::get<OffscreenUi>()->show(url, "RunningScripts");    //if (_runningScriptsWidget->isVisible()) {    //    if (_runningScriptsWidget->hasFocus()) {    //        _runningScriptsWidget->hide();    //    } else {    //        _runningScriptsWidget->raise();    //        setActiveWindow(_runningScriptsWidget);    //        _runningScriptsWidget->setFocus();    //    }    //} else {    //    _runningScriptsWidget->show();    //    _runningScriptsWidget->setFocus();    //}} void Application::toggleAssetServerWidget(QString filePath) {    if (!DependencyManager::get<NodeList>()->getThisNodeCanWriteAssets()) {        return;    }     static const QUrl url { "AssetServer.qml" };     auto startUpload = [=](QQmlContext* context, QObject* newObject){        if (!filePath.isEmpty()) {            emit uploadRequest(filePath);        }    };    DependencyManager::get<OffscreenUi>()->show(url, "AssetServer", startUpload);    startUpload(nullptr, nullptr);} void Application::packageModel() {    ModelPackager::package();} void Application::openUrl(const QUrl& url) const {    if (!url.isEmpty()) {        if (url.scheme() == HIFI_URL_SCHEME) {            DependencyManager::get<AddressManager>()->handleLookupString(url.toString());        } else {            // address manager did not handle - ask QDesktopServices to handle            QDesktopServices::openUrl(url);        }    }} void Application::loadDialog() {    auto scriptEngines = DependencyManager::get<ScriptEngines>();    QString fileNameString = OffscreenUi::getOpenFileName(        _glWidget, tr("Open Script"), getPreviousScriptLocation(), tr("JavaScript Files (*.js)"));    if (!fileNameString.isEmpty() && QFile(fileNameString).exists()) {        setPreviousScriptLocation(QFileInfo(fileNameString).absolutePath());        DependencyManager::get<ScriptEngines>()->loadScript(fileNameString, true, false, false, true);  // Don't load from cache    }} QString Application::getPreviousScriptLocation() {    QString result = _previousScriptLocation.get();    return result;} void Application::setPreviousScriptLocation(const QString& location) {    _previousScriptLocation.set(location);} void Application::loadScriptURLDialog() const {    auto newScript = OffscreenUi::getText(nullptr, "Open and Run Script", "Script URL");    if (!newScript.isEmpty()) {        DependencyManager::get<ScriptEngines>()->loadScript(newScript);    }} void Application::toggleLogDialog() {    if (! _logDialog) {        _logDialog = new LogDialog(_glWidget, getLogger());    }     if (_logDialog->isVisible()) {        _logDialog->hide();    } else {        _logDialog->show();    }} void Application::takeSnapshot() {    QMediaPlayer* player = new QMediaPlayer();    QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");    player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));    player->play();     QString fileName = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot());     auto accountManager = DependencyManager::get<AccountManager>();    if (!accountManager->isLoggedIn()) {        return;    }     DependencyManager::get<OffscreenUi>()->load("hifi/dialogs/SnapshotShareDialog.qml", [=](QQmlContext*, QObject* dialog) {        dialog->setProperty("source", QUrl::fromLocalFile(fileName));        connect(dialog, SIGNAL(uploadSnapshot(const QString& snapshot)), this, SLOT(uploadSnapshot(const QString& snapshot)));    });} float Application::getRenderResolutionScale() const {    if (Menu::getInstance()->isOptionChecked(MenuOption::RenderResolutionOne)) {        return 1.0f;

Lines 4501–5000 of 5476. Previous lines