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/examples/dropStuffNearMe.js b/examples/dropStuffNearMe.js new file mode 100644 index 0000000000..1c7c1f4f48 --- /dev/null +++ b/examples/dropStuffNearMe.js @@ -0,0 +1,87 @@ +// +// Created by Philip Rosedale on January 9, 2016 +// 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 +// +// Puts a bunch of entities in front of you, with various adjustable properties for testing. +// +// Note that when creating things quickly, the entity server will ignore data if we send updates too quickly. +// like Internet MTU, these rates are set by th domain operator, so in this script there is a RATE_PER_SECOND +// variable letting you set this speed. If entities are missing from the grid after a relog, this number +// being too high may be the reason. + +var SIZE = 0.5; +var TYPE = "Box"; // Right now this can be "Box" or "Model" or "Sphere" +var MODEL_URL = "http://s3.amazonaws.com/hifi-public/models/content/basketball2.fbx"; +var MODEL_DIMENSION = { x: 0.3, y: 0.3, z: 0.3 }; + +var RATE_PER_SECOND = 1000; // The entity server will drop data if we create things too fast. +var SCRIPT_INTERVAL = 100; +var LIFETIME = 90; + +var NUMBER_TO_CREATE = 300; + +var GRAVITY = { x: 0, y: -9.8, z: 0 }; +var VELOCITY = { x: 0.0, y: 0, z: 0 }; +var ANGULAR_VELOCITY = { x: 1, y: 1, z: 1 }; + +var DAMPING = 0.5; +var ANGULAR_DAMPING = 0.5; + +var collidable = true; +var gravity = true; + + +var x = 0; +var z = 0; +var totalCreated = 0; + +var RANGE = 10; +var HOW_FAR_IN_FRONT_OF_ME = 3 * RANGE; + + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(HOW_FAR_IN_FRONT_OF_ME, Quat.getFront(Camera.orientation))); + + +function randomVector(range) { + return { + x: (Math.random() - 0.5) * range.x, + y: (Math.random() - 0.5) * range.y, + z: (Math.random() - 0.5) * range.z + } +} + +Vec3.print("Center: ", center); + +Script.setInterval(function () { + if (!Entities.serversExist() || !Entities.canRez() || (totalCreated > NUMBER_TO_CREATE)) { + return; + } + + var numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0); + for (var i = 0; (i < numToCreate) && (totalCreated < NUMBER_TO_CREATE); i++) { + var position = Vec3.sum(center, randomVector({ x: RANGE, y: RANGE, z: RANGE })); + + Vec3.print("Position: ", position); + Entities.addEntity({ + type: TYPE, + modelURL: MODEL_URL, + name: "gridTest", + position: position, + dimensions: (TYPE == "Model") ? MODEL_DIMENSION : { x: SIZE, y: SIZE, z: SIZE }, + color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, + velocity: VELOCITY, + angularVelocity: Vec3.multiply(Math.random(), ANGULAR_VELOCITY), + damping: DAMPING, + angularDamping: ANGULAR_DAMPING, + gravity: (gravity ? GRAVITY : { x: 0, y: 0, z: 0}), + collisionsWillMove: collidable, + lifetime: LIFETIME + }); + + totalCreated++; + } +}, SCRIPT_INTERVAL); + diff --git a/examples/example/ui/energyBar.js b/examples/example/ui/energyBar.js new file mode 100644 index 0000000000..a45b09f6d4 --- /dev/null +++ b/examples/example/ui/energyBar.js @@ -0,0 +1,102 @@ +// energyBar.js +// examples/ui +// +// Created by Eric Levin on 1/4/15 +// Copyright 2015 High Fidelity, Inc. +// +// This script adds an energy bar overlay which displays the amount of energy a user has left for grabbing and moving objects +// +// 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/utils.js"); +var energyColor = {red: 0, green: 200, blue: 0}; +var lowEnergyColor = {red: 255, green: 0, blue: 0}; +var totalWidth = 200; +var paddingRight = 50; +var xPosition = Window.innerWidth - totalWidth - paddingRight; +var lowEnergyThreshold = 0.3; +var currentEnergy = 1.0; +var energyLossRate = 0.003; +var energyChargeRate = 0.003; +var isGrabbing = false; +var refractoryPeriod = 2000; + +var lastAvatarVelocity = MyAvatar.getVelocity(); +var lastAvatarPosition = MyAvatar.position; + +var background = Overlays.addOverlay("text", { + x: xPosition, + y: 20, + width: totalWidth, + height: 10, + backgroundColor: {red: 184, green: 181, blue: 178} +}) + +var bar = Overlays.addOverlay("text", { + x: xPosition, + y: 20, + width: totalWidth, + height: 10, + backgroundColor: energyColor +}); + + +// Takes an energy value between 0 and 1 and sets energy bar width appropriately +function setEnergy(energy) { + energy = clamp(energy, 0, 1); + var barWidth = totalWidth * energy; + var color = energy <= lowEnergyThreshold ? lowEnergyColor: energyColor; + Overlays.editOverlay(bar, { width: barWidth, backgroundColor: color}); +} + +function avatarAccelerationEnergy() { + var AVATAR_MOVEMENT_ENERGY_CONSTANT = 0.001; + var velocity = MyAvatar.getVelocity(); + var dV = Math.abs(Vec3.length(velocity) - Vec3.length(lastAvatarVelocity)); + var dE = Vec3.length(lastAvatarVelocity) * dV * AVATAR_MOVEMENT_ENERGY_CONSTANT; + lastAvatarVelocity = velocity; + return dE; +} + +function teleported() { + var MAX_AVATAR_MOVEMENT_PER_FRAME = 30.0; + var position = MyAvatar.position; + var dP = Vec3.length(Vec3.subtract(position, lastAvatarPosition)); + lastAvatarPosition = position; + return (dP > MAX_AVATAR_MOVEMENT_PER_FRAME); +} + +function audioEnergy() { + var AUDIO_ENERGY_CONSTANT = 0.000001; + return MyAvatar.audioLoudness * AUDIO_ENERGY_CONSTANT; +} + +function update() { + // refill energy + currentEnergy += energyChargeRate; + + // Avatar acceleration + currentEnergy -= avatarAccelerationEnergy(); + + // Teleport cost + if (teleported()) { + currentEnergy = 0; + } + + // Making sounds + currentEnergy -= audioEnergy(); + + + currentEnergy = clamp(currentEnergy, 0, 1); + setEnergy(currentEnergy); +} + +function cleanup() { + Overlays.deleteOverlay(background); + Overlays.deleteOverlay(bar); +} + +Script.update.connect(update); +Script.scriptEnding.connect(cleanup); diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index 115c1bcb65..b2b2902cb0 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -297,3 +297,8 @@ calculateHandSizeRatio = function() { var handSizeRatio = centerHandPoint/standardCenterHandPoint; return handSizeRatio; } + +clamp = function(val, min, max){ + return Math.max(min, Math.min(max, val)) + } + 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/src/Application.cpp b/interface/src/Application.cpp index 2fdd778339..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), @@ -452,8 +449,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _bookmarks = new Bookmarks(); // Before setting up the menu _runningScriptsWidget = new RunningScriptsWidget(_window); - _renderEngine->addTask(make_shared()); - _renderEngine->registerScene(_main3DScene); // start the nodeThread so its event loop is running QThread* nodeThread = new QThread(this); @@ -598,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); @@ -660,6 +676,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : _offscreenContext->makeCurrent(); initializeGL(); + // Start rendering + _renderEngine->addTask(make_shared()); + _renderEngine->registerScene(_main3DScene); _toolWindow = new ToolWindow(); _toolWindow->setWindowFlags((_toolWindow->windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::WindowMinimizeButtonHint); @@ -677,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 @@ -846,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 @@ -1016,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 @@ -1191,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()); @@ -1219,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()); @@ -1233,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(); @@ -4163,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. @@ -4231,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, @@ -4283,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 { @@ -4389,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; } @@ -4562,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()) { @@ -4799,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:"); @@ -4835,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()); 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/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/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index 596d042506..795a259a3c 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -48,7 +48,7 @@ public: // setters for lens attributes void setProjection(const glm::mat4 & projection); - void getFocalLength(float focalLength) { _focalLength = focalLength; } + void setFocalLength(float focalLength) { _focalLength = focalLength; } // getters for lens attributes const glm::mat4& getProjection() const { return _projection; } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index cf7c6ede93..fca581cd97 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -70,7 +70,7 @@ uniform SphericalHarmonics ambientSphere; <@include model/Light.slh@> <@func declareEvalAmbientGlobalColor()@> -vec3 evalAmbienGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { +vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { // Need the light now Light light = getLight(); @@ -93,7 +93,7 @@ vec3 evalAmbienGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 positi <$declareSphericalHarmonics()$> -vec3 evalAmbienSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { +vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, vec3 position, vec3 normal, vec3 diffuse, vec3 specular, float gloss) { // Need the light now Light light = getLight(); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index dfb0ecbffb..f00f558122 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -21,6 +21,8 @@ #include "model/Stage.h" #include "model/Geometry.h" +#include "render/ShapePipeline.h" + class RenderArgs; class SimpleProgramKey; struct LightLocations; @@ -30,7 +32,7 @@ class DeferredLightingEffect : public Dependency { SINGLETON_DEPENDENCY public: - static const int NORMAL_FITTING_MAP_SLOT = 10; + static const int NORMAL_FITTING_MAP_SLOT = render::ShapePipeline::Slot::NORMAL_FITTING_MAP; static const int DEFERRED_TRANSFORM_BUFFER_SLOT = 2; void init(); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 414ad9cf97..492021c1a9 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -14,30 +14,36 @@ #include #include "DeferredLightingEffect.h" - #include "Model.h" -namespace render { - template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { - if (payload) { - return payload->getKey(); - } - // Return opaque for lack of a better idea - return ItemKey::Builder::opaqueShape(); - } +using namespace render; - template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { - if (payload) { - return payload->getBound(); - } - return render::Item::Bound(); - } - template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { - return payload->render(args); +namespace render { +template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); } + return ItemKey::Builder::opaqueShape(); // for lack of a better idea } -using namespace render; +template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return Item::Bound(); +} + +template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getShapeKey(); + } + return ShapeKey::Builder::invalid(); +} + +template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); +} +} MeshPartPayload::MeshPartPayload(model::MeshPointer mesh, int partIndex, model::MaterialPointer material, const Transform& transform, const Transform& offsetTransform) { @@ -69,7 +75,7 @@ void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } -render::ItemKey MeshPartPayload::getKey() const { +ItemKey MeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); @@ -83,10 +89,32 @@ render::ItemKey MeshPartPayload::getKey() const { return builder.build(); } -render::Item::Bound MeshPartPayload::getBound() const { +Item::Bound MeshPartPayload::getBound() const { return _worldBound; } +ShapeKey MeshPartPayload::getShapeKey() const { + model::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + + ShapeKey::Builder builder; + if (drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap()) { + builder.withTranslucent(); + } + if (drawMaterialKey.isNormalMap()) { + builder.withTangents(); + } + if (drawMaterialKey.isGlossMap()) { + builder.withSpecular(); + } + if (drawMaterialKey.isLightmapMap()) { + builder.withLightmap(); + } + return builder.build(); +} + void MeshPartPayload::drawCall(gpu::Batch& batch) const { batch.drawIndexed(gpu::TRIANGLES, _drawPart._numIndices, _drawPart._startIndex); } @@ -104,14 +132,14 @@ void MeshPartPayload::bindMesh(gpu::Batch& batch) const { } } -void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const { +void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations) const { if (!_drawMaterial) { return; } auto textureCache = DependencyManager::get(); - batch.setUniformBuffer(ModelRender::MATERIAL_GPU_SLOT, _drawMaterial->getSchemaBuffer()); + batch.setUniformBuffer(ShapePipeline::Slot::MATERIAL_GPU, _drawMaterial->getSchemaBuffer()); auto materialKey = _drawMaterial->getKey(); auto textureMaps = _drawMaterial->getTextureMaps(); @@ -121,44 +149,44 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio if (materialKey.isDiffuseMap()) { auto diffuseMap = textureMaps[model::MaterialKey::DIFFUSE_MAP]; if (diffuseMap && diffuseMap->isDefined()) { - batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, diffuseMap->getTextureView()); + batch.setResourceTexture(ShapePipeline::Slot::DIFFUSE_MAP, diffuseMap->getTextureView()); if (!diffuseMap->getTextureTransform().isIdentity()) { diffuseMap->getTextureTransform().getMatrix(texcoordTransform[0]); } } else { - batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getGrayTexture()); + batch.setResourceTexture(ShapePipeline::Slot::DIFFUSE_MAP, textureCache->getGrayTexture()); } } else { - batch.setResourceTexture(ModelRender::DIFFUSE_MAP_SLOT, textureCache->getWhiteTexture()); + batch.setResourceTexture(ShapePipeline::Slot::DIFFUSE_MAP, textureCache->getWhiteTexture()); } // Normal map if (materialKey.isNormalMap()) { auto normalMap = textureMaps[model::MaterialKey::NORMAL_MAP]; if (normalMap && normalMap->isDefined()) { - batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, normalMap->getTextureView()); + batch.setResourceTexture(ShapePipeline::Slot::NORMAL_MAP, normalMap->getTextureView()); // texcoord are assumed to be the same has diffuse } else { - batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, textureCache->getBlueTexture()); + batch.setResourceTexture(ShapePipeline::Slot::NORMAL_MAP, textureCache->getBlueTexture()); } } else { - batch.setResourceTexture(ModelRender::NORMAL_MAP_SLOT, nullptr); + batch.setResourceTexture(ShapePipeline::Slot::NORMAL_MAP, nullptr); } // TODO: For now gloss map is used as the "specular map in the shading, we ll need to fix that if (materialKey.isGlossMap()) { auto specularMap = textureMaps[model::MaterialKey::GLOSS_MAP]; if (specularMap && specularMap->isDefined()) { - batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, specularMap->getTextureView()); + batch.setResourceTexture(ShapePipeline::Slot::SPECULAR_MAP, specularMap->getTextureView()); // texcoord are assumed to be the same has diffuse } else { - batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, textureCache->getBlackTexture()); + batch.setResourceTexture(ShapePipeline::Slot::SPECULAR_MAP, textureCache->getBlackTexture()); } } else { - batch.setResourceTexture(ModelRender::SPECULAR_MAP_SLOT, nullptr); + batch.setResourceTexture(ShapePipeline::Slot::SPECULAR_MAP, nullptr); } // TODO: For now lightmaop is piped into the emissive map unit, we need to fix that and support for real emissive too @@ -166,7 +194,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio auto lightmapMap = textureMaps[model::MaterialKey::LIGHTMAP_MAP]; if (lightmapMap && lightmapMap->isDefined()) { - batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, lightmapMap->getTextureView()); + batch.setResourceTexture(ShapePipeline::Slot::LIGHTMAP_MAP, lightmapMap->getTextureView()); auto lightmapOffsetScale = lightmapMap->getLightmapOffsetScale(); batch._glUniform2f(locations->emissiveParams, lightmapOffsetScale.x, lightmapOffsetScale.y); @@ -175,10 +203,10 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio lightmapMap->getTextureTransform().getMatrix(texcoordTransform[1]); } } else { - batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, textureCache->getGrayTexture()); + batch.setResourceTexture(ShapePipeline::Slot::LIGHTMAP_MAP, textureCache->getGrayTexture()); } } else { - batch.setResourceTexture(ModelRender::LIGHTMAP_MAP_SLOT, nullptr); + batch.setResourceTexture(ShapePipeline::Slot::LIGHTMAP_MAP, nullptr); } // Texcoord transforms ? @@ -187,7 +215,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ModelRender::Locatio } } -void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { +void MeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations) const { batch.setModelTransform(_drawTransform); } @@ -195,29 +223,12 @@ void MeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locati void MeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("MeshPartPayload::render"); - gpu::Batch& batch = *(args->_batch); - auto mode = args->_renderMode; - model::MaterialKey drawMaterialKey; - if (_drawMaterial) { - drawMaterialKey = _drawMaterial->getKey(); - } - bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); - - bool hasTangents = drawMaterialKey.isNormalMap(); - bool hasSpecular = drawMaterialKey.isGlossMap(); - bool hasLightmap = drawMaterialKey.isLightmapMap(); - bool isSkinned = false; - bool wireframe = false; - if (wireframe) { - translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; - } - - ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, - args, locations); + ShapeKey key = getShapeKey(); + auto locations = args->_pipeline->locations; + assert(locations); // Bind the model transform and the skinCLusterMatrices if needed bindTransform(batch, locations); @@ -230,7 +241,7 @@ void MeshPartPayload::render(RenderArgs* args) const { // TODO: We should be able to do that just in the renderTransparentJob - if (translucentMesh && locations->lightBufferUnit >= 0) { + if (key.isTranslucent() && locations->lightBufferUnit >= 0) { PerformanceTimer perfTimer("DLE->setupTransparent()"); DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); @@ -251,29 +262,32 @@ void MeshPartPayload::render(RenderArgs* args) const { } } - - namespace render { - template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload) { - if (payload) { - return payload->getKey(); - } - // Return opaque for lack of a better idea - return ItemKey::Builder::opaqueShape(); - } - - template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload) { - if (payload) { - return payload->getBound(); - } - return render::Item::Bound(); - } - template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args) { - return payload->render(args); +template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getKey(); } + return ItemKey::Builder::opaqueShape(); // for lack of a better idea } -using namespace render; +template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getBound(); + } + return Item::Bound(); +} + +template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload) { + if (payload) { + return payload->getShapeKey(); + } + return ShapeKey::Builder::invalid(); +} + +template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args) { + return payload->render(args); +} +} ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : _model(model), @@ -310,7 +324,7 @@ void ModelMeshPartPayload::notifyLocationChanged() { _model->_needsUpdateClusterMatrices = true; } -render::ItemKey ModelMeshPartPayload::getKey() const { +ItemKey ModelMeshPartPayload::getKey() const { ItemKey::Builder builder; builder.withTypeShape(); @@ -332,12 +346,79 @@ render::ItemKey ModelMeshPartPayload::getKey() const { return builder.build(); } -render::Item::Bound ModelMeshPartPayload::getBound() const { +Item::Bound ModelMeshPartPayload::getBound() const { // NOTE: we can't cache this bounds because we need to handle the case of a moving // entity or mesh part. return _model->getPartBounds(_meshIndex, _partIndex, _transform.getTranslation(), _transform.getRotation()); } +ShapeKey ModelMeshPartPayload::getShapeKey() const { + const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); + const std::vector>& networkMeshes = _model->_geometry->getMeshes(); + + // guard against partially loaded meshes + if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size()) { + return ShapeKey::Builder::invalid(); + } + + const FBXMesh& mesh = geometry.meshes.at(_meshIndex); + + // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown + // to false to rebuild out mesh groups. + if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { + _model->_meshGroupsKnown = false; // regenerate these lists next time around. + _model->_readyWhenAdded = false; // in case any of our users are using scenes + _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid + return ShapeKey::Builder::invalid(); + } + + + int vertexCount = mesh.vertices.size(); + if (vertexCount == 0) { + // sanity check + return ShapeKey::Builder::invalid(); // FIXME + } + + + model::MaterialKey drawMaterialKey; + if (_drawMaterial) { + drawMaterialKey = _drawMaterial->getKey(); + } + + bool isTranslucent = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); + bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); + bool hasSpecular = drawMaterialKey.isGlossMap(); + bool hasLightmap = drawMaterialKey.isLightmapMap(); + + bool isSkinned = _isSkinned; + bool wireframe = _model->isWireframe(); + + if (wireframe) { + isTranslucent = hasTangents = hasSpecular = hasLightmap = isSkinned = false; + } + + ShapeKey::Builder builder; + if (isTranslucent) { + builder.withTranslucent(); + } + if (hasTangents) { + builder.withTangents(); + } + if (hasSpecular) { + builder.withSpecular(); + } + if (hasLightmap) { + builder.withLightmap(); + } + if (isSkinned) { + builder.withSkinned(); + } + if (wireframe) { + builder.withWireframe(); + } + return builder.build(); +} + void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { if (!_isBlendShaped) { batch.setIndexBuffer(gpu::UINT32, (_drawMesh->getIndexBuffer()._buffer), 0); @@ -361,16 +442,16 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { } } -void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const { +void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations) const { // Still relying on the raw data from the model const Model::MeshState& state = _model->_meshStates.at(_meshIndex); Transform transform; if (state.clusterBuffer) { if (_model->_cauterizeBones) { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.cauterizedClusterBuffer); + batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.cauterizedClusterBuffer); } else { - batch.setUniformBuffer(ModelRender::SKINNING_GPU_SLOT, state.clusterBuffer); + batch.setUniformBuffer(ShapePipeline::Slot::SKINNING_GPU, state.clusterBuffer); } } else { if (_model->_cauterizeBones) { @@ -387,54 +468,18 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ModelRender::L void ModelMeshPartPayload::render(RenderArgs* args) const { PerformanceTimer perfTimer("ModelMeshPartPayload::render"); + if (!_model->_readyWhenAdded || !_model->_isVisible) { return; // bail asap } gpu::Batch& batch = *(args->_batch); - auto mode = args->_renderMode; - const FBXGeometry& geometry = _model->_geometry->getFBXGeometry(); - const std::vector>& networkMeshes = _model->_geometry->getMeshes(); - - // guard against partially loaded meshes - if (_meshIndex >= (int)networkMeshes.size() || _meshIndex >= (int)geometry.meshes.size() || _meshIndex >= (int)_model->_meshStates.size() ) { + ShapeKey key = getShapeKey(); + if (!key.isValid()) { return; } - - // Back to model to update the cluster matrices right now - _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); - - const FBXMesh& mesh = geometry.meshes.at(_meshIndex); - - // if our index is ever out of range for either meshes or networkMeshes, then skip it, and set our _meshGroupsKnown - // to false to rebuild out mesh groups. - if (_meshIndex < 0 || _meshIndex >= (int)networkMeshes.size() || _meshIndex > geometry.meshes.size()) { - _model->_meshGroupsKnown = false; // regenerate these lists next time around. - _model->_readyWhenAdded = false; // in case any of our users are using scenes - _model->invalidCalculatedMeshBoxes(); // if we have to reload, we need to assume our mesh boxes are all invalid - return; // FIXME! - } - - - int vertexCount = mesh.vertices.size(); - if (vertexCount == 0) { - // sanity check - return; // FIXME! - } - model::MaterialKey drawMaterialKey; - if (_drawMaterial) { - drawMaterialKey = _drawMaterial->getKey(); - } - bool translucentMesh = drawMaterialKey.isTransparent() || drawMaterialKey.isTransparentMap(); - - bool hasTangents = drawMaterialKey.isNormalMap() && !mesh.tangents.isEmpty(); - bool hasSpecular = drawMaterialKey.isGlossMap(); - bool hasLightmap = drawMaterialKey.isLightmapMap(); - bool isSkinned = _isSkinned; - bool wireframe = _model->isWireframe(); - // render the part bounding box #ifdef DEBUG_BOUNDING_PARTS { @@ -458,19 +503,11 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { } #endif //def DEBUG_BOUNDING_PARTS - if (wireframe) { - translucentMesh = hasTangents = hasSpecular = hasLightmap = isSkinned = false; - } - - ModelRender::Locations* locations = nullptr; - ModelRender::pickPrograms(batch, mode, translucentMesh, hasLightmap, hasTangents, hasSpecular, isSkinned, wireframe, - args, locations); - - if (!locations) { // the pipeline could not be found - return; - } + auto locations = args->_pipeline->locations; + assert(locations); // Bind the model transform and the skinCLusterMatrices if needed + _model->updateClusterMatrices(_transform.getTranslation(), _transform.getRotation()); bindTransform(batch, locations); //Bind the index buffer and vertex buffer and Blend shapes if needed @@ -481,7 +518,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // TODO: We should be able to do that just in the renderTransparentJob - if (translucentMesh && locations->lightBufferUnit >= 0) { + if (key.isTranslucent() && locations->lightBufferUnit >= 0) { PerformanceTimer perfTimer("DLE->setupTransparent()"); DependencyManager::get()->setupTransparent(args, locations->lightBufferUnit); diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index e03d1c8a68..d771a57203 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -15,11 +15,10 @@ #include #include +#include #include -#include "ModelRender.h" - class Model; class MeshPartPayload { @@ -40,13 +39,14 @@ public: // Render Item interface virtual render::ItemKey getKey() const; virtual render::Item::Bound getBound() const; + virtual render::ShapeKey getShapeKey() const; // shape interface virtual void render(RenderArgs* args) const; // ModelMeshPartPayload functions to perform render void drawCall(gpu::Batch& batch) const; virtual void bindMesh(gpu::Batch& batch) const; - virtual void bindMaterial(gpu::Batch& batch, const ModelRender::Locations* locations) const; - virtual void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const; + virtual void bindMaterial(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const; + virtual void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const; // Payload resource cached values model::MeshPointer _drawMesh; @@ -67,10 +67,10 @@ public: namespace render { template <> const ItemKey payloadGetKey(const MeshPartPayload::Pointer& payload); template <> const Item::Bound payloadGetBound(const MeshPartPayload::Pointer& payload); + template <> const ShapeKey shapeGetShapeKey(const MeshPartPayload::Pointer& payload); template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderArgs* args); } - class ModelMeshPartPayload : public MeshPartPayload { public: ModelMeshPartPayload(Model* model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); @@ -83,11 +83,12 @@ public: // Render Item interface render::ItemKey getKey() const override; render::Item::Bound getBound() const override; + render::ShapeKey getShapeKey() const override; // shape interface void render(RenderArgs* args) const override; - + // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) const override; - void bindTransform(gpu::Batch& batch, const ModelRender::Locations* locations) const override; + void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations) const override; void initCache(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ef9a837b27..e2d0d02af7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -22,8 +22,8 @@ #include #include "AbstractViewStateInterface.h" -#include "Model.h" #include "MeshPartPayload.h" +#include "Model.h" #include "RenderUtilsLogging.h" @@ -35,6 +35,8 @@ static int vec3VectorTypeId = qRegisterMetaType >(); float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" +model::MaterialPointer Model::_collisionHullMaterial; + Model::Model(RigPointer rig, QObject* parent) : QObject(parent), _translation(0.0f), @@ -1193,8 +1195,13 @@ void Model::segregateMeshGroups() { int totalParts = mesh.parts.size(); for (int partIndex = 0; partIndex < totalParts; partIndex++) { if (showingCollisionHull) { - _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, ModelRender::getCollisionHullMaterial(), transform, offset); - + if (!_collisionHullMaterial) { + _collisionHullMaterial = std::make_shared(); + _collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f)); + _collisionHullMaterial->setMetallic(0.02f); + _collisionHullMaterial->setGloss(1.0f); + } + _renderItemsSet << std::make_shared(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset); } else { _renderItemsSet << std::make_shared(this, i, partIndex, shapeID, transform, offset); } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3416a9b71e..157c6dbf70 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -347,6 +347,7 @@ protected: void recalculateMeshBoxes(bool pickAgainstTriangles = false); void segregateMeshGroups(); // used to calculate our list of translucent vs opaque meshes + static model::MaterialPointer _collisionHullMaterial; bool _meshGroupsKnown; bool _isWireframe; diff --git a/libraries/render-utils/src/ModelRender.cpp b/libraries/render-utils/src/ModelRender.cpp deleted file mode 100644 index 312d34e41b..0000000000 --- a/libraries/render-utils/src/ModelRender.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// -// ModelRender.cpp -// interface/src/renderer -// -// Created by Sam Gateau on 10/3/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "ModelRender.h" - -#include - -#include - -#include "DeferredLightingEffect.h" - -#include "model_vert.h" -#include "model_shadow_vert.h" -#include "model_normal_map_vert.h" -#include "model_lightmap_vert.h" -#include "model_lightmap_normal_map_vert.h" -#include "skin_model_vert.h" -#include "skin_model_shadow_vert.h" -#include "skin_model_normal_map_vert.h" - -#include "model_frag.h" -#include "model_shadow_frag.h" -#include "model_normal_map_frag.h" -#include "model_normal_specular_map_frag.h" -#include "model_specular_map_frag.h" -#include "model_lightmap_frag.h" -#include "model_lightmap_normal_map_frag.h" -#include "model_lightmap_normal_specular_map_frag.h" -#include "model_lightmap_specular_map_frag.h" -#include "model_translucent_frag.h" - -ModelRender::RenderPipelineLib ModelRender::_renderPipelineLib; - -const ModelRender::RenderPipelineLib& ModelRender::getRenderPipelineLib() { - if (_renderPipelineLib.empty()) { - // Vertex shaders - auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); - auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); - auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); - auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); - auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); - auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); - auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); - auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); - - // Pixel shaders - auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); - auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); - auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); - auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); - auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); - auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); - auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); - auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); - auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); - auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); - - // Fill the renderPipelineLib - - _renderPipelineLib.addRenderPipeline( - RenderKey(0), - modelVertex, modelPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS), - modelNormalMapVertex, modelNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_SPECULAR), - modelVertex, modelSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - modelNormalMapVertex, modelNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_TRANSLUCENT), - modelVertex, modelTranslucentPixel); - // FIXME Ignore lightmap for translucents meshpart - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_TRANSLUCENT | RenderKey::HAS_LIGHTMAP), - modelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), - modelNormalMapVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - modelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - modelNormalMapVertex, modelTranslucentPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP), - modelLightmapVertex, modelLightmapPixel); - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS), - modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_SPECULAR), - modelLightmapVertex, modelLightmapSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::HAS_LIGHTMAP | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED), - skinModelVertex, modelPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS), - skinModelNormalMapVertex, modelNormalMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR), - skinModelVertex, modelSpecularMapPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR), - skinModelNormalMapVertex, modelNormalSpecularMapPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_TRANSLUCENT), - skinModelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::IS_TRANSLUCENT), - skinModelNormalMapVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - skinModelVertex, modelTranslucentPixel); - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::HAS_TANGENTS | RenderKey::HAS_SPECULAR | RenderKey::IS_TRANSLUCENT), - skinModelNormalMapVertex, modelTranslucentPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), - modelShadowVertex, modelShadowPixel); - - - _renderPipelineLib.addRenderPipeline( - RenderKey(RenderKey::IS_SKINNED | RenderKey::IS_DEPTH_ONLY | RenderKey::IS_SHADOW), - skinModelShadowVertex, modelShadowPixel); - } - - return _renderPipelineLib; -} - - -void ModelRender::RenderPipelineLib::addRenderPipeline(ModelRender::RenderKey key, - gpu::ShaderPointer& vertexShader, - gpu::ShaderPointer& pixelShader) { - - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), ModelRender::SKINNING_GPU_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), ModelRender::MATERIAL_GPU_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), ModelRender::DIFFUSE_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), ModelRender::NORMAL_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), ModelRender::SPECULAR_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), ModelRender::LIGHTMAP_MAP_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), ModelRender::LIGHT_BUFFER_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), DeferredLightingEffect::NORMAL_FITTING_MAP_SLOT)); - - gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); - gpu::Shader::makeProgram(*program, slotBindings); - - - auto locations = std::make_shared(); - initLocations(program, *locations); - - - auto state = std::make_shared(); - - // Backface on shadow - if (key.isShadow()) { - state->setCullMode(gpu::State::CULL_FRONT); - state->setDepthBias(1.0f); - state->setDepthBiasSlopeScale(4.0f); - } else { - state->setCullMode(gpu::State::CULL_BACK); - } - - // Z test depends if transparent or not - state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(key.isTranslucent(), - gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, // For transparent only, this keep the highlight intensity - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - - // Good to go add the brand new pipeline - auto pipeline = gpu::Pipeline::create(program, state); - insert(value_type(key.getRaw(), RenderPipeline(pipeline, locations))); - - - if (!key.isWireFrame()) { - - RenderKey wireframeKey(key.getRaw() | RenderKey::IS_WIREFRAME); - auto wireframeState = std::make_shared(state->getValues()); - - wireframeState->setFillMode(gpu::State::FILL_LINE); - - // create a new RenderPipeline with the same shader side and the wireframe state - auto wireframePipeline = gpu::Pipeline::create(program, wireframeState); - insert(value_type(wireframeKey.getRaw(), RenderPipeline(wireframePipeline, locations))); - } -} - - -void ModelRender::RenderPipelineLib::initLocations(gpu::ShaderPointer& program, ModelRender::Locations& locations) { - locations.texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); - locations.emissiveParams = program->getUniforms().findLocation("emissiveParams"); - locations.normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); - locations.diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); - locations.normalTextureUnit = program->getTextures().findLocation("normalMap"); - locations.specularTextureUnit = program->getTextures().findLocation("specularMap"); - locations.emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); - locations.skinClusterBufferUnit = program->getBuffers().findLocation("skinClusterBuffer"); - locations.materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); - locations.lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); - -} - - -void ModelRender::pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, - Locations*& locations) { - - PerformanceTimer perfTimer("Model::pickPrograms"); - getRenderPipelineLib(); - - RenderKey key(mode, translucent, hasLightmap, hasTangents, hasSpecular, isSkinned, isWireframe); - auto pipeline = _renderPipelineLib.find(key.getRaw()); - if (pipeline == _renderPipelineLib.end()) { - qDebug() << "No good, couldn't find a pipeline from the key ?" << key.getRaw(); - locations = 0; - return; - } - - gpu::ShaderPointer program = (*pipeline).second._pipeline->getProgram(); - locations = (*pipeline).second._locations.get(); - - - // Setup the One pipeline - batch.setPipeline((*pipeline).second._pipeline); - - if ((locations->normalFittingMapUnit > -1)) { - batch.setResourceTexture(locations->normalFittingMapUnit, - DependencyManager::get()->getNormalFittingTexture()); - } -} - -model::MaterialPointer ModelRender::_collisionHullMaterial; - -model::MaterialPointer ModelRender::getCollisionHullMaterial() { - if (!_collisionHullMaterial) { - _collisionHullMaterial = std::make_shared(); - _collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f)); - _collisionHullMaterial->setMetallic(0.02f); - _collisionHullMaterial->setGloss(1.0f); - } - return _collisionHullMaterial; -} - diff --git a/libraries/render-utils/src/ModelRender.h b/libraries/render-utils/src/ModelRender.h deleted file mode 100644 index 8331440fb0..0000000000 --- a/libraries/render-utils/src/ModelRender.h +++ /dev/null @@ -1,157 +0,0 @@ -// -// ModelRender.h -// interface/src/renderer -// -// Created by Sam Gateau on 10/3/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_ModelRender_h -#define hifi_ModelRender_h - -#include - -#include - -class ModelRender { -public: - - static const int SKINNING_GPU_SLOT = 2; - static const int MATERIAL_GPU_SLOT = 3; - static const int DIFFUSE_MAP_SLOT = 0; - static const int NORMAL_MAP_SLOT = 1; - static const int SPECULAR_MAP_SLOT = 2; - static const int LIGHTMAP_MAP_SLOT = 3; - static const int LIGHT_BUFFER_SLOT = 4; - - class Locations { - public: - int texcoordMatrices; - int diffuseTextureUnit; - int normalTextureUnit; - int specularTextureUnit; - int emissiveTextureUnit; - int emissiveParams; - int normalFittingMapUnit; - int skinClusterBufferUnit; - int materialBufferUnit; - int lightBufferUnit; - }; - - static void pickPrograms(gpu::Batch& batch, RenderArgs::RenderMode mode, bool translucent, - bool hasLightmap, bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe, RenderArgs* args, - Locations*& locations); - - class RenderKey { - public: - enum FlagBit { - IS_TRANSLUCENT_FLAG = 0, - HAS_LIGHTMAP_FLAG, - HAS_TANGENTS_FLAG, - HAS_SPECULAR_FLAG, - HAS_EMISSIVE_FLAG, - IS_SKINNED_FLAG, - IS_STEREO_FLAG, - IS_DEPTH_ONLY_FLAG, - IS_SHADOW_FLAG, - IS_WIREFRAME_FLAG, - - NUM_FLAGS, - }; - - enum Flag { - IS_TRANSLUCENT = (1 << IS_TRANSLUCENT_FLAG), - HAS_LIGHTMAP = (1 << HAS_LIGHTMAP_FLAG), - HAS_TANGENTS = (1 << HAS_TANGENTS_FLAG), - HAS_SPECULAR = (1 << HAS_SPECULAR_FLAG), - HAS_EMISSIVE = (1 << HAS_EMISSIVE_FLAG), - IS_SKINNED = (1 << IS_SKINNED_FLAG), - IS_STEREO = (1 << IS_STEREO_FLAG), - IS_DEPTH_ONLY = (1 << IS_DEPTH_ONLY_FLAG), - IS_SHADOW = (1 << IS_SHADOW_FLAG), - IS_WIREFRAME = (1 << IS_WIREFRAME_FLAG), - }; - typedef unsigned short Flags; - - - - bool isFlag(short flagNum) const { return bool((_flags & flagNum) != 0); } - - bool isTranslucent() const { return isFlag(IS_TRANSLUCENT); } - bool hasLightmap() const { return isFlag(HAS_LIGHTMAP); } - bool hasTangents() const { return isFlag(HAS_TANGENTS); } - bool hasSpecular() const { return isFlag(HAS_SPECULAR); } - bool hasEmissive() const { return isFlag(HAS_EMISSIVE); } - bool isSkinned() const { return isFlag(IS_SKINNED); } - bool isStereo() const { return isFlag(IS_STEREO); } - bool isDepthOnly() const { return isFlag(IS_DEPTH_ONLY); } - bool isShadow() const { return isFlag(IS_SHADOW); } // = depth only but with back facing - bool isWireFrame() const { return isFlag(IS_WIREFRAME); } - - Flags _flags = 0; - short _spare = 0; - - int getRaw() { return *reinterpret_cast(this); } - - - RenderKey( - bool translucent, bool hasLightmap, - bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey((translucent ? IS_TRANSLUCENT : 0) - | (hasLightmap ? HAS_LIGHTMAP : 0) - | (hasTangents ? HAS_TANGENTS : 0) - | (hasSpecular ? HAS_SPECULAR : 0) - | (isSkinned ? IS_SKINNED : 0) - | (isWireframe ? IS_WIREFRAME : 0) - ) {} - - RenderKey(RenderArgs::RenderMode mode, - bool translucent, bool hasLightmap, - bool hasTangents, bool hasSpecular, bool isSkinned, bool isWireframe) : - RenderKey(((translucent && (mode != RenderArgs::SHADOW_RENDER_MODE)) ? IS_TRANSLUCENT : 0) - | (hasLightmap && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_LIGHTMAP : 0) // Lightmap, tangents and specular don't matter for depthOnly - | (hasTangents && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_TANGENTS : 0) - | (hasSpecular && (mode != RenderArgs::SHADOW_RENDER_MODE) ? HAS_SPECULAR : 0) - | (isSkinned ? IS_SKINNED : 0) - | (isWireframe ? IS_WIREFRAME : 0) - | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_DEPTH_ONLY : 0) - | ((mode == RenderArgs::SHADOW_RENDER_MODE) ? IS_SHADOW : 0) - ) {} - - RenderKey(int bitmask) : _flags(bitmask) {} - }; - - - class RenderPipeline { - public: - gpu::PipelinePointer _pipeline; - std::shared_ptr _locations; - RenderPipeline(gpu::PipelinePointer pipeline, std::shared_ptr locations) : - _pipeline(pipeline), _locations(locations) {} - }; - - typedef std::unordered_map BaseRenderPipelineMap; - class RenderPipelineLib : public BaseRenderPipelineMap { - public: - typedef RenderKey Key; - - - void addRenderPipeline(Key key, gpu::ShaderPointer& vertexShader, gpu::ShaderPointer& pixelShader); - - void initLocations(gpu::ShaderPointer& program, Locations& locations); - }; - static RenderPipelineLib _renderPipelineLib; - - static const RenderPipelineLib& getRenderPipelineLib(); - - // Collision hull Material - static model::MaterialPointer _collisionHullMaterial; - - static model::MaterialPointer getCollisionHullMaterial(); - -}; - -#endif // hifi_ModelRender_h \ No newline at end of file diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 98cfc35bd8..9993b64d78 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 @@ -29,6 +28,28 @@ #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" +#include "RenderDeferredTask.h" + +#include "model_vert.h" +#include "model_shadow_vert.h" +#include "model_normal_map_vert.h" +#include "model_lightmap_vert.h" +#include "model_lightmap_normal_map_vert.h" +#include "skin_model_vert.h" +#include "skin_model_shadow_vert.h" +#include "skin_model_normal_map_vert.h" + +#include "model_frag.h" +#include "model_shadow_frag.h" +#include "model_normal_map_frag.h" +#include "model_normal_specular_map_frag.h" +#include "model_specular_map_frag.h" +#include "model_lightmap_frag.h" +#include "model_lightmap_normal_map_frag.h" +#include "model_lightmap_normal_specular_map_frag.h" +#include "model_lightmap_specular_map_frag.h" +#include "model_translucent_frag.h" + #include "overlay3D_vert.h" #include "overlay3D_frag.h" @@ -36,6 +57,7 @@ using namespace render; +void initDeferredPipelines(render::ShapePlumber& plumber); void PrepareDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { DependencyManager::get()->prepare(renderContext->getArgs()); @@ -51,11 +73,15 @@ void ToneMappingDeferred::run(const SceneContextPointer& sceneContext, const Ren } RenderDeferredTask::RenderDeferredTask() : Task() { + // Prepare the ShapePipelines + ShapePlumberPointer shapePlumber = std::make_shared(); + initDeferredPipelines(*shapePlumber); + // CPU only, create the list of renderedOpaques items addJob("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) { context->getItemsConfig().opaque.numFeed = count; })); - addJob("CullOpaque", _jobs.back().getOutput()); + addJob>("CullOpaque", _jobs.back().getOutput()); addJob("DepthSortOpaque", _jobs.back().getOutput()); auto& renderedOpaques = _jobs.back().getOutput(); @@ -66,7 +92,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { context->getItemsConfig().transparent.numFeed = count; } )); - addJob("CullTransparent", _jobs.back().getOutput()); + addJob>("CullTransparent", _jobs.back().getOutput()); addJob("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)); auto& renderedTransparents = _jobs.back().getOutput(); @@ -74,7 +100,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { addJob("PrepareDeferred"); // Render opaque objects in DeferredBuffer - addJob("DrawOpaqueDeferred", renderedOpaques); + addJob("DrawOpaqueDeferred", renderedOpaques, shapePlumber); // Once opaque is all rendered create stencil background addJob("DrawOpaqueStencil"); @@ -99,7 +125,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _antialiasingJobIndex = (int)_jobs.size() - 1; // Render transparent objects forward in LigthingBuffer - addJob("TransparentDeferred", renderedTransparents); + addJob("DrawTransparentDeferred", renderedTransparents, shapePlumber); // Lighting Buffer ready for tone mapping addJob("ToneMapping"); @@ -120,7 +146,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _drawStatusJobIndex = (int)_jobs.size() - 1; } - addJob("DrawOverlay3D"); + addJob("DrawOverlay3D", shapePlumber); addJob("HitEffect"); _jobs.back().setEnabled(false); @@ -178,7 +204,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend assert(renderContext->getArgs()->_viewFrustum); RenderArgs* args = renderContext->getArgs(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; @@ -194,7 +220,7 @@ void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const Rend batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderItems(sceneContext, renderContext, inItems, opaque.maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, opaque.maxDrawn); args->_batch = nullptr; }); } @@ -204,7 +230,7 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const assert(renderContext->getArgs()->_viewFrustum); RenderArgs* args = renderContext->getArgs(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); args->_batch = &batch; @@ -220,11 +246,12 @@ void DrawTransparentDeferred::run(const SceneContextPointer& sceneContext, const batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderItems(sceneContext, renderContext, inItems, transparent.maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, transparent.maxDrawn); args->_batch = nullptr; }); } +// TODO: Move this to the shapePlumber gpu::PipelinePointer DrawOverlay3D::_opaquePipeline; const gpu::PipelinePointer& DrawOverlay3D::getOpaquePipeline() { if (!_opaquePipeline) { @@ -277,7 +304,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon } // Render the items - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; args->_whiteTexture = DependencyManager::get()->getWhiteTexture(); @@ -293,7 +320,7 @@ void DrawOverlay3D::run(const SceneContextPointer& sceneContext, const RenderCon batch.setPipeline(getOpaquePipeline()); batch.setResourceTexture(0, args->_whiteTexture); - renderItems(sceneContext, renderContext, inItems, renderContext->getItemsConfig().overlay3D.maxDrawn); + renderShapes(sceneContext, renderContext, _shapePlumber, inItems, renderContext->getItemsConfig().overlay3D.maxDrawn); }); args->_batch = nullptr; args->_whiteTexture.reset(); @@ -382,8 +409,7 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const batch.setProjectionTransform(projMat); batch.setViewTransform(viewMat); - renderItems(sceneContext, renderContext, inItems); - + renderLights(sceneContext, renderContext, inItems); }); args->_batch = nullptr; } @@ -487,3 +513,165 @@ int RenderDeferredTask::getToneMappingToneCurve() const { } } +void pipelineBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { + if (pipeline.locations->normalFittingMapUnit > -1) { + batch.setResourceTexture(pipeline.locations->normalFittingMapUnit, + DependencyManager::get()->getNormalFittingTexture()); + } +} + +void initDeferredPipelines(render::ShapePlumber& plumber) { + using Key = render::ShapeKey; + using ShaderPointer = gpu::ShaderPointer; + + auto addPipeline = [&plumber](const Key& key, const ShaderPointer& vertexShader, const ShaderPointer& pixelShader) { + auto state = std::make_shared(); + + // Cull backface + state->setCullMode(gpu::State::CULL_BACK); + + // Z test depends on transparency + state->setDepthTest(true, !key.isTranslucent(), gpu::LESS_EQUAL); + + // Blend if transparent + state->setBlendFunction(key.isTranslucent(), + // For transparency, keep the highlight intensity + gpu::State::ONE, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + + ShaderPointer program = gpu::Shader::createProgram(vertexShader, pixelShader); + plumber.addPipeline(key, program, state, &pipelineBatchSetter); + + // Add a wireframe version + if (!key.isWireFrame()) { + auto wireFrameKey = Key::Builder(key).withWireframe(); + auto wireFrameState = std::make_shared(state->getValues()); + + wireFrameState->setFillMode(gpu::State::FILL_LINE); + + plumber.addPipeline(wireFrameKey, program, wireFrameState, &pipelineBatchSetter); + } + }; + + // Vertex shaders + auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); + auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); + auto modelLightmapVertex = gpu::Shader::createVertex(std::string(model_lightmap_vert)); + auto modelLightmapNormalMapVertex = gpu::Shader::createVertex(std::string(model_lightmap_normal_map_vert)); + auto modelShadowVertex = gpu::Shader::createVertex(std::string(model_shadow_vert)); + auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); + auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); + auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + + // Pixel shaders + auto modelPixel = gpu::Shader::createPixel(std::string(model_frag)); + auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(model_normal_map_frag)); + auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(model_specular_map_frag)); + auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_normal_specular_map_frag)); + auto modelTranslucentPixel = gpu::Shader::createPixel(std::string(model_translucent_frag)); + auto modelShadowPixel = gpu::Shader::createPixel(std::string(model_shadow_frag)); + auto modelLightmapPixel = gpu::Shader::createPixel(std::string(model_lightmap_frag)); + auto modelLightmapNormalMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_map_frag)); + auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); + auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); + + // Fill the pipelineLib + addPipeline( + Key::Builder(), + modelVertex, modelPixel); + + addPipeline( + Key::Builder().withTangents(), + modelNormalMapVertex, modelNormalMapPixel); + + addPipeline( + Key::Builder().withSpecular(), + modelVertex, modelSpecularMapPixel); + + addPipeline( + Key::Builder().withTangents().withSpecular(), + modelNormalMapVertex, modelNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withTranslucent(), + modelVertex, modelTranslucentPixel); + // FIXME Ignore lightmap for translucents meshpart + addPipeline( + Key::Builder().withTranslucent().withLightmap(), + modelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withTangents().withTranslucent(), + modelNormalMapVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSpecular().withTranslucent(), + modelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withTangents().withSpecular().withTranslucent(), + modelNormalMapVertex, modelTranslucentPixel); + + + addPipeline( + Key::Builder().withLightmap(), + modelLightmapVertex, modelLightmapPixel); + + addPipeline( + Key::Builder().withLightmap().withTangents(), + modelLightmapNormalMapVertex, modelLightmapNormalMapPixel); + + addPipeline( + Key::Builder().withLightmap().withSpecular(), + modelLightmapVertex, modelLightmapSpecularMapPixel); + + addPipeline( + Key::Builder().withLightmap().withTangents().withSpecular(), + modelLightmapNormalMapVertex, modelLightmapNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withSkinned(), + skinModelVertex, modelPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents(), + skinModelNormalMapVertex, modelNormalMapPixel); + + addPipeline( + Key::Builder().withSkinned().withSpecular(), + skinModelVertex, modelSpecularMapPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withSpecular(), + skinModelNormalMapVertex, modelNormalSpecularMapPixel); + + + addPipeline( + Key::Builder().withSkinned().withTranslucent(), + skinModelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withTranslucent(), + skinModelNormalMapVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withSpecular().withTranslucent(), + skinModelVertex, modelTranslucentPixel); + + addPipeline( + Key::Builder().withSkinned().withTangents().withSpecular().withTranslucent(), + skinModelNormalMapVertex, modelTranslucentPixel); + + + addPipeline( + Key::Builder().withDepthOnly(), + modelShadowVertex, modelShadowPixel); + + + addPipeline( + Key::Builder().withSkinned().withDepthOnly(), + skinModelShadowVertex, modelShadowPixel); +} + diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index b4e926ddb9..85e3e7f211 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -51,26 +51,36 @@ public: class DrawOpaqueDeferred { public: + DrawOpaqueDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); using JobModel = render::Task::Job::ModelI; + +protected: + render::ShapePlumberPointer _shapePlumber; }; class DrawTransparentDeferred { public: + DrawTransparentDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext, const render::ItemIDsBounds& inItems); using JobModel = render::Task::Job::ModelI; + +protected: + render::ShapePlumberPointer _shapePlumber; }; class DrawStencilDeferred { - static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable public: static const gpu::PipelinePointer& getOpaquePipeline(); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); using JobModel = render::Task::Job::Model; + +protected: + static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable }; class DrawBackgroundDeferred { @@ -81,13 +91,17 @@ public: }; class DrawOverlay3D { - static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable public: + DrawOverlay3D(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} static const gpu::PipelinePointer& getOpaquePipeline(); - + void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); using JobModel = render::Task::Job::Model; + +protected: + static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable + render::ShapePlumberPointer _shapePlumber; }; class Blit { diff --git a/libraries/render-utils/src/directional_ambient_light.slf b/libraries/render-utils/src/directional_ambient_light.slf index 0d687f7204..a3bbcbedaa 100755 --- a/libraries/render-utils/src/directional_ambient_light.slf +++ b/libraries/render-utils/src/directional_ambient_light.slf @@ -36,7 +36,7 @@ void main(void) { frag.specularVal.xyz); _fragColor = vec4(color, 1.0); } else { - vec3 color = evalAmbienSphereGlobalColor( + vec3 color = evalAmbientSphereGlobalColor( deferredTransform.viewInverse, frag.obscurance, frag.position.xyz, diff --git a/libraries/render-utils/src/directional_light.slf b/libraries/render-utils/src/directional_light.slf index b8731054da..16f4caeb67 100644 --- a/libraries/render-utils/src/directional_light.slf +++ b/libraries/render-utils/src/directional_light.slf @@ -37,7 +37,7 @@ void main(void) { frag.specularVal.xyz); _fragColor = vec4(color, 1.0); } else { - vec3 color = evalAmbienGlobalColor( + vec3 color = evalAmbientGlobalColor( deferredTransform.viewInverse, frag.obscurance, frag.position.xyz, diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index d9fbc79d1e..2b977081a1 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -65,13 +65,17 @@ void render::cullItems(const SceneContextPointer& sceneContext, const RenderCont void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, ItemIDsBounds& outItems) { auto& scene = sceneContext->_scene; - auto& items = scene->getMasterBucket().at(_filter); outItems.clear(); - outItems.reserve(items.size()); - for (auto id : items) { - auto& item = scene->getItem(id); - outItems.emplace_back(ItemIDAndBounds(id, item.getBound())); + + const auto& bucket = scene->getMasterBucket(); + const auto& items = bucket.find(_filter); + if (items != bucket.end()) { + outItems.reserve(items->second.size()); + for (auto& id : items->second) { + auto& item = scene->getItem(id); + outItems.emplace_back(ItemIDAndBounds(id, item.getBound())); + } } if (_probeNumItems) { @@ -79,34 +83,6 @@ void FetchItems::run(const SceneContextPointer& sceneContext, const RenderContex } } -void CullItems::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - - outItems.clear(); - outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->getArgs(); - args->_details.pointTo(RenderDetails::OTHER_ITEM); - cullItems(sceneContext, renderContext, inItems, outItems); -} - -void CullItemsOpaque::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - - outItems.clear(); - outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->getArgs(); - args->_details.pointTo(RenderDetails::OPAQUE_ITEM); - cullItems(sceneContext, renderContext, inItems, outItems); -} - -void CullItemsTransparent::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { - - outItems.clear(); - outItems.reserve(inItems.size()); - RenderArgs* args = renderContext->getArgs(); - args->_details.pointTo(RenderDetails::TRANSLUCENT_ITEM); - cullItems(sceneContext, renderContext, inItems, outItems); -} - - struct ItemBound { float _centerDepth = 0.0f; float _nearDepth = 0.0f; @@ -177,29 +153,40 @@ void DepthSortItems::run(const SceneContextPointer& sceneContext, const RenderCo depthSortItems(sceneContext, renderContext, _frontToBack, inItems, outItems); } -void render::renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems) { +void render::renderLights(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { auto& scene = sceneContext->_scene; RenderArgs* args = renderContext->getArgs(); - // render - if ((maxDrawnItems < 0) || (maxDrawnItems > (int) inItems.size())) { - for (auto itemDetails : inItems) { - auto item = scene->getItem(itemDetails.id); + + for (const auto& itemDetails : inItems) { + auto& item = scene->getItem(itemDetails.id); + item.render(args); + } +} + +void renderShape(RenderArgs* args, const ShapePlumberPointer& shapeContext, const Item& item) { + assert(item.getKey().isShape()); + const auto& key = item.getShapeKey(); + if (key.isValid() && !key.hasOwnPipeline()) { + args->_pipeline = shapeContext->pickPipeline(args, key); + if (args->_pipeline) { item.render(args); } + } else if (key.hasOwnPipeline()) { + item.render(args); } else { - int numItems = 0; - for (auto itemDetails : inItems) { - auto item = scene->getItem(itemDetails.id); - if (numItems + 1 >= maxDrawnItems) { - item.render(args); - return; - } - item.render(args); - numItems++; - if (numItems >= maxDrawnItems) { - return; - } - } + qDebug() << "Item could not be rendered: invalid key ?" << key; + } +} + +void render::renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, + const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems) { + auto& scene = sceneContext->_scene; + RenderArgs* args = renderContext->getArgs(); + + auto numItemsToDraw = glm::max((int)inItems.size(), maxDrawnItems); + for (auto i = 0; i < numItemsToDraw; ++i) { + auto& item = scene->getItem(inItems[i].id); + renderShape(args, shapeContext, item); } } @@ -211,7 +198,6 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext auto& scene = sceneContext->_scene; auto& items = scene->getMasterBucket().at(ItemFilter::Builder::light()); - ItemIDsBounds inItems; inItems.reserve(items.size()); for (auto id : items) { @@ -219,15 +205,16 @@ void DrawLight::run(const SceneContextPointer& sceneContext, const RenderContext inItems.emplace_back(ItemIDAndBounds(id, item.getBound())); } + RenderArgs* args = renderContext->getArgs(); + ItemIDsBounds culledItems; culledItems.reserve(inItems.size()); - RenderArgs* args = renderContext->getArgs(); args->_details.pointTo(RenderDetails::OTHER_ITEM); cullItems(sceneContext, renderContext, inItems, culledItems); gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { args->_batch = &batch; - renderItems(sceneContext, renderContext, culledItems); + renderLights(sceneContext, renderContext, culledItems); + args->_batch = nullptr; }); - args->_batch = nullptr; } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 3efb06871a..b31ec9763e 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -13,6 +13,7 @@ #define hifi_render_DrawTask_h #include "Engine.h" +#include "ShapePipeline.h" #include "gpu/Batch.h" @@ -20,7 +21,8 @@ namespace render { 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 renderItems(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); +void renderLights(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); +void renderShapes(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ShapePlumberPointer& shapeContext, const ItemIDsBounds& inItems, int maxDrawnItems = -1); class FetchItems { @@ -37,22 +39,17 @@ public: using JobModel = Task::Job::ModelO; }; +template class CullItems { public: - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelIO; -}; + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems) { + outItems.clear(); + outItems.reserve(inItems.size()); + renderContext->getArgs()->_details.pointTo(T); + render::cullItems(sceneContext, renderContext, inItems, outItems); + } -class CullItemsOpaque { -public: - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelIO; -}; - -class CullItemsTransparent { -public: - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems); - using JobModel = Task::Job::ModelIO; + using JobModel = Task::Job::ModelIO, ItemIDsBounds, ItemIDsBounds>; }; class DepthSortItems { diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 1976801c96..567e054a8a 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -25,6 +25,7 @@ #include #include "model/Material.h" +#include "ShapePipeline.h" namespace render { @@ -82,6 +83,9 @@ public: }; ItemKey(const Builder& builder) : ItemKey(builder._flags) {} + bool isShape() const { return _flags[TYPE_SHAPE]; } + bool isLight() const { return _flags[TYPE_LIGHT]; } + bool isOpaque() const { return !_flags[TRANSLUCENT]; } bool isTransparent() const { return _flags[TRANSLUCENT]; } @@ -264,7 +268,7 @@ public: virtual void render(RenderArgs* args) = 0; - virtual const model::MaterialKey getMaterialKey() const = 0; + virtual const ShapeKey getShapeKey() const = 0; ~PayloadInterface() {} @@ -301,10 +305,10 @@ public: int getLayer() const { return _payload->getLayer(); } // Render call for the item - void render(RenderArgs* args) { _payload->render(args); } + void render(RenderArgs* args) const { _payload->render(args); } // Shape Type Interface - const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } + const ShapeKey getShapeKey() const { return _payload->getShapeKey(); } // Access the status const StatusPointer& getStatus() const { return _payload->getStatus(); } @@ -346,7 +350,10 @@ template int payloadGetLayer(const std::shared_ptr& payloadData) { template void payloadRender(const std::shared_ptr& payloadData, RenderArgs* args) { } // Shape type interface -template const model::MaterialKey shapeGetMaterialKey(const std::shared_ptr& payloadData) { return model::MaterialKey(); } +// This allows shapes to characterize their pipeline via a ShapeKey, to be picked with a subclass of Shape. +// When creating a new shape payload you need to create a specialized version, or the ShapeKey will be ownPipeline, +// implying that the shape will setup its own pipeline without the use of the ShapeKey. +template const ShapeKey shapeGetShapeKey(const std::shared_ptr& payloadData) { return ShapeKey::Builder::ownPipeline(); } template class Payload : public Item::PayloadInterface { public: @@ -364,7 +371,7 @@ public: virtual void render(RenderArgs* args) { payloadRender(_data, args); } // Shape Type interface - virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey(_data); } + virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey(_data); } protected: DataPointer _data; diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp new file mode 100644 index 0000000000..99424cbf4b --- /dev/null +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -0,0 +1,100 @@ +// +// ShapePipeline.cpp +// render/src/render +// +// Created by Zach Pomerantz on 12/31/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "DependencyManager.h" + +#include "ShapePipeline.h" + +#include + +using namespace render; + +ShapeKey::Filter::Builder::Builder() { + _mask.set(OWN_PIPELINE); + _mask.set(INVALID); +} + +void ShapePlumber::addPipelineHelper(const Filter& filter, ShapeKey key, int bit, const PipelinePointer& pipeline) { + // Iterate over all keys + if (bit < (int)ShapeKey::FlagBit::NUM_FLAGS) { + addPipelineHelper(filter, key, bit + 1, pipeline); + if (!filter._mask[bit]) { + // Toggle bits set as insignificant in filter._mask + key._flags.flip(bit); + addPipelineHelper(filter, key, bit + 1, pipeline); + } + } else { + // Add the brand new pipeline and cache its location in the lib + _pipelineMap.insert(PipelineMap::value_type(key, pipeline)); + } +} + +void ShapePlumber::addPipeline(const Key& key, const gpu::ShaderPointer& program, const gpu::StatePointer& state, + BatchSetter batchSetter) { + addPipeline(Filter{key}, program, state, batchSetter); +} + +void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state, + BatchSetter batchSetter) { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("skinClusterBuffer"), Slot::SKINNING_GPU)); + slotBindings.insert(gpu::Shader::Binding(std::string("materialBuffer"), Slot::MATERIAL_GPU)); + slotBindings.insert(gpu::Shader::Binding(std::string("diffuseMap"), Slot::DIFFUSE_MAP)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalMap"), Slot::NORMAL_MAP)); + slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), Slot::SPECULAR_MAP)); + slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Slot::LIGHTMAP_MAP)); + slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::LIGHT_BUFFER)); + slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), Slot::NORMAL_FITTING_MAP)); + + gpu::Shader::makeProgram(*program, slotBindings); + + auto locations = std::make_shared(); + locations->texcoordMatrices = program->getUniforms().findLocation("texcoordMatrices"); + locations->emissiveParams = program->getUniforms().findLocation("emissiveParams"); + locations->normalFittingMapUnit = program->getTextures().findLocation("normalFittingMap"); + locations->diffuseTextureUnit = program->getTextures().findLocation("diffuseMap"); + locations->normalTextureUnit = program->getTextures().findLocation("normalMap"); + locations->specularTextureUnit = program->getTextures().findLocation("specularMap"); + locations->emissiveTextureUnit = program->getTextures().findLocation("emissiveMap"); + locations->skinClusterBufferUnit = program->getBuffers().findLocation("skinClusterBuffer"); + locations->materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); + locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); + + ShapeKey key{filter._flags}; + auto gpuPipeline = gpu::Pipeline::create(program, state); + auto shapePipeline = std::make_shared(gpuPipeline, locations, batchSetter); + addPipelineHelper(filter, key, 0, shapePipeline); +} + +const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Key& key) const { + assert(!_pipelineMap.empty()); + assert(args); + assert(args->_batch); + + PerformanceTimer perfTimer("ShapePlumber::pickPipeline"); + + const auto& pipelineIterator = _pipelineMap.find(key); + if (pipelineIterator == _pipelineMap.end()) { + qDebug() << "Couldn't find a pipeline from ShapeKey ?" << key; + return PipelinePointer(nullptr); + } + + PipelinePointer shapePipeline(pipelineIterator->second); + auto& batch = args->_batch; + + // Run the pipeline's BatchSetter on the passed in batch + shapePipeline->batchSetter(*shapePipeline, *batch); + + // Setup the one pipeline (to rule them all) + batch->setPipeline(shapePipeline->pipeline); + + return shapePipeline; +} diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h new file mode 100644 index 0000000000..3d189f15fa --- /dev/null +++ b/libraries/render/src/render/ShapePipeline.h @@ -0,0 +1,249 @@ +// +// ShapePipeline.h +// render/src/render +// +// Created by Zach Pomerantz on 12/31/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_render_ShapePipeline_h +#define hifi_render_ShapePipeline_h + +#include +#include + +namespace render { + +class ShapeKey { +public: + enum FlagBit { + TRANSLUCENT = 0, + LIGHTMAP, + TANGENTS, + SPECULAR, + EMISSIVE, + SKINNED, + STEREO, + DEPTH_ONLY, + WIREFRAME, + + OWN_PIPELINE, + INVALID, + + NUM_FLAGS, // Not a valid flag + }; + using Flags = std::bitset; + + Flags _flags; + + ShapeKey() : _flags{0} {} + ShapeKey(const Flags& flags) : _flags{flags} {} + + class Builder { + public: + Builder() {} + Builder(ShapeKey key) : _flags{key._flags} {} + + ShapeKey build() const { return ShapeKey{_flags}; } + + Builder& withTranslucent() { _flags.set(TRANSLUCENT); return (*this); } + Builder& withLightmap() { _flags.set(LIGHTMAP); return (*this); } + Builder& withTangents() { _flags.set(TANGENTS); return (*this); } + Builder& withSpecular() { _flags.set(SPECULAR); return (*this); } + Builder& withEmissive() { _flags.set(EMISSIVE); return (*this); } + Builder& withSkinned() { _flags.set(SKINNED); return (*this); } + Builder& withStereo() { _flags.set(STEREO); return (*this); } + Builder& withDepthOnly() { _flags.set(DEPTH_ONLY); return (*this); } + Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } + + Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); } + Builder& invalidate() { _flags.set(INVALID); return (*this); } + + static const ShapeKey ownPipeline() { return Builder().withOwnPipeline(); } + static const ShapeKey invalid() { return Builder().invalidate(); } + + protected: + friend class ShapeKey; + Flags _flags{0}; + }; + ShapeKey(const Builder& builder) : ShapeKey{builder._flags} {} + + class Filter { + public: + Filter(Flags flags, Flags mask) : _flags{flags}, _mask{mask} {} + Filter(const ShapeKey& key) : _flags{ key._flags } { _mask.set(); } + + // Build a standard filter (will always exclude OWN_PIPELINE, INVALID) + class Builder { + public: + Builder(); + + Filter build() const { return Filter(_flags, _mask); } + + Builder& withOpaque() { _flags.reset(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); } + Builder& withTranslucent() { _flags.set(TRANSLUCENT); _mask.set(TRANSLUCENT); return (*this); } + + Builder& withLightmap() { _flags.reset(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); } + Builder& withoutLightmap() { _flags.set(LIGHTMAP); _mask.set(LIGHTMAP); return (*this); } + + Builder& withTangents() { _flags.reset(TANGENTS); _mask.set(TANGENTS); return (*this); } + Builder& withoutTangents() { _flags.set(TANGENTS); _mask.set(TANGENTS); return (*this); } + + Builder& withSpecular() { _flags.reset(SPECULAR); _mask.set(SPECULAR); return (*this); } + Builder& withoutSpecular() { _flags.set(SPECULAR); _mask.set(SPECULAR); return (*this); } + + Builder& withEmissive() { _flags.reset(EMISSIVE); _mask.set(EMISSIVE); return (*this); } + Builder& withoutEmissive() { _flags.set(EMISSIVE); _mask.set(EMISSIVE); return (*this); } + + Builder& withSkinned() { _flags.reset(SKINNED); _mask.set(SKINNED); return (*this); } + Builder& withoutSkinned() { _flags.set(SKINNED); _mask.set(SKINNED); return (*this); } + + Builder& withStereo() { _flags.reset(STEREO); _mask.set(STEREO); return (*this); } + Builder& withoutStereo() { _flags.set(STEREO); _mask.set(STEREO); return (*this); } + + Builder& withDepthOnly() { _flags.reset(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } + Builder& withoutDepthOnly() { _flags.set(DEPTH_ONLY); _mask.set(DEPTH_ONLY); return (*this); } + + Builder& withWireframe() { _flags.reset(WIREFRAME); _mask.set(WIREFRAME); return (*this); } + Builder& withoutWireframe() { _flags.set(WIREFRAME); _mask.set(WIREFRAME); return (*this); } + + protected: + friend class Filter; + Flags _flags{0}; + Flags _mask{0}; + }; + Filter(const Filter::Builder& builder) : Filter(builder._flags, builder._mask) {} + protected: + friend class ShapePlumber; + Flags _flags{0}; + Flags _mask{0}; + }; + + bool hasLightmap() const { return _flags[LIGHTMAP]; } + bool hasTangents() const { return _flags[TANGENTS]; } + bool hasSpecular() const { return _flags[SPECULAR]; } + bool hasEmissive() const { return _flags[EMISSIVE]; } + bool isTranslucent() const { return _flags[TRANSLUCENT]; } + bool isSkinned() const { return _flags[SKINNED]; } + bool isStereo() const { return _flags[STEREO]; } + bool isDepthOnly() const { return _flags[DEPTH_ONLY]; } + bool isWireFrame() const { return _flags[WIREFRAME]; } + + bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } + bool isValid() const { return !_flags[INVALID]; } + + // Hasher for use in unordered_maps + class Hash { + public: + size_t operator() (const ShapeKey& key) const { + return std::hash()(key._flags); + } + }; + + // Comparator for use in unordered_maps + class KeyEqual { + public: + bool operator()(const ShapeKey& lhs, const ShapeKey& rhs) const { return lhs._flags == rhs._flags; } + }; +}; + +inline QDebug operator<<(QDebug debug, const ShapeKey& renderKey) { + if (renderKey.isValid()) { + if (renderKey.hasOwnPipeline()) { + debug << "[ShapeKey: OWN_PIPELINE]"; + } else { + debug << "[ShapeKey:" + << "hasLightmap:" << renderKey.hasLightmap() + << "hasTangents:" << renderKey.hasTangents() + << "hasSpecular:" << renderKey.hasSpecular() + << "hasEmissive:" << renderKey.hasEmissive() + << "isTranslucent:" << renderKey.isTranslucent() + << "isSkinned:" << renderKey.isSkinned() + << "isStereo:" << renderKey.isStereo() + << "isDepthOnly:" << renderKey.isDepthOnly() + << "isWireFrame:" << renderKey.isWireFrame() + << "]"; + } + } else { + debug << "[ShapeKey: INVALID]"; + } + return debug; +} + +// Rendering abstraction over gpu::Pipeline and map locations +// Meta-information (pipeline and locations) to render a shape +class ShapePipeline { +public: + class Slot { + public: + static const int SKINNING_GPU = 2; + static const int MATERIAL_GPU = 3; + static const int DIFFUSE_MAP = 0; + static const int NORMAL_MAP = 1; + static const int SPECULAR_MAP = 2; + static const int LIGHTMAP_MAP = 3; + static const int LIGHT_BUFFER = 4; + static const int NORMAL_FITTING_MAP = 10; + }; + + class Locations { + public: + int texcoordMatrices; + int diffuseTextureUnit; + int normalTextureUnit; + int specularTextureUnit; + int emissiveTextureUnit; + int emissiveParams; + int normalFittingMapUnit; + int skinClusterBufferUnit; + int materialBufferUnit; + int lightBufferUnit; + }; + using LocationsPointer = std::shared_ptr; + + using BatchSetter = std::function; + + ShapePipeline(gpu::PipelinePointer pipeline, LocationsPointer locations, BatchSetter batchSetter) : + pipeline(pipeline), locations(locations), batchSetter(batchSetter) {} + + gpu::PipelinePointer pipeline; + std::shared_ptr locations; + +protected: + friend class ShapePlumber; + + BatchSetter batchSetter; +}; +using ShapePipelinePointer = std::shared_ptr; + +class ShapePlumber { +public: + using Key = ShapeKey; + using Filter = Key::Filter; + using Pipeline = ShapePipeline; + using PipelinePointer = ShapePipelinePointer; + using PipelineMap = std::unordered_map; + using Slot = Pipeline::Slot; + using Locations = Pipeline::Locations; + using LocationsPointer = Pipeline::LocationsPointer; + using BatchSetter = Pipeline::BatchSetter; + + void addPipeline(const Key& key, const gpu::ShaderPointer& program, const gpu::StatePointer& state, + BatchSetter batchSetter = nullptr); + void addPipeline(const Filter& filter, const gpu::ShaderPointer& program, const gpu::StatePointer& state, + BatchSetter batchSetter = nullptr); + + const PipelinePointer pickPipeline(RenderArgs* args, const Key& key) const; + +protected: + void addPipelineHelper(const Filter& filter, Key key, int bit, const PipelinePointer& pipeline); + PipelineMap _pipelineMap; +}; +using ShapePlumberPointer = std::shared_ptr; + +} + +#endif // hifi_render_ShapePipeline_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/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 640ccdcb1f..789033b826 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -20,6 +20,7 @@ class AABox; class OctreeRenderer; class ViewFrustum; + namespace gpu { class Batch; class Context; @@ -27,6 +28,10 @@ class Texture; class Framebuffer; } +namespace render { +class ShapePipeline; +} + class RenderDetails { public: enum Type { @@ -103,6 +108,7 @@ public: std::shared_ptr _context = nullptr; std::shared_ptr _blitFramebuffer = nullptr; + std::shared_ptr _pipeline = nullptr; OctreeRenderer* _renderer = nullptr; ViewFrustum* _viewFrustum = nullptr; glm::ivec4 _viewport{ 0, 0, 1, 1 }; 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/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/tests/gpu-test/CMakeLists.txt b/tests/gpu-test/CMakeLists.txt index 3d83c310cf..6b985da4c9 100644 --- a/tests/gpu-test/CMakeLists.txt +++ b/tests/gpu-test/CMakeLists.txt @@ -3,5 +3,5 @@ AUTOSCRIBE_SHADER_LIB(gpu model render-utils) # This is not a testcase -- just set it up as a regular hifi project setup_hifi_project(Quick Gui OpenGL Script Widgets) set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/") -link_hifi_libraries(networking gl gpu procedural shared fbx model model-networking animation script-engine render-utils ) +link_hifi_libraries(networking gl gpu procedural shared fbx model model-networking animation script-engine render render-utils ) package_libraries_for_deployment() \ No newline at end of file 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); }