Merge branch 'master' of https://github.com/highfidelity/hifi into hdr

This commit is contained in:
samcake 2016-01-12 10:02:15 -08:00
commit 31bea5034c
46 changed files with 1718 additions and 1247 deletions

View file

@ -31,6 +31,7 @@
#include <ShutdownEventListener.h>
#include <SoundCache.h>
#include <ResourceScriptingInterface.h>
#include <ScriptEngines.h>
#include "AssignmentFactory.h"
#include "AssignmentActionFactory.h"
@ -53,6 +54,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
auto scriptableAvatar = DependencyManager::set<ScriptableAvatar>();
auto addressManager = DependencyManager::set<AddressManager>();
auto scriptEngines = DependencyManager::set<ScriptEngines>();
// create a NodeList as an unassigned client, must be after addressManager
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned, listenPort);
@ -174,6 +176,8 @@ AssignmentClient::~AssignmentClient() {
void AssignmentClient::aboutToQuit() {
stopAssignmentClient();
DependencyManager::destroy<ScriptEngines>();
// clear the log handler so that Qt doesn't call the destructor on LogHandler
qInstallMessageHandler(0);
}

View file

@ -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);

View file

@ -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);

View file

@ -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))
}

View file

@ -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

View file

@ -103,6 +103,7 @@
#include <RecordingScriptingInterface.h>
#include <ScriptCache.h>
#include <SoundCache.h>
#include <ScriptEngines.h>
#include <TextureCache.h>
#include <Tooltip.h>
#include <udt/PacketHeaders.h>
@ -319,6 +320,7 @@ bool setupEssentials(int& argc, char** argv) {
Setting::init();
// Set dependencies
DependencyManager::set<ScriptEngines>();
DependencyManager::set<recording::Deck>();
DependencyManager::set<recording::Recorder>();
DependencyManager::set<AddressManager>();
@ -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<RenderDeferredTask>());
_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<ScriptEngines>().data();
scriptEngines->registerScriptInitializer([this](ScriptEngine* engine){
registerScriptEngineWithApplicationServices(engine);
});
connect(scriptEngines, &ScriptEngines::scriptCountChanged, scriptEngines, [this] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
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<RenderDeferredTask>());
_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<ScriptEngines>()->saveScripts();
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
DependencyManager::destroy<ScriptEngines>();
// 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<AnimationCache>().data());
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().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<ScriptEngines>().data());
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
rootContext->setContextProperty("SoundCache", DependencyManager::get<SoundCache>().data());
@ -1233,7 +1245,6 @@ void Application::initializeUi() {
rootContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
rootContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
rootContext->setContextProperty("Render", DependencyManager::get<RenderScriptingInterface>().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<SceneScriptingInterface>().data());
scriptEngine->registerGlobalObject("Render", DependencyManager::get<RenderScriptingInterface>().data());
scriptEngine->registerGlobalObject("ScriptDiscoveryService", this->getRunningScriptsWidget());
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().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<ScriptEngines>()->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<ScriptEngine*>(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<ScriptCache>();
for (QHash<QString, ScriptEngine*>::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<QString, ScriptEngine*>::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>();
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<ScriptCache>()->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<ScriptEngines>()->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<ScriptEngines>()->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());

View file

@ -106,10 +106,6 @@ public:
void postLambdaEvent(std::function<void()> 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<bool> _firstRun;
Setting::Handle<QString> _previousScriptLocation;
Setting::Handle<QString> _scriptsLocationHandle;
Setting::Handle<float> _fieldOfView;
float _scaleMirror;
@ -499,12 +467,7 @@ private:
TouchEvent _lastTouchEvent;
QReadWriteLock _scriptEnginesHashLock;
RunningScriptsWidget* _runningScriptsWidget;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
bool _runningScriptsWidgetWasVisible;
QString _scriptsLocation;
RunningScriptsWidget* _runningScriptsWidget { nullptr };
quint64 _lastNackTime;
quint64 _lastSendDownstreamAudioStats;

View file

@ -21,6 +21,7 @@
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#include <VrMenu.h>
#include <ScriptEngines.h>
#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<ScriptEngines>();
// 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]

View file

@ -79,9 +79,6 @@ HMDToolsDialog::HMDToolsDialog(QWidget* parent) :
// what screens we're allowed on
watchWindow(windowHandle());
auto dialogsManager = DependencyManager::get<DialogsManager>();
if (qApp->getRunningScriptsWidget()) {
watchWindow(qApp->getRunningScriptsWidget()->windowHandle());
}
if (qApp->getToolWindow()) {
watchWindow(qApp->getToolWindow()->windowHandle());
}

View file

@ -15,6 +15,7 @@
#include <QScrollBar>
#include <QtConcurrent/QtConcurrentRun>
#include <ScriptEngines.h>
#include <PathUtils.h>
#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<ScriptEngines>()->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&)));

View file

@ -17,6 +17,7 @@
#include <devices/DdeFaceTracker.h>
#include <devices/Faceshift.h>
#include <NetworkingConstants.h>
#include <ScriptEngines.h>
#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<ScriptEngines>()->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<ScriptEngines>()->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<ScriptEngines>()->setScriptsLocation(ui.scriptsLocationEdit->text());
}
myAvatar->getHead()->setPupilDilation(ui.pupilDilationSlider->value() / (float)ui.pupilDilationSlider->maximum());

View file

@ -22,8 +22,10 @@
#include <QWindow>
#include <PathUtils.h>
#include <ScriptEngines.h>
#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<ScriptEngines>().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<void(QSignalMapper::*)(const QString&)>(&QSignalMapper::mapped),
qApp, &Application::reloadOneScript);
[scriptEngines](const QString& scriptName) { scriptEngines->stopScript(scriptName, true); });
connect(&_stopSignalMapper, static_cast<void(QSignalMapper::*)(const QString&)>(&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<ScriptEngines>()->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<ScriptEngines>();
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<ScriptEngines>()->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<TreeNodeBase*> 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<TreeNodeFolder*>(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<TreeNodeScript*>(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<TreeNodeBase*> 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<TreeNodeScript*>(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<ScriptEngines>()->stopAllScripts();
}

View file

@ -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);
};

View file

@ -25,6 +25,7 @@
#include <QTimer>
#include <QWidget>
#include <ScriptEngines.h>
#include <NetworkAccessManager.h>
#include <OffscreenUi.h>
@ -101,17 +102,18 @@ bool ScriptEditorWidget::setRunning(bool run) {
disconnect(_scriptEngine, &ScriptEngine::finished, this, &ScriptEditorWidget::onScriptFinished);
}
auto scriptEngines = DependencyManager::get<ScriptEngines>();
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<ScriptEngines>()->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<ScriptEngines>();
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;

View file

@ -16,6 +16,7 @@
#include "ScriptEditorWidget.h"
#include <QGridLayout>
#include <QtCore/QSignalMapper>
#include <QFrame>
#include <QLayoutItem>
#include <QMainWindow>
@ -27,6 +28,7 @@
#include <QTimer>
#include <QWidget>
#include <ScriptEngines.h>
#include "Application.h"
#include "PathUtils.h"
@ -85,11 +87,12 @@ void ScriptEditorWindow::loadScriptMenu(const QString& scriptName) {
}
void ScriptEditorWindow::loadScriptClicked() {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
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<ScriptEngines>()->getRunningScripts();;
if (runningScripts.count() > 0) {
QSignalMapper* signalMapper = new QSignalMapper(this);
foreach (const QString& runningScript, runningScripts) {

View file

@ -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; }

View file

@ -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();

View file

@ -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();

View file

@ -14,30 +14,36 @@
#include <PerfStat.h>
#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<TextureCache>();
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<DeferredLightingEffect>()->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<std::unique_ptr<NetworkMesh>>& 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<std::unique_ptr<NetworkMesh>>& 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<DeferredLightingEffect>()->setupTransparent(args, locations->lightBufferUnit);

View file

@ -15,11 +15,10 @@
#include <gpu/Batch.h>
#include <render/Scene.h>
#include <render/ShapePipeline.h>
#include <model/Geometry.h>
#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();

View file

@ -22,8 +22,8 @@
#include <ViewFrustum.h>
#include "AbstractViewStateInterface.h"
#include "Model.h"
#include "MeshPartPayload.h"
#include "Model.h"
#include "RenderUtilsLogging.h"
@ -35,6 +35,8 @@ static int vec3VectorTypeId = qRegisterMetaType<QVector<glm::vec3> >();
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<MeshPartPayload>(networkMesh._mesh, partIndex, ModelRender::getCollisionHullMaterial(), transform, offset);
if (!_collisionHullMaterial) {
_collisionHullMaterial = std::make_shared<model::Material>();
_collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f));
_collisionHullMaterial->setMetallic(0.02f);
_collisionHullMaterial->setGloss(1.0f);
}
_renderItemsSet << std::make_shared<MeshPartPayload>(networkMesh._mesh, partIndex, _collisionHullMaterial, transform, offset);
} else {
_renderItemsSet << std::make_shared<ModelMeshPartPayload>(this, i, partIndex, shapeID, transform, offset);
}

View file

@ -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;

View file

@ -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 <model-networking/TextureCache.h>
#include <PerfStat.h>
#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<Locations>();
initLocations(program, *locations);
auto state = std::make_shared<gpu::State>();
// 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<gpu::State>(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<TextureCache>()->getNormalFittingTexture());
}
}
model::MaterialPointer ModelRender::_collisionHullMaterial;
model::MaterialPointer ModelRender::getCollisionHullMaterial() {
if (!_collisionHullMaterial) {
_collisionHullMaterial = std::make_shared<model::Material>();
_collisionHullMaterial->setDiffuse(glm::vec3(1.0f, 0.5f, 0.0f));
_collisionHullMaterial->setMetallic(0.02f);
_collisionHullMaterial->setGloss(1.0f);
}
return _collisionHullMaterial;
}

View file

@ -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 <gpu/Batch.h>
#include <render/Scene.h>
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<int*>(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> _locations;
RenderPipeline(gpu::PipelinePointer pipeline, std::shared_ptr<Locations> locations) :
_pipeline(pipeline), _locations(locations) {}
};
typedef std::unordered_map<int, RenderPipeline> 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

View file

@ -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 <PerfStat.h>
#include <PathUtils.h>
@ -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<DeferredLightingEffect>()->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<ShapePlumber>();
initDeferredPipelines(*shapePlumber);
// CPU only, create the list of renderedOpaques items
addJob<FetchItems>("FetchOpaque", FetchItems([](const RenderContextPointer& context, int count) {
context->getItemsConfig().opaque.numFeed = count;
}));
addJob<CullItemsOpaque>("CullOpaque", _jobs.back().getOutput());
addJob<CullItems<RenderDetails::OPAQUE_ITEM>>("CullOpaque", _jobs.back().getOutput());
addJob<DepthSortItems>("DepthSortOpaque", _jobs.back().getOutput());
auto& renderedOpaques = _jobs.back().getOutput();
@ -66,7 +92,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
context->getItemsConfig().transparent.numFeed = count;
}
));
addJob<CullItemsTransparent>("CullTransparent", _jobs.back().getOutput());
addJob<CullItems<RenderDetails::TRANSLUCENT_ITEM>>("CullTransparent", _jobs.back().getOutput());
addJob<DepthSortItems>("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false));
auto& renderedTransparents = _jobs.back().getOutput();
@ -74,7 +100,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
addJob<PrepareDeferred>("PrepareDeferred");
// Render opaque objects in DeferredBuffer
addJob<DrawOpaqueDeferred>("DrawOpaqueDeferred", renderedOpaques);
addJob<DrawOpaqueDeferred>("DrawOpaqueDeferred", renderedOpaques, shapePlumber);
// Once opaque is all rendered create stencil background
addJob<DrawStencilDeferred>("DrawOpaqueStencil");
@ -99,7 +125,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_antialiasingJobIndex = (int)_jobs.size() - 1;
// Render transparent objects forward in LigthingBuffer
addJob<DrawTransparentDeferred>("TransparentDeferred", renderedTransparents);
addJob<DrawTransparentDeferred>("DrawTransparentDeferred", renderedTransparents, shapePlumber);
// Lighting Buffer ready for tone mapping
addJob<ToneMappingDeferred>("ToneMapping");
@ -120,7 +146,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
_drawStatusJobIndex = (int)_jobs.size() - 1;
}
addJob<DrawOverlay3D>("DrawOverlay3D");
addJob<DrawOverlay3D>("DrawOverlay3D", shapePlumber);
addJob<HitEffect>("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<TextureCache>()->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<TextureCache>()->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<gpu::State>();
// 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<gpu::State>(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);
}

View file

@ -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<DrawOpaqueDeferred, render::ItemIDsBounds>;
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<DrawTransparentDeferred, render::ItemIDsBounds>;
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<DrawStencilDeferred>;
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<DrawOverlay3D>;
protected:
static gpu::PipelinePointer _opaquePipeline; //lazy evaluation hence mutable
render::ShapePlumberPointer _shapePlumber;
};
class Blit {

View file

@ -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,

View file

@ -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,

View file

@ -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;
}

View file

@ -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<FetchItems, ItemIDsBounds>;
};
template<RenderDetails::Type T = RenderDetails::Type::OTHER_ITEM>
class CullItems {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelIO<CullItems, ItemIDsBounds, ItemIDsBounds>;
};
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<CullItemsOpaque, ItemIDsBounds, ItemIDsBounds>;
};
class CullItemsTransparent {
public:
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems, ItemIDsBounds& outItems);
using JobModel = Task::Job::ModelIO<CullItemsTransparent, ItemIDsBounds, ItemIDsBounds>;
using JobModel = Task::Job::ModelIO<CullItems<T>, ItemIDsBounds, ItemIDsBounds>;
};
class DepthSortItems {

View file

@ -25,6 +25,7 @@
#include <RenderArgs.h>
#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 <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) {
template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
// Shape type interface
template <class T> const model::MaterialKey shapeGetMaterialKey(const std::shared_ptr<T>& 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 <class T> const ShapeKey shapeGetShapeKey(const std::shared_ptr<T>& payloadData) { return ShapeKey::Builder::ownPipeline(); }
template <class T> class Payload : public Item::PayloadInterface {
public:
@ -364,7 +371,7 @@ public:
virtual void render(RenderArgs* args) { payloadRender<T>(_data, args); }
// Shape Type interface
virtual const model::MaterialKey getMaterialKey() const { return shapeGetMaterialKey<T>(_data); }
virtual const ShapeKey getShapeKey() const { return shapeGetShapeKey<T>(_data); }
protected:
DataPointer _data;

View file

@ -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 <PerfStat.h>
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>();
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<Pipeline>(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;
}

View file

@ -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 <gpu/Batch.h>
#include <RenderArgs.h>
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<NUM_FLAGS>;
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<ShapeKey::Flags>()(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<Locations>;
using BatchSetter = std::function<void(const ShapePipeline&, gpu::Batch&)>;
ShapePipeline(gpu::PipelinePointer pipeline, LocationsPointer locations, BatchSetter batchSetter) :
pipeline(pipeline), locations(locations), batchSetter(batchSetter) {}
gpu::PipelinePointer pipeline;
std::shared_ptr<Locations> locations;
protected:
friend class ShapePlumber;
BatchSetter batchSetter;
};
using ShapePipelinePointer = std::shared_ptr<ShapePipeline>;
class ShapePlumber {
public:
using Key = ShapeKey;
using Filter = Key::Filter;
using Pipeline = ShapePipeline;
using PipelinePointer = ShapePipelinePointer;
using PipelineMap = std::unordered_map<ShapeKey, PipelinePointer, ShapeKey::Hash, ShapeKey::KeyEqual>;
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<ShapePlumber>;
}
#endif // hifi_render_ShapePipeline_h

View file

@ -46,11 +46,13 @@
#include "TypedArrays.h"
#include "XMLHttpRequestClass.h"
#include "WebSocketClass.h"
#include "RecordingScriptingInterface.h"
#include "ScriptEngines.h"
#include "MIDIEvent.h"
std::atomic<bool> 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<ScriptEngines>()->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<ScriptEngines>()->removeScriptEngine(this);
}
void ScriptEngine::disconnectNonEssentialSignals() {
@ -187,67 +180,6 @@ void ScriptEngine::runInThread() {
workerThread->start();
}
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
QMutex ScriptEngine::_allScriptsMutex;
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
void ScriptEngine::stopAllScripts(QObject* application) {
_allScriptsMutex.lock();
_stoppingAllScripts = true;
qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
QMutableSetIterator<ScriptEngine*> 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) {

View file

@ -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<ScriptEngine*> _allKnownScriptEngines;
static QMutex _allScriptsMutex;
friend class ScriptEngines;
static std::atomic<bool> _stoppingAllScripts;
};

View file

@ -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 <QtCore/QStandardPaths>
#include <QtCore/QCoreApplication>
#include <SettingHandle.h>
#include <UserActivityLogger.h>
#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<ScriptEngine*> 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<TreeNodeBase*> 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<TreeNodeFolder*>(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<TreeNodeScript*>(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<TreeNodeBase*> 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<TreeNodeScript*>(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<ScriptCache>();
for (QHash<QString, ScriptEngine*>::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<QString, ScriptEngine*>::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>();
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<ScriptCache>()->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<ScriptEngine*>(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);
}

View file

@ -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 <functional>
#include <atomic>
#include <memory>
#include <QtCore/QObject>
#include <QtCore/QMutex>
#include <QtCore/QReadWriteLock>
#include <SettingHandle.h>
#include <DependencyManager.h>
#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<void(ScriptEngine*)>;
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<bool> _firstRun { "firstRun", true };
QReadWriteLock _scriptEnginesHashLock;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
QSet<ScriptEngine*> _allKnownScriptEngines;
QMutex _allScriptsMutex;
std::atomic<bool> _stoppingAllScripts { false };
std::list<ScriptInitializer> _scriptInitializers;
mutable Setting::Handle<QString> _scriptsLocationHandle;
mutable Setting::Handle<QString> _previousScriptLocation;
ScriptsModel _scriptsModel;
ScriptsModelFilter _scriptsModelFilter;
};
#endif // hifi_ScriptEngine_h

View file

@ -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 <QUrl>
#include <QUrlQuery>
#include <QXmlStreamReader>
#include <NetworkAccessManager.h>
#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<ScriptEngines>();
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;
}

View file

@ -92,6 +92,7 @@ protected:
void rebuildTree();
private:
friend class ScriptEngines;
bool _loadingScripts;
QDir _localDirectory;
QFileSystemWatcher _fsWatcher;

View file

@ -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<gpu::Context> _context = nullptr;
std::shared_ptr<gpu::Framebuffer> _blitFramebuffer = nullptr;
std::shared_ptr<render::ShapePipeline> _pipeline = nullptr;
OctreeRenderer* _renderer = nullptr;
ViewFrustum* _viewFrustum = nullptr;
glm::ivec4 _viewport{ 0, 0, 1, 1 };

View file

@ -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 {

View file

@ -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);

View file

@ -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()

View file

@ -11,19 +11,21 @@
#include "TransformTests.h"
#include <algorithm>
#include <glm/glm.hpp>
#include <SharedLogging.h>
#include <Transform.h>
#include "../QTestExtensions.h"
#include <QtCore/QDebug>
#include <Transform.h>
#include <StreamUtils.h>
#include <glm/glm.hpp>
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);
}