mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-25 17:14:59 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
5fcf8e5922
23 changed files with 732 additions and 370 deletions
|
@ -19,3 +19,4 @@ Script.load("users.js");
|
|||
Script.load("grab.js");
|
||||
Script.load("pointer.js");
|
||||
Script.load("directory.js");
|
||||
Script.load("dialTone.js");
|
||||
|
|
|
@ -11,19 +11,26 @@
|
|||
//
|
||||
|
||||
// setup the local sound we're going to use
|
||||
var connectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/hello.wav");
|
||||
var disconnectSound = SoundCache.getSound("file://" + Paths.resources + "sounds/goodbye.wav");
|
||||
var connectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/hello.wav");
|
||||
var disconnectSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav");
|
||||
var micMutedSound = SoundCache.getSound("file:///" + Paths.resources + "sounds/goodbye.wav");
|
||||
|
||||
// setup the options needed for that sound
|
||||
var connectSoundOptions = {
|
||||
var soundOptions = {
|
||||
localOnly: true
|
||||
};
|
||||
|
||||
// play the sound locally once we get the first audio packet from a mixer
|
||||
Audio.receivedFirstPacket.connect(function(){
|
||||
Audio.playSound(connectSound, connectSoundOptions);
|
||||
Audio.playSound(connectSound, soundOptions);
|
||||
});
|
||||
|
||||
Audio.disconnected.connect(function(){
|
||||
Audio.playSound(disconnectSound, connectSoundOptions);
|
||||
Audio.playSound(disconnectSound, soundOptions);
|
||||
});
|
||||
|
||||
AudioDevice.muteToggled.connect(function () {
|
||||
if (AudioDevice.getMuted()) {
|
||||
Audio.playSound(micMutedSound, soundOptions);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -140,7 +140,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES})
|
|||
# link required hifi libraries
|
||||
link_hifi_libraries(shared octree environment gpu model render fbx networking entities avatars
|
||||
audio audio-client animation script-engine physics
|
||||
render-utils entities-renderer ui)
|
||||
render-utils entities-renderer ui auto-updater)
|
||||
|
||||
add_dependency_external_projects(sdl2)
|
||||
|
||||
|
|
172
interface/resources/qml/UpdateDialog.qml
Normal file
172
interface/resources/qml/UpdateDialog.qml
Normal file
|
@ -0,0 +1,172 @@
|
|||
import Hifi 1.0
|
||||
import QtQuick 2.3
|
||||
import QtQuick.Controls.Styles 1.3
|
||||
import QtGraphicalEffects 1.0
|
||||
import "controls"
|
||||
import "styles"
|
||||
|
||||
DialogContainer {
|
||||
HifiConstants { id: hifi }
|
||||
id: root
|
||||
objectName: "UpdateDialog"
|
||||
implicitWidth: updateDialog.width
|
||||
implicitHeight: updateDialog.height
|
||||
x: parent ? parent.width / 2 - width / 2 : 0
|
||||
y: parent ? parent.height / 2 - height / 2 : 0
|
||||
|
||||
UpdateDialog {
|
||||
id: updateDialog
|
||||
|
||||
implicitWidth: backgroundRectangle.width
|
||||
implicitHeight: backgroundRectangle.height
|
||||
|
||||
readonly property int inputWidth: 500
|
||||
readonly property int inputHeight: 60
|
||||
readonly property int borderWidth: 30
|
||||
readonly property int closeMargin: 16
|
||||
readonly property int inputSpacing: 16
|
||||
readonly property int buttonWidth: 150
|
||||
readonly property int buttonHeight: 50
|
||||
readonly property int buttonRadius: 15
|
||||
|
||||
signal triggerBuildDownload
|
||||
signal closeUpdateDialog
|
||||
|
||||
Column {
|
||||
id: mainContent
|
||||
width: updateDialog.inputWidth
|
||||
spacing: updateDialog.inputSpacing
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: backgroundRectangle
|
||||
color: "#2c86b1"
|
||||
opacity: 0.85
|
||||
radius: updateDialog.closeMargin * 2
|
||||
|
||||
width: updateDialog.inputWidth + updateDialog.borderWidth * 2
|
||||
height: updateDialog.inputHeight * 6 + updateDialog.closeMargin * 2
|
||||
|
||||
Rectangle {
|
||||
id: dialogTitle
|
||||
width: updateDialog.inputWidth
|
||||
height: updateDialog.inputHeight
|
||||
radius: height / 2
|
||||
color: "#ebebeb"
|
||||
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: updateDialog.inputSpacing
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
id: updateAvailableText
|
||||
text: "Update Available"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: updateDialog.inputSpacing
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: updateDialog.updateAvailableDetails
|
||||
font.pixelSize: 14
|
||||
color: hifi.colors.text
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: updateAvailableText.right
|
||||
leftMargin: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Flickable {
|
||||
id: scrollArea
|
||||
anchors {
|
||||
top: dialogTitle.bottom
|
||||
}
|
||||
contentWidth: updateDialog.inputWidth
|
||||
contentHeight: backgroundRectangle.height - (dialogTitle.height * 2.5)
|
||||
width: updateDialog.inputWidth
|
||||
height: backgroundRectangle.height - (dialogTitle.height * 2.5)
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
clip: true
|
||||
|
||||
TextEdit {
|
||||
id: releaseNotes
|
||||
wrapMode: TextEdit.Wrap
|
||||
width: parent.width
|
||||
readOnly: true
|
||||
text: updateDialog.releaseNotes
|
||||
font.pixelSize: 14
|
||||
color: hifi.colors.text
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: updateDialog.borderWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: downloadButton
|
||||
width: updateDialog.buttonWidth
|
||||
height: updateDialog.buttonHeight
|
||||
radius: updateDialog.buttonRadius
|
||||
color: "green"
|
||||
anchors {
|
||||
top: scrollArea.bottom
|
||||
topMargin: 10
|
||||
right: backgroundRectangle.right
|
||||
rightMargin: 15
|
||||
}
|
||||
Text {
|
||||
text: "Upgrade"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: downloadButtonAction
|
||||
anchors.fill: parent
|
||||
onClicked: updateDialog.triggerUpgrade()
|
||||
cursorShape: "PointingHandCursor"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cancelButton
|
||||
width: updateDialog.buttonWidth
|
||||
height: updateDialog.buttonHeight
|
||||
radius: updateDialog.buttonRadius
|
||||
color: "red"
|
||||
anchors {
|
||||
top: scrollArea.bottom
|
||||
topMargin: 10
|
||||
right: downloadButton.left
|
||||
rightMargin: 15
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Cancel"
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
id: cancelButtonAction
|
||||
anchors.fill: parent
|
||||
onClicked: updateDialog.closeDialog()
|
||||
cursorShape: "PointingHandCursor"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@
|
|||
#include <CursorManager.h>
|
||||
#include <AmbientOcclusionEffect.h>
|
||||
#include <AudioInjector.h>
|
||||
#include <AutoUpdater.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
|
@ -147,6 +148,7 @@
|
|||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/AddressBarDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
|
||||
|
||||
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
|
@ -296,6 +298,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
|
||||
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::set<OffscreenUi>();
|
||||
auto autoUpdater = DependencyManager::set<AutoUpdater>();
|
||||
auto pathUtils = DependencyManager::set<PathUtils>();
|
||||
auto actionFactory = DependencyManager::set<InterfaceActionFactory>();
|
||||
|
||||
|
@ -563,8 +566,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
// allow you to move an entity around in your hand
|
||||
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
||||
|
||||
checkVersion();
|
||||
|
||||
_overlays.init(); // do this before scripts load
|
||||
|
||||
_runningScriptsWidget->setRunningScripts(getRunningScripts());
|
||||
|
@ -636,9 +637,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
ddeTracker->init();
|
||||
connect(ddeTracker.data(), &FaceTracker::muteToggled, this, &Application::faceTrackerMuteToggled);
|
||||
#endif
|
||||
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
connect(applicationUpdater.data(), &AutoUpdater::newVersionIsAvailable, dialogsManager.data(), &DialogsManager::showUpdateDialog);
|
||||
applicationUpdater->checkForUpdate();
|
||||
}
|
||||
|
||||
|
||||
void Application::aboutToQuit() {
|
||||
emit beforeAboutToQuit();
|
||||
|
||||
|
@ -828,6 +832,7 @@ void Application::initializeUi() {
|
|||
LoginDialog::registerType();
|
||||
MessageDialog::registerType();
|
||||
VrMenu::registerType();
|
||||
UpdateDialog::registerType();
|
||||
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
offscreenUi->create(_glWidget->context()->contextHandle());
|
||||
|
@ -4523,72 +4528,6 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::checkVersion() {
|
||||
QNetworkRequest latestVersionRequest((QUrl(CHECK_VERSION_URL)));
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
latestVersionRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
QNetworkReply* reply = NetworkAccessManager::getInstance().get(latestVersionRequest);
|
||||
connect(reply, SIGNAL(finished()), SLOT(parseVersionXml()));
|
||||
}
|
||||
|
||||
void Application::parseVersionXml() {
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
QString operatingSystem("win");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
QString operatingSystem("mac");
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
QString operatingSystem("ubuntu");
|
||||
#endif
|
||||
|
||||
QString latestVersion;
|
||||
QUrl downloadUrl;
|
||||
QString releaseNotes("Unavailable");
|
||||
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
QXmlStreamReader xml(sender);
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) {
|
||||
while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) {
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "version") {
|
||||
xml.readNext();
|
||||
latestVersion = xml.text().toString();
|
||||
}
|
||||
if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name().toString() == "url") {
|
||||
xml.readNext();
|
||||
downloadUrl = QUrl(xml.text().toString());
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) {
|
||||
new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl);
|
||||
}
|
||||
sender->deleteLater();
|
||||
}
|
||||
|
||||
bool Application::shouldSkipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::ReadWrite);
|
||||
QString skipVersion(skipFile.readAll());
|
||||
return (skipVersion == latestVersion || applicationVersion() == "dev");
|
||||
}
|
||||
|
||||
void Application::skipVersion(QString latestVersion) {
|
||||
QFile skipFile(SKIP_FILENAME);
|
||||
skipFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
skipFile.seek(0);
|
||||
skipFile.write(latestVersion.toStdString().c_str());
|
||||
}
|
||||
|
||||
void Application::takeSnapshot() {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
|
||||
|
|
|
@ -66,7 +66,6 @@
|
|||
#include "ui/SnapshotShareDialog.h"
|
||||
#include "ui/LodToolsDialog.h"
|
||||
#include "ui/LogDialog.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
#include "ui/overlays/Overlays.h"
|
||||
#include "ui/ApplicationOverlay.h"
|
||||
#include "ui/RunningScriptsWidget.h"
|
||||
|
@ -312,8 +311,6 @@ public:
|
|||
|
||||
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
|
||||
|
||||
void skipVersion(QString latestVersion);
|
||||
|
||||
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
|
||||
ScriptEngine* getScriptEngine(QString scriptHash) { return _scriptEnginesHash.contains(scriptHash) ? _scriptEnginesHash[scriptHash] : NULL; }
|
||||
|
||||
|
@ -460,8 +457,6 @@ private slots:
|
|||
void restoreMirrorView();
|
||||
void shrinkMirrorView();
|
||||
|
||||
void parseVersionXml();
|
||||
|
||||
void manageRunningScriptsWidgetVisibility(bool shown);
|
||||
|
||||
void runTests();
|
||||
|
@ -627,9 +622,6 @@ private:
|
|||
|
||||
FileLogger* _logger;
|
||||
|
||||
void checkVersion();
|
||||
void displayUpdateDialog();
|
||||
bool shouldSkipVersion(QString latestVersion);
|
||||
void takeSnapshot();
|
||||
|
||||
TouchEvent _lastTouchEvent;
|
||||
|
|
|
@ -90,6 +90,30 @@ Menu::Menu() {
|
|||
addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J,
|
||||
qApp, SLOT(toggleRunningScriptsWidget()));
|
||||
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "History");
|
||||
|
||||
QAction* backAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Back,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goBack()));
|
||||
|
||||
QAction* forwardAction = addActionToQMenuAndActionHash(fileMenu,
|
||||
MenuOption::Forward,
|
||||
0,
|
||||
addressManager.data(),
|
||||
SLOT(goForward()));
|
||||
|
||||
// connect to the AddressManager signal to enable and disable the back and forward menu items
|
||||
connect(addressManager.data(), &AddressManager::goBackPossible, backAction, &QAction::setEnabled);
|
||||
connect(addressManager.data(), &AddressManager::goForwardPossible, forwardAction, &QAction::setEnabled);
|
||||
|
||||
// set the two actions to start disabled since the stacks are clear on startup
|
||||
backAction->setDisabled(true);
|
||||
forwardAction->setDisabled(true);
|
||||
|
||||
addDisabledActionAndSeparator(fileMenu, "Location");
|
||||
qApp->getBookmarks()->setupMenus(this, fileMenu);
|
||||
|
||||
|
@ -98,7 +122,6 @@ Menu::Menu() {
|
|||
Qt::CTRL | Qt::Key_L,
|
||||
dialogsManager.data(),
|
||||
SLOT(toggleAddressBar()));
|
||||
auto addressManager = DependencyManager::get<AddressManager>();
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0,
|
||||
addressManager.data(), SLOT(copyAddress()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0,
|
||||
|
@ -407,7 +430,7 @@ Menu::Menu() {
|
|||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default
|
||||
qApp, SLOT(toggleFaceTrackerMute()));
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true);
|
||||
addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, false);
|
||||
#endif
|
||||
|
||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||
|
|
|
@ -149,6 +149,7 @@ namespace MenuOption {
|
|||
const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams";
|
||||
const QString AutoMuteAudio = "Auto Mute Microphone";
|
||||
const QString AvatarReceiveStats = "Show Receive Stats";
|
||||
const QString Back = "Back";
|
||||
const QString BandwidthDetails = "Bandwidth Details";
|
||||
const QString BinaryEyelidControl = "Binary Eyelid Control";
|
||||
const QString BlueSpeechSphere = "Blue Sphere While Speaking";
|
||||
|
@ -194,12 +195,12 @@ namespace MenuOption {
|
|||
const QString Faceshift = "Faceshift";
|
||||
const QString FilterSixense = "Smooth Sixense Movement";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString Forward = "Forward";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString Fullscreen = "Fullscreen";
|
||||
const QString FullscreenMirror = "Fullscreen Mirror";
|
||||
const QString GlowWhenSpeaking = "Glow When Speaking";
|
||||
const QString NamesAboveHeads = "Names Above Heads";
|
||||
const QString GoToUser = "Go To User";
|
||||
const QString HMDTools = "HMD Tools";
|
||||
const QString IncreaseAvatarSize = "Increase Avatar Size";
|
||||
const QString KeyboardMotorControl = "Enable Keyboard Motor Control";
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "OctreeStatsDialog.h"
|
||||
#include "PreferencesDialog.h"
|
||||
#include "ScriptEditorWindow.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
|
||||
void DialogsManager::toggleAddressBar() {
|
||||
|
@ -50,6 +51,10 @@ void DialogsManager::showLoginDialog() {
|
|||
LoginDialog::show();
|
||||
}
|
||||
|
||||
void DialogsManager::showUpdateDialog() {
|
||||
UpdateDialog::show();
|
||||
}
|
||||
|
||||
void DialogsManager::octreeStatsDetails() {
|
||||
if (!_octreeStatsDialog) {
|
||||
_octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats());
|
||||
|
|
|
@ -35,6 +35,7 @@ class ScriptEditorWindow;
|
|||
class QMessageBox;
|
||||
class AvatarAppearanceDialog;
|
||||
class DomainConnectionDialog;
|
||||
class UpdateDialog;
|
||||
|
||||
class DialogsManager : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
|
@ -64,6 +65,9 @@ public slots:
|
|||
void showIRCLink();
|
||||
void changeAvatarAppearance();
|
||||
void showDomainConnectionDialog();
|
||||
|
||||
// Application Update
|
||||
void showUpdateDialog();
|
||||
|
||||
private slots:
|
||||
void toggleToolWindow();
|
||||
|
@ -101,6 +105,7 @@ private:
|
|||
QPointer<ScriptEditorWindow> _scriptEditor;
|
||||
QPointer<AvatarAppearanceDialog> _avatarAppearanceDialog;
|
||||
QPointer<DomainConnectionDialog> _domainConnectionDialog;
|
||||
QPointer<UpdateDialog> _updateDialog;
|
||||
};
|
||||
|
||||
#endif // hifi_DialogsManager_h
|
||||
|
|
|
@ -2,54 +2,50 @@
|
|||
// UpdateDialog.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Leonardo Murillo on 6/3/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "ui_updateDialog.h"
|
||||
|
||||
#include "Application.h"
|
||||
#include "UpdateDialog.h"
|
||||
|
||||
#include <AutoUpdater.h>
|
||||
|
||||
UpdateDialog::UpdateDialog(QWidget *parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL) :
|
||||
QDialog(parent),
|
||||
_latestVersion(latestVersion),
|
||||
_downloadUrl(downloadURL)
|
||||
#include "DependencyManager.h"
|
||||
|
||||
HIFI_QML_DEF(UpdateDialog)
|
||||
|
||||
UpdateDialog::UpdateDialog(QQuickItem* parent) :
|
||||
OffscreenQmlDialog(parent)
|
||||
{
|
||||
Ui::Dialog dialogUI;
|
||||
dialogUI.setupUi(this);
|
||||
|
||||
QString updateRequired = QString("You are currently running build %1, the latest build released is %2."
|
||||
"\n\nPlease download and install the most recent release to access the latest features and bug fixes.")
|
||||
.arg(Application::getInstance()->applicationVersion(), latestVersion);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
QPushButton* downloadButton = findChild<QPushButton*>("downloadButton");
|
||||
QPushButton* skipButton = findChild<QPushButton*>("skipButton");
|
||||
QPushButton* closeButton = findChild<QPushButton*>("closeButton");
|
||||
QLabel* updateContent = findChild<QLabel*>("updateContent");
|
||||
|
||||
updateContent->setText(updateRequired);
|
||||
|
||||
connect(downloadButton, SIGNAL(released()), this, SLOT(handleDownload()));
|
||||
connect(skipButton, SIGNAL(released()), this, SLOT(handleSkip()));
|
||||
connect(closeButton, SIGNAL(released()), this, SLOT(close()));
|
||||
|
||||
QMetaObject::invokeMethod(this, "show", Qt::QueuedConnection);
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
int currentVersion = QCoreApplication::applicationVersion().toInt();
|
||||
int latestVersion = applicationUpdater.data()->getBuildData().lastKey();
|
||||
int versionsBehind = latestVersion - currentVersion;
|
||||
_updateAvailableDetails = "v" + QString::number(latestVersion) + " released on " + applicationUpdater.data()->getBuildData()[latestVersion]["releaseTime"];
|
||||
_updateAvailableDetails += "\nYou are " + QString::number(versionsBehind) + " versions behind";
|
||||
_releaseNotes = applicationUpdater.data()->getBuildData()[latestVersion]["releaseNotes"];
|
||||
}
|
||||
|
||||
void UpdateDialog::handleDownload() {
|
||||
QDesktopServices::openUrl(_downloadUrl);
|
||||
Application::getInstance()->quit();
|
||||
const QString& UpdateDialog::updateAvailableDetails() const {
|
||||
return _updateAvailableDetails;
|
||||
}
|
||||
|
||||
void UpdateDialog::handleSkip() {
|
||||
Application::getInstance()->skipVersion(_latestVersion);
|
||||
this->close();
|
||||
const QString& UpdateDialog::releaseNotes() const {
|
||||
return _releaseNotes;
|
||||
}
|
||||
|
||||
void UpdateDialog::closeDialog() {
|
||||
hide();
|
||||
}
|
||||
|
||||
void UpdateDialog::hide() {
|
||||
((QQuickItem*)parent())->setEnabled(false);
|
||||
}
|
||||
|
||||
void UpdateDialog::triggerUpgrade() {
|
||||
auto applicationUpdater = DependencyManager::get<AutoUpdater>();
|
||||
applicationUpdater.data()->performAutoUpdate(applicationUpdater.data()->getBuildData().lastKey());
|
||||
}
|
|
@ -2,30 +2,42 @@
|
|||
// UpdateDialog.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
// Created by Leonardo Murillo on 6/3/15.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef hifi_UpdateDialog_h
|
||||
#define hifi_UpdateDialog_h
|
||||
|
||||
#include <QWidget>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
class UpdateDialog : public QDialog {
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
class UpdateDialog : public OffscreenQmlDialog {
|
||||
Q_OBJECT
|
||||
HIFI_QML_DECL
|
||||
|
||||
Q_PROPERTY(QString updateAvailableDetails READ updateAvailableDetails)
|
||||
Q_PROPERTY(QString releaseNotes READ releaseNotes)
|
||||
|
||||
public:
|
||||
UpdateDialog(QWidget* parent, const QString& releaseNotes, const QString& latestVersion, const QUrl& downloadURL);
|
||||
UpdateDialog(QQuickItem* parent = nullptr);
|
||||
const QString& updateAvailableDetails() const;
|
||||
const QString& releaseNotes() const;
|
||||
|
||||
private:
|
||||
QString _latestVersion;
|
||||
QUrl _downloadUrl;
|
||||
|
||||
private slots:
|
||||
void handleDownload();
|
||||
void handleSkip();
|
||||
QString _updateAvailableDetails;
|
||||
QString _releaseNotes;
|
||||
|
||||
protected:
|
||||
void hide();
|
||||
Q_INVOKABLE void triggerUpgrade();
|
||||
Q_INVOKABLE void closeDialog();
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_UpdateDialog_h
|
||||
|
|
|
@ -1,132 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::NonModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>750</width>
|
||||
<height>213</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Update Required</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(255, 255, 255);</string>
|
||||
</property>
|
||||
<widget class="QLabel" name="updateContent">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>20</y>
|
||||
<width>641</width>
|
||||
<height>111</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>-1</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">font-family: Arial;
|
||||
font-size: 20px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>360</x>
|
||||
<y>160</y>
|
||||
<width>374</width>
|
||||
<height>42</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="downloadButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Download</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="skipButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Skip Version</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"> background-color: #333333;
|
||||
border-width: 0;
|
||||
border-radius: 9px;
|
||||
border-radius: 9px;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
font-weight: 100;
|
||||
color: #b7b7b7;
|
||||
width: 120px;
|
||||
height: 40px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
3
libraries/auto-updater/CMakeLists.txt
Normal file
3
libraries/auto-updater/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
set(TARGET_NAME auto-updater)
|
||||
setup_hifi_library(Network)
|
||||
link_hifi_libraries(shared networking)
|
144
libraries/auto-updater/src/AutoUpdater.cpp
Normal file
144
libraries/auto-updater/src/AutoUpdater.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
//
|
||||
// AutoUpdater.cpp
|
||||
// libraries/auto-update/src
|
||||
//
|
||||
// Created by Leonardo Murillo on 6/1/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AutoUpdater.h"
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
AutoUpdater::AutoUpdater() {
|
||||
#if defined Q_OS_WIN32
|
||||
_operatingSystem = "windows";
|
||||
#elif defined Q_OS_MAC
|
||||
_operatingSystem = "mac";
|
||||
#elif defined Q_OS_LINUX
|
||||
_operatingSystem = "ubuntu";
|
||||
#endif
|
||||
|
||||
connect(this, SIGNAL(latestVersionDataParsed()), this, SLOT(checkVersionAndNotify()));
|
||||
}
|
||||
|
||||
void AutoUpdater::checkForUpdate() {
|
||||
this->getLatestVersionData();
|
||||
}
|
||||
|
||||
void AutoUpdater::getLatestVersionData() {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest latestVersionRequest(BUILDS_XML_URL);
|
||||
latestVersionRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(latestVersionRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &AutoUpdater::parseLatestVersionData);
|
||||
}
|
||||
|
||||
void AutoUpdater::parseLatestVersionData() {
|
||||
QNetworkReply* sender = qobject_cast<QNetworkReply*>(QObject::sender());
|
||||
|
||||
QXmlStreamReader xml(sender);
|
||||
|
||||
int version;
|
||||
QString downloadUrl;
|
||||
QString releaseTime;
|
||||
QString releaseNotes;
|
||||
QString commitSha;
|
||||
QString pullRequestNumber;
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
if (xml.name().toString() == "project" &&
|
||||
xml.attributes().hasAttribute("name") &&
|
||||
xml.attributes().value("name").toString() == "interface") {
|
||||
xml.readNext();
|
||||
|
||||
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "project") {
|
||||
if (xml.name().toString() == "platform" &&
|
||||
xml.attributes().hasAttribute("name") &&
|
||||
xml.attributes().value("name").toString() == _operatingSystem) {
|
||||
xml.readNext();
|
||||
while (!xml.atEnd() && !xml.hasError() &&
|
||||
xml.name().toString() != "platform") {
|
||||
|
||||
if (xml.name().toString() == "build" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
xml.readNext();
|
||||
version = xml.readElementText().toInt();
|
||||
xml.readNext();
|
||||
downloadUrl = xml.readElementText();
|
||||
xml.readNext();
|
||||
releaseTime = xml.readElementText();
|
||||
xml.readNext();
|
||||
if (xml.name().toString() == "notes" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
xml.readNext();
|
||||
while (!xml.atEnd() && !xml.hasError() && xml.name().toString() != "notes") {
|
||||
if (xml.name().toString() == "note" && xml.tokenType() != QXmlStreamReader::EndElement) {
|
||||
releaseNotes = releaseNotes + "\n" + xml.readElementText();
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
commitSha = xml.readElementText();
|
||||
xml.readNext();
|
||||
pullRequestNumber = xml.readElementText();
|
||||
appendBuildData(version, downloadUrl, releaseTime, releaseNotes, pullRequestNumber);
|
||||
releaseNotes = "";
|
||||
}
|
||||
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
xml.readNext();
|
||||
}
|
||||
|
||||
} else {
|
||||
xml.readNext();
|
||||
}
|
||||
}
|
||||
sender->deleteLater();
|
||||
emit latestVersionDataParsed();
|
||||
}
|
||||
|
||||
void AutoUpdater::checkVersionAndNotify() {
|
||||
if (QCoreApplication::applicationVersion() == "dev" || _builds.empty()) {
|
||||
// No version checking is required in dev builds or when no build
|
||||
// data was found for the platform
|
||||
return;
|
||||
}
|
||||
int latestVersionAvailable = _builds.lastKey();
|
||||
if (QCoreApplication::applicationVersion().toInt() < latestVersionAvailable) {
|
||||
emit newVersionIsAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
void AutoUpdater::performAutoUpdate(int version) {
|
||||
// NOTE: This is not yet auto updating - however this is a checkpoint towards that end
|
||||
// Next PR will handle the automatic download, upgrading and application restart
|
||||
const QMap<QString, QString>& chosenVersion = _builds.value(version);
|
||||
const QUrl& downloadUrl = chosenVersion.value("downloadUrl");
|
||||
QDesktopServices::openUrl(downloadUrl);
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
|
||||
void AutoUpdater::downloadUpdateVersion(int version) {
|
||||
emit newVersionIsDownloaded();
|
||||
}
|
||||
|
||||
void AutoUpdater::appendBuildData(int versionNumber,
|
||||
const QString& downloadURL,
|
||||
const QString& releaseTime,
|
||||
const QString& releaseNotes,
|
||||
const QString& pullRequestNumber) {
|
||||
|
||||
QMap<QString, QString> thisBuildDetails;
|
||||
thisBuildDetails.insert("downloadUrl", downloadURL);
|
||||
thisBuildDetails.insert("releaseTime", releaseTime);
|
||||
thisBuildDetails.insert("releaseNotes", releaseNotes);
|
||||
thisBuildDetails.insert("pullRequestNumber", pullRequestNumber);
|
||||
_builds.insert(versionNumber, thisBuildDetails);
|
||||
|
||||
}
|
68
libraries/auto-updater/src/AutoUpdater.h
Normal file
68
libraries/auto-updater/src/AutoUpdater.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// AutoUpdater.h
|
||||
// libraries/auto-update/src
|
||||
//
|
||||
// Created by Leonardo Murillo on 6/1/2015.
|
||||
// Copyright 2015 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_AutoUpdater_h
|
||||
#define hifi_AutoUpdater_h
|
||||
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QXmlStreamAttributes>
|
||||
#include <QtCore/QXmlStreamReader>
|
||||
#include <QtGui/QDesktopServices>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkConfiguration>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
const QUrl BUILDS_XML_URL("https://highfidelity.com/builds.xml");
|
||||
|
||||
class AutoUpdater : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
AutoUpdater();
|
||||
|
||||
void checkForUpdate();
|
||||
const QMap<int, QMap<QString, QString>>& getBuildData() { return _builds; }
|
||||
void performAutoUpdate(int version);
|
||||
|
||||
signals:
|
||||
void latestVersionDataParsed();
|
||||
void newVersionIsAvailable();
|
||||
void newVersionIsDownloaded();
|
||||
|
||||
private:
|
||||
QMap<int, QMap<QString, QString>> _builds;
|
||||
QString _operatingSystem;
|
||||
|
||||
void getLatestVersionData();
|
||||
void downloadUpdateVersion(int version);
|
||||
void appendBuildData(int versionNumber,
|
||||
const QString& downloadURL,
|
||||
const QString& releaseTime,
|
||||
const QString& releaseNotes,
|
||||
const QString& pullRequestNumber);
|
||||
|
||||
private slots:
|
||||
void parseLatestVersionData();
|
||||
void checkVersionAndNotify();
|
||||
};
|
||||
|
||||
#endif // _hifi_AutoUpdater_h
|
|
@ -41,7 +41,11 @@ public:
|
|||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
|
||||
|
||||
virtual void somethingChangedNotification() { _needsInitialSimulation = true; }
|
||||
virtual void somethingChangedNotification() {
|
||||
// FIX ME: this is overly aggressive. We only really need to simulate() if something about
|
||||
// the world space transform has changed and/or if some animation is occurring.
|
||||
_needsInitialSimulation = true;
|
||||
}
|
||||
|
||||
virtual bool readyToAddToScene(RenderArgs* renderArgs = nullptr);
|
||||
virtual bool addToScene(EntityItemPointer self, std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
|
|
|
@ -53,7 +53,7 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
|||
batch.setUniformTexture(0, _texture->getGPUTexture());
|
||||
}
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch);
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, textured);
|
||||
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID);
|
||||
};
|
||||
|
||||
|
@ -75,31 +75,34 @@ void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool text
|
|||
vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
|
||||
|
||||
if (textured) {
|
||||
positions.reserve(getLivingParticleCount());
|
||||
textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
|
||||
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
positions.append(_particlePositions[i]);
|
||||
|
||||
}
|
||||
positions.reserve(getLivingParticleCount());
|
||||
|
||||
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
positions.append(_particlePositions[i]);
|
||||
if (textured) {
|
||||
textureCoords.append(glm::vec2(0, 1));
|
||||
textureCoords.append(glm::vec2(1, 1));
|
||||
textureCoords.append(glm::vec2(1, 0));
|
||||
textureCoords.append(glm::vec2(0, 0));
|
||||
}
|
||||
|
||||
// sort particles back to front
|
||||
::zSortAxis = args->_viewFrustum->getDirection();
|
||||
qSort(positions.begin(), positions.end(), zSort);
|
||||
}
|
||||
|
||||
// sort particles back to front
|
||||
::zSortAxis = args->_viewFrustum->getDirection();
|
||||
qSort(positions.begin(), positions.end(), zSort);
|
||||
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i];
|
||||
|
||||
// generate corners of quad aligned to face the camera.
|
||||
vertices.append(pos - rightOffset + upOffset);
|
||||
vertices.append(pos + rightOffset + upOffset);
|
||||
vertices.append(pos + rightOffset - upOffset);
|
||||
vertices.append(pos - rightOffset + upOffset);
|
||||
vertices.append(pos - rightOffset - upOffset);
|
||||
vertices.append(pos + rightOffset - upOffset);
|
||||
|
||||
}
|
||||
|
||||
if (textured) {
|
||||
|
|
|
@ -54,9 +54,33 @@ const QUrl AddressManager::currentAddress() const {
|
|||
|
||||
void AddressManager::loadSettings(const QString& lookupString) {
|
||||
if (lookupString.isEmpty()) {
|
||||
handleLookupString(currentAddressHandle.get().toString());
|
||||
handleUrl(currentAddressHandle.get().toString(), LookupTrigger::StartupFromSettings);
|
||||
} else {
|
||||
handleLookupString(lookupString);
|
||||
handleUrl(lookupString, LookupTrigger::StartupFromSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void AddressManager::goBack() {
|
||||
if (_backStack.size() > 0) {
|
||||
// go to that address
|
||||
handleUrl(_backStack.pop(), LookupTrigger::Back);
|
||||
|
||||
if (_backStack.size() == 0) {
|
||||
// the back stack is now empty so it is no longer possible to go back - emit that signal
|
||||
emit goBackPossible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddressManager::goForward() {
|
||||
if (_forwardStack.size() > 0) {
|
||||
// pop a URL from the forwardStack and go to that address
|
||||
handleUrl(_forwardStack.pop(), LookupTrigger::Forward);
|
||||
|
||||
if (_forwardStack.size() == 0) {
|
||||
// the forward stack is empty so it is no longer possible to go forwards - emit that signal
|
||||
emit goForwardPossible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +126,7 @@ const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
|
|||
return callbackParams;
|
||||
}
|
||||
|
||||
bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
||||
bool AddressManager::handleUrl(const QUrl& lookupUrl, LookupTrigger trigger) {
|
||||
if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
|
||||
|
||||
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
|
||||
|
@ -121,17 +145,17 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
|||
// we're assuming this is either a network address or global place name
|
||||
// check if it is a network address first
|
||||
if (handleNetworkAddress(lookupUrl.host()
|
||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
|
||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())), trigger)) {
|
||||
// we may have a path that defines a relative viewpoint - if so we should jump to that now
|
||||
handlePath(lookupUrl.path());
|
||||
handlePath(lookupUrl.path(), trigger);
|
||||
} else if (handleDomainID(lookupUrl.host())){
|
||||
// no place name - this is probably a domain ID
|
||||
// try to look up the domain ID on the metaverse API
|
||||
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path());
|
||||
attemptDomainIDLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
||||
} else {
|
||||
// wasn't an address - lookup the place name
|
||||
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
|
||||
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path());
|
||||
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path(), trigger);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,8 +164,10 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
|||
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
|
||||
|
||||
// if this is a relative path then handle it as a relative viewpoint
|
||||
handlePath(lookupUrl.path());
|
||||
handlePath(lookupUrl.path(), trigger, true);
|
||||
emit lookupResultsFinished();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -183,6 +209,7 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
|
|||
}
|
||||
|
||||
const char OVERRIDE_PATH_KEY[] = "override_path";
|
||||
const char LOOKUP_TRIGGER_KEY[] = "lookup_trigger";
|
||||
|
||||
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
|
||||
|
||||
|
@ -243,6 +270,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
||||
}
|
||||
|
||||
LookupTrigger trigger = (LookupTrigger) reply.property(LOOKUP_TRIGGER_KEY).toInt();
|
||||
|
||||
// set our current root place id to the ID that came back
|
||||
const QString PLACE_ID_KEY = "id";
|
||||
_rootPlaceID = rootMap[PLACE_ID_KEY].toUuid();
|
||||
|
@ -251,16 +280,16 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
const QString PLACE_NAME_KEY = "name";
|
||||
QString placeName = rootMap[PLACE_NAME_KEY].toString();
|
||||
if (!placeName.isEmpty()) {
|
||||
setHost(placeName);
|
||||
setHost(placeName, trigger);
|
||||
} else {
|
||||
setHost(domainIDString);
|
||||
setHost(domainIDString, trigger);
|
||||
}
|
||||
|
||||
// check if we had a path to override the path returned
|
||||
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
||||
|
||||
if (!overridePath.isEmpty()) {
|
||||
handlePath(overridePath);
|
||||
handlePath(overridePath, trigger);
|
||||
} else {
|
||||
// take the path that came back
|
||||
const QString PLACE_PATH_KEY = "path";
|
||||
|
@ -269,10 +298,14 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
|
|||
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
|
||||
|
||||
if (!returnedPath.isEmpty()) {
|
||||
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
|
||||
if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) {
|
||||
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -"
|
||||
<< returnedPath;
|
||||
if (shouldFaceViewpoint) {
|
||||
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
|
||||
if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) {
|
||||
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -"
|
||||
<< returnedPath;
|
||||
}
|
||||
} else {
|
||||
handlePath(overridePath, trigger);
|
||||
}
|
||||
} else {
|
||||
// we didn't override the path or get one back - ask the DS for the viewpoint of its index path
|
||||
|
@ -306,15 +339,20 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
|||
|
||||
const QString GET_PLACE = "/api/v1/places/%1";
|
||||
|
||||
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) {
|
||||
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) {
|
||||
// assume this is a place name and see if we can get any info on it
|
||||
QString placeName = QUrl::toPercentEncoding(lookupString);
|
||||
|
||||
QVariantMap requestParams;
|
||||
|
||||
// if the user asked for a specific path with this lookup then keep it with the request so we can use it later
|
||||
if (!overridePath.isEmpty()) {
|
||||
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
|
||||
}
|
||||
|
||||
// remember how this lookup was triggered for history storage handling later
|
||||
requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast<int>(trigger));
|
||||
|
||||
AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName),
|
||||
AccountManagerAuth::None,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
|
@ -324,15 +362,20 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q
|
|||
|
||||
const QString GET_DOMAIN_ID = "/api/v1/domains/%1";
|
||||
|
||||
void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath) {
|
||||
void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger) {
|
||||
// assume this is a domain ID and see if we can get any info on it
|
||||
QString domainID = QUrl::toPercentEncoding(lookupString);
|
||||
|
||||
QVariantMap requestParams;
|
||||
|
||||
// if the user asked for a specific path with this lookup then keep it with the request so we can use it later
|
||||
if (!overridePath.isEmpty()) {
|
||||
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
|
||||
}
|
||||
|
||||
// remember how this lookup was triggered for history storage handling later
|
||||
requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast<int>(trigger));
|
||||
|
||||
AccountManager::getInstance().sendRequest(GET_DOMAIN_ID.arg(domainID),
|
||||
AccountManagerAuth::None,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
|
@ -340,7 +383,7 @@ void AddressManager::attemptDomainIDLookup(const QString& lookupString, const QS
|
|||
QByteArray(), NULL, requestParams);
|
||||
}
|
||||
|
||||
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
||||
bool AddressManager::handleNetworkAddress(const QString& lookupString, LookupTrigger trigger) {
|
||||
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
|
||||
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
|
||||
|
||||
|
@ -358,7 +401,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
|||
}
|
||||
|
||||
emit lookupResultsFinished();
|
||||
setDomainInfo(domainIPString, domainPort);
|
||||
setDomainInfo(domainIPString, domainPort, trigger);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -375,7 +418,7 @@ bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
|||
}
|
||||
|
||||
emit lookupResultsFinished();
|
||||
setDomainInfo(domainHostname, domainPort);
|
||||
setDomainInfo(domainHostname, domainPort, trigger);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -391,15 +434,26 @@ bool AddressManager::handleDomainID(const QString& host) {
|
|||
return (domainIDRegex.indexIn(host) != -1);
|
||||
}
|
||||
|
||||
void AddressManager::handlePath(const QString& path) {
|
||||
if (!handleViewpoint(path)) {
|
||||
void AddressManager::handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly) {
|
||||
if (!handleViewpoint(path, false, wasPathOnly)) {
|
||||
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path <<
|
||||
"- wll attempt to ask domain-server to resolve.";
|
||||
|
||||
if (!wasPathOnly) {
|
||||
// if we received a path with a host then we need to remember what it was here so we can not
|
||||
// double set add to the history stack once handle viewpoint is called with the result
|
||||
_newHostLookupPath = path;
|
||||
} else {
|
||||
// clear the _newHostLookupPath so it doesn't match when this return comes in
|
||||
_newHostLookupPath = QString();
|
||||
}
|
||||
|
||||
emit pathChangeRequired(path);
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) {
|
||||
bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace,
|
||||
bool definitelyPathOnly, const QString& pathString) {
|
||||
const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
|
||||
const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*";
|
||||
const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
|
||||
|
@ -416,8 +470,18 @@ bool AddressManager::handleViewpoint(const QString& viewpointString, bool should
|
|||
positionRegex.cap(2).toFloat(),
|
||||
positionRegex.cap(3).toFloat());
|
||||
|
||||
// we're about to jump positions - store the current address in our history
|
||||
addCurrentAddressToHistory();
|
||||
// We need to use definitelyPathOnly, pathString and _newHostLookupPath to determine if the current address
|
||||
// should be stored in the history before we ask for a position/orientation change. A relative path that was
|
||||
// not associated with a host lookup should always trigger a history change (definitelyPathOnly) and a viewpointString
|
||||
// with a non empty pathString (suggesting this is the result of a lookup with the domain-server) that does not match
|
||||
// _newHostLookupPath should always trigger a history change.
|
||||
//
|
||||
// We use _newHostLookupPath to determine if the client has already stored its last address
|
||||
// before moving to a new host thanks to the information in the same lookup URL.
|
||||
|
||||
if (definitelyPathOnly || (!pathString.isEmpty() && pathString != _newHostLookupPath)) {
|
||||
addCurrentAddressToHistory(LookupTrigger::UserInput);
|
||||
}
|
||||
|
||||
if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) {
|
||||
glm::quat newOrientation;
|
||||
|
@ -469,11 +533,11 @@ bool AddressManager::handleUsername(const QString& lookupString) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void AddressManager::setHost(const QString& host) {
|
||||
void AddressManager::setHost(const QString& host, LookupTrigger trigger) {
|
||||
if (host != _host) {
|
||||
|
||||
// if the host is being changed we should store current address in the history
|
||||
addCurrentAddressToHistory();
|
||||
addCurrentAddressToHistory(trigger);
|
||||
|
||||
_host = host;
|
||||
emit hostChanged(_host);
|
||||
|
@ -481,8 +545,8 @@ void AddressManager::setHost(const QString& host) {
|
|||
}
|
||||
|
||||
|
||||
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
||||
setHost(hostname);
|
||||
void AddressManager::setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger) {
|
||||
setHost(hostname, trigger);
|
||||
|
||||
_rootPlaceID = QUuid();
|
||||
|
||||
|
@ -495,11 +559,17 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
|
|||
|
||||
void AddressManager::goToUser(const QString& username) {
|
||||
QString formattedUsername = QUrl::toPercentEncoding(username);
|
||||
|
||||
// for history storage handling we remember how this lookup was trigged - for a username it's always user input
|
||||
QVariantMap requestParams;
|
||||
requestParams.insert(LOOKUP_TRIGGER_KEY, static_cast<int>(LookupTrigger::UserInput));
|
||||
|
||||
// this is a username - pull the captured name and lookup that user's location
|
||||
AccountManager::getInstance().sendRequest(GET_USER_LOCATION.arg(formattedUsername),
|
||||
AccountManagerAuth::Optional,
|
||||
QNetworkAccessManager::GetOperation,
|
||||
apiCallbackParameters());
|
||||
apiCallbackParameters(),
|
||||
QByteArray(), nullptr, requestParams);
|
||||
}
|
||||
|
||||
void AddressManager::copyAddress() {
|
||||
|
@ -510,21 +580,36 @@ void AddressManager::copyPath() {
|
|||
QApplication::clipboard()->setText(currentPath());
|
||||
}
|
||||
|
||||
void AddressManager::addCurrentAddressToHistory() {
|
||||
if (_lastHistoryAppend == 0) {
|
||||
// we don't store the first address on application load
|
||||
// just update the last append time so the next is stored
|
||||
_lastHistoryAppend = usecTimestampNow();
|
||||
} else {
|
||||
const quint64 DOUBLE_STORE_THRESHOLD_USECS = 500000;
|
||||
void AddressManager::addCurrentAddressToHistory(LookupTrigger trigger) {
|
||||
|
||||
// avoid double storing when the host changes and the viewpoint changes immediately after
|
||||
if (usecTimestampNow() - _lastHistoryAppend > DOUBLE_STORE_THRESHOLD_USECS) {
|
||||
// add the current address to the history
|
||||
_history.append(currentAddress());
|
||||
// if we're cold starting and this is called for the first address (from settings) we don't do anything
|
||||
if (trigger != LookupTrigger::StartupFromSettings) {
|
||||
if (trigger == LookupTrigger::UserInput) {
|
||||
// anyime the user has manually looked up an address we know we should clear the forward stack
|
||||
_forwardStack.clear();
|
||||
|
||||
// change our last history append to now
|
||||
_lastHistoryAppend = usecTimestampNow();
|
||||
emit goForwardPossible(false);
|
||||
}
|
||||
|
||||
if (trigger == LookupTrigger::Back) {
|
||||
// we're about to push to the forward stack
|
||||
// if it's currently empty emit our signal to say that going forward is now possible
|
||||
if (_forwardStack.size() == 0) {
|
||||
emit goForwardPossible(true);
|
||||
}
|
||||
|
||||
// when the user is going back, we move the current address to the forward stack
|
||||
// and do not but it into the back stack
|
||||
_forwardStack.push(currentAddress());
|
||||
} else {
|
||||
// we're about to push to the back stack
|
||||
// if it's currently empty emit our signal to say that going forward is now possible
|
||||
if (_forwardStack.size() == 0) {
|
||||
emit goBackPossible(true);
|
||||
}
|
||||
|
||||
// unless this was triggered from the result of a named path lookup, add the current address to the history
|
||||
_backStack.push(currentAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#ifndef hifi_AddressManager_h
|
||||
#define hifi_AddressManager_h
|
||||
|
||||
#include <qobject.h>
|
||||
#include <QSharedMemory>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QStack>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
@ -38,6 +38,14 @@ class AddressManager : public QObject, public Dependency {
|
|||
Q_PROPERTY(QString hostname READ getHost)
|
||||
Q_PROPERTY(QString pathname READ currentPath)
|
||||
public:
|
||||
|
||||
enum LookupTrigger {
|
||||
UserInput,
|
||||
Back,
|
||||
Forward,
|
||||
StartupFromSettings
|
||||
};
|
||||
|
||||
bool isConnected();
|
||||
const QString& getProtocol() { return HIFI_URL_SCHEME; };
|
||||
|
||||
|
@ -47,10 +55,7 @@ public:
|
|||
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
|
||||
|
||||
const QString& getHost() const { return _host; }
|
||||
void setHost(const QString& host);
|
||||
|
||||
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
|
||||
void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath = QString());
|
||||
|
||||
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
||||
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
||||
|
@ -60,9 +65,12 @@ public:
|
|||
public slots:
|
||||
void handleLookupString(const QString& lookupString);
|
||||
|
||||
void goToUser(const QString& username);
|
||||
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
|
||||
bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); }
|
||||
// we currently expect this to be called from NodeList once handleLookupString has been called with a path
|
||||
bool goToViewpointForPath(const QString& viewpointString, const QString& pathString)
|
||||
{ return handleViewpoint(viewpointString, false, false, pathString); }
|
||||
|
||||
void goBack();
|
||||
void goForward();
|
||||
|
||||
void storeCurrentAddress();
|
||||
|
||||
|
@ -73,40 +81,57 @@ signals:
|
|||
void lookupResultsFinished();
|
||||
void lookupResultIsOffline();
|
||||
void lookupResultIsNotFound();
|
||||
|
||||
void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort);
|
||||
void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID);
|
||||
|
||||
void locationChangeRequired(const glm::vec3& newPosition,
|
||||
bool hasOrientationChange, const glm::quat& newOrientation,
|
||||
bool shouldFaceLocation);
|
||||
void pathChangeRequired(const QString& newPath);
|
||||
void hostChanged(const QString& newHost);
|
||||
|
||||
void goBackPossible(bool isPossible);
|
||||
void goForwardPossible(bool isPossible);
|
||||
|
||||
protected:
|
||||
AddressManager();
|
||||
private slots:
|
||||
void handleAPIResponse(QNetworkReply& requestReply);
|
||||
void handleAPIError(QNetworkReply& errorReply);
|
||||
|
||||
void goToUser(const QString& username);
|
||||
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
|
||||
private:
|
||||
void setDomainInfo(const QString& hostname, quint16 port);
|
||||
void setHost(const QString& host, LookupTrigger trigger);
|
||||
void setDomainInfo(const QString& hostname, quint16 port, LookupTrigger trigger);
|
||||
|
||||
const JSONCallbackParameters& apiCallbackParameters();
|
||||
|
||||
bool handleUrl(const QUrl& lookupUrl);
|
||||
bool handleUrl(const QUrl& lookupUrl, LookupTrigger trigger = UserInput);
|
||||
|
||||
bool handleNetworkAddress(const QString& lookupString);
|
||||
void handlePath(const QString& path);
|
||||
bool handleViewpoint(const QString& viewpointString, bool shouldFace = false);
|
||||
bool handleNetworkAddress(const QString& lookupString, LookupTrigger trigger);
|
||||
void handlePath(const QString& path, LookupTrigger trigger, bool wasPathOnly = false);
|
||||
bool handleViewpoint(const QString& viewpointString, bool shouldFace = false,
|
||||
bool definitelyPathOnly = false, const QString& pathString = QString());
|
||||
bool handleUsername(const QString& lookupString);
|
||||
bool handleDomainID(const QString& host);
|
||||
|
||||
void addCurrentAddressToHistory();
|
||||
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger);
|
||||
void attemptDomainIDLookup(const QString& lookupString, const QString& overridePath, LookupTrigger trigger);
|
||||
|
||||
void addCurrentAddressToHistory(LookupTrigger trigger);
|
||||
|
||||
QString _host;
|
||||
QUuid _rootPlaceID;
|
||||
PositionGetter _positionGetter;
|
||||
OrientationGetter _orientationGetter;
|
||||
|
||||
QList<QUrl> _history;
|
||||
quint64 _lastHistoryAppend = 0;
|
||||
QStack<QUrl> _backStack;
|
||||
QStack<QUrl> _forwardStack;
|
||||
quint64 _lastBackPush = 0;
|
||||
|
||||
QString _newHostLookupPath;
|
||||
};
|
||||
|
||||
#endif // hifi_AddressManager_h
|
||||
|
|
|
@ -467,7 +467,7 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) {
|
|||
QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes);
|
||||
|
||||
// Hand it off to the AddressManager so it can handle it as a relative viewpoint
|
||||
if (DependencyManager::get<AddressManager>()->goToViewpoint(viewpoint)) {
|
||||
if (DependencyManager::get<AddressManager>()->goToViewpointForPath(viewpoint, pathQuery)) {
|
||||
qCDebug(networking) << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
|
||||
} else {
|
||||
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
|
||||
|
|
|
@ -82,6 +82,7 @@ Model::Model(QObject* parent) :
|
|||
_isVisible(true),
|
||||
_blendNumber(0),
|
||||
_appliedBlendNumber(0),
|
||||
_calculatedMeshPartOffsetValid(false),
|
||||
_calculatedMeshPartBoxesValid(false),
|
||||
_calculatedMeshBoxesValid(false),
|
||||
_calculatedMeshTrianglesValid(false),
|
||||
|
@ -544,6 +545,10 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
|
|||
// we can use the AABox's ray intersection by mapping our origin and direction into the model frame
|
||||
// and testing intersection there.
|
||||
if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face)) {
|
||||
|
||||
if (!_calculatedMeshBoxesValid) {
|
||||
recalculateMeshBoxes(pickAgainstTriangles);
|
||||
}
|
||||
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
|
||||
|
@ -660,6 +665,25 @@ bool Model::convexHullContains(glm::vec3 point) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void Model::recalculateMeshPartOffsets() {
|
||||
if (!_calculatedMeshPartOffsetValid) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
int numberOfMeshes = geometry.meshes.size();
|
||||
_calculatedMeshPartOffset.clear();
|
||||
for (int i = 0; i < numberOfMeshes; i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
qint64 partOffset = 0;
|
||||
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||
const FBXMeshPart& part = mesh.parts.at(j);
|
||||
_calculatedMeshPartOffset[QPair<int,int>(i, j)] = partOffset;
|
||||
partOffset += part.quadIndices.size() * sizeof(int);
|
||||
partOffset += part.triangleIndices.size() * sizeof(int);
|
||||
|
||||
}
|
||||
}
|
||||
_calculatedMeshPartOffsetValid = true;
|
||||
}
|
||||
}
|
||||
// TODO: we seem to call this too often when things haven't actually changed... look into optimizing this
|
||||
// Any script might trigger findRayIntersectionAgainstSubMeshes (and maybe convexHullContains), so these
|
||||
// can occur multiple times. In addition, rendering does it's own ray picking in order to decide which
|
||||
|
@ -675,7 +699,8 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
|
|||
_calculatedMeshTriangles.clear();
|
||||
_calculatedMeshTriangles.resize(numberOfMeshes);
|
||||
_calculatedMeshPartBoxes.clear();
|
||||
_calculatedMeshPartOffet.clear();
|
||||
_calculatedMeshPartOffset.clear();
|
||||
_calculatedMeshPartOffsetValid = false;
|
||||
for (int i = 0; i < numberOfMeshes; i++) {
|
||||
const FBXMesh& mesh = geometry.meshes.at(i);
|
||||
Extents scaledMeshExtents = calculateScaledOffsetExtents(mesh.meshExtents);
|
||||
|
@ -770,7 +795,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
|
|||
}
|
||||
}
|
||||
_calculatedMeshPartBoxes[QPair<int,int>(i, j)] = thisPartBounds;
|
||||
_calculatedMeshPartOffet[QPair<int,int>(i, j)] = partOffset;
|
||||
_calculatedMeshPartOffset[QPair<int,int>(i, j)] = partOffset;
|
||||
|
||||
partOffset += part.quadIndices.size() * sizeof(int);
|
||||
partOffset += part.triangleIndices.size() * sizeof(int);
|
||||
|
@ -778,6 +803,7 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
|
|||
}
|
||||
_calculatedMeshTriangles[i] = thisMeshTriangles;
|
||||
_calculatedMeshPartBoxesValid = true;
|
||||
_calculatedMeshPartOffsetValid = true;
|
||||
}
|
||||
}
|
||||
_calculatedMeshBoxesValid = true;
|
||||
|
@ -786,16 +812,6 @@ void Model::recalculateMeshBoxes(bool pickAgainstTriangles) {
|
|||
}
|
||||
|
||||
void Model::renderSetup(RenderArgs* args) {
|
||||
// if we don't have valid mesh boxes, calculate them now, this only matters in cases
|
||||
// where our caller has passed RenderArgs which will include a view frustum we can cull
|
||||
// against. We cache the results of these calculations so long as the model hasn't been
|
||||
// simulated and the mesh hasn't changed.
|
||||
if (args && !_calculatedMeshBoxesValid) {
|
||||
_mutex.lock();
|
||||
recalculateMeshBoxes();
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
// set up dilated textures on first render after load/simulate
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
if (_dilatedTextures.isEmpty()) {
|
||||
|
@ -1342,18 +1358,15 @@ void Model::snapToRegistrationPoint() {
|
|||
}
|
||||
|
||||
void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||
/*
|
||||
qDebug() << "Model::simulate()";
|
||||
qDebug() << " _translation:" << _translation;
|
||||
qDebug() << " _rotation:" << _rotation;
|
||||
*/
|
||||
|
||||
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|
||||
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|
||||
|
||||
if (isActive() && fullUpdate) {
|
||||
// NOTE: this seems problematic... need to review
|
||||
_calculatedMeshBoxesValid = false; // if we have to simulate, we need to assume our mesh boxes are all invalid
|
||||
// NOTE: This is overly aggressive and we are invalidating the MeshBoxes when in fact they may not be invalid
|
||||
// they really only become invalid if something about the transform to world space has changed. This is
|
||||
// not too bad at this point, because it doesn't impact rendering. However it does slow down ray picking
|
||||
// because ray picking needs valid boxes to work
|
||||
_calculatedMeshBoxesValid = false;
|
||||
_calculatedMeshTrianglesValid = false;
|
||||
|
||||
// check for scale to fit
|
||||
|
@ -1761,10 +1774,6 @@ void Model::setupBatchTransform(gpu::Batch& batch, RenderArgs* args) {
|
|||
}
|
||||
|
||||
AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
||||
if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) {
|
||||
recalculateMeshBoxes(true);
|
||||
}
|
||||
|
||||
if (meshIndex < _meshStates.size()) {
|
||||
const MeshState& state = _meshStates.at(meshIndex);
|
||||
bool isSkinned = state.clusterMatrices.size() > 1;
|
||||
|
@ -1774,7 +1783,7 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_calculatedMeshPartBoxesValid && _calculatedMeshPartBoxes.contains(QPair<int,int>(meshIndex, partIndex))) {
|
||||
if (_geometry->getFBXGeometry().meshes.size() > meshIndex) {
|
||||
|
||||
// FIX ME! - This is currently a hack because for some mesh parts our efforts to calculate the bounding
|
||||
// box of the mesh part fails. It seems to create boxes that are not consistent with where the
|
||||
|
@ -1795,15 +1804,13 @@ AABox Model::getPartBounds(int meshIndex, int partIndex) {
|
|||
}
|
||||
|
||||
void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool translucent) {
|
||||
|
||||
if (!_readyWhenAdded) {
|
||||
return; // bail asap
|
||||
}
|
||||
|
||||
// we always need these properly calculated before we can render, this will likely already have been done
|
||||
// since the engine will call our getPartBounds() before rendering us.
|
||||
if (!_calculatedMeshPartBoxesValid || !_calculatedMeshBoxesValid) {
|
||||
recalculateMeshBoxes(true);
|
||||
// We need to make sure we have valid offsets calculated before we can render
|
||||
if (!_calculatedMeshPartOffsetValid) {
|
||||
recalculateMeshPartOffsets();
|
||||
}
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
|
||||
|
@ -2011,7 +2018,7 @@ void Model::renderPart(RenderArgs* args, int meshIndex, int partIndex, bool tran
|
|||
}
|
||||
}
|
||||
|
||||
qint64 offset = _calculatedMeshPartOffet[QPair<int,int>(meshIndex, partIndex)];
|
||||
qint64 offset = _calculatedMeshPartOffset[QPair<int,int>(meshIndex, partIndex)];
|
||||
|
||||
if (part.quadIndices.size() > 0) {
|
||||
batch.drawIndexed(gpu::QUADS, part.quadIndices.size(), offset);
|
||||
|
|
|
@ -369,7 +369,8 @@ private:
|
|||
};
|
||||
|
||||
QHash<QPair<int,int>, AABox> _calculatedMeshPartBoxes; // world coordinate AABoxes for all sub mesh part boxes
|
||||
QHash<QPair<int,int>, qint64> _calculatedMeshPartOffet;
|
||||
QHash<QPair<int,int>, qint64> _calculatedMeshPartOffset;
|
||||
bool _calculatedMeshPartOffsetValid;
|
||||
|
||||
|
||||
bool _calculatedMeshPartBoxesValid;
|
||||
|
@ -381,6 +382,7 @@ private:
|
|||
QMutex _mutex;
|
||||
|
||||
void recalculateMeshBoxes(bool pickAgainstTriangles = false);
|
||||
void recalculateMeshPartOffsets();
|
||||
|
||||
void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes
|
||||
|
||||
|
|
Loading…
Reference in a new issue