diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 0d62b8dcc7..3bd38c4ae7 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "AssignmentFactory.h" #include "AssignmentActionFactory.h" @@ -53,6 +54,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); + auto scriptEngines = DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); @@ -174,6 +176,8 @@ AssignmentClient::~AssignmentClient() { void AssignmentClient::aboutToQuit() { stopAssignmentClient(); + DependencyManager::destroy(); + // clear the log handler so that Qt doesn't call the destructor on LogHandler qInstallMessageHandler(0); } diff --git a/assignment-client/src/entities/AssignmentParentFinder.cpp b/assignment-client/src/entities/AssignmentParentFinder.cpp index cc5c7557dc..294556383e 100644 --- a/assignment-client/src/entities/AssignmentParentFinder.cpp +++ b/assignment-client/src/entities/AssignmentParentFinder.cpp @@ -13,6 +13,12 @@ SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const { SpatiallyNestableWeakPointer parent; + + if (parentID.isNull()) { + success = true; + return parent; + } + // search entities parent = _tree->findEntityByEntityItemID(parentID); if (parent.expired()) { diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index f06af70fb3..443e4b3bf8 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -11,6 +11,7 @@ Script.load("away.js"); Script.load("progress.js"); Script.load("edit.js"); +Script.load("marketplace.js"); Script.load("selectAudioDevice.js"); Script.load("inspect.js"); Script.load("notifications.js"); diff --git a/examples/directory.js b/examples/directory.js index 8d9993ffda..a18442747f 100644 --- a/examples/directory.js +++ b/examples/directory.js @@ -9,89 +9,118 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -Script.include("libraries/globals.js"); +Script.include([ + "libraries/toolBars.js", +]); -var directory = (function () { +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; - var DIRECTORY_URL = "https://metaverse.highfidelity.com/directory", - directoryWindow, - DIRECTORY_BUTTON_URL = HIFI_PUBLIC_BUCKET + "images/tools/directory.svg", - BUTTON_WIDTH = 50, - BUTTON_HEIGHT = 50, - BUTTON_ALPHA = 0.9, - BUTTON_MARGIN = 8, - directoryButton, - EDIT_TOOLBAR_BUTTONS = 10, // Number of buttons in edit.js toolbar - viewport; +var DIRECTORY_WINDOW_URL = "https://metaverse.highfidelity.com/directory"; +var directoryWindow = new OverlayWebWindow({ + title: 'directory', + source: "about:blank", + width: 900, + height: 700, + visible: false +}); - function updateButtonPosition() { - Overlays.editOverlay(directoryButton, { - x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN, - y: (viewport.y - (EDIT_TOOLBAR_BUTTONS + 1) * (BUTTON_HEIGHT + BUTTON_MARGIN) - BUTTON_MARGIN) / 2 - 1 +var toolHeight = 50; +var toolWidth = 50; + + +function showDirectory() { + directoryWindow.setURL(DIRECTORY_WINDOW_URL); + directoryWindow.setVisible(true); +} + +function hideDirectory() { + directoryWindow.setVisible(false); + directoryWindow.setURL("about:blank"); +} + +function toggleDirectory() { + if (directoryWindow.visible) { + hideDirectory(); + } else { + showDirectory(); + } +} + +var toolBar = (function() { + var that = {}, + toolBar, + browseDirectoryButton; + + function initialize() { + ToolBar.SPACING = 16; + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.directory.toolbar", function(windowDimensions, toolbar) { + return { + x: windowDimensions.x - 8 - toolbar.width, + y: 50 + }; }); + browseDirectoryButton = toolBar.addTool({ + imageURL: toolIconUrl + "directory.svg", + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: true, + }); + + toolBar.showTool(browseDirectoryButton, true); } - function onMousePressEvent(event) { - var clickedOverlay; + var browseDirectoryButtonDown = false; + that.mousePressEvent = function(event) { + var clickedOverlay, + url, + file; - clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y }); - - if (clickedOverlay === directoryButton) { - if (directoryWindow.url !== DIRECTORY_URL) { - directoryWindow.setURL(DIRECTORY_URL); - } - directoryWindow.setVisible(true); - directoryWindow.raise(); + if (!event.isLeftButton) { + // if another mouse button than left is pressed ignore it + return false; } - } - function onDomainChanged() { - directoryWindow.setVisible(false); - } + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); - function onScriptUpdate() { - var oldViewport = viewport; - viewport = Controller.getViewportDimensions(); - if (viewport.x !== oldViewport.x || viewport.y !== oldViewport.y) { - updateButtonPosition(); + if (browseDirectoryButton === toolBar.clicked(clickedOverlay)) { + toggleDirectory(); + return true; } + + return false; + }; + + that.mouseReleaseEvent = function(event) { + var handled = false; + + + if (browseDirectoryButtonDown) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + } + + newModelButtonDown = false; + browseDirectoryButtonDown = false; + + return handled; } - function setUp() { - viewport = Controller.getViewportDimensions(); + that.cleanup = function() { + toolBar.cleanup(); + }; - directoryWindow = new OverlayWebWindow({ - title: 'Directory', - source: DIRECTORY_URL, - width: 900, - height: 700, - visible: false - }); + initialize(); + return that; +}()); - directoryButton = Overlays.addOverlay("image", { - imageURL: DIRECTORY_BUTTON_URL, - width: BUTTON_WIDTH, - height: BUTTON_HEIGHT, - x: viewport.x - BUTTON_WIDTH - BUTTON_MARGIN, - y: BUTTON_MARGIN, - alpha: BUTTON_ALPHA, - visible: true - }); - - updateButtonPosition(); - - Controller.mousePressEvent.connect(onMousePressEvent); - Window.domainChanged.connect(onDomainChanged); - - Script.update.connect(onScriptUpdate); - } - - function tearDown() { - Overlays.deleteOverlay(directoryButton); - } - - setUp(); - Script.scriptEnding.connect(tearDown); -}()); \ No newline at end of file +Controller.mousePressEvent.connect(toolBar.mousePressEvent) +Script.scriptEnding.connect(toolBar.cleanup); diff --git a/examples/edit.js b/examples/edit.js index 990e99b32d..6d77aa2f11 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -183,8 +183,7 @@ var toolBar = (function() { newTextButton, newWebButton, newZoneButton, - newPolyVoxButton, - browseMarketplaceButton; + newPolyVoxButton; function initialize() { toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.edit.toolbar", function(windowDimensions, toolbar) { @@ -194,13 +193,7 @@ var toolBar = (function() { }; }); - browseMarketplaceButton = toolBar.addTool({ - imageURL: toolIconUrl + "marketplace.svg", - width: toolWidth, - height: toolHeight, - alpha: 0.9, - visible: true, - }); + activeButton = toolBar.addTool({ imageURL: toolIconUrl + "edit-status.svg", @@ -415,7 +408,6 @@ var toolBar = (function() { } var newModelButtonDown = false; - var browseMarketplaceButtonDown = false; that.mousePressEvent = function(event) { var clickedOverlay, url, @@ -443,11 +435,7 @@ var toolBar = (function() { return true; } - if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - toggleMarketplace(); - return true; - } - + if (newCubeButton === toolBar.clicked(clickedOverlay)) { createNewEntity({ type: "Box", @@ -652,22 +640,10 @@ var toolBar = (function() { } handled = true; } - } else if (browseMarketplaceButtonDown) { - var clickedOverlay = Overlays.getOverlayAtPoint({ - x: event.x, - y: event.y - }); - if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { - url = Window.s3Browse(".*(fbx|FBX|obj|OBJ)"); - if (url !== null && url !== "") { - addModel(url); - } - handled = true; - } } newModelButtonDown = false; - browseMarketplaceButtonDown = false; + return handled; } diff --git a/examples/marketplace.js b/examples/marketplace.js new file mode 100644 index 0000000000..83f1287ed8 --- /dev/null +++ b/examples/marketplace.js @@ -0,0 +1,131 @@ +// +// marketplace.js +// examples +// +// Created by Eric Levin on 8 Jan 2016 +// Copyright 2016 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 +// + +Script.include([ + "libraries/toolBars.js", +]); + +HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; +var toolIconUrl = HIFI_PUBLIC_BUCKET + "images/tools/"; + +var MARKETPLACE_URL = "https://metaverse.highfidelity.com/marketplace"; +var marketplaceWindow = new OverlayWebWindow({ + title: 'Marketplace', + source: "about:blank", + width: 900, + height: 700, + visible: false +}); + +var toolHeight = 50; +var toolWidth = 50; + + +function showMarketplace(marketplaceID) { + var url = MARKETPLACE_URL; + if (marketplaceID) { + url = url + "/items/" + marketplaceID; + } + print("setting marketplace URL to " + url); + marketplaceWindow.setURL(url); + marketplaceWindow.setVisible(true); +} + +function hideMarketplace() { + marketplaceWindow.setVisible(false); + marketplaceWindow.setURL("about:blank"); +} + +function toggleMarketplace() { + if (marketplaceWindow.visible) { + hideMarketplace(); + } else { + showMarketplace(); + } +} + +var toolBar = (function() { + var that = {}, + toolBar, + browseMarketplaceButton; + + function initialize() { + ToolBar.SPACING = 16; + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL, "highfidelity.marketplace.toolbar", function(windowDimensions, toolbar) { + return { + x: windowDimensions.x - 8 - toolbar.width, + y: 135 + }; + }); + browseMarketplaceButton = toolBar.addTool({ + imageURL: toolIconUrl + "marketplace.svg", + width: toolWidth, + height: toolHeight, + alpha: 0.9, + visible: true, + }); + + toolBar.showTool(browseMarketplaceButton, true); + } + + var browseMarketplaceButtonDown = false; + that.mousePressEvent = function(event) { + var clickedOverlay, + url, + file; + + if (!event.isLeftButton) { + // if another mouse button than left is pressed ignore it + return false; + } + + clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + + + + if (browseMarketplaceButton === toolBar.clicked(clickedOverlay)) { + toggleMarketplace(); + return true; + } + + return false; + }; + + that.mouseReleaseEvent = function(event) { + var handled = false; + + + if (browseMarketplaceButtonDown) { + var clickedOverlay = Overlays.getOverlayAtPoint({ + x: event.x, + y: event.y + }); + } + + newModelButtonDown = false; + browseMarketplaceButtonDown = false; + + return handled; + } + + that.cleanup = function() { + toolBar.cleanup(); + }; + + initialize(); + return that; +}()); + +Controller.mousePressEvent.connect(toolBar.mousePressEvent) +Script.scriptEnding.connect(toolBar.cleanup); diff --git a/interface/resources/html/interface-welcome.html b/interface/resources/html/interface-welcome.html index 1fc719ed72..113de7f2a0 100644 --- a/interface/resources/html/interface-welcome.html +++ b/interface/resources/html/interface-welcome.html @@ -1,4 +1,4 @@ - + @@ -14,7 +14,7 @@ body { background: white; - width: 839px; + width: 100%; overflow-x: hidden; } @@ -181,7 +181,7 @@ function overrideBodyWidth() { document.body.style.width = "100%"; - container.style.width = "0"; + } //]]> diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 6c94f9d254..e8ebb5d5fb 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -32,9 +32,14 @@ DialogContainer { property int maximumY: parent ? parent.height - height : 0 + Rectangle { + id: dragRegion + visible: dragMouseArea.containsMouse + } + AddressBarDialog { id: addressBarDialog - + z: dragRegion.z + 1 implicitWidth: backgroundImage.width implicitHeight: backgroundImage.height @@ -48,11 +53,13 @@ DialogContainer { property int inputAreaStep: (height - inputAreaHeight) / 2 MouseArea { + id: dragMouseArea // Drag the icon width: parent.height height: parent.height x: 0 y: 0 + hoverEnabled: true drag { target: root minimumX: -parent.inputAreaStep @@ -61,6 +68,8 @@ DialogContainer { maximumY: root.parent ? root.maximumY + parent.inputAreaStep : 0 } } + + MouseArea { // Drag the input rectangle diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 738ec34a02..689171f9e9 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -51,6 +51,16 @@ Hifi.VrMenu { VrMenuView { property int menuDepth: root.models.length - 1 model: root.models[menuDepth] + + function fit(position, size, maxposition) { + var padding = 8; + if (position < padding) { + position = padding; + } else if (position + size + padding > maxposition) { + position = maxposition - (size + padding); + } + return position; + } Component.onCompleted: { if (menuDepth === 0) { @@ -61,6 +71,8 @@ Hifi.VrMenu { x = lastColumn.x + 64; y = lastMousePosition.y - height / 2; } + x = fit(x, width, parent.width); + y = fit(y, height, parent.height); } onSelected: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3a165afb88..e499da9645 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -103,6 +103,7 @@ #include #include #include +#include #include #include #include @@ -319,6 +320,7 @@ bool setupEssentials(int& argc, char** argv) { Setting::init(); // Set dependencies + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -402,16 +404,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _entityClipboard(new EntityTree()), _lastQueriedTime(usecTimestampNow()), _mirrorViewRect(QRect(MIRROR_VIEW_LEFT_PADDING, MIRROR_VIEW_TOP_PADDING, MIRROR_VIEW_WIDTH, MIRROR_VIEW_HEIGHT)), - _firstRun("firstRun", true), - _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION), - _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), _fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES), _scaleMirror(1.0f), _rotateMirror(0.0f), _raiseMirror(0.0f), _enableProcessOctreeThread(true), - _runningScriptsWidget(NULL), - _runningScriptsWidgetWasVisible(false), _lastNackTime(usecTimestampNow()), _lastSendDownstreamAudioStats(usecTimestampNow()), _aboutToQuit(false), @@ -596,6 +593,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : connect(addressManager.data(), &AddressManager::hostChanged, this, &Application::updateWindowTitle); connect(this, &QCoreApplication::aboutToQuit, addressManager.data(), &AddressManager::storeCurrentAddress); + auto scriptEngines = DependencyManager::get().data(); + scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){ + registerScriptEngineWithApplicationServices(engine); + }); + + connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] { + auto scriptEngines = DependencyManager::get(); + if (scriptEngines->getRunningScripts().isEmpty()) { + getMyAvatar()->clearScriptableSettings(); + } + }, Qt::QueuedConnection); + + connect(scriptEngines, &ScriptEngines::scriptsReloading, scriptEngines, [this] { + getEntities()->reloadEntityScripts(); + }, Qt::QueuedConnection); + + connect(scriptEngines, &ScriptEngines::scriptLoadError, + scriptEngines, [](const QString& filename, const QString& error){ + OffscreenUi::warning(nullptr, "Error Loading Script", filename + " failed to load."); + }, Qt::QueuedConnection); + #ifdef _WIN32 WSADATA WsaData; int wsaresult = WSAStartup(MAKEWORD(2, 2), &WsaData); @@ -678,9 +696,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _overlays.init(); // do this before scripts load - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - - connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts())); connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit())); // hook up bandwidth estimator @@ -847,18 +862,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); - // check first run... - if (_firstRun.get()) { - qCDebug(interfaceapp) << "This is a first run..."; - // clear the scripts, and set out script to our default scripts - clearScriptsBeforeRunning(); - loadScript(DEFAULT_SCRIPTS_JS_URL); - - _firstRun.set(false); - } else { - // do this as late as possible so that all required subsystems are initialized - loadScripts(); - } + // force the model the look at the correct directory (weird order of operations issue) + scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); + // do this as late as possible so that all required subsystems are initialized + scriptEngines->loadScripts(); loadSettings(); int SAVE_SETTINGS_INTERVAL = 10 * MSECS_PER_SECOND; // Let's save every seconds for now @@ -1017,7 +1024,9 @@ void Application::cleanupBeforeQuit() { nodeList->getPacketReceiver().setShouldDropPackets(true); getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts - ScriptEngine::stopAllScripts(this); // stop all currently running global scripts + DependencyManager::get()->saveScripts(); + DependencyManager::get()->shutdownScripting(); // stop all currently running global scripts + DependencyManager::destroy(); // first stop all timers directly or by invokeMethod // depending on what thread they run in @@ -1192,8 +1201,9 @@ void Application::initializeUi() { connect(engine, &QQmlEngine::quit, [] { qApp->quit(); }); - rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); + rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); + rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance()); rootContext->setContextProperty("Controller", DependencyManager::get().data()); rootContext->setContextProperty("Entities", DependencyManager::get().data()); rootContext->setContextProperty("MyAvatar", getMyAvatar()); @@ -1220,6 +1230,7 @@ void Application::initializeUi() { rootContext->setContextProperty("Menu", MenuScriptingInterface::getInstance()); rootContext->setContextProperty("Stats", Stats::getInstance()); rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); + rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); rootContext->setContextProperty("SoundCache", DependencyManager::get().data()); @@ -1234,7 +1245,6 @@ void Application::initializeUi() { rootContext->setContextProperty("HMD", DependencyManager::get().data()); rootContext->setContextProperty("Scene", DependencyManager::get().data()); rootContext->setContextProperty("Render", DependencyManager::get().data()); - rootContext->setContextProperty("ScriptDiscoveryService", this->getRunningScriptsWidget()); _glWidget->installEventFilter(offscreenUi.data()); VrMenu::load(); @@ -4164,48 +4174,6 @@ int Application::processOctreeStats(ReceivedMessage& message, SharedNodePointer void Application::packetSent(quint64 length) { } -const QString SETTINGS_KEY = "Settings"; - -void Application::loadScripts() { - // loads all saved scripts - Settings settings; - int size = settings.beginReadArray(SETTINGS_KEY); - for (int i = 0; i < size; ++i){ - settings.setArrayIndex(i); - QString string = settings.value("script").toString(); - if (!string.isEmpty()) { - loadScript(string); - } - } - settings.endArray(); -} - -void Application::clearScriptsBeforeRunning() { - // clears all scripts from the settingsSettings settings; - Settings settings; - settings.beginWriteArray(SETTINGS_KEY); - settings.remove(""); -} - -void Application::saveScripts() { - // Saves all currently running user-loaded scripts - Settings settings; - settings.beginWriteArray(SETTINGS_KEY); - settings.remove(""); - - QStringList runningScripts = getRunningScripts(); - int i = 0; - for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) { - if (getScriptEngine(*it)->isUserLoaded()) { - settings.setArrayIndex(i); - settings.setValue("script", *it); - ++i; - } - } - settings.endArray(); -} - - 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. @@ -4232,11 +4200,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable); connect(scriptEngine, &ScriptEngine::finished, clipboardScriptable, &ClipboardScriptingInterface::deleteLater); - connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection); - - connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool))); - connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool))); - scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); qScriptRegisterMetaType(scriptEngine, RayToOverlayIntersectionResultToScriptValue, @@ -4284,7 +4247,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Render", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget()); + scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data()); } bool Application::canAcceptURL(const QString& urlString) const { @@ -4390,7 +4353,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { if (reply == QMessageBox::Yes) { qCDebug(interfaceapp) << "Chose to run the script: " << scriptFilenameOrURL; - loadScript(scriptFilenameOrURL); + DependencyManager::get()->loadScript(scriptFilenameOrURL); } else { qCDebug(interfaceapp) << "Declined to run the script: " << scriptFilenameOrURL; } @@ -4563,186 +4526,6 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } } -ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, - bool loadScriptFromEditor, bool activateMainWindow, bool reload) { - - if (isAboutToQuit()) { - return NULL; - } - - QUrl scriptUrl(scriptFilename); - const QString& scriptURLString = scriptUrl.toString(); - { - QReadLocker lock(&_scriptEnginesHashLock); - if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor - && !_scriptEnginesHash[scriptURLString]->isFinished()) { - - return _scriptEnginesHash[scriptURLString]; - } - } - - ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface); - scriptEngine->setUserLoaded(isUserLoaded); - - if (scriptFilename.isNull()) { - // This appears to be the script engine used by the script widget's evaluation window before the file has been saved... - - // this had better be the script editor (we should de-couple so somebody who thinks they are loading a script - // doesn't just get an empty script engine) - - // we can complete setup now since there isn't a script we have to load - registerScriptEngineWithApplicationServices(scriptEngine); - scriptEngine->runInThread(); - } else { - // connect to the appropriate signals of this script engine - connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &Application::handleScriptEngineLoaded); - connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); - - // get the script engine object to load the script at the designated script URL - scriptEngine->loadURL(scriptUrl, reload); - } - - // restore the main window's active state - if (activateMainWindow && !loadScriptFromEditor) { - _window->activateWindow(); - } - - return scriptEngine; -} - -void Application::reloadScript(const QString& scriptName, bool isUserLoaded) { - loadScript(scriptName, isUserLoaded, false, false, true); -} - -// FIXME - change to new version of ScriptCache loading notification -void Application::handleScriptEngineLoaded(const QString& scriptFilename) { - ScriptEngine* scriptEngine = qobject_cast(sender()); - - { - QWriteLocker lock(&_scriptEnginesHashLock); - _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); - } - - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - UserActivityLogger::getInstance().loadedScript(scriptFilename); - - // register our application services and set it off on its own thread - registerScriptEngineWithApplicationServices(scriptEngine); - scriptEngine->runInThread(); -} - -// FIXME - change to new version of ScriptCache loading notification -void Application::handleScriptLoadError(const QString& scriptFilename) { - qCDebug(interfaceapp) << "Application::loadScript(), script failed to load..."; - OffscreenUi::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load."); -} - -QStringList Application::getRunningScripts() { - QReadLocker lock(&_scriptEnginesHashLock); - return _scriptEnginesHash.keys(); -} - -ScriptEngine* Application::getScriptEngine(const QString& scriptHash) { - QReadLocker lock(&_scriptEnginesHashLock); - return _scriptEnginesHash.value(scriptHash, nullptr); -} - -void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) { - bool removed = false; - { - QWriteLocker lock(&_scriptEnginesHashLock); - const QString& scriptURLString = QUrl(scriptName).toString(); - for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) { - if (it.value() == engine) { - _scriptEnginesHash.erase(it); - removed = true; - break; - } - } - } - if (removed) { - postLambdaEvent([this, scriptName]() { - _runningScriptsWidget->scriptStopped(scriptName); - _runningScriptsWidget->setRunningScripts(getRunningScripts()); - }); - } -} - -void Application::stopAllScripts(bool restart) { - { - QReadLocker lock(&_scriptEnginesHashLock); - - if (restart) { - // Delete all running scripts from cache so that they are re-downloaded when they are restarted - auto scriptCache = DependencyManager::get(); - for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); - it != _scriptEnginesHash.constEnd(); it++) { - if (!it.value()->isFinished()) { - scriptCache->deleteScript(it.key()); - } - } - } - - // Stop and possibly restart all currently running scripts - for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); - it != _scriptEnginesHash.constEnd(); it++) { - if (it.value()->isFinished()) { - continue; - } - if (restart && it.value()->isUserLoaded()) { - connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { - reloadScript(scriptName); - }); - } - QMetaObject::invokeMethod(it.value(), "stop"); - //it.value()->stop(); - qCDebug(interfaceapp) << "stopping script..." << it.key(); - } - } - getMyAvatar()->clearScriptableSettings(); -} - -bool Application::stopScript(const QString& scriptHash, bool restart) { - bool stoppedScript = false; - { - QReadLocker lock(&_scriptEnginesHashLock); - if (_scriptEnginesHash.contains(scriptHash)) { - ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash]; - if (restart) { - auto scriptCache = DependencyManager::get(); - scriptCache->deleteScript(QUrl(scriptHash)); - connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { - reloadScript(scriptName); - }); - } - scriptEngine->stop(); - stoppedScript = true; - qCDebug(interfaceapp) << "stopping script..." << scriptHash; - } - } - if (_scriptEnginesHash.empty()) { - getMyAvatar()->clearScriptableSettings(); - } - return stoppedScript; -} - -void Application::reloadAllScripts() { - DependencyManager::get()->clearCache(); - getEntities()->reloadEntityScripts(); - stopAllScripts(true); -} - -void Application::reloadOneScript(const QString& scriptName) { - stopScript(scriptName, true); -} - -void Application::loadDefaultScripts() { - QReadLocker lock(&_scriptEnginesHashLock); - if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) { - loadScript(DEFAULT_SCRIPTS_JS_URL); - } -} - void Application::toggleRunningScriptsWidget() { if (_runningScriptsWidget->isVisible()) { if (_runningScriptsWidget->hasFocus()) { @@ -4800,27 +4583,17 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject qCDebug(interfaceapp) << "Destination wallet UUID for edit payments is" << voxelWalletUUID; } -QString Application::getPreviousScriptLocation() { - return _previousScriptLocation.get(); -} - -void Application::setPreviousScriptLocation(const QString& previousScriptLocation) { - _previousScriptLocation.set(previousScriptLocation); -} - void Application::loadDialog() { - - QString fileNameString = QFileDialog::getOpenFileName(_glWidget, - tr("Open Script"), - getPreviousScriptLocation(), - tr("JavaScript Files (*.js)")); + // To be migratd to QML + QString fileNameString = QFileDialog::getOpenFileName( + _glWidget, tr("Open Script"), "", tr("JavaScript Files (*.js)")); if (!fileNameString.isEmpty()) { - setPreviousScriptLocation(fileNameString); - loadScript(fileNameString); + DependencyManager::get()->loadScript(fileNameString); } } void Application::loadScriptURLDialog() { + // To be migratd to QML QInputDialog scriptURLDialog(getWindow()); scriptURLDialog.setWindowTitle("Open and Run Script URL"); scriptURLDialog.setLabelText("Script:"); @@ -4836,19 +4609,10 @@ void Application::loadScriptURLDialog() { // the user input a new hostname, use that newScript = scriptURLDialog.textValue(); } - loadScript(newScript); + DependencyManager::get()->loadScript(newScript); } } -QString Application::getScriptsLocation() { - return _scriptsLocationHandle.get(); -} - -void Application::setScriptsLocation(const QString& scriptsLocation) { - _scriptsLocationHandle.set(scriptsLocation); - emit scriptLocationChanged(scriptsLocation); -} - void Application::toggleLogDialog() { if (! _logDialog) { _logDialog = new LogDialog(_glWidget, getLogger()); @@ -5063,8 +4827,23 @@ const DisplayPlugin* Application::getActiveDisplayPlugin() const { static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool active = false) { auto menu = Menu::getInstance(); QString name = displayPlugin->getName(); + auto grouping = displayPlugin->getGrouping(); + QString groupingMenu { "" }; Q_ASSERT(!menu->menuItemExists(MenuOption::OutputMenu, name)); + // assign the meny grouping based on plugin grouping + switch (grouping) { + case Plugin::ADVANCED: + groupingMenu = "Advanced"; + break; + case Plugin::DEVELOPER: + groupingMenu = "Developer"; + break; + default: + groupingMenu = "Standard"; + break; + } + static QActionGroup* displayPluginGroup = nullptr; if (!displayPluginGroup) { displayPluginGroup = new QActionGroup(menu); @@ -5073,7 +4852,9 @@ static void addDisplayPluginToMenu(DisplayPluginPointer displayPlugin, bool acti auto parent = menu->getMenu(MenuOption::OutputMenu); auto action = menu->addActionToQMenuAndActionHash(parent, name, 0, qApp, - SLOT(updateDisplayMode())); + SLOT(updateDisplayMode()), + QAction::NoRole, UNSPECIFIED_POSITION, groupingMenu); + action->setCheckable(true); action->setChecked(active); displayPluginGroup->addAction(action); @@ -5087,7 +4868,31 @@ void Application::updateDisplayMode() { static std::once_flag once; std::call_once(once, [&] { bool first = true; + + // first sort the plugins into groupings: standard, advanced, developer + DisplayPluginList standard; + DisplayPluginList advanced; + DisplayPluginList developer; foreach(auto displayPlugin, displayPlugins) { + auto grouping = displayPlugin->getGrouping(); + switch (grouping) { + case Plugin::ADVANCED: + advanced.push_back(displayPlugin); + break; + case Plugin::DEVELOPER: + developer.push_back(displayPlugin); + break; + default: + standard.push_back(displayPlugin); + break; + } + } + + // concactonate the groupings into a single list in the order: standard, advanced, developer + standard.insert(std::end(standard), std::begin(advanced), std::end(advanced)); + standard.insert(std::end(standard), std::begin(developer), std::end(developer)); + + foreach(auto displayPlugin, standard) { addDisplayPluginToMenu(displayPlugin, first); // This must be a queued connection to avoid a deadlock QObject::connect(displayPlugin.get(), &DisplayPlugin::requestRender, @@ -5099,6 +4904,11 @@ void Application::updateDisplayMode() { first = false; } + + // after all plugins have been added to the menu, add a seperator to the menu + auto menu = Menu::getInstance(); + auto parent = menu->getMenu(MenuOption::OutputMenu); + parent->addSeparator(); }); diff --git a/interface/src/Application.h b/interface/src/Application.h index 39e5453d1d..9dd479bb5e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -106,10 +106,6 @@ public: void postLambdaEvent(std::function f); - void loadScripts(); - QString getPreviousScriptLocation(); - void setPreviousScriptLocation(const QString& previousScriptLocation); - void clearScriptsBeforeRunning(); void initializeGL(); void initializeUi(); void paintGL(); @@ -197,9 +193,6 @@ public: NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; } - QStringList getRunningScripts(); - ScriptEngine* getScriptEngine(const QString& scriptHash); - float getRenderResolutionScale() const; bool isAboutToQuit() const { return _aboutToQuit; } @@ -213,13 +206,8 @@ public: glm::mat4 getEyeProjection(int eye) const; QRect getDesirableApplicationGeometry(); - RunningScriptsWidget* getRunningScriptsWidget() { return _runningScriptsWidget; } - Bookmarks* getBookmarks() const { return _bookmarks; } - QString getScriptsLocation(); - void setScriptsLocation(const QString& scriptsLocation); - virtual bool canAcceptURL(const QString& url) const override; virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; @@ -241,8 +229,6 @@ public: float getAverageSimsPerSecond(); signals: - void scriptLocationChanged(const QString& newPath); - void svoImportRequested(const QString& url); void checkBackgroundDownloads(); @@ -263,14 +249,6 @@ public slots: void loadDialog(); void loadScriptURLDialog(); void toggleLogDialog(); - - ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, - bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); - void stopAllScripts(bool restart = false); - bool stopScript(const QString& scriptHash, bool restart = false); - void reloadAllScripts(); - void reloadOneScript(const QString& scriptName); - void loadDefaultScripts(); void toggleRunningScriptsWidget(); void showFriendsWindow(); @@ -313,9 +291,6 @@ private slots: void idle(uint64_t now); void aboutToQuit(); - void handleScriptEngineLoaded(const QString& scriptFilename); - void handleScriptLoadError(const QString& scriptFilename); - void connectedToDomain(const QString& hostname); void audioMuteToggled(); @@ -331,10 +306,6 @@ private slots: void loadSettings(); void saveSettings(); - void scriptFinished(const QString& scriptName, ScriptEngine* engine); - void saveScripts(); - void reloadScript(const QString& scriptName, bool isUserLoaded = true); - bool acceptSnapshot(const QString& urlString); bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); @@ -468,9 +439,6 @@ private: Camera _mirrorCamera; // Cammera for mirror view QRect _mirrorViewRect; - Setting::Handle _firstRun; - Setting::Handle _previousScriptLocation; - Setting::Handle _scriptsLocationHandle; Setting::Handle _fieldOfView; float _scaleMirror; @@ -499,12 +467,7 @@ private: TouchEvent _lastTouchEvent; - QReadWriteLock _scriptEnginesHashLock; - RunningScriptsWidget* _runningScriptsWidget; - QHash _scriptEnginesHash; - bool _runningScriptsWidgetWasVisible; - QString _scriptsLocation; - + RunningScriptsWidget* _runningScriptsWidget { nullptr }; quint64 _lastNackTime; quint64 _lastSendDownstreamAudioStats; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6373b3fdb2..eec04d4640 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "Application.h" #include "AccountManager.h" @@ -114,13 +115,15 @@ Menu::Menu() { Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); + auto scriptEngines = DependencyManager::get(); // Edit > Stop All Scripts... [advanced] - addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts()), + addActionToQMenuAndActionHash(editMenu, MenuOption::StopAllScripts, 0, + scriptEngines.data(), SLOT(stopAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); // Edit > Reload All Scripts... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, - qApp, SLOT(reloadAllScripts()), + scriptEngines.data(), SLOT(reloadAllScripts()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); // Edit > Scripts Editor... [advanced] diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 085b349b8f..6a544f31c9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -243,7 +243,7 @@ namespace MenuOption { const QString OctreeStats = "Entity Statistics"; const QString OnePointCalibration = "1 Point Calibration"; const QString OnlyDisplayTopTen = "Only Display Top Ten"; - const QString OutputMenu = "Display>Mode"; + const QString OutputMenu = "Display"; const QString PackageModel = "Package Model..."; const QString Pair = "Pair"; const QString PhysicsShowOwned = "Highlight Simulation Ownership"; diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index c1ea541864..f9fc444d4b 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -79,9 +79,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) : // what screens we're allowed on watchWindow(windowHandle()); auto dialogsManager = DependencyManager::get(); - if (qApp->getRunningScriptsWidget()) { - watchWindow(qApp->getRunningScriptsWidget()->windowHandle()); - } if (qApp->getToolWindow()) { watchWindow(qApp->getToolWindow()->windowHandle()); } diff --git a/interface/src/ui/JSConsole.cpp b/interface/src/ui/JSConsole.cpp index 2d34ba5608..5d197f5ddc 100644 --- a/interface/src/ui/JSConsole.cpp +++ b/interface/src/ui/JSConsole.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include "Application.h" @@ -84,7 +85,7 @@ void JSConsole::setScriptEngine(ScriptEngine* scriptEngine) { // if scriptEngine is NULL then create one and keep track of it using _ownScriptEngine _ownScriptEngine = scriptEngine == NULL; - _scriptEngine = _ownScriptEngine ? qApp->loadScript(QString(), false) : scriptEngine; + _scriptEngine = _ownScriptEngine ? DependencyManager::get()->loadScript(QString(), false) : scriptEngine; connect(_scriptEngine, SIGNAL(printedMessage(const QString&)), this, SLOT(handlePrint(const QString&))); connect(_scriptEngine, SIGNAL(errorMessage(const QString&)), this, SLOT(handleError(const QString&))); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 216c11a05c..b5e24fef1e 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "Application.h" #include "DialogsManager.h" @@ -44,7 +45,9 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : connect(ui.buttonBrowseLocation, &QPushButton::clicked, this, &PreferencesDialog::openSnapshotLocationBrowser); connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser); - connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, qApp, &Application::loadDefaultScripts); + connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, [] { + DependencyManager::get()->loadDefaultScripts(); + }); connect(ui.buttonChangeAppearance, &QPushButton::clicked, this, &PreferencesDialog::openFullAvatarModelBrowser); connect(ui.appearanceDescription, &QLineEdit::editingFinished, this, &PreferencesDialog::changeFullAvatarURL); @@ -171,7 +174,7 @@ void PreferencesDialog::loadPreferences() { ui.snapshotLocationEdit->setText(Snapshot::snapshotsLocation.get()); - ui.scriptsLocationEdit->setText(qApp->getScriptsLocation()); + ui.scriptsLocationEdit->setText(DependencyManager::get()->getScriptsLocation()); ui.pupilDilationSlider->setValue(myAvatar->getHead()->getPupilDilation() * ui.pupilDilationSlider->maximum()); @@ -265,7 +268,7 @@ void PreferencesDialog::savePreferences() { } if (!ui.scriptsLocationEdit->text().isEmpty() && QDir(ui.scriptsLocationEdit->text()).exists()) { - qApp->setScriptsLocation(ui.scriptsLocationEdit->text()); + DependencyManager::get()->setScriptsLocation(ui.scriptsLocationEdit->text()); } myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum()); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 3752ea2176..1d2fe111f9 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -22,8 +22,10 @@ #include #include +#include #include "Application.h" +#include "MainWindow.h" #include "Menu.h" #include "ScriptsModel.h" #include "UIUtil.h" @@ -33,57 +35,55 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : Qt::WindowCloseButtonHint), ui(new Ui::RunningScriptsWidget), _reloadSignalMapper(this), - _stopSignalMapper(this), - _scriptsModelFilter(this), - _scriptsModel(this) { + _stopSignalMapper(this) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose, false); ui->filterLineEdit->installEventFilter(this); - connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, + auto scriptEngines = DependencyManager::get().data(); + connect(scriptEngines->scriptsModelFilter(), &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); // FIXME: menu isn't prepared at this point. //QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); //ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); - - _scriptsModelFilter.setSourceModel(&_scriptsModel); - _scriptsModelFilter.sort(0, Qt::AscendingOrder); - _scriptsModelFilter.setDynamicSortFilter(true); - ui->scriptTreeView->setModel(&_scriptsModelFilter); + ui->scriptTreeView->setModel(scriptEngines->scriptsModelFilter()); connect(ui->filterLineEdit, &QLineEdit::textChanged, this, &RunningScriptsWidget::updateFileFilter); connect(ui->scriptTreeView, &QTreeView::doubleClicked, this, &RunningScriptsWidget::loadScriptFromList); - connect(ui->reloadAllButton, &QPushButton::clicked, qApp, &Application::reloadAllScripts); - connect(ui->stopAllButton, &QPushButton::clicked, this, &RunningScriptsWidget::allScriptsStopped); + connect(ui->reloadAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::reloadAllScripts); + connect(ui->stopAllButton, &QPushButton::clicked, scriptEngines, &ScriptEngines::stopAllScripts); connect(ui->loadScriptFromDiskButton, &QPushButton::clicked, qApp, &Application::loadDialog); connect(ui->loadScriptFromURLButton, &QPushButton::clicked, qApp, &Application::loadScriptURLDialog); connect(&_reloadSignalMapper, static_cast(&QSignalMapper::mapped), - qApp, &Application::reloadOneScript); - + [scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName, true); }); connect(&_stopSignalMapper, static_cast(&QSignalMapper::mapped), - [](const QString& script) { qApp->stopScript(script); }); + [scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName); }); + setRunningScripts(scriptEngines->getRunningScripts()); + connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this, scriptEngines] { + setRunningScripts(scriptEngines->getRunningScripts()); + }, Qt::QueuedConnection); UIUtil::scaleWidgetFontSizes(this); } RunningScriptsWidget::~RunningScriptsWidget() { delete ui; - _scriptsModel.deleteLater(); } void RunningScriptsWidget::updateFileFilter(const QString& filter) { QRegExp regex("^.*" + QRegExp::escape(filter) + ".*$", Qt::CaseInsensitive); - _scriptsModelFilter.setFilterRegExp(regex); + DependencyManager::get()->scriptsModelFilter()->setFilterRegExp(regex); selectFirstInList(); } void RunningScriptsWidget::loadScriptFromList(const QModelIndex& index) { - QVariant scriptFile = _scriptsModelFilter.data(index, ScriptsModel::ScriptPath); - qApp->loadScript(scriptFile.toString()); + auto scriptEngines = DependencyManager::get(); + QVariant scriptFile = scriptEngines->scriptsModelFilter()->data(index, ScriptsModel::ScriptPath); + scriptEngines->loadScript(scriptFile.toString()); } void RunningScriptsWidget::loadSelectedScript() { @@ -183,8 +183,9 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { } void RunningScriptsWidget::selectFirstInList() { - if (_scriptsModelFilter.rowCount() > 0) { - ui->scriptTreeView->setCurrentIndex(_scriptsModelFilter.index(0, 0)); + auto model = DependencyManager::get()->scriptsModelFilter(); + if (model->rowCount() > 0) { + ui->scriptTreeView->setCurrentIndex(model->index(0, 0)); } } @@ -217,90 +218,5 @@ void RunningScriptsWidget::keyPressEvent(QKeyEvent *keyEvent) { } void RunningScriptsWidget::allScriptsStopped() { - qApp->stopAllScripts(); -} - -QVariantList RunningScriptsWidget::getRunning() { - const int WINDOWS_DRIVE_LETTER_SIZE = 1; - QVariantList result; - foreach(const QString& runningScript, qApp->getRunningScripts()) { - QUrl runningScriptURL = QUrl(runningScript); - if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { - runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded))); - } - QVariantMap resultNode; - resultNode.insert("name", runningScriptURL.fileName()); - resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded))); - // The path contains the exact path/URL of the script, which also is used in the stopScript function. - resultNode.insert("path", runningScript); - resultNode.insert("local", runningScriptURL.isLocalFile()); - result.append(resultNode); - } - return result; -} - -QVariantList RunningScriptsWidget::getPublic() { - return getPublicChildNodes(NULL); -} - -QVariantList RunningScriptsWidget::getPublicChildNodes(TreeNodeFolder* parent) { - QVariantList result; - QList treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel() - ->getFolderNodes(parent); - for (int i = 0; i < treeNodes.size(); i++) { - TreeNodeBase* node = treeNodes.at(i); - if (node->getType() == TREE_NODE_TYPE_FOLDER) { - TreeNodeFolder* folder = static_cast(node); - QVariantMap resultNode; - resultNode.insert("name", node->getName()); - resultNode.insert("type", "folder"); - resultNode.insert("children", getPublicChildNodes(folder)); - result.append(resultNode); - continue; - } - TreeNodeScript* script = static_cast(node); - if (script->getOrigin() == ScriptOrigin::SCRIPT_ORIGIN_LOCAL) { - continue; - } - QVariantMap resultNode; - resultNode.insert("name", node->getName()); - resultNode.insert("type", "script"); - resultNode.insert("url", script->getFullPath()); - result.append(resultNode); - } - return result; -} - -QVariantList RunningScriptsWidget::getLocal() { - QVariantList result; - QList treeNodes = qApp->getRunningScriptsWidget()->getScriptsModel() - ->getFolderNodes(NULL); - for (int i = 0; i < treeNodes.size(); i++) { - TreeNodeBase* node = treeNodes.at(i); - if (node->getType() != TREE_NODE_TYPE_SCRIPT) { - continue; - } - TreeNodeScript* script = static_cast(node); - if (script->getOrigin() != ScriptOrigin::SCRIPT_ORIGIN_LOCAL) { - continue; - } - QVariantMap resultNode; - resultNode.insert("name", node->getName()); - resultNode.insert("path", script->getFullPath()); - result.append(resultNode); - } - return result; -} - -bool RunningScriptsWidget::stopScriptByName(const QString& name) { - foreach (const QString& runningScript, qApp->getRunningScripts()) { - if (QUrl(runningScript).fileName().toLower() == name.trimmed().toLower()) { - return qApp->stopScript(runningScript, false); - } - } - return false; -} - -bool RunningScriptsWidget::stopScript(const QString& name, bool restart) { - return qApp->stopScript(name, restart); + DependencyManager::get()->stopAllScripts(); } diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 9029b13c56..b60586b5e7 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -33,24 +33,12 @@ public: void setRunningScripts(const QStringList& list); - const ScriptsModel* getScriptsModel() { return &_scriptsModel; } - -signals: - void scriptStopped(const QString& scriptName); - protected: virtual bool eventFilter(QObject* sender, QEvent* event); virtual void keyPressEvent(QKeyEvent* event); virtual void showEvent(QShowEvent* event); -public slots: - QVariantList getRunning(); - QVariantList getPublic(); - QVariantList getLocal(); - bool stopScript(const QString& name, bool restart = false); - bool stopScriptByName(const QString& name); - private slots: void allScriptsStopped(); void updateFileFilter(const QString& filter); @@ -62,9 +50,6 @@ private: Ui::RunningScriptsWidget* ui; QSignalMapper _reloadSignalMapper; QSignalMapper _stopSignalMapper; - ScriptsModelFilter _scriptsModelFilter; - ScriptsModel _scriptsModel; - QVariantList getPublicChildNodes(TreeNodeFolder* parent); }; diff --git a/interface/src/ui/ScriptEditorWidget.cpp b/interface/src/ui/ScriptEditorWidget.cpp index 380b645681..a261c208d7 100644 --- a/interface/src/ui/ScriptEditorWidget.cpp +++ b/interface/src/ui/ScriptEditorWidget.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -101,17 +102,18 @@ bool ScriptEditorWidget::setRunning(bool run) { disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } + auto scriptEngines = DependencyManager::get(); if (run) { const QString& scriptURLString = QUrl(_currentScript).toString(); // Reload script so that an out of date copy is not retrieved from the cache - _scriptEngine = qApp->loadScript(scriptURLString, true, true, false, true); + _scriptEngine = scriptEngines->loadScript(scriptURLString, true, true, false, true); connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); } else { connect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished); const QString& scriptURLString = QUrl(_currentScript).toString(); - qApp->stopScript(scriptURLString); + scriptEngines->stopScript(scriptURLString); _scriptEngine = NULL; } _console->setScriptEngine(_scriptEngine); @@ -173,7 +175,7 @@ void ScriptEditorWidget::loadFile(const QString& scriptPath) { } } const QString& scriptURLString = QUrl(_currentScript).toString(); - _scriptEngine = qApp->getScriptEngine(scriptURLString); + _scriptEngine = DependencyManager::get()->getScriptEngine(scriptURLString); if (_scriptEngine != NULL) { connect(_scriptEngine, &ScriptEngine::runningStateChanged, this, &ScriptEditorWidget::runningStateChanged); connect(_scriptEngine, &ScriptEngine::update, this, &ScriptEditorWidget::onScriptModified); @@ -187,11 +189,12 @@ bool ScriptEditorWidget::save() { } bool ScriptEditorWidget::saveAs() { + auto scriptEngines = DependencyManager::get(); QString fileName = QFileDialog::getSaveFileName(this, tr("Save script"), - qApp->getPreviousScriptLocation(), + scriptEngines->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!fileName.isEmpty()) { - qApp->setPreviousScriptLocation(fileName); + scriptEngines->setPreviousScriptLocation(fileName); return saveFile(fileName); } else { return false; diff --git a/interface/src/ui/ScriptEditorWindow.cpp b/interface/src/ui/ScriptEditorWindow.cpp index 3c6d0f73d5..919c8b64d3 100644 --- a/interface/src/ui/ScriptEditorWindow.cpp +++ b/interface/src/ui/ScriptEditorWindow.cpp @@ -16,6 +16,7 @@ #include "ScriptEditorWidget.h" #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include "Application.h" #include "PathUtils.h" @@ -85,11 +87,12 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) { } void ScriptEditorWindow::loadScriptClicked() { + auto scriptEngines = DependencyManager::get(); QString scriptName = QFileDialog::getOpenFileName(this, tr("Interface"), - qApp->getPreviousScriptLocation(), + scriptEngines->getPreviousScriptLocation(), tr("JavaScript Files (*.js)")); if (!scriptName.isEmpty()) { - qApp->setPreviousScriptLocation(scriptName); + scriptEngines->setPreviousScriptLocation(scriptName); addScriptEditorWidget("loading...")->loadFile(scriptName); updateButtons(); } @@ -97,7 +100,7 @@ void ScriptEditorWindow::loadScriptClicked() { void ScriptEditorWindow::loadMenuAboutToShow() { _loadMenu->clear(); - QStringList runningScripts = qApp->getRunningScripts(); + QStringList runningScripts = DependencyManager::get()->getRunningScripts();; if (runningScripts.count() > 0) { QSignalMapper* signalMapper = new QSignalMapper(this); foreach (const QString& runningScript, runningScripts) { diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 290e2e4f79..fb6e9c3b65 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -15,14 +15,10 @@ #include -const QString Basic2DWindowOpenGLDisplayPlugin::NAME("2D Display"); +const QString Basic2DWindowOpenGLDisplayPlugin::NAME("Desktop"); static const QString FULLSCREEN = "Fullscreen"; -const QString& Basic2DWindowOpenGLDisplayPlugin::getName() const { - return NAME; -} - void Basic2DWindowOpenGLDisplayPlugin::activate() { WindowOpenGLDisplayPlugin::activate(); diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h index e3633b5fe8..6523b58914 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.h @@ -18,7 +18,7 @@ class Basic2DWindowOpenGLDisplayPlugin : public WindowOpenGLDisplayPlugin { Q_OBJECT public: - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } virtual float getTargetFrameRate() override { return _framerateTarget ? (float) _framerateTarget : TARGET_FRAMERATE_Basic2DWindowOpenGL; } diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp index f780534bc9..e8145db900 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.cpp @@ -14,10 +14,6 @@ const QString NullDisplayPlugin::NAME("NullDisplayPlugin"); -const QString & NullDisplayPlugin::getName() const { - return NAME; -} - glm::uvec2 NullDisplayPlugin::getRecommendedRenderSize() const { return glm::uvec2(100, 100); } diff --git a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h index 23e23e2c4e..14b5529a29 100644 --- a/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/NullDisplayPlugin.h @@ -13,7 +13,8 @@ class NullDisplayPlugin : public DisplayPlugin { public: virtual ~NullDisplayPlugin() final {} - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } + virtual grouping getGrouping() const override { return DEVELOPER; } void stop() override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp index ffaf005533..40c6e6306b 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.cpp @@ -44,11 +44,7 @@ void main() { )FS"; -const QString InterleavedStereoDisplayPlugin::NAME("Interleaved Stereo Display"); - -const QString & InterleavedStereoDisplayPlugin::getName() const { - return NAME; -} +const QString InterleavedStereoDisplayPlugin::NAME("3D TV - Interleaved"); InterleavedStereoDisplayPlugin::InterleavedStereoDisplayPlugin() { } diff --git a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h index 7116363e44..df2a9f4800 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/InterleavedStereoDisplayPlugin.h @@ -13,7 +13,9 @@ class InterleavedStereoDisplayPlugin : public StereoDisplayPlugin { Q_OBJECT public: InterleavedStereoDisplayPlugin(); - virtual const QString & getName() const override; + + virtual const QString& getName() const override { return NAME; } + virtual grouping getGrouping() const override { return ADVANCED; } // initialize OpenGL context settings needed by the plugin virtual void customizeContext() override; diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp index 12865cf4cd..e8a83d2b08 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.cpp @@ -11,10 +11,6 @@ const QString SideBySideStereoDisplayPlugin::NAME("3D TV - Side by Side Stereo"); -const QString & SideBySideStereoDisplayPlugin::getName() const { - return NAME; -} - SideBySideStereoDisplayPlugin::SideBySideStereoDisplayPlugin() { } diff --git a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h index 9f8440227f..70f69ba4cb 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/SideBySideStereoDisplayPlugin.h @@ -15,7 +15,8 @@ class SideBySideStereoDisplayPlugin : public StereoDisplayPlugin { Q_OBJECT public: SideBySideStereoDisplayPlugin(); - virtual const QString& getName() const override; + virtual const QString& getName() const override { return NAME; } + virtual grouping getGrouping() const override { return ADVANCED; } virtual glm::uvec2 getRecommendedRenderSize() const override; private: static const QString NAME; diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h index b31c59d11a..6acb2bf041 100644 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.h @@ -67,7 +67,7 @@ public: // Plugin functions virtual bool isSupported() const override { return true; } virtual bool isJointController() const override { return false; } - const QString& getName() const override { return NAME; } + virtual const QString& getName() const override { return NAME; } virtual void pluginFocusOutEvent() override { _inputDevice->focusOutEvent(); } virtual void pluginUpdate(float deltaTime, bool jointsCaptured) override; diff --git a/libraries/plugins/src/plugins/Plugin.h b/libraries/plugins/src/plugins/Plugin.h index c030b1073f..ee10ce331c 100644 --- a/libraries/plugins/src/plugins/Plugin.h +++ b/libraries/plugins/src/plugins/Plugin.h @@ -19,6 +19,11 @@ public: /// \return human-readable name virtual const QString& getName() const = 0; + typedef enum { STANDARD, ADVANCED, DEVELOPER } grouping; + + /// \return human-readable grouping for the plugin, STANDARD, ADVANCED, or DEVELOPER + virtual grouping getGrouping() const { return STANDARD; } + /// \return string ID (not necessarily human-readable) virtual const QString& getID() const { assert(false); return UNKNOWN_PLUGIN_ID; } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 6153795ea6..55970a2d6e 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -22,7 +22,7 @@ public: AmbientOcclusion(); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; const gpu::PipelinePointer& getOcclusionPipeline(); const gpu::PipelinePointer& getHBlurPipeline(); diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index c7cce4cb15..51a8e3a1de 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -22,7 +22,7 @@ public: Antialiasing(); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; const gpu::PipelinePointer& getAntialiasingPipeline(); const gpu::PipelinePointer& getBlendPipeline(); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 682888b2af..c9ea24644a 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -18,7 +18,7 @@ class DebugDeferredBuffer { public: - using JobModel = render::Job::Model; + using JobModel = render::Task::Job::Model; DebugDeferredBuffer(); diff --git a/libraries/render-utils/src/HitEffect.h b/libraries/render-utils/src/HitEffect.h index 0a96a5300d..a83fb36693 100644 --- a/libraries/render-utils/src/HitEffect.h +++ b/libraries/render-utils/src/HitEffect.h @@ -17,7 +17,7 @@ public: HitEffect(); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; const gpu::PipelinePointer& getHitEffectPipeline(); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 1ad0bb4abf..e533e2cb49 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -9,7 +9,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "RenderDeferredTask.h" #include #include @@ -24,10 +23,13 @@ #include "HitEffect.h" #include "TextureCache.h" +#include "render/DrawTask.h" #include "render/DrawStatus.h" #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" +#include "RenderDeferredTask.h" + #include "model_vert.h" #include "model_shadow_vert.h" #include "model_normal_map_vert.h" @@ -81,63 +83,61 @@ RenderDeferredTask::RenderDeferredTask() : Task() { initDeferredPipelines(*shapePlumber); // CPU only, create the list of renderedOpaques items - _jobs.push_back(Job(new FetchItems::JobModel("FetchOpaque", - FetchItems([](const RenderContextPointer& context, int count) { - context->getItemsConfig().opaque.numFeed = count; - }) - ))); - _jobs.push_back(Job(new CullItems::JobModel("CullOpaque", _jobs.back().getOutput()))); - _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); + addJob("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) { + context->getItemsConfig().opaque.numFeed = count; + })); + addJob>("CullOpaque", _jobs.back().getOutput()); + addJob("DepthSortOpaque", _jobs.back().getOutput()); auto& renderedOpaques = _jobs.back().getOutput(); // CPU only, create the list of renderedTransparents items - _jobs.push_back(Job(new FetchItems::JobModel("FetchTransparent", - FetchItems(ItemFilter::Builder::transparentShape().withoutLayered(), - [](const RenderContextPointer& context, int count) { - context->getItemsConfig().transparent.numFeed = count; - }) - ))); - _jobs.push_back(Job(new CullItems::JobModel("CullTransparent", _jobs.back().getOutput()))); - _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); + addJob("FetchTransparent", FetchItems( + ItemFilter::Builder::transparentShape().withoutLayered(), + [](const RenderContextPointer& context, int count) { + context->getItemsConfig().transparent.numFeed = count; + } + )); + addJob>("CullTransparent", _jobs.back().getOutput()); + addJob("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)); auto& renderedTransparents = _jobs.back().getOutput(); // GPU Jobs: Start preparing the deferred and lighting buffer - _jobs.push_back(Job(new PrepareDeferred::JobModel("PrepareDeferred"))); + addJob("PrepareDeferred"); // Render opaque objects in DeferredBuffer - _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", renderedOpaques, DrawOpaqueDeferred(shapePlumber)))); + addJob("DrawOpaqueDeferred", renderedOpaques, shapePlumber); // Once opaque is all rendered create stencil background - _jobs.push_back(Job(new DrawStencilDeferred::JobModel("DrawOpaqueStencil"))); + addJob("DrawOpaqueStencil"); // Use Stencil and start drawing background in Lighting buffer - _jobs.push_back(Job(new DrawBackgroundDeferred::JobModel("DrawBackgroundDeferred"))); + addJob("DrawBackgroundDeferred"); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. - _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); + addJob("DrawLight"); // DeferredBuffer is complete, now let's shade it into the LightingBuffer - _jobs.push_back(Job(new RenderDeferred::JobModel("RenderDeferred"))); + addJob("RenderDeferred"); // AO job, to be revisited - _jobs.push_back(Job(new AmbientOcclusion::JobModel("AmbientOcclusion"))); + addJob("AmbientOcclusion"); _jobs.back().setEnabled(false); _occlusionJobIndex = (int)_jobs.size() - 1; // AA job to be revisited - _jobs.push_back(Job(new Antialiasing::JobModel("Antialiasing"))); + addJob("Antialiasing"); _jobs.back().setEnabled(false); _antialiasingJobIndex = (int)_jobs.size() - 1; // Render transparent objects forward in LigthingBuffer - _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", renderedTransparents, DrawTransparentDeferred(shapePlumber)))); + addJob("DrawTransparentDeferred", renderedTransparents, shapePlumber); // Lighting Buffer ready for tone mapping - _jobs.push_back(Job(new ToneMappingDeferred::JobModel("ToneMapping"))); + addJob("ToneMapping"); _toneMappingJobIndex = (int)_jobs.size() - 1; // Debugging Deferred buffer job - _jobs.push_back(Job(new DebugDeferredBuffer::JobModel("DebugDeferredBuffer"))); + addJob("DebugDeferredBuffer"); _jobs.back().setEnabled(false); _drawDebugDeferredBufferIndex = (int)_jobs.size() - 1; @@ -146,21 +146,18 @@ RenderDeferredTask::RenderDeferredTask() : Task() { // Grab a texture map representing the different status icons and assign that to the drawStatsuJob auto iconMapPath = PathUtils::resourcesPath() + "icons/statusIconAtlas.svg"; auto statusIconMap = DependencyManager::get()->getImageTexture(iconMapPath); - _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)))); + addJob("DrawStatus", renderedOpaques, DrawStatus(statusIconMap)); _jobs.back().setEnabled(false); _drawStatusJobIndex = (int)_jobs.size() - 1; } - _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D", DrawOverlay3D(shapePlumber)))); + addJob("DrawOverlay3D", shapePlumber); - _jobs.push_back(Job(new HitEffect::JobModel("HitEffect"))); + addJob("HitEffect"); _jobs.back().setEnabled(false); _drawHitEffectJobIndex = (int)_jobs.size() -1; - _jobs.push_back(Job(new Blit::JobModel("Blit"))); -} - -RenderDeferredTask::~RenderDeferredTask() { + addJob("Blit"); } void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index b93cd0ec02..85e3e7f211 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -12,8 +12,7 @@ #ifndef hifi_RenderDeferredTask_h #define hifi_RenderDeferredTask_h -#include "render/DrawTask.h" -#include "render/ShapePipeline.h" +#include "render/Engine.h" #include "gpu/Pipeline.h" @@ -23,14 +22,14 @@ class SetupDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; class PrepareDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; @@ -38,7 +37,7 @@ class RenderDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; class ToneMappingDeferred { @@ -47,7 +46,7 @@ public: ToneMappingEffect _toneMappingEffect; - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; class DrawOpaqueDeferred { @@ -55,7 +54,7 @@ public: DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); - typedef render::Job::ModelI JobModel; + using JobModel = render::Task::Job::ModelI; protected: render::ShapePlumberPointer _shapePlumber; @@ -66,7 +65,7 @@ public: DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); - typedef render::Job::ModelI JobModel; + using JobModel = render::Task::Job::ModelI; protected: render::ShapePlumberPointer _shapePlumber; @@ -78,7 +77,7 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; protected: static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable @@ -88,7 +87,7 @@ class DrawBackgroundDeferred { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; class DrawOverlay3D { @@ -98,7 +97,7 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; protected: static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable @@ -109,17 +108,14 @@ class Blit { public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); - typedef render::Job::Model JobModel; + using JobModel = render::Task::Job::Model; }; class RenderDeferredTask : public render::Task { public: RenderDeferredTask(); - ~RenderDeferredTask(); - render::Jobs _jobs; - int _drawDebugDeferredBufferIndex = -1; int _drawStatusJobIndex = -1; int _drawHitEffectJobIndex = -1; diff --git a/libraries/render/src/render/Context.cpp b/libraries/render/src/render/Context.cpp new file mode 100644 index 0000000000..8facaad1b2 --- /dev/null +++ b/libraries/render/src/render/Context.cpp @@ -0,0 +1,32 @@ +// +// Context.cpp +// render/src/render +// +// Created by Zach Pomerantz on 1/6/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 "Context.h" + +using namespace render; + +RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) + : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, + _args{ nullptr }, + _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, + _items{ items }, _tone{ tone } +{ +} + +void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { + _occlusionStatus = occlusion; + _fxaaStatus = fxaa; + + if (showOwned) { + _drawStatus |= render::showNetworkStatusFlag; + } +}; + diff --git a/libraries/render/src/render/Context.h b/libraries/render/src/render/Context.h new file mode 100644 index 0000000000..4eeba0ee08 --- /dev/null +++ b/libraries/render/src/render/Context.h @@ -0,0 +1,109 @@ +// +// Context.h +// render/src/render +// +// Created by Zach Pomerantz on 1/6/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_render_Context_h +#define hifi_render_Context_h + +#include "Scene.h" + +namespace render { + +class SceneContext { +public: + ScenePointer _scene; + + SceneContext() {} +}; +using SceneContextPointer = std::shared_ptr; + +// see examples/utilities/tools/renderEngineDebug.js +const int showDisplayStatusFlag = 1; +const int showNetworkStatusFlag = 2; + +class RenderContext { +public: + class ItemsConfig { + public: + class Counter { + public: + Counter() {} + Counter(const Counter& counter) : maxDrawn { counter.maxDrawn } {} + + void setCounts(const Counter& counter) { + numFeed = counter.numFeed; + numDrawn = counter.numDrawn; + }; + + int numFeed { 0 }; + int numDrawn { 0 }; + int maxDrawn { -1 }; + }; + + class State : public Counter { + public: + bool render { true }; + bool cull { true }; + bool sort { true }; + + Counter counter{}; + }; + + ItemsConfig(State opaqueState, State transparentState, Counter overlay3DCounter) + : opaque{ opaqueState }, transparent{ transparentState }, overlay3D{ overlay3DCounter } {} + ItemsConfig() : ItemsConfig{ {}, {}, {} } {} + + // TODO: If member count increases, store counters in a map instead of multiple members + State opaque{}; + State transparent{}; + Counter overlay3D{}; + }; + + class Tone { + public: + int toneCurve = 1; // Means just Gamma 2.2 correction + float exposure = 0.0; + }; + + RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); + RenderContext() {} + + void setArgs(RenderArgs* args) { _args = args; } + RenderArgs* getArgs() { return _args; } + ItemsConfig& getItemsConfig() { return _items; } + Tone& getTone() { return _tone; } + int getDrawStatus() { return _drawStatus; } + bool getDrawHitEffect() { return _drawHitEffect; } + bool getOcclusionStatus() { return _occlusionStatus; } + bool getFxaaStatus() { return _fxaaStatus; } + void setOptions(bool occlusion, bool fxaa, bool showOwned); + + // Debugging + int _deferredDebugMode; + glm::vec4 _deferredDebugSize; + +protected: + RenderArgs* _args; + + // Options + int _drawStatus; // bitflag + bool _drawHitEffect; + bool _occlusionStatus { false }; + bool _fxaaStatus = { false }; + + ItemsConfig _items; + Tone _tone; +}; +typedef std::shared_ptr RenderContextPointer; + +} + +#endif // hifi_render_Context_h + diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h index 1239819911..c9cf16c0cb 100644 --- a/libraries/render/src/render/DrawStatus.h +++ b/libraries/render/src/render/DrawStatus.h @@ -38,7 +38,7 @@ namespace render { void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); - typedef Job::ModelI JobModel; + using JobModel = Task::Job::ModelI; const gpu::PipelinePointer getDrawItemBoundsPipeline(); const gpu::PipelinePointer getDrawItemStatusPipeline(); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 4791324002..2b977081a1 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -15,44 +15,12 @@ #include #include -#include #include #include using namespace render; -DrawSceneTask::DrawSceneTask() : Task() { -} - -DrawSceneTask::~DrawSceneTask() { -} - -void DrawSceneTask::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - // sanity checks - assert(sceneContext); - if (!sceneContext->_scene) { - return; - } - - - // Is it possible that we render without a viewFrustum ? - if (!(renderContext->getArgs() && renderContext->getArgs()->_viewFrustum)) { - return; - } - - for (auto job : _jobs) { - job.run(sceneContext, renderContext); - } -}; - -Job::~Job() { -} - - - - - void render::cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { assert(renderContext->getArgs()); assert(renderContext->getArgs()->_viewFrustum); @@ -247,19 +215,6 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; renderLights(sceneContext, renderContext, culledItems); + args->_batch = nullptr; }); - args->_batch = nullptr; -} - -void ItemMaterialBucketMap::insert(const ItemID& id, const model::MaterialKey& key) { - // Insert the itemID in every bucket where it filters true - for (auto& bucket : (*this)) { - if (bucket.first.test(key)) { - bucket.second.push_back(id); - } - } -} - -void ItemMaterialBucketMap::allocateStandardMaterialBuckets() { - (*this)[model::MaterialFilter::Builder::opaqueDiffuse()]; } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index f2eb6920cf..b31ec9763e 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -9,216 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_render_Task_h -#define hifi_render_Task_h +#ifndef hifi_render_DrawTask_h +#define hifi_render_DrawTask_h #include "Engine.h" #include "ShapePipeline.h" #include "gpu/Batch.h" -#include namespace render { -template void jobRun(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - jobModel.run(sceneContext, renderContext); -} -template void jobRunI(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input) { - jobModel.run(sceneContext, renderContext, input); -} -template void jobRunO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, O& output) { - jobModel.run(sceneContext, renderContext, output); -} -template void jobRunIO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { - jobModel.run(sceneContext, renderContext, input, output); -} - -class Job { -public: - - // Varying represent a varying piece of data - class Varying { - public: - - Varying(const Varying& var): _concept(var._concept) {} - - Varying() {} - template - Varying(const T& data) : _concept(std::make_shared>(data)) {} - - // Access the _data contained win the concept explicitely - template T& edit() { return std::static_pointer_cast>(_concept)->_data; } - template const T& get() const { return std::static_pointer_cast>(_concept)->_data; } - - protected: - friend class Job; - - std::vector> _consumerJobs; - - void addJobConsumer(const std::shared_ptr& job) { - _consumerJobs.push_back(job); - } - - class Concept { - public: - virtual ~Concept() = default; - }; - template class Model : public Concept { - public: - typedef T Data; - Data _data; - Model(const Model& source): _data(source.data) {} - Model(const Data& data): _data(data) {} - virtual ~Model() {} - }; - - std::shared_ptr _concept; - }; - - Job(const Job& other) : _concept(other._concept) {} - ~Job(); - - bool isEnabled() const { return _concept->isEnabled(); } - void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); } - - const std::string& getName() const { return _concept->getName(); } - const Varying getInput() const { return _concept->getInput(); } - const Varying getOutput() const { return _concept->getOutput(); } - - template T& edit() { - auto theConcept = std::dynamic_pointer_cast(_concept); - assert(theConcept); - return theConcept->_data; - } - template const T& get() const { - auto theConcept = std::dynamic_pointer_cast(_concept); - assert(theConcept); - return theConcept->_data; - } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - PerformanceTimer perfTimer(getName().c_str()); - PROFILE_RANGE(getName().c_str()); - _concept->run(sceneContext, renderContext); - } - -public: - class Concept { - std::string _name; - bool _isEnabled = true; - public: - Concept() : _name() {} - Concept(const std::string& name) : _name(name) {} - virtual ~Concept() = default; - - void setName(const std::string& name) { _name = name; } - const std::string& getName() const { return _name; } - - bool isEnabled() const { return _isEnabled; } - void setEnabled(bool isEnabled) { _isEnabled = isEnabled; } - - virtual const Varying getInput() const { return Varying(); } - virtual const Varying getOutput() const { return Varying(); } - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; - }; - - Job(Concept* concept) : _concept(concept) {} - -public: - template class Model : public Concept { - public: - typedef T Data; - - Data _data; - - Model() {} - Model(const std::string& name): Concept(name) {} - Model(Data data): _data(data) {} - Model(const std::string& name, Data data): Concept(name), _data(data) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRun(_data, sceneContext, renderContext); - } - } - }; - - template class ModelI : public Concept { - public: - typedef T Data; - typedef I Input; - - Data _data; - Varying _input; - - const Varying getInput() const { return _input; } - - ModelI(const std::string& name, const Varying& input, Data data = Data()) : Concept(name), _data(data), _input(input) {} - ModelI(const std::string& name, Data data): Concept(name), _data(data) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunI(_data, sceneContext, renderContext, _input.get()); - } - } - }; - - template class ModelO : public Concept { - public: - typedef T Data; - typedef O Output; - - Data _data; - Varying _output; - - const Varying getOutput() const { return _output; } - - ModelO(const std::string& name): Concept(name), _output(Output()) { - - } - - ModelO(const std::string& name, Data data): Concept(name), _data(data), _output(Output()) {} - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunO(_data, sceneContext, renderContext, _output.edit()); - } - } - }; - - template class ModelIO : public Concept { - public: - typedef T Data; - typedef I Input; - typedef O Output; - - Data _data; - Varying _input; - Varying _output; - - const Varying getInput() const { return _input; } - const Varying getOutput() const { return _output; } - - ModelIO(const std::string& name, const Varying& input, Data data = Data()): Concept(name), _data(data), _input(input), _output(Output()) {} - ModelIO(const std::string& name, Data data, Output output): Concept(name), _data(data), _output(output) {} - - void setInput(const Varying& input) { _input = input; } - - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - if (isEnabled()) { - jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); - } - } - }; - - std::shared_ptr _concept; -}; - - - - -typedef std::vector Jobs; - void cullItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outITems); void depthSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, bool frontToBack, const ItemIDsBounds& inItems, ItemIDsBounds& outITems); void renderLights(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); @@ -236,7 +36,7 @@ public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems); - typedef Job::ModelO JobModel; + using JobModel = Task::Job::ModelO; }; template @@ -248,7 +48,8 @@ public: renderContext->getArgs()->_details.pointTo(T); render::cullItems(sceneContext, renderContext, inItems, outItems); } - typedef Job::ModelIO JobModel; + + using JobModel = Task::Job::ModelIO, ItemIDsBounds, ItemIDsBounds>; }; class DepthSortItems { @@ -259,43 +60,16 @@ public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outITems); - typedef Job::ModelIO JobModel; + using JobModel = Task::Job::ModelIO; }; class DrawLight { public: void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - typedef Job::Model JobModel; + using JobModel = Task::Job::Model; }; -class DrawSceneTask : public Task { -public: - - DrawSceneTask(); - ~DrawSceneTask(); - - Jobs _jobs; - - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext); - -}; - - -// A map of ItemIDs allowing to create bucket lists of SHAPE type items which are filtered by their -// Material -class ItemMaterialBucketMap : public std::map { -public: - - ItemMaterialBucketMap() {} - - void insert(const ItemID& id, const model::MaterialKey& key); - - // standard builders allocating the main buckets - void allocateStandardMaterialBuckets(); -}; -void materialSortItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - } -#endif // hifi_render_Task_h +#endif // hifi_render_DrawTask_h diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 907c836347..aa5816475a 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -8,26 +8,11 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + #include "Engine.h" -#include "DrawTask.h" using namespace render; -RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) - : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, - _args{ nullptr }, - _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, - _items{ items }, _tone{ tone } {} - -void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { - _occlusionStatus = occlusion; - _fxaaStatus = fxaa; - - if (showOwned) { - _drawStatus |= render::showNetworkStatusFlag; - } -}; - Engine::Engine() : _sceneContext(std::make_shared()), _renderContext(std::make_shared()) @@ -49,16 +34,8 @@ void Engine::addTask(const TaskPointer& task) { } void Engine::run() { + // TODO: Tasks will need to be specified such that their data can feed into each other for (auto task : _tasks) { task->run(_sceneContext, _renderContext); } } - -void Engine::buildStandardTaskPipeline() { - if (!_tasks.empty()) { - _tasks.clear(); - } - - addTask(std::make_shared()); -} - diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 4192dd3ed9..fa01ef6018 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -12,115 +12,11 @@ #ifndef hifi_render_Engine_h #define hifi_render_Engine_h -#include "Scene.h" +#include "Context.h" +#include "Task.h" namespace render { - -class SceneContext { -public: - ScenePointer _scene; - - SceneContext() {} -}; -using SceneContextPointer = std::shared_ptr; - -// see examples/utilities/tools/renderEngineDebug.js -const int showDisplayStatusFlag = 1; -const int showNetworkStatusFlag = 2; - - -class RenderContext { -public: - class ItemsConfig { - public: - class Counter { - public: - Counter() {}; - Counter(const Counter& counter) { - numFeed = numDrawn = 0; - maxDrawn = counter.maxDrawn; - }; - - void setCounts(const Counter& counter) { - numFeed = counter.numFeed; - numDrawn = counter.numDrawn; - }; - - int numFeed = 0; - int numDrawn = 0; - int maxDrawn = -1; - }; - - class State : public Counter { - public: - bool render = true; - bool cull = true; - bool sort = true; - - Counter counter{}; - }; - - ItemsConfig(State opaqueState, State transparentState, Counter overlay3DCounter) - : opaque{ opaqueState }, transparent{ transparentState }, overlay3D{ overlay3DCounter } {} - ItemsConfig() : ItemsConfig{ {}, {}, {} } {} - - // TODO: If member count increases, store counters in a map instead of multiple members - State opaque{}; - State transparent{}; - Counter overlay3D{}; - }; - - class Tone { - public: - int toneCurve = 1; // Means just Gamma 2.2 correction - float exposure = 0.0; - }; - - RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); - RenderContext() : RenderContext({}, {}, {}, {}, {}, {}) {}; - - void setArgs(RenderArgs* args) { _args = args; } - inline RenderArgs* getArgs() { return _args; } - inline ItemsConfig& getItemsConfig() { return _items; } - inline Tone& getTone() { return _tone; } - inline int getDrawStatus() { return _drawStatus; } - inline bool getDrawHitEffect() { return _drawHitEffect; } - inline bool getOcclusionStatus() { return _occlusionStatus; } - inline bool getFxaaStatus() { return _fxaaStatus; } - void setOptions(bool occlusion, bool fxaa, bool showOwned); - - // Debugging - int _deferredDebugMode; - glm::vec4 _deferredDebugSize; - -protected: - RenderArgs* _args; - - // Options - int _drawStatus; // bitflag - bool _drawHitEffect; - bool _occlusionStatus = false; - bool _fxaaStatus = false; - - ItemsConfig _items; - Tone _tone; -}; -typedef std::shared_ptr RenderContextPointer; - -// The base class for a task that runs on the SceneContext -class Task { -public: - Task() {} - ~Task() {} - - virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {} - -protected: -}; -typedef std::shared_ptr TaskPointer; -typedef std::vector Tasks; - // The root of the tasks, the Engine, should not be known from the Tasks, // The SceneContext is what navigates from the engine down to the Tasks class Engine { @@ -142,11 +38,7 @@ public: void run(); - // standard pipeline of tasks - void buildStandardTaskPipeline(); - protected: - Tasks _tasks; SceneContextPointer _sceneContext; @@ -157,3 +49,4 @@ typedef std::shared_ptr EnginePointer; } #endif // hifi_render_Engine_h + diff --git a/libraries/render/src/render/Task.h b/libraries/render/src/render/Task.h new file mode 100644 index 0000000000..4d2a3728c6 --- /dev/null +++ b/libraries/render/src/render/Task.h @@ -0,0 +1,227 @@ +// +// Task.h +// render/src/render +// +// Created by Zach Pomerantz on 1/6/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_render_Task_h +#define hifi_render_Task_h + +#include "Context.h" + +#include "gpu/Batch.h" +#include + +namespace render { + +// A varying piece of data, to be used as Job/Task I/O +// TODO: Task IO +class Varying { +public: + Varying() {} + Varying(const Varying& var) : _concept(var._concept) {} + template Varying(const T& data) : _concept(std::make_shared>(data)) {} + + template T& edit() { return std::static_pointer_cast>(_concept)->_data; } + template const T& get() { return std::static_pointer_cast>(_concept)->_data; } + +protected: + class Concept { + public: + virtual ~Concept() = default; + }; + template class Model : public Concept { + public: + using Data = T; + + Model(const Data& data) : _data(data) {} + virtual ~Model() = default; + + Data _data; + }; + + std::shared_ptr _concept; +}; + +// FIXME: In c++17, use default classes of nullptr_t to combine these +template void jobRun(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + jobModel.run(sceneContext, renderContext); +} +template void jobRunI(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input) { + jobModel.run(sceneContext, renderContext, input); +} +template void jobRunO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, O& output) { + jobModel.run(sceneContext, renderContext, output); +} +template void jobRunIO(T& jobModel, const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const I& input, O& output) { + jobModel.run(sceneContext, renderContext, input, output); +} + +// The base class for a task that runs on the SceneContext +class Task { +public: + // The guts of a task; tasks are composed of multiple Jobs that execute serially + class Job { + public: + friend class Task; + + // The guts of a job; jobs are composed of a concept + class Concept { + public: + Concept() = default; + virtual ~Concept() = default; + + bool isEnabled() const { return _isEnabled; } + void setEnabled(bool isEnabled) { _isEnabled = isEnabled; } + + virtual const Varying getInput() const { return Varying(); } + virtual const Varying getOutput() const { return Varying(); } + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; + + protected: + + bool _isEnabled = true; + }; + using ConceptPointer = std::shared_ptr; + + + template class Model : public Concept { + public: + typedef T Data; + + Data _data; + + Model() {} + Model(Data data): _data(data) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRun(_data, sceneContext, renderContext); + } + } + }; + + template class ModelI : public Concept { + public: + typedef T Data; + typedef I Input; + + Data _data; + Varying _input; + + const Varying getInput() const { return _input; } + + ModelI(const Varying& input, Data data = Data()) : _data(data), _input(input) {} + ModelI(Data data) : _data(data) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunI(_data, sceneContext, renderContext, _input.get()); + } + } + }; + + template class ModelO : public Concept { + public: + typedef T Data; + typedef O Output; + + Data _data; + Varying _output; + + const Varying getOutput() const { return _output; } + + ModelO(Data data) : _data(data), _output(Output()) {} + ModelO() : _output(Output()) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunO(_data, sceneContext, renderContext, _output.edit()); + } + } + }; + + template class ModelIO : public Concept { + public: + typedef T Data; + typedef I Input; + typedef O Output; + + Data _data; + Varying _input; + Varying _output; + + const Varying getInput() const { return _input; } + const Varying getOutput() const { return _output; } + + ModelIO(const Varying& input, Data data = Data()) : _data(data), _input(input), _output(Output()) {} + ModelIO(Data data) : _data(data), _output(Output()) {} + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); + } + } + }; + + Job(ConceptPointer concept) : _concept(concept) {} + Job(std::string name, ConceptPointer concept) : _concept(concept), _name(name) {} + + bool isEnabled() const { return _concept->isEnabled(); } + void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); } + + const Varying getInput() const { return _concept->getInput(); } + const Varying getOutput() const { return _concept->getOutput(); } + + template T& edit() { + auto concept = std::static_pointer_cast(_concept); + assert(concept); + return concept->_data; + } + template const T& get() const { + auto concept = std::static_pointer_cast(_concept); + assert(concept); + return concept->_data; + } + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + PerformanceTimer perfTimer(_name.c_str()); + PROFILE_RANGE(_name.c_str()); + + _concept->run(sceneContext, renderContext); + } + protected: + ConceptPointer _concept; + std::string _name = ""; + + }; + using Jobs = std::vector; + +public: + Task() = default; + virtual ~Task() = default; + + // Queue a new job to the task; returns the job's index + template size_t addJob(std::string name, A&&... args) { + _jobs.emplace_back(name, std::make_shared(std::forward(args)...)); + return _jobs.size() - 1; + } + + const Job& getJob(size_t i) const { return _jobs.at(i); } + + virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {} + +protected: + Jobs _jobs; +}; +typedef std::shared_ptr TaskPointer; +typedef std::vector Tasks; + +} + +#endif // hifi_render_Task_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 8026102478..e997879391 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -46,11 +46,13 @@ #include "TypedArrays.h" #include "XMLHttpRequestClass.h" #include "WebSocketClass.h" - #include "RecordingScriptingInterface.h" +#include "ScriptEngines.h" #include "MIDIEvent.h" +std::atomic ScriptEngine::_stoppingAllScripts { false }; + static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3"; Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) @@ -132,9 +134,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _fileNameString(fileNameString), _arrayBufferClass(new ArrayBufferClass(this)) { - _allScriptsMutex.lock(); - _allKnownScriptEngines.insert(this); - _allScriptsMutex.unlock(); + DependencyManager::get()->addScriptEngine(this); connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { hadUncaughtExceptions(*this, _fileNameString); @@ -144,14 +144,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam ScriptEngine::~ScriptEngine() { qCDebug(scriptengine) << "Script Engine shutting down (destructor) for script:" << getFilename(); - // If we're not already in the middle of stopping all scripts, then we should remove ourselves - // from the list of running scripts. We don't do this if we're in the process of stopping all scripts - // because that method removes scripts from its list as it iterates them - if (!_stoppingAllScripts) { - _allScriptsMutex.lock(); - _allKnownScriptEngines.remove(this); - _allScriptsMutex.unlock(); - } + DependencyManager::get()->removeScriptEngine(this); } void ScriptEngine::disconnectNonEssentialSignals() { @@ -187,67 +180,6 @@ void ScriptEngine::runInThread() { workerThread->start(); } -QSet ScriptEngine::_allKnownScriptEngines; -QMutex ScriptEngine::_allScriptsMutex; -std::atomic ScriptEngine::_stoppingAllScripts { false }; - -void ScriptEngine::stopAllScripts(QObject* application) { - _allScriptsMutex.lock(); - _stoppingAllScripts = true; - - qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); - - QMutableSetIterator i(_allKnownScriptEngines); - while (i.hasNext()) { - ScriptEngine* scriptEngine = i.next(); - - QString scriptName = scriptEngine->getFilename(); - - // NOTE: typically all script engines are running. But there's at least one known exception to this, the - // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using - // them. We don't need to stop scripts that aren't running. - if (scriptEngine->isRunning()) { - - // If the script is running, but still evaluating then we need to wait for its evaluation step to - // complete. After that we can handle the stop process appropriately - if (scriptEngine->evaluatePending()) { - while (scriptEngine->evaluatePending()) { - - // This event loop allows any started, but not yet finished evaluate() calls to complete - // we need to let these complete so that we can be guaranteed that the script engine isn't - // in a partially setup state, which can confuse our shutdown unwinding. - QEventLoop loop; - QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit); - loop.exec(); - } - } - - // We disconnect any script engine signals from the application because we don't want to do any - // extra stopScript/loadScript processing that the Application normally does when scripts start - // and stop. We can safely short circuit this because we know we're in the "quitting" process - scriptEngine->disconnect(application); - - // Calling stop on the script engine will set it's internal _isFinished state to true, and result - // in the ScriptEngine gracefully ending it's run() method. - scriptEngine->stop(); - - // We need to wait for the engine to be done running before we proceed, because we don't - // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing - // any application state after we leave this stopAllScripts() method - qCDebug(scriptengine) << "waiting on script:" << scriptName; - scriptEngine->waitTillDoneRunning(); - qCDebug(scriptengine) << "done waiting on script:" << scriptName; - - // If the script is stopped, we can remove it from our set - i.remove(); - } - } - _stoppingAllScripts = false; - _allScriptsMutex.unlock(); - qCDebug(scriptengine) << "DONE Stopping all scripts...."; -} - - void ScriptEngine::waitTillDoneRunning() { // If the script never started running or finished running before we got here, we don't need to wait for it if (_isRunning && _isThreaded) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 67a206d673..f89998d9f0 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -129,7 +129,6 @@ public: bool isRunning() const { return _isRunning; } // used by ScriptWidget void disconnectNonEssentialSignals(); - static void stopAllScripts(QObject* application); // used by Application on shutdown //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - These are the callback implementations for ScriptUser the get called by ScriptCache when the contents @@ -204,8 +203,7 @@ protected: void forwardHandlerCall(const EntityItemID& entityID, const QString& eventName, QScriptValueList eventHanderArgs); Q_INVOKABLE void entityScriptContentAvailable(const EntityItemID& entityID, const QString& scriptOrURL, const QString& contents, bool isURL, bool success); - static QSet _allKnownScriptEngines; - static QMutex _allScriptsMutex; + friend class ScriptEngines; static std::atomic _stoppingAllScripts; }; diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp new file mode 100644 index 0000000000..293d798cd0 --- /dev/null +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -0,0 +1,435 @@ +// +// Created by Bradley Austin Davis on 2016/01/08 +// Copyright 2013-2016 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 "ScriptEngines.h" + +#include +#include + +#include +#include + +#include "ScriptEngine.h" +#include "ScriptEngineLogging.h" + +#define __STR2__(x) #x +#define __STR1__(x) __STR2__(x) +#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: " + +#ifndef __APPLE__ +static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +#else +// Temporary fix to Qt bug: http://stackoverflow.com/questions/16194475 +static const QString DESKTOP_LOCATION = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).append("/script.js"); +#endif + +ScriptsModel& getScriptsModel() { + static ScriptsModel scriptsModel; + return scriptsModel; +} + +ScriptEngines::ScriptEngines() + : _scriptsLocationHandle("scriptsLocation", DESKTOP_LOCATION), + _previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION) +{ + _scriptsModelFilter.setSourceModel(&_scriptsModel); + _scriptsModelFilter.sort(0, Qt::AscendingOrder); + _scriptsModelFilter.setDynamicSortFilter(true); +} + + +QObject* scriptsModel(); + +void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) { + _scriptInitializers.push_back(initializer); +} + +void ScriptEngines::addScriptEngine(ScriptEngine* engine) { + _allScriptsMutex.lock(); + _allKnownScriptEngines.insert(engine); + _allScriptsMutex.unlock(); +} + +void ScriptEngines::removeScriptEngine(ScriptEngine* engine) { + // If we're not already in the middle of stopping all scripts, then we should remove ourselves + // from the list of running scripts. We don't do this if we're in the process of stopping all scripts + // because that method removes scripts from its list as it iterates them + if (!_stoppingAllScripts) { + _allScriptsMutex.lock(); + _allKnownScriptEngines.remove(engine); + _allScriptsMutex.unlock(); + } +} + +void ScriptEngines::shutdownScripting() { + _allScriptsMutex.lock(); + _stoppingAllScripts = true; + ScriptEngine::_stoppingAllScripts = true; + qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); + + QMutableSetIterator i(_allKnownScriptEngines); + while (i.hasNext()) { + ScriptEngine* scriptEngine = i.next(); + QString scriptName = scriptEngine->getFilename(); + + // NOTE: typically all script engines are running. But there's at least one known exception to this, the + // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using + // them. We don't need to stop scripts that aren't running. + if (scriptEngine->isRunning()) { + + // If the script is running, but still evaluating then we need to wait for its evaluation step to + // complete. After that we can handle the stop process appropriately + if (scriptEngine->evaluatePending()) { + while (scriptEngine->evaluatePending()) { + + // This event loop allows any started, but not yet finished evaluate() calls to complete + // we need to let these complete so that we can be guaranteed that the script engine isn't + // in a partially setup state, which can confuse our shutdown unwinding. + QEventLoop loop; + QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit); + loop.exec(); + } + } + + // We disconnect any script engine signals from the application because we don't want to do any + // extra stopScript/loadScript processing that the Application normally does when scripts start + // and stop. We can safely short circuit this because we know we're in the "quitting" process + scriptEngine->disconnect(this); + + // Calling stop on the script engine will set it's internal _isFinished state to true, and result + // in the ScriptEngine gracefully ending it's run() method. + scriptEngine->stop(); + + // We need to wait for the engine to be done running before we proceed, because we don't + // want any of the scripts final "scriptEnding()" or pending "update()" methods from accessing + // any application state after we leave this stopAllScripts() method + qCDebug(scriptengine) << "waiting on script:" << scriptName; + scriptEngine->waitTillDoneRunning(); + qCDebug(scriptengine) << "done waiting on script:" << scriptName; + + // If the script is stopped, we can remove it from our set + i.remove(); + } + } + _stoppingAllScripts = false; + _allScriptsMutex.unlock(); + qCDebug(scriptengine) << "DONE Stopping all scripts...."; +} + +QVariantList getPublicChildNodes(TreeNodeFolder* parent) { + QVariantList result; + QList treeNodes = getScriptsModel().getFolderNodes(parent); + for (int i = 0; i < treeNodes.size(); i++) { + TreeNodeBase* node = treeNodes.at(i); + if (node->getType() == TREE_NODE_TYPE_FOLDER) { + TreeNodeFolder* folder = static_cast(node); + QVariantMap resultNode; + resultNode.insert("name", node->getName()); + resultNode.insert("type", "folder"); + resultNode.insert("children", getPublicChildNodes(folder)); + result.append(resultNode); + continue; + } + TreeNodeScript* script = static_cast(node); + if (script->getOrigin() == ScriptOrigin::SCRIPT_ORIGIN_LOCAL) { + continue; + } + QVariantMap resultNode; + resultNode.insert("name", node->getName()); + resultNode.insert("type", "script"); + resultNode.insert("url", script->getFullPath()); + result.append(resultNode); + } + return result; +} + +QVariantList ScriptEngines::getPublic() { + return getPublicChildNodes(NULL); +} + +QVariantList ScriptEngines::getLocal() { + QVariantList result; + QList treeNodes = getScriptsModel().getFolderNodes(NULL); + for (int i = 0; i < treeNodes.size(); i++) { + TreeNodeBase* node = treeNodes.at(i); + if (node->getType() != TREE_NODE_TYPE_SCRIPT) { + continue; + } + TreeNodeScript* script = static_cast(node); + if (script->getOrigin() != ScriptOrigin::SCRIPT_ORIGIN_LOCAL) { + continue; + } + QVariantMap resultNode; + resultNode.insert("name", node->getName()); + resultNode.insert("path", script->getFullPath()); + result.append(resultNode); + } + return result; +} + +QVariantList ScriptEngines::getRunning() { + const int WINDOWS_DRIVE_LETTER_SIZE = 1; + QVariantList result; + auto runningScripts = getRunningScripts(); + foreach(const QString& runningScript, runningScripts) { + QUrl runningScriptURL = QUrl(runningScript); + if (runningScriptURL.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) { + runningScriptURL = QUrl::fromLocalFile(runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded))); + } + QVariantMap resultNode; + resultNode.insert("name", runningScriptURL.fileName()); + resultNode.insert("url", runningScriptURL.toDisplayString(QUrl::FormattingOptions(QUrl::FullyEncoded))); + // The path contains the exact path/URL of the script, which also is used in the stopScript function. + resultNode.insert("path", runningScript); + resultNode.insert("local", runningScriptURL.isLocalFile()); + result.append(resultNode); + } + return result; +} + + +static const QString SETTINGS_KEY = "Settings"; +static const QString DEFAULT_SCRIPTS_JS_URL = "http://s3.amazonaws.com/hifi-public/scripts/defaultScripts.js"; + +void ScriptEngines::loadDefaultScripts() { + loadScript(DEFAULT_SCRIPTS_JS_URL); +} + +void ScriptEngines::loadOneScript(const QString& scriptFilename) { + loadScript(scriptFilename); +} + +void ScriptEngines::loadScripts() { + // check first run... + if (_firstRun.get()) { + qCDebug(scriptengine) << "This is a first run..."; + // clear the scripts, and set out script to our default scripts + clearScripts(); + loadDefaultScripts(); + _firstRun.set(false); + return; + } + + // loads all saved scripts + Settings settings; + int size = settings.beginReadArray(SETTINGS_KEY); + for (int i = 0; i < size; ++i) { + settings.setArrayIndex(i); + QString string = settings.value("script").toString(); + if (!string.isEmpty()) { + loadScript(string); + } + } + settings.endArray(); +} + +void ScriptEngines::clearScripts() { + // clears all scripts from the settingsSettings settings; + Settings settings; + settings.beginWriteArray(SETTINGS_KEY); + settings.remove(""); +} + +void ScriptEngines::saveScripts() { + // Saves all currently running user-loaded scripts + Settings settings; + settings.beginWriteArray(SETTINGS_KEY); + settings.remove(""); + + QStringList runningScripts = getRunningScripts(); + int i = 0; + for (auto it = runningScripts.begin(); it != runningScripts.end(); ++it) { + if (getScriptEngine(*it)->isUserLoaded()) { + settings.setArrayIndex(i); + settings.setValue("script", *it); + ++i; + } + } + settings.endArray(); +} + +QStringList ScriptEngines::getRunningScripts() { + QReadLocker lock(&_scriptEnginesHashLock); + return _scriptEnginesHash.keys(); +} + +void ScriptEngines::stopAllScripts(bool restart) { + QReadLocker lock(&_scriptEnginesHashLock); + if (restart) { + // Delete all running scripts from cache so that they are re-downloaded when they are restarted + auto scriptCache = DependencyManager::get(); + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (!it.value()->isFinished()) { + scriptCache->deleteScript(it.key()); + } + } + } + + // Stop and possibly restart all currently running scripts + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (it.value()->isFinished()) { + continue; + } + if (restart && it.value()->isUserLoaded()) { + connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { + reloadScript(scriptName); + }); + } + QMetaObject::invokeMethod(it.value(), "stop"); + //it.value()->stop(); + qCDebug(scriptengine) << "stopping script..." << it.key(); + } +} + +bool ScriptEngines::stopScript(const QString& scriptHash, bool restart) { + bool stoppedScript = false; + { + QReadLocker lock(&_scriptEnginesHashLock); + if (_scriptEnginesHash.contains(scriptHash)) { + ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash]; + if (restart) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(QUrl(scriptHash)); + connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) { + reloadScript(scriptName); + }); + } + scriptEngine->stop(); + stoppedScript = true; + qCDebug(scriptengine) << "stopping script..." << scriptHash; + } + } + return stoppedScript; +} + +QString ScriptEngines::getScriptsLocation() const { + return _scriptsLocationHandle.get(); +} + +void ScriptEngines::setScriptsLocation(const QString& scriptsLocation) { + _scriptsLocationHandle.set(scriptsLocation); + _scriptsModel.updateScriptsLocation(scriptsLocation); +} + +void ScriptEngines::reloadAllScripts() { + DependencyManager::get()->clearCache(); + emit scriptsReloading(); + stopAllScripts(true); +} + +ScriptEngine* ScriptEngines::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow, bool reload) { + if (thread() != QThread::currentThread()) { + ScriptEngine* result { nullptr }; + QMetaObject::invokeMethod(this, "loadScript", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ScriptEngine*, result), + Q_ARG(QString, scriptFilename), + Q_ARG(bool, isUserLoaded), + Q_ARG(bool, loadScriptFromEditor), + Q_ARG(bool, activateMainWindow), + Q_ARG(bool, reload)); + return result; + } + QUrl scriptUrl(scriptFilename); + auto scriptEngine = getScriptEngine(scriptUrl.toString()); + if (scriptEngine) { + return scriptEngine; + } + + scriptEngine = new ScriptEngine(NO_SCRIPT, "", true); + scriptEngine->setUserLoaded(isUserLoaded); + + if (scriptFilename.isNull()) { + launchScriptEngine(scriptEngine); + } else { + // connect to the appropriate signals of this script engine + connect(scriptEngine, &ScriptEngine::scriptLoaded, this, &ScriptEngines::onScriptEngineLoaded); + connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &ScriptEngines::onScriptEngineError); + + // get the script engine object to load the script at the designated script URL + scriptEngine->loadURL(scriptUrl, reload); + } + + return scriptEngine; +} + +ScriptEngine* ScriptEngines::getScriptEngine(const QString& scriptHash) { + ScriptEngine* result = nullptr; + { + QReadLocker lock(&_scriptEnginesHashLock); + auto it = _scriptEnginesHash.find(scriptHash); + if (it != _scriptEnginesHash.end()) { + result = it.value(); + } + } + return result; +} + +// FIXME - change to new version of ScriptCache loading notification +void ScriptEngines::onScriptEngineLoaded(const QString& scriptFilename) { + UserActivityLogger::getInstance().loadedScript(scriptFilename); + ScriptEngine* scriptEngine = qobject_cast(sender()); + + launchScriptEngine(scriptEngine); + + { + QWriteLocker lock(&_scriptEnginesHashLock); + _scriptEnginesHash.insertMulti(scriptFilename, scriptEngine); + } + emit scriptCountChanged(); +} + +void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) { + connect(scriptEngine, &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection); + connect(scriptEngine, &ScriptEngine::loadScript, [&](const QString& scriptName, bool userLoaded) { + loadScript(scriptName, userLoaded); + }); + connect(scriptEngine, &ScriptEngine::reloadScript, [&](const QString& scriptName, bool userLoaded) { + loadScript(scriptName, userLoaded, false, false, true); + }); + + // register our application services and set it off on its own thread + for (auto initializer : _scriptInitializers) { + initializer(scriptEngine); + } + scriptEngine->runInThread(); +} + + +void ScriptEngines::onScriptFinished(const QString& scriptName, ScriptEngine* engine) { + bool removed = false; + { + QWriteLocker lock(&_scriptEnginesHashLock); + const QString& scriptURLString = QUrl(scriptName).toString(); + for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) { + if (it.value() == engine) { + _scriptEnginesHash.erase(it); + removed = true; + break; + } + } + } + + if (removed) { + emit scriptCountChanged(); + } +} + +// FIXME - change to new version of ScriptCache loading notification +void ScriptEngines::onScriptEngineError(const QString& scriptFilename) { + qCDebug(scriptengine) << "Application::loadScript(), script failed to load..."; + emit scriptLoadError(scriptFilename, ""); +} + +QString ScriptEngines::getPreviousScriptLocation() const { + return _previousScriptLocation.get(); +} + +void ScriptEngines::setPreviousScriptLocation(const QString& previousScriptLocation) { + _previousScriptLocation.set(previousScriptLocation); +} diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h new file mode 100644 index 0000000000..6d78f3cfa0 --- /dev/null +++ b/libraries/script-engine/src/ScriptEngines.h @@ -0,0 +1,103 @@ +// +// Created by Bradley Austin Davis on 2016/01/08 +// Copyright 2013-2016 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_ScriptEngines_h +#define hifi_ScriptEngines_h + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "ScriptsModel.h" +#include "ScriptsModelFilter.h" + +class ScriptEngine; + +class ScriptEngines : public QObject, public Dependency { + Q_OBJECT + + Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT) + Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT) + +public: + using ScriptInitializer = std::function; + + ScriptEngines(); + void registerScriptInitializer(ScriptInitializer initializer); + + void loadScripts(); + void saveScripts(); + void clearScripts(); + + QString getScriptsLocation() const; + void loadDefaultScripts(); + void setScriptsLocation(const QString& scriptsLocation); + QStringList getRunningScripts(); + ScriptEngine* getScriptEngine(const QString& scriptHash); + + QString getPreviousScriptLocation() const; + void setPreviousScriptLocation(const QString& previousScriptLocation); + + ScriptsModel* scriptsModel() { return &_scriptsModel; }; + ScriptsModelFilter* scriptsModelFilter() { return &_scriptsModelFilter; }; + + Q_INVOKABLE void loadOneScript(const QString& scriptFilename); + Q_INVOKABLE ScriptEngine* loadScript(const QString& scriptFilename = QString(), + bool isUserLoaded = true, bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); + Q_INVOKABLE bool stopScript(const QString& scriptHash, bool restart = false); + + Q_INVOKABLE void reloadAllScripts(); + Q_INVOKABLE void stopAllScripts(bool restart = false); + + Q_INVOKABLE QVariantList getRunning(); + Q_INVOKABLE QVariantList getPublic(); + Q_INVOKABLE QVariantList getLocal(); + + // Called at shutdown time + void shutdownScripting(); + +signals: + void scriptCountChanged(); + void scriptsReloading(); + void scriptLoadError(const QString& filename, const QString& error); + +protected slots: + void onScriptFinished(const QString& fileNameString, ScriptEngine* engine); + +protected: + friend class ScriptEngine; + + void reloadScript(const QString& scriptName) { loadScript(scriptName, true, false, false, true); } + void addScriptEngine(ScriptEngine* engine); + void removeScriptEngine(ScriptEngine* engine); + void onScriptEngineLoaded(const QString& scriptFilename); + void onScriptEngineError(const QString& scriptFilename); + void launchScriptEngine(ScriptEngine* engine); + + + Setting::Handle _firstRun { "firstRun", true }; + QReadWriteLock _scriptEnginesHashLock; + QHash _scriptEnginesHash; + QSet _allKnownScriptEngines; + QMutex _allScriptsMutex; + std::atomic _stoppingAllScripts { false }; + std::list _scriptInitializers; + mutable Setting::Handle _scriptsLocationHandle; + mutable Setting::Handle _previousScriptLocation; + ScriptsModel _scriptsModel; + ScriptsModelFilter _scriptsModelFilter; +}; + +#endif // hifi_ScriptEngine_h diff --git a/interface/src/ScriptsModel.cpp b/libraries/script-engine/src/ScriptsModel.cpp similarity index 96% rename from interface/src/ScriptsModel.cpp rename to libraries/script-engine/src/ScriptsModel.cpp index a59d499863..07518dded1 100644 --- a/interface/src/ScriptsModel.cpp +++ b/libraries/script-engine/src/ScriptsModel.cpp @@ -1,7 +1,4 @@ // -// ScriptsModel.cpp -// interface/src -// // Created by Ryan Huffman on 05/12/14. // Copyright 2014 High Fidelity, Inc. // @@ -11,17 +8,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ScriptsModel.h" + #include #include #include #include -#include "Application.h" -#include "Menu.h" -#include "InterfaceLogging.h" +#include "ScriptEngine.h" +#include "ScriptEngines.h" +#include "ScriptEngineLogging.h" +#define __STR2__(x) #x +#define __STR1__(x) __STR2__(x) +#define __LOC__ __FILE__ "("__STR1__(__LINE__)") : Warning Msg: " -#include "ScriptsModel.h" static const QString S3_URL = "http://s3.amazonaws.com/hifi-public"; static const QString PUBLIC_URL = "http://public.highfidelity.io"; @@ -60,11 +61,8 @@ ScriptsModel::ScriptsModel(QObject* parent) : _localDirectory.setFilter(QDir::Files | QDir::Readable); _localDirectory.setNameFilters(QStringList("*.js")); - updateScriptsLocation(qApp->getScriptsLocation()); - + auto scriptEngines = DependencyManager::get(); connect(&_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &ScriptsModel::reloadLocalFiles); - connect(qApp, &Application::scriptLocationChanged, this, &ScriptsModel::updateScriptsLocation); - reloadLocalFiles(); reloadRemoteFiles(); } @@ -182,7 +180,7 @@ void ScriptsModel::downloadFinished() { if (!data.isEmpty()) { finished = parseXML(data); } else { - qCDebug(interfaceapp) << "Error: Received no data when loading remote scripts"; + qCDebug(scriptengine) << "Error: Received no data when loading remote scripts"; } } @@ -231,7 +229,7 @@ bool ScriptsModel::parseXML(QByteArray xmlFile) { // Error handling if (xml.hasError()) { - qCDebug(interfaceapp) << "Error loading remote scripts: " << xml.errorString(); + qCDebug(scriptengine) << "Error loading remote scripts: " << xml.errorString(); return true; } diff --git a/interface/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h similarity index 98% rename from interface/src/ScriptsModel.h rename to libraries/script-engine/src/ScriptsModel.h index ae75acfa3b..df9716d43b 100644 --- a/interface/src/ScriptsModel.h +++ b/libraries/script-engine/src/ScriptsModel.h @@ -92,6 +92,7 @@ protected: void rebuildTree(); private: + friend class ScriptEngines; bool _loadingScripts; QDir _localDirectory; QFileSystemWatcher _fsWatcher; diff --git a/interface/src/ScriptsModelFilter.cpp b/libraries/script-engine/src/ScriptsModelFilter.cpp similarity index 100% rename from interface/src/ScriptsModelFilter.cpp rename to libraries/script-engine/src/ScriptsModelFilter.cpp diff --git a/interface/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h similarity index 100% rename from interface/src/ScriptsModelFilter.h rename to libraries/script-engine/src/ScriptsModelFilter.h diff --git a/libraries/shared/src/Transform.h b/libraries/shared/src/Transform.h index ec81513f3e..310e2d0409 100644 --- a/libraries/shared/src/Transform.h +++ b/libraries/shared/src/Transform.h @@ -104,8 +104,6 @@ public: const Vec3& getScale() const; Transform& setScale(float scale); Transform& setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale] - Transform& preScale(float scale); - Transform& preScale(const Vec3& scale); Transform& postScale(float scale); // [new this] = [this] * [scale] equivalent to:glScale Transform& postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to:glScale @@ -350,14 +348,6 @@ inline Transform& Transform::setScale(const Vec3& scale) { return *this; } -inline Transform& Transform::preScale(float scale) { - return setScale(getScale() * scale); -} - -inline Transform& Transform::preScale(const Vec3& scale) { - return setScale(getScale() * scale); -} - inline Transform& Transform::postScale(float scale) { if (!isValidScale(scale) || scale == 1.0f) { return *this; @@ -376,6 +366,9 @@ inline Transform& Transform::postScale(const Vec3& scale) { return *this; } invalidCache(); + if ((scale.x != scale.y) || (scale.x != scale.z)) { + flagNonUniform(); + } if (isScaling()) { _scale *= scale; } else { diff --git a/plugins/hifiNeuron/src/NeuronPlugin.h b/plugins/hifiNeuron/src/NeuronPlugin.h index c85a5dd383..33c6879f5b 100644 --- a/plugins/hifiNeuron/src/NeuronPlugin.h +++ b/plugins/hifiNeuron/src/NeuronPlugin.h @@ -28,7 +28,7 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } - const QString& getName() const override { return NAME; } + virtual const QString& getName() const override { return NAME; } const QString& getID() const override { return NEURON_ID_STRING; } virtual void activate() override; diff --git a/plugins/hifiSdl2/src/SDL2Manager.h b/plugins/hifiSdl2/src/SDL2Manager.h index 61e2d37fd5..6506cdccb6 100644 --- a/plugins/hifiSdl2/src/SDL2Manager.h +++ b/plugins/hifiSdl2/src/SDL2Manager.h @@ -29,7 +29,7 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return false; } - const QString& getName() const override { return NAME; } + virtual const QString& getName() const override { return NAME; } virtual void init() override; virtual void deinit() override; diff --git a/plugins/hifiSixense/src/SixenseManager.h b/plugins/hifiSixense/src/SixenseManager.h index 753974e996..bbb9774368 100644 --- a/plugins/hifiSixense/src/SixenseManager.h +++ b/plugins/hifiSixense/src/SixenseManager.h @@ -29,8 +29,8 @@ public: // Plugin functions virtual bool isSupported() const override; virtual bool isJointController() const override { return true; } - const QString& getName() const override { return NAME; } - const QString& getID() const override { return HYDRA_ID_STRING; } + virtual const QString& getName() const override { return NAME; } + virtual const QString& getID() const override { return HYDRA_ID_STRING; } virtual void activate() override; virtual void deactivate() override; diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index d6811c3529..0be66bab67 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -78,7 +78,6 @@ void OculusBaseDisplayPlugin::deinit() { } void OculusBaseDisplayPlugin::activate() { - WindowOpenGLDisplayPlugin::activate(); if (!OVR_SUCCESS(ovr_Initialize(nullptr))) { qFatal("Could not init OVR"); } @@ -87,6 +86,8 @@ void OculusBaseDisplayPlugin::activate() { qFatal("Failed to acquire HMD"); } + WindowOpenGLDisplayPlugin::activate(); + _hmdDesc = ovr_GetHmdDesc(_session); _ipd = ovr_GetFloat(_session, OVR_KEY_IPD, _ipd); diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp index 26bb3cf9b2..94a3670e4b 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.cpp @@ -10,10 +10,6 @@ const QString OculusDebugDisplayPlugin::NAME("Oculus Rift (Simulator)"); -const QString & OculusDebugDisplayPlugin::getName() const { - return NAME; -} - static const QString DEBUG_FLAG("HIFI_DEBUG_OCULUS"); static bool enableDebugOculus = QProcessEnvironment::systemEnvironment().contains("HIFI_DEBUG_OCULUS"); diff --git a/plugins/oculus/src/OculusDebugDisplayPlugin.h b/plugins/oculus/src/OculusDebugDisplayPlugin.h index 04b68704cc..c62fe12c73 100644 --- a/plugins/oculus/src/OculusDebugDisplayPlugin.h +++ b/plugins/oculus/src/OculusDebugDisplayPlugin.h @@ -11,7 +11,8 @@ class OculusDebugDisplayPlugin : public OculusBaseDisplayPlugin { public: - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } + virtual grouping getGrouping() const override { return DEVELOPER; } virtual bool isSupported() const override; protected: diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index c8fb0ba080..3002c746a2 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -138,10 +138,6 @@ private: const QString OculusDisplayPlugin::NAME("Oculus Rift"); -const QString & OculusDisplayPlugin::getName() const { - return NAME; -} - static const QString MONO_PREVIEW = "Mono Preview"; static const QString FRAMERATE = DisplayPlugin::MENU_PATH() + ">Framerate"; diff --git a/plugins/oculus/src/OculusDisplayPlugin.h b/plugins/oculus/src/OculusDisplayPlugin.h index 03c9ba7511..98535a1790 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.h +++ b/plugins/oculus/src/OculusDisplayPlugin.h @@ -17,7 +17,7 @@ const float TARGET_RATE_Oculus = 75.0f; class OculusDisplayPlugin : public OculusBaseDisplayPlugin { public: virtual void activate() override; - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } virtual void setEyeRenderPose(uint32_t frameIndex, Eye eye, const glm::mat4& pose) override final; virtual float getTargetFrameRate() override { return TARGET_RATE_Oculus; } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp index f0f200fe1a..4bded4e58f 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.cpp @@ -26,11 +26,7 @@ using namespace oglplus; -const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5) (Simulated)"); - -const QString & OculusLegacyDisplayPlugin::getName() const { - return NAME; -} +const QString OculusLegacyDisplayPlugin::NAME("Oculus Rift (0.5) (Legacy)"); OculusLegacyDisplayPlugin::OculusLegacyDisplayPlugin() { } diff --git a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h index cd1177b581..1ba288cc65 100644 --- a/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h +++ b/plugins/oculusLegacy/src/OculusLegacyDisplayPlugin.h @@ -19,7 +19,7 @@ class OculusLegacyDisplayPlugin : public WindowOpenGLDisplayPlugin { public: OculusLegacyDisplayPlugin(); virtual bool isSupported() const override; - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } virtual void activate() override; virtual void deactivate() override; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 58f7536856..7fb70180c4 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -31,10 +31,6 @@ const QString OpenVrDisplayPlugin::NAME("OpenVR (Vive)"); const QString StandingHMDSensorMode = "Standing HMD Sensor Mode"; // this probably shouldn't be hardcoded here -const QString & OpenVrDisplayPlugin::getName() const { - return NAME; -} - static vr::IVRCompositor* _compositor{ nullptr }; vr::TrackedDevicePose_t _trackedDevicePose[vr::k_unMaxTrackedDeviceCount]; mat4 _trackedDevicePoseMat4[vr::k_unMaxTrackedDeviceCount]; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 8186e59936..e290368de0 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -18,7 +18,7 @@ const float TARGET_RATE_OpenVr = 90.0f; // FIXME: get from sdk tracked device p class OpenVrDisplayPlugin : public WindowOpenGLDisplayPlugin { public: virtual bool isSupported() const override; - virtual const QString & getName() const override; + virtual const QString& getName() const override { return NAME; } virtual bool isHmd() const override { return true; } virtual float getTargetFrameRate() override { return TARGET_RATE_OpenVr; } diff --git a/tests/shared/src/TransformTests.cpp b/tests/shared/src/TransformTests.cpp index b936d45555..ca55bbf873 100644 --- a/tests/shared/src/TransformTests.cpp +++ b/tests/shared/src/TransformTests.cpp @@ -11,19 +11,21 @@ #include "TransformTests.h" #include -#include #include -#include #include "../QTestExtensions.h" +#include +#include +#include +#include -using namespace glm; +//using namespace glm; const vec3 xAxis(1.0f, 0.0f, 0.0f); const vec3 yAxis(0.0f, 1.0f, 0.0f); const vec3 zAxis(0.0f, 0.0f, 1.0f); -const quat rot90 = angleAxis((float)M_PI / 2.0f, yAxis); +const quat rot90 = glm::angleAxis((float)M_PI / 2.0f, yAxis); QTEST_MAIN(TransformTests) @@ -71,19 +73,26 @@ void TransformTests::getInverseMatrix() { vec4( 0.0f, 1.0f, 0.0f, 0.0f), vec4( 0.0f, 0.0f, 1.0f, 0.0f), vec4( 0.0f, 0.0f, 0.0f, 1.0f)); - const mat4 result_a = inverse(m * mirrorX); + const mat4 result_a = glm::inverse(m * mirrorX); Transform xform; xform.setTranslation(t); xform.setRotation(rot90); - - // - // change postScale to preScale and the test will pass... - // - xform.postScale(vec3(-1.0f, 1.0f, 1.0f)); + mat4 result_b; xform.getInverseMatrix(result_b); - QCOMPARE_WITH_ABS_ERROR(result_a, result_b, EPSILON); + // don't check elements directly, instead compare each axis transformed by the matrix. + auto xa = transformPoint(result_a, xAxis); + auto ya = transformPoint(result_a, yAxis); + auto za = transformPoint(result_a, zAxis); + + auto xb = transformPoint(result_b, xAxis); + auto yb = transformPoint(result_b, yAxis); + auto zb = transformPoint(result_b, zAxis); + + QCOMPARE_WITH_ABS_ERROR(xa, xb, EPSILON); + QCOMPARE_WITH_ABS_ERROR(ya, yb, EPSILON); + QCOMPARE_WITH_ABS_ERROR(za, zb, EPSILON); }