mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 22:36:57 +02:00
merge upstream/master into andrew/bispinor
This commit is contained in:
commit
d85be49694
16 changed files with 476 additions and 131 deletions
|
@ -88,8 +88,17 @@ endif ()
|
||||||
|
|
||||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH})
|
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_CMAKE_PREFIX_PATH})
|
||||||
|
|
||||||
# set our OS X deployment target to
|
if (APPLE)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
|
# set our OS X deployment target
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.8)
|
||||||
|
|
||||||
|
# find the 10.9 SDK path
|
||||||
|
execute_process(COMMAND xcodebuild -sdk -version OUTPUT_VARIABLE XCODE_SDK_VERSIONS)
|
||||||
|
string(REGEX MATCH \\/.+MacOSX10.9.sdk OSX_SDK_PATH ${XCODE_SDK_VERSIONS})
|
||||||
|
|
||||||
|
# set that as the SDK to use
|
||||||
|
set(CMAKE_OSX_SYSROOT ${OSX_SDK_PATH})
|
||||||
|
endif ()
|
||||||
|
|
||||||
# Find includes in corresponding build directories
|
# Find includes in corresponding build directories
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
|
@ -396,6 +396,10 @@ var toolBar = (function () {
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Window.domainChanged.connect(function() {
|
||||||
|
that.setActive(false);
|
||||||
|
});
|
||||||
|
|
||||||
that.cleanup = function () {
|
that.cleanup = function () {
|
||||||
toolBar.cleanup();
|
toolBar.cleanup();
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,11 +38,13 @@
|
||||||
// function onIncomingMessage(user, message) {
|
// function onIncomingMessage(user, message) {
|
||||||
// //do stuff here;
|
// //do stuff here;
|
||||||
// var text = "This is a notification";
|
// var text = "This is a notification";
|
||||||
// wordWrap(text);
|
// var wrappedText = wordWrap(text);
|
||||||
|
// createNotification(wrappedText, NotificationType.SNAPSHOT);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown.
|
// This new function must call wordWrap(text) if the length of message is longer than 42 chars or unknown.
|
||||||
// wordWrap() will format the text to fit the notifications overlay and send it to createNotification(text).
|
// wordWrap() will format the text to fit the notifications overlay and return it
|
||||||
|
// after that we will send it to createNotification(text).
|
||||||
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
|
// If the message is 42 chars or less you should bypass wordWrap() and call createNotification() directly.
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,12 +52,12 @@
|
||||||
//
|
//
|
||||||
// 1. Add a key to the keyPressEvent(key).
|
// 1. Add a key to the keyPressEvent(key).
|
||||||
// 2. Declare a text string.
|
// 2. Declare a text string.
|
||||||
// 3. Call createNotifications(text) parsing the text.
|
// 3. Call createNotifications(text, NotificationType) parsing the text.
|
||||||
// example:
|
// example:
|
||||||
// var welcome;
|
// var welcome;
|
||||||
// if (key.text == "q") { //queries number of users online
|
// if (key.text == "q") { //queries number of users online
|
||||||
// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now.";
|
// var welcome = "There are " + GlobalServices.onlineUsers.length + " users online now.";
|
||||||
// createNotification(welcome);
|
// createNotification(welcome, NotificationType.USERS_ONLINE);
|
||||||
// }
|
// }
|
||||||
Script.include("./libraries/globals.js");
|
Script.include("./libraries/globals.js");
|
||||||
Script.include("./libraries/soundArray.js");
|
Script.include("./libraries/soundArray.js");
|
||||||
|
@ -83,6 +85,46 @@ var last_users = GlobalServices.onlineUsers;
|
||||||
var users = [];
|
var users = [];
|
||||||
var ctrlIsPressed = false;
|
var ctrlIsPressed = false;
|
||||||
var ready = true;
|
var ready = true;
|
||||||
|
var MENU_NAME = 'Tools > Notifications';
|
||||||
|
var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
|
||||||
|
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
||||||
|
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
||||||
|
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
||||||
|
|
||||||
|
var NotificationType = {
|
||||||
|
UNKNOWN: 0,
|
||||||
|
USER_JOINS: 1,
|
||||||
|
USER_LEAVES: 2,
|
||||||
|
MUTE_TOGGLE: 3,
|
||||||
|
CHAT_MENTION: 4,
|
||||||
|
USERS_ONLINE: 5,
|
||||||
|
SNAPSHOT: 6,
|
||||||
|
WINDOW_RESIZE: 7,
|
||||||
|
properties: [
|
||||||
|
{ text: "User Join" },
|
||||||
|
{ text: "User Leave" },
|
||||||
|
{ text: "Mute Toggle" },
|
||||||
|
{ text: "Chat Mention" },
|
||||||
|
{ text: "Users Online" },
|
||||||
|
{ text: "Snapshot" },
|
||||||
|
{ text: "Window Resize" }
|
||||||
|
],
|
||||||
|
getTypeFromMenuItem: function(menuItemName) {
|
||||||
|
if (menuItemName.substr(menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length) !== NOTIFICATION_MENU_ITEM_POST) {
|
||||||
|
return NotificationType.UNKNOWN;
|
||||||
|
}
|
||||||
|
var preMenuItemName = menuItemName.substr(0, menuItemName.length - NOTIFICATION_MENU_ITEM_POST.length);
|
||||||
|
for (type in this.properties) {
|
||||||
|
if (this.properties[type].text === preMenuItemName) {
|
||||||
|
return parseInt(type) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NotificationType.UNKNOWN;
|
||||||
|
},
|
||||||
|
getMenuString: function(type) {
|
||||||
|
return this.properties[type - 1].text + NOTIFICATION_MENU_ITEM_POST;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var randomSounds = new SoundArray({ localOnly: true }, true);
|
var randomSounds = new SoundArray({ localOnly: true }, true);
|
||||||
var numberOfSounds = 2;
|
var numberOfSounds = 2;
|
||||||
|
@ -90,15 +132,6 @@ for (var i = 1; i <= numberOfSounds; i++) {
|
||||||
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/notification-general" + i + ".raw");
|
randomSounds.addSound(HIFI_PUBLIC_BUCKET + "sounds/UI/notification-general" + i + ".raw");
|
||||||
}
|
}
|
||||||
|
|
||||||
// When our script shuts down, we should clean up all of our overlays
|
|
||||||
function scriptEnding() {
|
|
||||||
for (i = 0; i < notifications.length; i++) {
|
|
||||||
Overlays.deleteOverlay(notifications[i]);
|
|
||||||
Overlays.deleteOverlay(buttons[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
|
||||||
|
|
||||||
var notifications = [];
|
var notifications = [];
|
||||||
var buttons = [];
|
var buttons = [];
|
||||||
var times = [];
|
var times = [];
|
||||||
|
@ -211,8 +244,6 @@ function notify(notice, button, height) {
|
||||||
positions,
|
positions,
|
||||||
last;
|
last;
|
||||||
|
|
||||||
randomSounds.playRandom();
|
|
||||||
|
|
||||||
if (isOnHMD) {
|
if (isOnHMD) {
|
||||||
// Calculate 3D values from 2D overlay properties.
|
// Calculate 3D values from 2D overlay properties.
|
||||||
|
|
||||||
|
@ -257,7 +288,7 @@ function notify(notice, button, height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function creates and sizes the overlays
|
// This function creates and sizes the overlays
|
||||||
function createNotification(text) {
|
function createNotification(text, notificationType) {
|
||||||
var count = (text.match(/\n/g) || []).length,
|
var count = (text.match(/\n/g) || []).length,
|
||||||
breakPoint = 43.0, // length when new line is added
|
breakPoint = 43.0, // length when new line is added
|
||||||
extraLine = 0,
|
extraLine = 0,
|
||||||
|
@ -307,6 +338,12 @@ function createNotification(text) {
|
||||||
alpha: backgroundAlpha
|
alpha: backgroundAlpha
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
||||||
|
Menu.isOptionChecked(NotificationType.getMenuString(notificationType)))
|
||||||
|
{
|
||||||
|
randomSounds.playRandom();
|
||||||
|
}
|
||||||
|
|
||||||
notify(noticeProperties, buttonProperties, height);
|
notify(noticeProperties, buttonProperties, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,8 +382,7 @@ function stringDivider(str, slotWidth, spaceReplacer) {
|
||||||
|
|
||||||
// formats string to add newline every 43 chars
|
// formats string to add newline every 43 chars
|
||||||
function wordWrap(str) {
|
function wordWrap(str) {
|
||||||
var result = stringDivider(str, 43.0, "\n");
|
return stringDivider(str, 43.0, "\n");
|
||||||
createNotification(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This fires a notification on window resize
|
// This fires a notification on window resize
|
||||||
|
@ -358,7 +394,7 @@ function checkSize() {
|
||||||
windowDimensions = Controller.getViewportDimensions();
|
windowDimensions = Controller.getViewportDimensions();
|
||||||
overlayLocationX = (windowDimensions.x - (width + 60.0));
|
overlayLocationX = (windowDimensions.x - (width + 60.0));
|
||||||
buttonLocationX = overlayLocationX + (width - 35.0);
|
buttonLocationX = overlayLocationX + (width - 35.0);
|
||||||
createNotification(windowResize);
|
createNotification(windowResize, NotificationType.WINDOW_RESIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,10 +478,7 @@ var STARTUP_TIMEOUT = 500, // ms
|
||||||
|
|
||||||
// This reports the number of users online at startup
|
// This reports the number of users online at startup
|
||||||
function reportUsers() {
|
function reportUsers() {
|
||||||
var welcome;
|
createNotification("Welcome! There are " + GlobalServices.onlineUsers.length + " users online now.", NotificationType.USERS_ONLINE);
|
||||||
|
|
||||||
welcome = "Welcome! There are " + GlobalServices.onlineUsers.length + " users online now.";
|
|
||||||
createNotification(welcome);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishStartup() {
|
function finishStartup() {
|
||||||
|
@ -472,13 +505,13 @@ function onOnlineUsersChanged(users) {
|
||||||
if (!isStartingUp()) { // Skip user notifications at startup.
|
if (!isStartingUp()) { // Skip user notifications at startup.
|
||||||
for (i = 0; i < users.length; i += 1) {
|
for (i = 0; i < users.length; i += 1) {
|
||||||
if (last_users.indexOf(users[i]) === -1.0) {
|
if (last_users.indexOf(users[i]) === -1.0) {
|
||||||
createNotification(users[i] + " has joined");
|
createNotification(users[i] + " has joined", NotificationType.USER_JOINS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < last_users.length; i += 1) {
|
for (i = 0; i < last_users.length; i += 1) {
|
||||||
if (users.indexOf(last_users[i]) === -1.0) {
|
if (users.indexOf(last_users[i]) === -1.0) {
|
||||||
createNotification(last_users[i] + " has left");
|
createNotification(last_users[i] + " has left", NotificationType.USER_LEAVES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,7 +530,7 @@ function onIncomingMessage(user, message) {
|
||||||
thisAlert = user + ": " + myMessage;
|
thisAlert = user + ": " + myMessage;
|
||||||
|
|
||||||
if (myMessage.indexOf(alertMe) > -1.0) {
|
if (myMessage.indexOf(alertMe) > -1.0) {
|
||||||
wordWrap(thisAlert);
|
CreateNotification(wordWrap(thisAlert), NotificationType.CHAT_MENTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,9 +539,9 @@ function onMuteStateChanged() {
|
||||||
var muteState,
|
var muteState,
|
||||||
muteString;
|
muteString;
|
||||||
|
|
||||||
muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
|
muteState = AudioDevice.getMuted() ? "muted" : "unmuted";
|
||||||
muteString = "Microphone is now " + muteState;
|
muteString = "Microphone is now " + muteState;
|
||||||
createNotification(muteString);
|
createNotification(muteString, NotificationType.MUTE_TOGGLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handles mouse clicks on buttons
|
// handles mouse clicks on buttons
|
||||||
|
@ -551,25 +584,58 @@ function keyPressEvent(key) {
|
||||||
if (key.text === "q") { //queries number of users online
|
if (key.text === "q") { //queries number of users online
|
||||||
numUsers = GlobalServices.onlineUsers.length;
|
numUsers = GlobalServices.onlineUsers.length;
|
||||||
welcome = "There are " + numUsers + " users online now.";
|
welcome = "There are " + numUsers + " users online now.";
|
||||||
createNotification(welcome);
|
createNotification(welcome, NotificationType.USERS_ONLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.text === "s") {
|
if (key.text === "s") {
|
||||||
if (ctrlIsPressed === true) {
|
if (ctrlIsPressed === true) {
|
||||||
noteString = "Snapshot taken.";
|
noteString = "Snapshot taken.";
|
||||||
createNotification(noteString);
|
createNotification(noteString, NotificationType.SNAPSHOT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
Menu.addMenu(MENU_NAME);
|
||||||
|
var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING);
|
||||||
|
checked = checked === '' ? true : checked;
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: MENU_NAME,
|
||||||
|
menuItemName: PLAY_NOTIFICATION_SOUNDS_MENU_ITEM,
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING)
|
||||||
|
});
|
||||||
|
Menu.addSeparator(MENU_NAME, "Play sounds for:");
|
||||||
|
for (type in NotificationType.properties) {
|
||||||
|
checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type) + 1));
|
||||||
|
checked = checked === '' ? true : checked;
|
||||||
|
Menu.addMenuItem({
|
||||||
|
menuName: MENU_NAME,
|
||||||
|
menuItemName: NotificationType.properties[type].text + NOTIFICATION_MENU_ITEM_POST,
|
||||||
|
isCheckable: true,
|
||||||
|
isChecked: checked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// When our script shuts down, we should clean up all of our overlays
|
// When our script shuts down, we should clean up all of our overlays
|
||||||
function scriptEnding() {
|
function scriptEnding() {
|
||||||
var i;
|
for (var i = 0; i < notifications.length; i++) {
|
||||||
|
|
||||||
for (i = 0; i < notifications.length; i += 1) {
|
|
||||||
Overlays.deleteOverlay(notifications[i]);
|
Overlays.deleteOverlay(notifications[i]);
|
||||||
Overlays.deleteOverlay(buttons[i]);
|
Overlays.deleteOverlay(buttons[i]);
|
||||||
}
|
}
|
||||||
|
Menu.removeMenu(MENU_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
function menuItemEvent(menuItem) {
|
||||||
|
if (menuItem === PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) {
|
||||||
|
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_SETTING, Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var notificationType = NotificationType.getTypeFromMenuItem(menuItem);
|
||||||
|
if (notificationType !== notificationType.UNKNOWN) {
|
||||||
|
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + notificationType, Menu.isOptionChecked(menuItem));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDevice.muteToggled.connect(onMuteStateChanged);
|
AudioDevice.muteToggled.connect(onMuteStateChanged);
|
||||||
|
@ -580,3 +646,6 @@ GlobalServices.incomingMessage.connect(onIncomingMessage);
|
||||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
Script.scriptEnding.connect(scriptEnding);
|
Script.scriptEnding.connect(scriptEnding);
|
||||||
|
Menu.menuItemEvent.connect(menuItemEvent);
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
|
@ -533,6 +533,10 @@ void Application::aboutToQuit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::cleanupBeforeQuit() {
|
void Application::cleanupBeforeQuit() {
|
||||||
|
|
||||||
|
_entities.shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
|
||||||
|
ScriptEngine::stopAllScripts(this); // stop all currently running global scripts
|
||||||
|
|
||||||
// first stop all timers directly or by invokeMethod
|
// first stop all timers directly or by invokeMethod
|
||||||
// depending on what thread they run in
|
// depending on what thread they run in
|
||||||
locationUpdateTimer->stop();
|
locationUpdateTimer->stop();
|
||||||
|
@ -597,13 +601,11 @@ Application::~Application() {
|
||||||
|
|
||||||
DependencyManager::destroy<GLCanvas>();
|
DependencyManager::destroy<GLCanvas>();
|
||||||
|
|
||||||
qDebug() << "start destroying ResourceCaches Application::~Application() line:" << __LINE__;
|
|
||||||
DependencyManager::destroy<AnimationCache>();
|
DependencyManager::destroy<AnimationCache>();
|
||||||
DependencyManager::destroy<TextureCache>();
|
DependencyManager::destroy<TextureCache>();
|
||||||
DependencyManager::destroy<GeometryCache>();
|
DependencyManager::destroy<GeometryCache>();
|
||||||
DependencyManager::destroy<ScriptCache>();
|
DependencyManager::destroy<ScriptCache>();
|
||||||
DependencyManager::destroy<SoundCache>();
|
DependencyManager::destroy<SoundCache>();
|
||||||
qDebug() << "done destroying ResourceCaches Application::~Application() line:" << __LINE__;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::initializeGL() {
|
void Application::initializeGL() {
|
||||||
|
@ -1469,6 +1471,10 @@ void Application::checkFPS() {
|
||||||
|
|
||||||
void Application::idle() {
|
void Application::idle() {
|
||||||
PerformanceTimer perfTimer("idle");
|
PerformanceTimer perfTimer("idle");
|
||||||
|
|
||||||
|
if (_aboutToQuit) {
|
||||||
|
return; // bail early, nothing to do here.
|
||||||
|
}
|
||||||
|
|
||||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||||
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
|
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
|
||||||
|
@ -3535,19 +3541,20 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
|
scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// TODO: Consider moving some of this functionality into the ScriptEngine class instead. It seems wrong that this
|
||||||
|
// work is being done in the Application class when really these dependencies are more related to the ScriptEngine's
|
||||||
|
// implementation
|
||||||
QThread* workerThread = new QThread(this);
|
QThread* workerThread = new QThread(this);
|
||||||
workerThread->setObjectName("Script Engine Thread");
|
QString scriptEngineName = QString("Script Thread:") + scriptEngine->getFilename();
|
||||||
|
workerThread->setObjectName(scriptEngineName);
|
||||||
|
|
||||||
// when the worker thread is started, call our engine's run..
|
// when the worker thread is started, call our engine's run..
|
||||||
connect(workerThread, &QThread::started, scriptEngine, &ScriptEngine::run);
|
connect(workerThread, &QThread::started, scriptEngine, &ScriptEngine::run);
|
||||||
|
|
||||||
// when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
|
// when the thread is terminated, add both scriptEngine and thread to the deleteLater queue
|
||||||
connect(scriptEngine, SIGNAL(finished(const QString&)), scriptEngine, SLOT(deleteLater()));
|
connect(scriptEngine, SIGNAL(doneRunning()), scriptEngine, SLOT(deleteLater()));
|
||||||
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
|
connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
|
||||||
|
|
||||||
// when the application is about to quit, stop our script engine so it unwinds properly
|
|
||||||
connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop()));
|
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
connect(nodeList.data(), &NodeList::nodeKilled, scriptEngine, &ScriptEngine::nodeKilled);
|
connect(nodeList.data(), &NodeList::nodeKilled, scriptEngine, &ScriptEngine::nodeKilled);
|
||||||
|
|
||||||
|
@ -3558,7 +3565,12 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded,
|
||||||
bool loadScriptFromEditor, bool activateMainWindow) {
|
bool loadScriptFromEditor, bool activateMainWindow) {
|
||||||
|
|
||||||
|
if (isAboutToQuit()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
QUrl scriptUrl(scriptFilename);
|
QUrl scriptUrl(scriptFilename);
|
||||||
const QString& scriptURLString = scriptUrl.toString();
|
const QString& scriptURLString = scriptUrl.toString();
|
||||||
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
|
||||||
|
@ -3749,8 +3761,8 @@ void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject
|
||||||
voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
|
voxelWalletUUID = QUuid(voxelObject[VOXEL_WALLET_UUID].toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Voxel costs are" << satoshisPerVoxel << "per voxel and" << satoshisPerMeterCubed << "per meter cubed";
|
qDebug() << "Octree edits costs are" << satoshisPerVoxel << "per octree cell and" << satoshisPerMeterCubed << "per meter cubed";
|
||||||
qDebug() << "Destination wallet UUID for voxel payments is" << voxelWalletUUID;
|
qDebug() << "Destination wallet UUID for edit payments is" << voxelWalletUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Application::getPreviousScriptLocation() {
|
QString Application::getPreviousScriptLocation() {
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "DomainHandler.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "Menu.h"
|
#include "Menu.h"
|
||||||
#include "ui/ModelsBrowser.h"
|
#include "ui/ModelsBrowser.h"
|
||||||
|
@ -34,6 +35,8 @@ WindowScriptingInterface::WindowScriptingInterface() :
|
||||||
_nonBlockingFormActive(false),
|
_nonBlockingFormActive(false),
|
||||||
_formResult(QDialog::Rejected)
|
_formResult(QDialog::Rejected)
|
||||||
{
|
{
|
||||||
|
const DomainHandler& domainHandler = DependencyManager::get<NodeList>()->getDomainHandler();
|
||||||
|
connect(&domainHandler, &DomainHandler::hostnameChanged, this, &WindowScriptingInterface::domainChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) {
|
WebWindowClass* WindowScriptingInterface::doCreateWebWindow(const QString& title, const QString& url, int width, int height) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ public slots:
|
||||||
QScriptValue peekNonBlockingFormResult(QScriptValue array);
|
QScriptValue peekNonBlockingFormResult(QScriptValue array);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void domainChanged(const QString& domainHostname);
|
||||||
void inlineButtonClicked(const QString& name);
|
void inlineButtonClicked(const QString& name);
|
||||||
void nonBlockingFormClosed();
|
void nonBlockingFormClosed();
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "GeometryUtil.h"
|
||||||
|
#include "PlaneShape.h"
|
||||||
|
|
||||||
#include "BillboardOverlay.h"
|
#include "BillboardOverlay.h"
|
||||||
|
|
||||||
|
@ -191,19 +193,25 @@ bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::v
|
||||||
float& distance, BoxFace& face) {
|
float& distance, BoxFace& face) {
|
||||||
|
|
||||||
if (_texture) {
|
if (_texture) {
|
||||||
|
glm::quat rotation;
|
||||||
|
if (_isFacingAvatar) {
|
||||||
|
// rotate about vertical to face the camera
|
||||||
|
rotation = Application::getInstance()->getCamera()->getRotation();
|
||||||
|
rotation *= glm::angleAxis(glm::pi<float>(), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
} else {
|
||||||
|
rotation = _rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce the dimensions of the billboard based on the image's aspect ratio and the overlay's scale.
|
||||||
bool isNull = _fromImage.isNull();
|
bool isNull = _fromImage.isNull();
|
||||||
float width = isNull ? _texture->getWidth() : _fromImage.width();
|
float width = isNull ? _texture->getWidth() : _fromImage.width();
|
||||||
float height = isNull ? _texture->getHeight() : _fromImage.height();
|
float height = isNull ? _texture->getHeight() : _fromImage.height();
|
||||||
|
|
||||||
float maxSize = glm::max(width, height);
|
float maxSize = glm::max(width, height);
|
||||||
float x = width / (2.0f * maxSize);
|
glm::vec2 dimensions = _scale * glm::vec2(width / maxSize, height / maxSize);
|
||||||
float y = -height / (2.0f * maxSize);
|
|
||||||
float maxDimension = glm::max(x,y);
|
return findRayRectangleIntersection(origin, direction, rotation, _position, dimensions);
|
||||||
float scaledDimension = maxDimension * _scale;
|
|
||||||
glm::vec3 corner = getCenter() - glm::vec3(scaledDimension, scaledDimension, scaledDimension) ;
|
|
||||||
AACube myCube(corner, scaledDimension * 2.0f);
|
|
||||||
return myCube.findRayIntersection(origin, direction, distance, face);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <StreamUtils.h>
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
|
#include "GeometryUtil.h"
|
||||||
|
|
||||||
#include "Planar3DOverlay.h"
|
#include "Planar3DOverlay.h"
|
||||||
|
|
||||||
const float DEFAULT_SIZE = 1.0f;
|
const float DEFAULT_SIZE = 1.0f;
|
||||||
|
@ -93,29 +95,5 @@ QScriptValue Planar3DOverlay::getProperty(const QString& property) {
|
||||||
|
|
||||||
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool Planar3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
float& distance, BoxFace& face) {
|
float& distance, BoxFace& face) {
|
||||||
RayIntersectionInfo rayInfo;
|
return findRayRectangleIntersection(origin, direction, _rotation, _position, _dimensions);
|
||||||
rayInfo._rayStart = origin;
|
|
||||||
rayInfo._rayDirection = direction;
|
|
||||||
rayInfo._rayLength = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
PlaneShape plane;
|
|
||||||
|
|
||||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
|
||||||
glm::vec3 normal = _rotation * UNROTATED_NORMAL;
|
|
||||||
plane.setNormal(normal);
|
|
||||||
plane.setPoint(_position); // the position is definitely a point on our plane
|
|
||||||
|
|
||||||
bool intersects = plane.findRayIntersection(rayInfo);
|
|
||||||
|
|
||||||
if (intersects) {
|
|
||||||
distance = rayInfo._hitDistance;
|
|
||||||
|
|
||||||
glm::vec3 hitPosition = origin + (distance * direction);
|
|
||||||
glm::vec3 localHitPosition = glm::inverse(_rotation) * (hitPosition - _position);
|
|
||||||
glm::vec2 halfDimensions = _dimensions / 2.0f;
|
|
||||||
|
|
||||||
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
|
|
||||||
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
|
|
||||||
}
|
|
||||||
return intersects;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,10 +59,17 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTreeRenderer::~EntityTreeRenderer() {
|
EntityTreeRenderer::~EntityTreeRenderer() {
|
||||||
// NOTE: we don't need to delete _entitiesScriptEngine because it's owned by the application and gets cleaned up
|
// NOTE: we don't need to delete _entitiesScriptEngine because it is registered with the application and has a
|
||||||
// automatically but we do need to delete our sandbox script engine.
|
// signal tied to call it's deleteLater on doneRunning
|
||||||
delete _sandboxScriptEngine;
|
if (_sandboxScriptEngine) {
|
||||||
_sandboxScriptEngine = NULL;
|
// TODO: consider reworking how _sandboxScriptEngine is managed. It's treated differently than _entitiesScriptEngine
|
||||||
|
// because we don't call registerScriptEngineWithApplicationServices() for it. This implementation is confusing and
|
||||||
|
// potentially error prone because it's not a full fledged ScriptEngine that has been fully connected to the
|
||||||
|
// application. We did this so that scripts that were ill-formed could be evaluated but not execute against the
|
||||||
|
// application services. But this means it's shutdown behavior is different from other ScriptEngines
|
||||||
|
delete _sandboxScriptEngine;
|
||||||
|
_sandboxScriptEngine = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::clear() {
|
void EntityTreeRenderer::clear() {
|
||||||
|
@ -97,6 +104,11 @@ void EntityTreeRenderer::init() {
|
||||||
connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
|
connect(entityTree, &EntityTree::changingEntityID, this, &EntityTreeRenderer::changingEntityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::shutdown() {
|
||||||
|
_shuttingDown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
|
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
|
||||||
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||||
return loadEntityScript(entity);
|
return loadEntityScript(entity);
|
||||||
|
@ -156,6 +168,10 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
||||||
|
|
||||||
|
|
||||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
||||||
|
if (_shuttingDown) {
|
||||||
|
return QScriptValue(); // since we're shutting down, we don't load any more scripts
|
||||||
|
}
|
||||||
|
|
||||||
if (!entity) {
|
if (!entity) {
|
||||||
return QScriptValue(); // no entity...
|
return QScriptValue(); // no entity...
|
||||||
}
|
}
|
||||||
|
@ -235,7 +251,7 @@ void EntityTreeRenderer::setTree(Octree* newTree) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::update() {
|
void EntityTreeRenderer::update() {
|
||||||
if (_tree) {
|
if (_tree && !_shuttingDown) {
|
||||||
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
EntityTree* tree = static_cast<EntityTree*>(_tree);
|
||||||
tree->update();
|
tree->update();
|
||||||
|
|
||||||
|
@ -258,7 +274,7 @@ void EntityTreeRenderer::update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::checkEnterLeaveEntities() {
|
void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||||
if (_tree) {
|
if (_tree && !_shuttingDown) {
|
||||||
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
||||||
glm::vec3 avatarPosition = _viewState->getAvatarPosition() / (float) TREE_SCALE;
|
glm::vec3 avatarPosition = _viewState->getAvatarPosition() / (float) TREE_SCALE;
|
||||||
|
|
||||||
|
@ -309,7 +325,7 @@ void EntityTreeRenderer::checkEnterLeaveEntities() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::leaveAllEntities() {
|
void EntityTreeRenderer::leaveAllEntities() {
|
||||||
if (_tree) {
|
if (_tree && !_shuttingDown) {
|
||||||
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
_tree->lockForWrite(); // so that our scripts can do edits if they want
|
||||||
|
|
||||||
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
// for all of our previous containing entities, if they are no longer containing then send them a leave event
|
||||||
|
@ -330,7 +346,7 @@ void EntityTreeRenderer::leaveAllEntities() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
|
void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) {
|
||||||
if (_tree) {
|
if (_tree && !_shuttingDown) {
|
||||||
Model::startScene(renderSide);
|
Model::startScene(renderSide);
|
||||||
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide,
|
RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, renderSide,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
@ -700,9 +716,14 @@ QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entity
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
|
// process these events.
|
||||||
|
if (!_tree || _shuttingDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
|
PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent");
|
||||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||||
|
|
||||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
|
@ -714,7 +735,7 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
||||||
if (entityScript.property("mousePressOnEntity").isValid()) {
|
if (entityScript.property("mousePressOnEntity").isValid()) {
|
||||||
entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs);
|
entityScript.property("mousePressOnEntity").call(entityScript, entityScriptArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentClickingOnEntityID = rayPickResult.entityID;
|
_currentClickingOnEntityID = rayPickResult.entityID;
|
||||||
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
emit clickDownOnEntity(_currentClickingOnEntityID, MouseEvent(*event, deviceID));
|
||||||
if (entityScript.property("clickDownOnEntity").isValid()) {
|
if (entityScript.property("clickDownOnEntity").isValid()) {
|
||||||
|
@ -726,6 +747,11 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int device
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
|
// process these events.
|
||||||
|
if (!_tree || _shuttingDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
|
PerformanceTimer perfTimer("EntityTreeRenderer::mouseReleaseEvent");
|
||||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||||
bool precisionPicking = !_dontDoPrecisionPicking;
|
bool precisionPicking = !_dontDoPrecisionPicking;
|
||||||
|
@ -740,7 +766,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs);
|
entityScript.property("mouseReleaseOnEntity").call(entityScript, entityScriptArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
// Even if we're no longer intersecting with an entity, if we started clicking on it, and now
|
||||||
// we're releasing the button, then this is considered a clickOn event
|
// we're releasing the button, then this is considered a clickOn event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
|
@ -752,7 +778,7 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
currentClickingEntity.property("clickReleaseOnEntity").call(currentClickingEntity, currentClickingEntityArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes it the unknown ID, we just released so we can't be clicking on anything
|
// makes it the unknown ID, we just released so we can't be clicking on anything
|
||||||
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
|
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID();
|
||||||
_lastMouseEvent = MouseEvent(*event, deviceID);
|
_lastMouseEvent = MouseEvent(*event, deviceID);
|
||||||
|
@ -760,10 +786,15 @@ void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event, unsigned int devi
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
|
||||||
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
|
// process these events.
|
||||||
|
if (!_tree || _shuttingDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
|
PerformanceTimer perfTimer("EntityTreeRenderer::mouseMoveEvent");
|
||||||
|
|
||||||
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
PickRay ray = _viewState->computePickRay(event->x(), event->y());
|
||||||
|
|
||||||
bool precisionPicking = false; // for mouse moves we do not do precision picking
|
bool precisionPicking = false; // for mouse moves we do not do precision picking
|
||||||
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
|
||||||
if (rayPickResult.intersects) {
|
if (rayPickResult.intersects) {
|
||||||
|
@ -774,15 +805,15 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
if (entityScript.property("mouseMoveEvent").isValid()) {
|
if (entityScript.property("mouseMoveEvent").isValid()) {
|
||||||
entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs);
|
entityScript.property("mouseMoveEvent").call(entityScript, entityScriptArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
//qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID;
|
//qDebug() << "mouseMoveEvent over entity:" << rayPickResult.entityID;
|
||||||
emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
emit mouseMoveOnEntity(rayPickResult.entityID, MouseEvent(*event, deviceID));
|
||||||
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
if (entityScript.property("mouseMoveOnEntity").isValid()) {
|
||||||
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
entityScript.property("mouseMoveOnEntity").call(entityScript, entityScriptArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the hover logic...
|
// handle the hover logic...
|
||||||
|
|
||||||
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
// if we were previously hovering over an entity, and this new entity is not the same as our previous entity
|
||||||
// then we need to send the hover leave.
|
// then we need to send the hover leave.
|
||||||
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
if (!_currentHoverOverEntityID.isInvalidID() && rayPickResult.entityID != _currentHoverOverEntityID) {
|
||||||
|
@ -832,7 +863,7 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
// Even if we're no longer intersecting with an entity, if we started clicking on an entity and we have
|
||||||
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
// not yet released the hold then this is still considered a holdingClickOnEntity event
|
||||||
if (!_currentClickingOnEntityID.isInvalidID()) {
|
if (!_currentClickingOnEntityID.isInvalidID()) {
|
||||||
|
@ -850,29 +881,37 @@ void EntityTreeRenderer::mouseMoveEvent(QMouseEvent* event, unsigned int deviceI
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
|
||||||
checkAndCallUnload(entityID);
|
if (_tree && !_shuttingDown) {
|
||||||
|
checkAndCallUnload(entityID);
|
||||||
|
}
|
||||||
_entityScripts.remove(entityID);
|
_entityScripts.remove(entityID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
||||||
checkAndCallUnload(entityID);
|
if (_tree && !_shuttingDown) {
|
||||||
checkAndCallPreload(entityID);
|
checkAndCallUnload(entityID);
|
||||||
|
checkAndCallPreload(entityID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
|
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
|
||||||
// load the entity script if needed...
|
if (_tree && !_shuttingDown) {
|
||||||
QScriptValue entityScript = loadEntityScript(entityID);
|
// load the entity script if needed...
|
||||||
if (entityScript.property("preload").isValid()) {
|
QScriptValue entityScript = loadEntityScript(entityID);
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
if (entityScript.property("preload").isValid()) {
|
||||||
entityScript.property("preload").call(entityScript, entityArgs);
|
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||||
|
entityScript.property("preload").call(entityScript, entityArgs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
void EntityTreeRenderer::checkAndCallUnload(const EntityItemID& entityID) {
|
||||||
QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID);
|
if (_tree && !_shuttingDown) {
|
||||||
if (entityScript.property("unload").isValid()) {
|
QScriptValue entityScript = getPreviouslyLoadedEntityScript(entityID);
|
||||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
if (entityScript.property("unload").isValid()) {
|
||||||
entityScript.property("unload").call(entityScript, entityArgs);
|
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||||
|
entityScript.property("unload").call(entityScript, entityArgs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,6 +926,11 @@ void EntityTreeRenderer::changingEntityID(const EntityItemID& oldEntityID, const
|
||||||
|
|
||||||
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,
|
void EntityTreeRenderer::entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB,
|
||||||
const Collision& collision) {
|
const Collision& collision) {
|
||||||
|
// If we don't have a tree, or we're in the process of shutting down, then don't
|
||||||
|
// process these events.
|
||||||
|
if (!_tree || _shuttingDown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
QScriptValue entityScriptA = loadEntityScript(idA);
|
QScriptValue entityScriptA = loadEntityScript(idA);
|
||||||
if (entityScriptA.property("collisionWithEntity").isValid()) {
|
if (entityScriptA.property("collisionWithEntity").isValid()) {
|
||||||
QScriptValueList args;
|
QScriptValueList args;
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
virtual int getBoundaryLevelAdjust() const;
|
virtual int getBoundaryLevelAdjust() const;
|
||||||
virtual void setTree(Octree* newTree);
|
virtual void setTree(Octree* newTree);
|
||||||
|
|
||||||
|
void shutdown();
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
EntityTree* getTree() { return static_cast<EntityTree*>(_tree); }
|
EntityTree* getTree() { return static_cast<EntityTree*>(_tree); }
|
||||||
|
@ -154,6 +155,8 @@ private:
|
||||||
bool _displayModelElementProxy;
|
bool _displayModelElementProxy;
|
||||||
bool _dontDoPrecisionPicking;
|
bool _dontDoPrecisionPicking;
|
||||||
|
|
||||||
|
bool _shuttingDown = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityTreeRenderer_h
|
#endif // hifi_EntityTreeRenderer_h
|
||||||
|
|
|
@ -1959,7 +1959,7 @@ HeightfieldNode* HeightfieldNode::fillHeight(const glm::vec3& translation, const
|
||||||
} else {
|
} else {
|
||||||
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
|
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
|
||||||
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
|
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
|
||||||
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
|
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
|
||||||
}
|
}
|
||||||
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
|
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
|
||||||
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
|
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
|
||||||
|
@ -2185,7 +2185,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
||||||
} else {
|
} else {
|
||||||
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
|
colorWidth = innerHeightWidth + HeightfieldData::SHARED_EDGE;
|
||||||
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
|
colorHeight = innerHeightHeight + HeightfieldData::SHARED_EDGE;
|
||||||
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
|
newColorContents = QByteArray(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
|
||||||
}
|
}
|
||||||
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
|
int innerColorWidth = colorWidth - HeightfieldData::SHARED_EDGE;
|
||||||
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
|
int innerColorHeight = colorHeight - HeightfieldData::SHARED_EDGE;
|
||||||
|
@ -2428,7 +2428,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight);
|
int nextAlphaY = stackDest->getEntryAlpha(y + 1, voxelHeight);
|
||||||
if (nextAlphaY == currentAlpha) {
|
if (nextAlphaY == currentAlpha) {
|
||||||
entryDest->hermiteY = 0;
|
entryDest->hermiteY = 0;
|
||||||
|
|
||||||
|
@ -2447,7 +2447,7 @@ HeightfieldNode* HeightfieldNode::setMaterial(const glm::vec3& translation, cons
|
||||||
}
|
}
|
||||||
int nextStackZ = (int)stackZ + 1;
|
int nextStackZ = (int)stackZ + 1;
|
||||||
if (nextStackZ <= innerStackHeight) {
|
if (nextStackZ <= innerStackHeight) {
|
||||||
bool nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha(
|
int nextAlphaZ = newStackContents.at(nextStackZ * stackWidth + (int)stackX).getEntryAlpha(
|
||||||
y, nextVoxelHeightZ);
|
y, nextVoxelHeightZ);
|
||||||
if (nextAlphaZ == currentAlpha) {
|
if (nextAlphaZ == currentAlpha) {
|
||||||
entryDest->hermiteZ = 0;
|
entryDest->hermiteZ = 0;
|
||||||
|
@ -2828,7 +2828,7 @@ void HeightfieldNode::mergeChildren(bool height, bool colorMaterial) {
|
||||||
_height.reset();
|
_height.reset();
|
||||||
}
|
}
|
||||||
if (colorWidth > 0 && colorMaterial) {
|
if (colorWidth > 0 && colorMaterial) {
|
||||||
QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
|
QByteArray colorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
|
||||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||||
HeightfieldColorPointer childColor = _children[i]->getColor();
|
HeightfieldColorPointer childColor = _children[i]->getColor();
|
||||||
if (!childColor) {
|
if (!childColor) {
|
||||||
|
@ -3266,7 +3266,7 @@ HeightfieldNode* HeightfieldNode::subdivide(const QVector<quint16>& heightConten
|
||||||
}
|
}
|
||||||
for (int i = 0; i < CHILD_COUNT; i++) {
|
for (int i = 0; i < CHILD_COUNT; i++) {
|
||||||
QVector<quint16> childHeightContents(heightWidth * heightHeight);
|
QVector<quint16> childHeightContents(heightWidth * heightHeight);
|
||||||
QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFF);
|
QByteArray childColorContents(colorWidth * colorHeight * DataBlock::COLOR_BYTES, 0xFFu);
|
||||||
QByteArray childMaterialContents(materialWidth * materialHeight, 0);
|
QByteArray childMaterialContents(materialWidth * materialHeight, 0);
|
||||||
QVector<StackArray> childStackContents(stackWidth * stackHeight);
|
QVector<StackArray> childStackContents(stackWidth * stackHeight);
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
|
|
||||||
#include "MIDIEvent.h"
|
#include "MIDIEvent.h"
|
||||||
|
|
||||||
|
|
||||||
EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
|
EntityScriptingInterface ScriptEngine::_entityScriptingInterface;
|
||||||
|
|
||||||
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
|
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){
|
||||||
|
@ -94,8 +95,109 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam
|
||||||
_isUserLoaded(false),
|
_isUserLoaded(false),
|
||||||
_arrayBufferClass(new ArrayBufferClass(this))
|
_arrayBufferClass(new ArrayBufferClass(this))
|
||||||
{
|
{
|
||||||
|
_allScriptsMutex.lock();
|
||||||
|
_allKnownScriptEngines.insert(this);
|
||||||
|
_allScriptsMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScriptEngine::~ScriptEngine() {
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
|
||||||
|
QMutex ScriptEngine::_allScriptsMutex;
|
||||||
|
bool ScriptEngine::_stoppingAllScripts = false;
|
||||||
|
bool ScriptEngine::_doneRunningThisScript = false;
|
||||||
|
|
||||||
|
void ScriptEngine::stopAllScripts(QObject* application) {
|
||||||
|
_allScriptsMutex.lock();
|
||||||
|
_stoppingAllScripts = true;
|
||||||
|
|
||||||
|
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
|
||||||
|
scriptEngine->waitTillDoneRunning();
|
||||||
|
|
||||||
|
// If the script is stopped, we can remove it from our set
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_stoppingAllScripts = false;
|
||||||
|
_allScriptsMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ScriptEngine::waitTillDoneRunning() {
|
||||||
|
QString scriptName = getFilename();
|
||||||
|
|
||||||
|
// If the script never started running or finished running before we got here, we don't need to wait for it
|
||||||
|
if (_isRunning) {
|
||||||
|
|
||||||
|
_doneRunningThisScript = false; // NOTE: this is static, we serialize our waiting for scripts to finish
|
||||||
|
|
||||||
|
// NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts()
|
||||||
|
// we want the application thread to continue to process events, because the scripts will likely need to
|
||||||
|
// marshall messages across to the main thread. For example if they access Settings or Meny in any of their
|
||||||
|
// shutdown code.
|
||||||
|
while (!_doneRunningThisScript) {
|
||||||
|
|
||||||
|
// process events for the main application thread, allowing invokeMethod calls to pass between threads
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScriptEngine::getFilename() const {
|
||||||
|
QStringList fileNameParts = _fileNameString.split("/");
|
||||||
|
QString lastPart;
|
||||||
|
if (!fileNameParts.isEmpty()) {
|
||||||
|
lastPart = fileNameParts.last();
|
||||||
|
}
|
||||||
|
return lastPart;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ScriptEngine::setIsAvatar(bool isAvatar) {
|
void ScriptEngine::setIsAvatar(bool isAvatar) {
|
||||||
_isAvatar = isAvatar;
|
_isAvatar = isAvatar;
|
||||||
|
|
||||||
|
@ -295,12 +397,18 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::evaluate() {
|
void ScriptEngine::evaluate() {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
return; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
if (!_isInitialized) {
|
if (!_isInitialized) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue result = evaluate(_scriptContents);
|
QScriptValue result = evaluate(_scriptContents);
|
||||||
|
|
||||||
|
// TODO: why do we check this twice? It seems like the call to clearExcpetions() in the lower level evaluate call
|
||||||
|
// will cause this code to never actually run...
|
||||||
if (hasUncaughtException()) {
|
if (hasUncaughtException()) {
|
||||||
int line = uncaughtExceptionLineNumber();
|
int line = uncaughtExceptionLineNumber();
|
||||||
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
|
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
|
||||||
|
@ -310,11 +418,17 @@ void ScriptEngine::evaluate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
|
QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
return QScriptValue(); // bail early
|
||||||
|
}
|
||||||
|
|
||||||
|
_evaluatesPending++;
|
||||||
QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
|
QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber);
|
||||||
if (hasUncaughtException()) {
|
if (hasUncaughtException()) {
|
||||||
int line = uncaughtExceptionLineNumber();
|
int line = uncaughtExceptionLineNumber();
|
||||||
qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
|
qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString();
|
||||||
}
|
}
|
||||||
|
_evaluatesPending--;
|
||||||
emit evaluationFinished(result, hasUncaughtException());
|
emit evaluationFinished(result, hasUncaughtException());
|
||||||
clearExceptions();
|
clearExceptions();
|
||||||
return result;
|
return result;
|
||||||
|
@ -333,6 +447,9 @@ void ScriptEngine::sendAvatarBillboardPacket() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::run() {
|
void ScriptEngine::run() {
|
||||||
|
// TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if
|
||||||
|
// we're in the process of stopping?
|
||||||
|
|
||||||
if (!_isInitialized) {
|
if (!_isInitialized) {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
@ -341,12 +458,6 @@ void ScriptEngine::run() {
|
||||||
emit runningStateChanged();
|
emit runningStateChanged();
|
||||||
|
|
||||||
QScriptValue result = evaluate(_scriptContents);
|
QScriptValue result = evaluate(_scriptContents);
|
||||||
if (hasUncaughtException()) {
|
|
||||||
int line = uncaughtExceptionLineNumber();
|
|
||||||
qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString();
|
|
||||||
emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString());
|
|
||||||
clearExceptions();
|
|
||||||
}
|
|
||||||
|
|
||||||
QElapsedTimer startTime;
|
QElapsedTimer startTime;
|
||||||
startTime.start();
|
startTime.start();
|
||||||
|
@ -373,7 +484,7 @@ void ScriptEngine::run() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_entityScriptingInterface.getEntityPacketSender()->serversExist()) {
|
if (!_isFinished && _entityScriptingInterface.getEntityPacketSender()->serversExist()) {
|
||||||
// release the queue of edit entity messages.
|
// release the queue of edit entity messages.
|
||||||
_entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages();
|
_entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages();
|
||||||
|
|
||||||
|
@ -383,7 +494,7 @@ void ScriptEngine::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_isAvatar && _avatarData) {
|
if (!_isFinished && _isAvatar && _avatarData) {
|
||||||
|
|
||||||
const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE)
|
const int SCRIPT_AUDIO_BUFFER_SAMPLES = floor(((SCRIPT_DATA_CALLBACK_USECS * AudioConstants::SAMPLE_RATE)
|
||||||
/ (1000 * 1000)) + 0.5);
|
/ (1000 * 1000)) + 0.5);
|
||||||
|
@ -493,9 +604,14 @@ void ScriptEngine::run() {
|
||||||
clearExceptions();
|
clearExceptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit update(deltaTime);
|
if (!_isFinished) {
|
||||||
|
emit update(deltaTime);
|
||||||
|
}
|
||||||
lastUpdate = now;
|
lastUpdate = now;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stopAllTimers(); // make sure all our timers are stopped if the script is ending
|
||||||
emit scriptEnding();
|
emit scriptEnding();
|
||||||
|
|
||||||
// kill the avatar identity timer
|
// kill the avatar identity timer
|
||||||
|
@ -520,6 +636,21 @@ void ScriptEngine::run() {
|
||||||
|
|
||||||
_isRunning = false;
|
_isRunning = false;
|
||||||
emit runningStateChanged();
|
emit runningStateChanged();
|
||||||
|
|
||||||
|
emit doneRunning();
|
||||||
|
|
||||||
|
_doneRunningThisScript = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is private because it must be called on the same thread that created the timers, which is why
|
||||||
|
// we want to only call it in our own run "shutdown" processing.
|
||||||
|
void ScriptEngine::stopAllTimers() {
|
||||||
|
QMutableHashIterator<QTimer*, QScriptValue> i(_timerFunctionMap);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
QTimer* timer = i.key();
|
||||||
|
stopTimer(timer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::stop() {
|
void ScriptEngine::stop() {
|
||||||
|
@ -560,10 +691,20 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
|
QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
qDebug() << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();
|
||||||
|
return NULL; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
return setupTimerWithInterval(function, intervalMS, false);
|
return setupTimerWithInterval(function, intervalMS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
|
QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
qDebug() << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();
|
||||||
|
return NULL; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
return setupTimerWithInterval(function, timeoutMS, true);
|
return setupTimerWithInterval(function, timeoutMS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,13 +745,16 @@ void ScriptEngine::print(const QString& message) {
|
||||||
emit printedMessage(message);
|
emit printedMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// If a callback is specified, the included files will be loaded asynchronously and the callback will be called
|
||||||
* If a callback is specified, the included files will be loaded asynchronously and the callback will be called
|
// when all of the files have finished loading.
|
||||||
* when all of the files have finished loading.
|
// If no callback is specified, the included files will be loaded synchronously and will block execution until
|
||||||
* If no callback is specified, the included files will be loaded synchronously and will block execution until
|
// all of the files have finished loading.
|
||||||
* all of the files have finished loading.
|
|
||||||
*/
|
|
||||||
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
|
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
qDebug() << "Script.include() while shutting down is ignored..."
|
||||||
|
<< "includeFiles:" << includeFiles << "parent script:" << getFilename();
|
||||||
|
return; // bail early
|
||||||
|
}
|
||||||
QList<QUrl> urls;
|
QList<QUrl> urls;
|
||||||
for (QString file : includeFiles) {
|
for (QString file : includeFiles) {
|
||||||
urls.append(resolvePath(file));
|
urls.append(resolvePath(file));
|
||||||
|
@ -650,12 +794,27 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
|
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
qDebug() << "Script.include() while shutting down is ignored... "
|
||||||
|
<< "includeFile:" << includeFile << "parent script:" << getFilename();
|
||||||
|
return; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
QStringList urls;
|
QStringList urls;
|
||||||
urls.append(includeFile);
|
urls.append(includeFile);
|
||||||
include(urls, callback);
|
include(urls, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: The load() command is similar to the include() command except that it loads the script
|
||||||
|
// as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which
|
||||||
|
// the Application or other context will connect to in order to know to actually load the script
|
||||||
void ScriptEngine::load(const QString& loadFile) {
|
void ScriptEngine::load(const QString& loadFile) {
|
||||||
|
if (_stoppingAllScripts) {
|
||||||
|
qDebug() << "Script.load() while shutting down is ignored... "
|
||||||
|
<< "loadFile:" << loadFile << "parent script:" << getFilename();
|
||||||
|
return; // bail early
|
||||||
|
}
|
||||||
|
|
||||||
QUrl url = resolvePath(loadFile);
|
QUrl url = resolvePath(loadFile);
|
||||||
emit loadScript(url.toString(), false);
|
emit loadScript(url.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QUrl>
|
#include <QtCore/QUrl>
|
||||||
|
#include <QtCore/QWaitCondition>
|
||||||
#include <QtScript/QScriptEngine>
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
#include <AnimationCache.h>
|
#include <AnimationCache.h>
|
||||||
|
@ -43,6 +44,8 @@ public:
|
||||||
const QString& fileNameString = QString(""),
|
const QString& fileNameString = QString(""),
|
||||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||||
|
|
||||||
|
~ScriptEngine();
|
||||||
|
|
||||||
/// Access the EntityScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
/// Access the EntityScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||||
static EntityScriptingInterface* getEntityScriptingInterface() { return &_entityScriptingInterface; }
|
static EntityScriptingInterface* getEntityScriptingInterface() { return &_entityScriptingInterface; }
|
||||||
|
|
||||||
|
@ -83,11 +86,18 @@ public:
|
||||||
|
|
||||||
bool isFinished() const { return _isFinished; }
|
bool isFinished() const { return _isFinished; }
|
||||||
bool isRunning() const { return _isRunning; }
|
bool isRunning() const { return _isRunning; }
|
||||||
|
bool evaluatePending() const { return _evaluatesPending > 0; }
|
||||||
|
|
||||||
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
|
void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; }
|
||||||
bool isUserLoaded() const { return _isUserLoaded; }
|
bool isUserLoaded() const { return _isUserLoaded; }
|
||||||
|
|
||||||
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
void setParentURL(const QString& parentURL) { _parentURL = parentURL; }
|
||||||
|
|
||||||
|
QString getFilename() const;
|
||||||
|
|
||||||
|
static void stopAllScripts(QObject* application);
|
||||||
|
|
||||||
|
void waitTillDoneRunning();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void loadURL(const QUrl& scriptURL);
|
void loadURL(const QUrl& scriptURL);
|
||||||
|
@ -118,12 +128,14 @@ signals:
|
||||||
void runningStateChanged();
|
void runningStateChanged();
|
||||||
void evaluationFinished(QScriptValue result, bool isException);
|
void evaluationFinished(QScriptValue result, bool isException);
|
||||||
void loadScript(const QString& scriptName, bool isUserLoaded);
|
void loadScript(const QString& scriptName, bool isUserLoaded);
|
||||||
|
void doneRunning();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
QString _parentURL;
|
QString _parentURL;
|
||||||
bool _isFinished;
|
bool _isFinished;
|
||||||
bool _isRunning;
|
bool _isRunning;
|
||||||
|
int _evaluatesPending = 0;
|
||||||
bool _isInitialized;
|
bool _isInitialized;
|
||||||
bool _isAvatar;
|
bool _isAvatar;
|
||||||
QTimer* _avatarIdentityTimer;
|
QTimer* _avatarIdentityTimer;
|
||||||
|
@ -134,6 +146,7 @@ protected:
|
||||||
int _numAvatarSoundSentBytes;
|
int _numAvatarSoundSentBytes;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void stopAllTimers();
|
||||||
void sendAvatarIdentityPacket();
|
void sendAvatarIdentityPacket();
|
||||||
void sendAvatarBillboardPacket();
|
void sendAvatarBillboardPacket();
|
||||||
|
|
||||||
|
@ -156,6 +169,12 @@ private:
|
||||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||||
private slots:
|
private slots:
|
||||||
void handleScriptDownload();
|
void handleScriptDownload();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||||
|
static QMutex _allScriptsMutex;
|
||||||
|
static bool _stoppingAllScripts;
|
||||||
|
static bool _doneRunningThisScript;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_ScriptEngine_h
|
#endif // hifi_ScriptEngine_h
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "SharedUtil.h"
|
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
|
#include "PlaneShape.h"
|
||||||
|
#include "RayIntersectionInfo.h"
|
||||||
|
#include "SharedUtil.h"
|
||||||
|
|
||||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||||
// compute the projection of the point vector onto the segment vector
|
// compute the projection of the point vector onto the segment vector
|
||||||
|
@ -491,3 +493,34 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions) {
|
||||||
|
RayIntersectionInfo rayInfo;
|
||||||
|
rayInfo._rayStart = origin;
|
||||||
|
rayInfo._rayDirection = direction;
|
||||||
|
rayInfo._rayLength = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
PlaneShape plane;
|
||||||
|
|
||||||
|
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
||||||
|
glm::vec3 normal = rotation * UNROTATED_NORMAL;
|
||||||
|
plane.setNormal(normal);
|
||||||
|
plane.setPoint(position); // the position is definitely a point on our plane
|
||||||
|
|
||||||
|
bool intersects = plane.findRayIntersection(rayInfo);
|
||||||
|
|
||||||
|
if (intersects) {
|
||||||
|
float distance = rayInfo._hitDistance;
|
||||||
|
|
||||||
|
glm::vec3 hitPosition = origin + (distance * direction);
|
||||||
|
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
|
||||||
|
|
||||||
|
glm::vec2 halfDimensions = 0.5f * dimensions;
|
||||||
|
|
||||||
|
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
|
||||||
|
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intersects;
|
||||||
|
}
|
||||||
|
|
|
@ -76,6 +76,9 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi
|
||||||
bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool findRayCapsuleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
const glm::vec3& start, const glm::vec3& end, float radius, float& distance);
|
const glm::vec3& start, const glm::vec3& end, float radius, float& distance);
|
||||||
|
|
||||||
|
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
|
const glm::quat& rotation, const glm::vec3& position, const glm::vec2& dimensions);
|
||||||
|
|
||||||
bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance);
|
const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance);
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#ifdef _WINDOWS
|
#ifdef _WINDOWS
|
||||||
#include <winsock2.h>
|
#include <WS2tcpip.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
@ -76,7 +76,7 @@ void runSend(const char* addressOption, int port, int gap, int size, int report)
|
||||||
|
|
||||||
memset(&servaddr, 0, sizeof(servaddr));
|
memset(&servaddr, 0, sizeof(servaddr));
|
||||||
servaddr.sin_family = AF_INET;
|
servaddr.sin_family = AF_INET;
|
||||||
servaddr.sin_addr.s_addr = inet_addr(addressOption);
|
inet_pton(AF_INET, addressOption, &servaddr.sin_addr);
|
||||||
servaddr.sin_port = htons(port);
|
servaddr.sin_port = htons(port);
|
||||||
|
|
||||||
const int SAMPLES_FOR_SECOND = 1000000 / gap;
|
const int SAMPLES_FOR_SECOND = 1000000 / gap;
|
||||||
|
|
Loading…
Reference in a new issue