mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 18:23:54 +02:00
Merge pull request #2480 from ZappoMan/scripting_improvements
Scripting improvements
This commit is contained in:
commit
5551396a7b
9 changed files with 214 additions and 40 deletions
|
@ -3,3 +3,4 @@
|
|||
Script.include("lookWithTouch.js");
|
||||
Script.include("editVoxels.js");
|
||||
Script.include("selectAudioDevice.js");
|
||||
Script.include("hydraMove.js");
|
|
@ -400,9 +400,9 @@ function calcScaleFromThumb(newThumbX) {
|
|||
}
|
||||
|
||||
function setAudioPosition() {
|
||||
var camera = Camera.getPosition();
|
||||
var position = MyAvatar.position;
|
||||
var forwardVector = Quat.getFront(MyAvatar.orientation);
|
||||
audioOptions.position = Vec3.sum(camera, forwardVector);
|
||||
audioOptions.position = Vec3.sum(position, forwardVector);
|
||||
}
|
||||
|
||||
function getNewPasteVoxel(pickRay) {
|
||||
|
@ -735,6 +735,7 @@ function trackKeyReleaseEvent(event) {
|
|||
if (event.text == "TAB") {
|
||||
editToolsOn = !editToolsOn;
|
||||
moveTools();
|
||||
setAudioPosition(); // make sure we set the audio position before playing sounds
|
||||
showPreviewGuides();
|
||||
Audio.playSound(clickSound, audioOptions);
|
||||
}
|
||||
|
|
16
examples/includeExample.js
Normal file
16
examples/includeExample.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// includeExample.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 3/24/14
|
||||
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Script.include() feature
|
||||
//
|
||||
|
||||
// You can include scripts from URLs
|
||||
Script.include("http://public.highfidelity.io/scripts/lookWithTouch.js");
|
||||
|
||||
// You can also include scripts that are relative to the current script
|
||||
Script.include("editVoxels.js");
|
||||
Script.include("../examples/selectAudioDevice.js");
|
|
@ -28,6 +28,7 @@
|
|||
#include <QDesktopWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QImage>
|
||||
#include <QInputDialog>
|
||||
#include <QKeyEvent>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
|
@ -250,7 +251,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
|
||||
|
||||
_settings = new QSettings(this);
|
||||
|
||||
|
||||
// Check to see if the user passed in a command line option for loading a local
|
||||
// Voxel File.
|
||||
_voxelsFilename = getCmdOption(argc, constArgv, "-i");
|
||||
|
@ -329,9 +330,20 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
|
||||
LocalVoxelsList::getInstance()->addPersistantTree(DOMAIN_TREE_NAME, _voxels.getTree());
|
||||
LocalVoxelsList::getInstance()->addPersistantTree(CLIPBOARD_TREE_NAME, &_clipboard);
|
||||
|
||||
// do this as late as possible so that all required subsystems are inialized
|
||||
loadScripts();
|
||||
|
||||
// check first run...
|
||||
QVariant firstRunValue = _settings->value("firstRun",QVariant(true));
|
||||
if (firstRunValue.isValid() && firstRunValue.toBool()) {
|
||||
qDebug() << "This is a first run...";
|
||||
// clear the scripts, and set out script to our default scripts
|
||||
clearScriptsBeforeRunning();
|
||||
loadScript("http://public.highfidelity.io/scripts/defaultScripts.js");
|
||||
|
||||
_settings->setValue("firstRun",QVariant(false));
|
||||
} else {
|
||||
// do this as late as possible so that all required subsystems are inialized
|
||||
loadScripts();
|
||||
}
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
@ -3453,6 +3465,13 @@ void Application::loadScripts() {
|
|||
settings->endArray();
|
||||
}
|
||||
|
||||
void Application::clearScriptsBeforeRunning() {
|
||||
// clears all scripts from the settings
|
||||
QSettings* settings = new QSettings(this);
|
||||
settings->beginWriteArray("Settings");
|
||||
settings->endArray();
|
||||
}
|
||||
|
||||
void Application::saveScripts() {
|
||||
// saves all current running scripts
|
||||
QSettings* settings = new QSettings(this);
|
||||
|
@ -3508,35 +3527,17 @@ void Application::cleanupScriptMenuItem(const QString& scriptMenuName) {
|
|||
Menu::getInstance()->removeAction(Menu::getInstance()->getActiveScriptsMenu(), scriptMenuName);
|
||||
}
|
||||
|
||||
void Application::loadScript(const QString& fileNameString) {
|
||||
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
|
||||
const char* fileName = fileNameAscii.data();
|
||||
|
||||
std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate);
|
||||
if(!file.is_open()) {
|
||||
qDebug("Error loading file %s", fileName);
|
||||
return;
|
||||
}
|
||||
qDebug("Loading file %s...", fileName);
|
||||
_activeScripts.append(fileNameString);
|
||||
|
||||
// get file length....
|
||||
unsigned long fileLength = file.tellg();
|
||||
file.seekg( 0, std::ios::beg );
|
||||
|
||||
// read the entire file into a buffer, WHAT!? Why not.
|
||||
char* entireFile = new char[fileLength+1];
|
||||
file.read((char*)entireFile, fileLength);
|
||||
file.close();
|
||||
|
||||
entireFile[fileLength] = 0;// null terminate
|
||||
QString script(entireFile);
|
||||
delete[] entireFile;
|
||||
void Application::loadScript(const QString& scriptName) {
|
||||
|
||||
// start the script on a new thread...
|
||||
bool wantMenuItems = true; // tells the ScriptEngine object to add menu items for itself
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), wantMenuItems, &_controllerScriptingInterface);
|
||||
|
||||
ScriptEngine* scriptEngine = new ScriptEngine(script, wantMenuItems, fileName, &_controllerScriptingInterface);
|
||||
if (!scriptEngine->hasScript()) {
|
||||
qDebug() << "Application::loadScript(), script failed to load...";
|
||||
return;
|
||||
}
|
||||
_activeScripts.append(scriptName);
|
||||
|
||||
// add a stop menu item
|
||||
Menu::getInstance()->addActionToQMenuAndActionHash(Menu::getInstance()->getActiveScriptsMenu(),
|
||||
|
@ -3599,6 +3600,31 @@ void Application::loadDialog() {
|
|||
loadScript(fileNameString);
|
||||
}
|
||||
|
||||
void Application::loadScriptURLDialog() {
|
||||
|
||||
QInputDialog scriptURLDialog(Application::getInstance()->getWindow());
|
||||
scriptURLDialog.setWindowTitle("Open and Run Script URL");
|
||||
scriptURLDialog.setLabelText("Script:");
|
||||
scriptURLDialog.setWindowFlags(Qt::Sheet);
|
||||
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
|
||||
scriptURLDialog.resize(scriptURLDialog.parentWidget()->size().width() * DIALOG_RATIO_OF_WINDOW,
|
||||
scriptURLDialog.size().height());
|
||||
|
||||
int dialogReturn = scriptURLDialog.exec();
|
||||
QString newScript;
|
||||
if (dialogReturn == QDialog::Accepted) {
|
||||
if (scriptURLDialog.textValue().size() > 0) {
|
||||
// the user input a new hostname, use that
|
||||
newScript = scriptURLDialog.textValue();
|
||||
}
|
||||
loadScript(newScript);
|
||||
}
|
||||
|
||||
sendFakeEnterEvent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Application::toggleLogDialog() {
|
||||
if (! _logDialog) {
|
||||
_logDialog = new LogDialog(_glWidget, getLogger());
|
||||
|
|
|
@ -115,6 +115,7 @@ public:
|
|||
void loadScript(const QString& fileNameString);
|
||||
void loadScripts();
|
||||
void storeSizeAndPosition();
|
||||
void clearScriptsBeforeRunning();
|
||||
void saveScripts();
|
||||
void initializeGL();
|
||||
void paintGL();
|
||||
|
@ -254,6 +255,7 @@ public slots:
|
|||
void setRenderVoxels(bool renderVoxels);
|
||||
void doKillLocalVoxels();
|
||||
void loadDialog();
|
||||
void loadScriptURLDialog();
|
||||
void toggleLogDialog();
|
||||
void initAvatarAndViewFrustum();
|
||||
void stopAllScripts();
|
||||
|
|
|
@ -110,6 +110,8 @@ Menu::Menu() :
|
|||
|
||||
addDisabledActionAndSeparator(fileMenu, "Scripts");
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL,
|
||||
Qt::CTRL | Qt::SHIFT | Qt::Key_O, appInstance, SLOT(loadScriptURLDialog()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts()));
|
||||
addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts()));
|
||||
_activeScriptsMenu = fileMenu->addMenu("Running Scripts");
|
||||
|
|
|
@ -274,7 +274,8 @@ namespace MenuOption {
|
|||
const QString OffAxisProjection = "Off-Axis Projection";
|
||||
const QString OldVoxelCullingMode = "Old Voxel Culling Mode";
|
||||
const QString TurnWithHead = "Turn using Head";
|
||||
const QString LoadScript = "Open and Run Script...";
|
||||
const QString LoadScript = "Open and Run Script File...";
|
||||
const QString LoadScriptURL = "Open and Run Script from URL...";
|
||||
const QString Oscilloscope = "Audio Oscilloscope";
|
||||
const QString Pair = "Pair";
|
||||
const QString Particles = "Particles";
|
||||
|
@ -306,4 +307,6 @@ namespace MenuOption {
|
|||
const QString VoxelTextures = "Voxel Textures";
|
||||
}
|
||||
|
||||
void sendFakeEnterEvent();
|
||||
|
||||
#endif /* defined(__hifi__Menu__) */
|
||||
|
|
|
@ -64,13 +64,10 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co
|
|||
_quatLibrary(),
|
||||
_vec3Library()
|
||||
{
|
||||
QByteArray fileNameAscii = fileNameString.toLocal8Bit();
|
||||
const char* scriptMenuName = fileNameAscii.data();
|
||||
|
||||
// some clients will use these menu features
|
||||
if (!fileNameString.isEmpty()) {
|
||||
_scriptMenuName = "Stop ";
|
||||
_scriptMenuName.append(scriptMenuName);
|
||||
_scriptMenuName.append(qPrintable(fileNameString));
|
||||
_scriptMenuName.append(QString(" [%1]").arg(_scriptNumber));
|
||||
} else {
|
||||
_scriptMenuName = "Stop Script ";
|
||||
|
@ -79,6 +76,72 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, bool wantMenuItems, co
|
|||
_scriptNumber++;
|
||||
}
|
||||
|
||||
ScriptEngine::ScriptEngine(const QUrl& scriptURL, bool wantMenuItems,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface) :
|
||||
_scriptContents(),
|
||||
_isFinished(false),
|
||||
_isRunning(false),
|
||||
_isInitialized(false),
|
||||
_engine(),
|
||||
_isAvatar(false),
|
||||
_avatarIdentityTimer(NULL),
|
||||
_avatarBillboardTimer(NULL),
|
||||
_timerFunctionMap(),
|
||||
_isListeningToAudioStream(false),
|
||||
_avatarSound(NULL),
|
||||
_numAvatarSoundSentBytes(0),
|
||||
_controllerScriptingInterface(controllerScriptingInterface),
|
||||
_avatarData(NULL),
|
||||
_wantMenuItems(wantMenuItems),
|
||||
_scriptMenuName(),
|
||||
_fileNameString(),
|
||||
_quatLibrary(),
|
||||
_vec3Library()
|
||||
{
|
||||
QString scriptURLString = scriptURL.toString();
|
||||
_fileNameString = scriptURLString;
|
||||
// some clients will use these menu features
|
||||
if (!scriptURLString.isEmpty()) {
|
||||
_scriptMenuName = "Stop ";
|
||||
_scriptMenuName.append(qPrintable(scriptURLString));
|
||||
_scriptMenuName.append(QString(" [%1]").arg(_scriptNumber));
|
||||
} else {
|
||||
_scriptMenuName = "Stop Script ";
|
||||
_scriptMenuName.append(_scriptNumber);
|
||||
}
|
||||
_scriptNumber++;
|
||||
|
||||
QUrl url(scriptURL);
|
||||
|
||||
// if the scheme is empty, maybe they typed in a file, let's try
|
||||
if (url.scheme().isEmpty()) {
|
||||
url = QUrl::fromLocalFile(scriptURLString);
|
||||
}
|
||||
|
||||
// ok, let's see if it's valid... and if so, load it
|
||||
if (url.isValid()) {
|
||||
if (url.scheme() == "file") {
|
||||
QString fileName = url.toLocalFile();
|
||||
QFile scriptFile(fileName);
|
||||
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||
qDebug() << "Loading file:" << fileName;
|
||||
QTextStream in(&scriptFile);
|
||||
_scriptContents = in.readAll();
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << fileName;
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << url;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
_scriptContents = reply->readAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::setIsAvatar(bool isAvatar) {
|
||||
_isAvatar = isAvatar;
|
||||
|
||||
|
@ -113,11 +176,12 @@ void ScriptEngine::cleanupMenuItems() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ScriptEngine::setScriptContents(const QString& scriptContents) {
|
||||
bool ScriptEngine::setScriptContents(const QString& scriptContents, const QString& fileNameString) {
|
||||
if (_isRunning) {
|
||||
return false;
|
||||
}
|
||||
_scriptContents = scriptContents;
|
||||
_fileNameString = fileNameString;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -436,3 +500,55 @@ void ScriptEngine::stopTimer(QTimer *timer) {
|
|||
delete timer;
|
||||
}
|
||||
}
|
||||
|
||||
QUrl ScriptEngine::resolveInclude(const QString& include) const {
|
||||
// first lets check to see if it's already a full URL
|
||||
QUrl url(include);
|
||||
if (!url.scheme().isEmpty()) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// we apparently weren't a fully qualified url, so, let's assume we're relative
|
||||
// to the original URL of our script
|
||||
QUrl parentURL(_fileNameString);
|
||||
|
||||
// if the parent URL's scheme is empty, then this is probably a local file...
|
||||
if (parentURL.scheme().isEmpty()) {
|
||||
parentURL = QUrl::fromLocalFile(_fileNameString);
|
||||
}
|
||||
|
||||
// at this point we should have a legitimate fully qualified URL for our parent
|
||||
url = parentURL.resolved(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
void ScriptEngine::include(const QString& includeFile) {
|
||||
QUrl url = resolveInclude(includeFile);
|
||||
QString includeContents;
|
||||
|
||||
if (url.scheme() == "file") {
|
||||
QString fileName = url.toLocalFile();
|
||||
QFile scriptFile(fileName);
|
||||
if (scriptFile.open(QFile::ReadOnly | QFile::Text)) {
|
||||
qDebug() << "Loading file:" << fileName;
|
||||
QTextStream in(&scriptFile);
|
||||
includeContents = in.readAll();
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << fileName;
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager* networkManager = new QNetworkAccessManager(this);
|
||||
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
|
||||
qDebug() << "Downloading included script at" << includeFile;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
includeContents = reply->readAll();
|
||||
}
|
||||
|
||||
QScriptValue result = _engine.evaluate(includeContents);
|
||||
if (_engine.hasUncaughtException()) {
|
||||
int line = _engine.uncaughtExceptionLineNumber();
|
||||
qDebug() << "Uncaught exception at (" << includeFile << ") line" << line << ":" << result.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,11 @@ const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 10
|
|||
class ScriptEngine : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
||||
const QString& scriptMenuName = QString(""),
|
||||
ScriptEngine(const QUrl& scriptURL, bool wantMenuItems = false,
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||
|
||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT, bool wantMenuItems = false,
|
||||
const QString& fileNameString = QString(""),
|
||||
AbstractControllerScriptingInterface* controllerScriptingInterface = NULL);
|
||||
|
||||
/// Access the VoxelsScriptingInterface in order to initialize it with a custom packet sender and jurisdiction listener
|
||||
|
@ -44,7 +47,7 @@ public:
|
|||
static ParticlesScriptingInterface* getParticlesScriptingInterface() { return &_particlesScriptingInterface; }
|
||||
|
||||
/// sets the script contents, will return false if failed, will fail if script is already running
|
||||
bool setScriptContents(const QString& scriptContents);
|
||||
bool setScriptContents(const QString& scriptContents, const QString& fileNameString = QString(""));
|
||||
|
||||
const QString& getScriptMenuName() const { return _scriptMenuName; }
|
||||
void cleanupMenuItems();
|
||||
|
@ -68,6 +71,8 @@ public:
|
|||
|
||||
void timerFired();
|
||||
|
||||
bool hasScript() const { return !_scriptContents.isEmpty(); }
|
||||
|
||||
public slots:
|
||||
void stop();
|
||||
|
||||
|
@ -75,6 +80,7 @@ public slots:
|
|||
QObject* setTimeout(const QScriptValue& function, int timeoutMS);
|
||||
void clearInterval(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
|
||||
void clearTimeout(QObject* timer) { stopTimer(reinterpret_cast<QTimer*>(timer)); }
|
||||
void include(const QString& includeFile);
|
||||
|
||||
signals:
|
||||
void update(float deltaTime);
|
||||
|
@ -97,6 +103,7 @@ protected:
|
|||
int _numAvatarSoundSentBytes;
|
||||
|
||||
private:
|
||||
QUrl resolveInclude(const QString& include) const;
|
||||
void sendAvatarIdentityPacket();
|
||||
void sendAvatarBillboardPacket();
|
||||
|
||||
|
|
Loading…
Reference in a new issue