diff --git a/examples/edit.js b/examples/edit.js index 92e4737f29..23cc942192 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -1321,6 +1321,15 @@ PropertiesTool = function(opts) { pushCommandForSelections(); selectionManager._update(); } + } else if (data.action == "reloadScript") { + if (selectionManager.hasSelection()) { + var timestamp = Date.now(); + for (var i = 0; i < selectionManager.selections.length; i++) { + Entities.editEntity(selectionManager.selections[i], { + scriptTimestamp: timestamp, + }); + } + } } else if (data.action == "centerAtmosphereToZone") { if (selectionManager.hasSelection()) { selectionManager.saveProperties(); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index c974845585..8a15147f16 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -250,6 +250,8 @@ var elCollisionSoundURL = document.getElementById("property-collision-sound-url"); var elLifetime = document.getElementById("property-lifetime"); var elScriptURL = document.getElementById("property-script-url"); + var elScriptTimestamp = document.getElementById("property-script-timestamp"); + var elReloadScriptButton = document.getElementById("reload-script-button"); var elUserData = document.getElementById("property-user-data"); var elColorSection = document.getElementById("color-section"); @@ -470,6 +472,7 @@ elCollisionSoundURL.value = properties.collisionSoundURL; elLifetime.value = properties.lifetime; elScriptURL.value = properties.script; + elScriptTimestamp.value = properties.scriptTimestamp; elUserData.value = properties.userData; elHyperlinkHref.value = properties.href; @@ -688,6 +691,7 @@ elLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('lifetime')); elScriptURL.addEventListener('change', createEmitTextPropertyUpdateFunction('script')); + elScriptTimestamp.addEventListener('change', createEmitNumberPropertyUpdateFunction('scriptTimestamp')); elUserData.addEventListener('change', createEmitTextPropertyUpdateFunction('userData')); var colorChangeFunction = createEmitColorPropertyUpdateFunction( @@ -889,6 +893,12 @@ percentage: parseInt(elRescaleDimensionsPct.value), })); }); + elReloadScriptButton.addEventListener("click", function() { + EventBridge.emitWebEvent(JSON.stringify({ + type: "action", + action: "reloadScript" + })); + }); elCenterAtmosphereToZone.addEventListener("click", function() { EventBridge.emitWebEvent(JSON.stringify({ type: "action", @@ -1181,7 +1191,10 @@
-
Script URL
+
Script URL + + +
diff --git a/examples/html/style.css b/examples/html/style.css index f12cfb6dd0..e63b6338fc 100644 --- a/examples/html/style.css +++ b/examples/html/style.css @@ -260,6 +260,11 @@ table#properties-list { height: 1.2em; } +#properties-list .label input[type=button] { + float: right; + padding: 0 5px 1px; +} + #properties-list .value > div{ padding: 3pt 0; } diff --git a/interface/resources/images/reload-script.svg b/interface/resources/images/reload-script.svg new file mode 100644 index 0000000000..a33de448dc --- /dev/null +++ b/interface/resources/images/reload-script.svg @@ -0,0 +1,50 @@ + +image/svg+xml \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d9c5589631..eb6a53f765 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4048,6 +4048,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&))); connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool))); + connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool))); scriptEngine->registerGlobalObject("Overlays", &_overlays); qScriptRegisterMetaType(scriptEngine, OverlayPropertyResultToScriptValue, OverlayPropertyResultFromScriptValue); @@ -4263,7 +4264,7 @@ bool Application::askToLoadScript(const QString& scriptFilenameOrURL) { } ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, - bool loadScriptFromEditor, bool activateMainWindow) { + bool loadScriptFromEditor, bool activateMainWindow, bool reload) { if (isAboutToQuit()) { return NULL; @@ -4292,7 +4293,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); // get the script engine object to load the script at the designated script URL - scriptEngine->loadURL(scriptUrl); + scriptEngine->loadURL(scriptUrl, reload); } // restore the main window's active state @@ -4303,6 +4304,10 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser return scriptEngine; } +void Application::reloadScript(const QString& scriptName, bool isUserLoaded) { + loadScript(scriptName, isUserLoaded, false, false, true); +} + void Application::handleScriptEngineLoaded(const QString& scriptFilename) { ScriptEngine* scriptEngine = qobject_cast(sender()); @@ -4330,14 +4335,25 @@ void Application::scriptFinished(const QString& scriptName) { } void Application::stopAllScripts(bool restart) { - // stops all current running scripts + if (restart) { + // Delete all running scripts from cache so that they are re-downloaded when they are restarted + auto scriptCache = DependencyManager::get(); + for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); + it != _scriptEnginesHash.constEnd(); it++) { + if (!it.value()->isFinished()) { + scriptCache->deleteScript(it.key()); + } + } + } + + // Stop and possibly restart all currently running scripts for (QHash::const_iterator it = _scriptEnginesHash.constBegin(); it != _scriptEnginesHash.constEnd(); it++) { if (it.value()->isFinished()) { continue; } if (restart && it.value()->isUserLoaded()) { - connect(it.value(), SIGNAL(finished(const QString&)), SLOT(loadScript(const QString&))); + connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); } it.value()->stop(); qCDebug(interfaceapp) << "stopping script..." << it.key(); @@ -4345,13 +4361,20 @@ void Application::stopAllScripts(bool restart) { // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. // TODO: expose animation priorities and provide a layered animation control system. + _myAvatar->clearJointAnimationPriorities(); _myAvatar->clearScriptableSettings(); } -void Application::stopScript(const QString &scriptName) { +void Application::stopScript(const QString &scriptName, bool restart) { const QString& scriptURLString = QUrl(scriptName).toString(); if (_scriptEnginesHash.contains(scriptURLString)) { - _scriptEnginesHash.value(scriptURLString)->stop(); + ScriptEngine* scriptEngine = _scriptEnginesHash[scriptURLString]; + if (restart) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(scriptName); + connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&))); + } + scriptEngine->stop(); qCDebug(interfaceapp) << "stopping script..." << scriptName; // HACK: ATM scripts cannot set/get their animation priorities, so we clear priorities // whenever a script stops in case it happened to have been setting joint rotations. @@ -4367,6 +4390,10 @@ void Application::reloadAllScripts() { stopAllScripts(true); } +void Application::reloadOneScript(const QString& scriptName) { + stopScript(scriptName, true); +} + void Application::loadDefaultScripts() { if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) { loadScript(DEFAULT_SCRIPTS_JS_URL); diff --git a/interface/src/Application.h b/interface/src/Application.h index 2c78327f02..0787cffbdc 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -409,15 +409,19 @@ public slots: bool acceptSnapshot(const QString& urlString); bool askToSetAvatarUrl(const QString& url); bool askToLoadScript(const QString& scriptFilenameOrURL); + ScriptEngine* loadScript(const QString& scriptFilename = QString(), bool isUserLoaded = true, - bool loadScriptFromEditor = false, bool activateMainWindow = false); + bool loadScriptFromEditor = false, bool activateMainWindow = false, bool reload = false); + void reloadScript(const QString& scriptName, bool isUserLoaded = true); void scriptFinished(const QString& scriptName); void stopAllScripts(bool restart = false); - void stopScript(const QString& scriptName); + void stopScript(const QString& scriptName, bool restart = false); void reloadAllScripts(); + void reloadOneScript(const QString& scriptName); void loadDefaultScripts(); void toggleRunningScriptsWidget(); void saveScripts(); + void showFriendsWindow(); void friendsWindowClosed(); diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 31c9271c22..1165de7592 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -32,7 +32,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : QWidget(parent, Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), ui(new Ui::RunningScriptsWidget), - _signalMapper(this), + _reloadSignalMapper(this), + _stopSignalMapper(this), _scriptsModelFilter(this), _scriptsModel(this) { ui->setupUi(this); @@ -64,7 +65,8 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : Application::getInstance(), &Application::loadDialog); connect(ui->loadScriptFromURLButton, &QPushButton::clicked, Application::getInstance(), &Application::loadScriptURLDialog); - connect(&_signalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&))); + connect(&_reloadSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(reloadOneScript(const QString&))); + connect(&_stopSignalMapper, SIGNAL(mapped(QString)), Application::getInstance(), SLOT(stopScript(const QString&))); UIUtil::scaleWidgetFontSizes(this); } @@ -115,6 +117,17 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { name->setText(name->text() + "(" + QString::number(hash.find(list.at(i)).value()) + ")"); } ++hash[list.at(i)]; + + QPushButton* reloadButton = new QPushButton(row); + reloadButton->setFlat(true); + reloadButton->setIcon( + QIcon(QPixmap(PathUtils::resourcesPath() + "images/reload-script.svg").scaledToHeight(CLOSE_ICON_HEIGHT))); + reloadButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); + reloadButton->setStyleSheet("border: 0;"); + reloadButton->setCursor(Qt::PointingHandCursor); + connect(reloadButton, SIGNAL(clicked()), &_reloadSignalMapper, SLOT(map())); + _reloadSignalMapper.setMapping(reloadButton, url.toString()); + QPushButton* closeButton = new QPushButton(row); closeButton->setFlat(true); closeButton->setIcon( @@ -122,9 +135,8 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { closeButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred)); closeButton->setStyleSheet("border: 0;"); closeButton->setCursor(Qt::PointingHandCursor); - - connect(closeButton, SIGNAL(clicked()), &_signalMapper, SLOT(map())); - _signalMapper.setMapping(closeButton, url.toString()); + connect(closeButton, SIGNAL(clicked()), &_stopSignalMapper, SLOT(map())); + _stopSignalMapper.setMapping(closeButton, url.toString()); row->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); @@ -132,6 +144,7 @@ void RunningScriptsWidget::setRunningScripts(const QStringList& list) { row->layout()->setSpacing(0); row->layout()->addWidget(name); + row->layout()->addWidget(reloadButton); row->layout()->addWidget(closeButton); row->setToolTip(url.toString()); diff --git a/interface/src/ui/RunningScriptsWidget.h b/interface/src/ui/RunningScriptsWidget.h index 5d3f6843af..c09bce5443 100644 --- a/interface/src/ui/RunningScriptsWidget.h +++ b/interface/src/ui/RunningScriptsWidget.h @@ -36,7 +36,7 @@ public: const ScriptsModel* getScriptsModel() { return &_scriptsModel; } signals: - void stopScriptName(const QString& name); + void stopScriptName(const QString& name, bool restart = false); protected: virtual bool eventFilter(QObject* sender, QEvent* event); @@ -59,7 +59,8 @@ private slots: private: Ui::RunningScriptsWidget* ui; - QSignalMapper _signalMapper; + QSignalMapper _reloadSignalMapper; + QSignalMapper _stopSignalMapper; ScriptsModelFilter _scriptsModelFilter; ScriptsModel _scriptsModel; ScriptsTableWidget* _recentlyLoadedScriptsTable; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 0ea6080165..dfbc765228 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -148,13 +148,14 @@ void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) { } } -QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload) { +QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload, bool reload) { EntityItemPointer entity = static_cast(_tree)->findEntityByEntityItemID(entityItemID); - return loadEntityScript(entity, isPreload); + return loadEntityScript(entity, isPreload, reload); } -QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut) { +QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut, + bool& reload) { isPending = false; QUrl url(scriptMaybeURLorText); @@ -192,7 +193,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe auto scriptCache = DependencyManager::get(); if (!scriptCache->isInBadScriptList(url)) { - scriptContents = scriptCache->getScript(url, this, isPending); + scriptContents = scriptCache->getScript(url, this, isPending, reload); } } } @@ -201,7 +202,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe } -QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload) { +QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool isPreload, bool reload) { if (_shuttingDown) { return QScriptValue(); // since we're shutting down, we don't load any more scripts } @@ -221,8 +222,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool if (_entityScripts.contains(entityID)) { EntityScriptDetails details = _entityScripts[entityID]; - // check to make sure our script text hasn't changed on us since we last loaded it - if (details.scriptText == entityScript) { + // check to make sure our script text hasn't changed on us since we last loaded it and we're not redownloading it + if (details.scriptText == entityScript && !reload) { return details.scriptObject; // previously loaded } @@ -237,7 +238,7 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItemPointer entity, bool bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text. bool isPending = false; QUrl url; - QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url); + QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url, reload); if (isPending && isPreload && isURL) { _waitingOnPreload.insert(url, entityID); @@ -1030,17 +1031,17 @@ void EntityTreeRenderer::addEntityToScene(EntityItemPointer entity) { } -void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) { +void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID, const bool reload) { if (_tree && !_shuttingDown) { checkAndCallUnload(entityID); - checkAndCallPreload(entityID); + checkAndCallPreload(entityID, reload); } } -void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) { +void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, const bool reload) { if (_tree && !_shuttingDown) { // load the entity script if needed... - QScriptValue entityScript = loadEntityScript(entityID, true); // is preload! + QScriptValue entityScript = loadEntityScript(entityID, true, reload); // is preload! if (entityScript.property("preload").isValid()) { QScriptValueList entityArgs = createEntityArgs(entityID); entityScript.property("preload").call(entityScript, entityArgs); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 56a8208eb4..bd6044516f 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -110,7 +110,7 @@ signals: public slots: void addingEntity(const EntityItemID& entityID); void deletingEntity(const EntityItemID& entityID); - void entitySciptChanging(const EntityItemID& entityID); + void entitySciptChanging(const EntityItemID& entityID, const bool reload); void entityCollisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); // optional slots that can be wired to menu items @@ -127,7 +127,7 @@ private: void applyZonePropertiesToScene(std::shared_ptr zone); void renderElementProxy(EntityTreeElement* entityTreeElement, RenderArgs* args); - void checkAndCallPreload(const EntityItemID& entityID); + void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); void checkAndCallUnload(const EntityItemID& entityID); QList _releasedModels; @@ -148,10 +148,10 @@ private: ScriptEngine* _entitiesScriptEngine; ScriptEngine* _sandboxScriptEngine; - QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false); - QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false); + QScriptValue loadEntityScript(EntityItemPointer entity, bool isPreload = false, bool reload = false); + QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false, bool reload = false); QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID); - QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url); + QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url, bool& reload); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent); diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp index d06ffb9400..245cf00a3d 100644 --- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp @@ -16,6 +16,9 @@ #include #include #include +#include + + #include "RenderableTextEntityItem.h" #include "GLMHelpers.h" @@ -37,14 +40,22 @@ void RenderableTextEntityItem::render(RenderArgs* args) { transformToTopLeft.postTranslate(glm::vec3(-0.5f, 0.5f, 0.0f)); // Go to the top left transformToTopLeft.setScale(1.0f); // Use a scale of one so that the text is not deformed + // Render background + glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND); + glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); + + // Batch render calls Q_ASSERT(args->_batch); gpu::Batch& batch = *args->_batch; batch.setModelTransform(transformToTopLeft); - // Render background - glm::vec3 minCorner = glm::vec3(0.0f, -dimensions.y, SLIGHTLY_BEHIND); - glm::vec3 maxCorner = glm::vec3(dimensions.x, 0.0f, SLIGHTLY_BEHIND); + //rotate about vertical to face the camera + if (getFaceCamera()) { + transformToTopLeft.postRotate(args->_viewFrustum->getOrientation()); + batch.setModelTransform(transformToTopLeft); + } + DependencyManager::get()->renderQuad(batch, minCorner, maxCorner, backgroundColor); float scale = _lineHeight / _textRenderer->getFontSize(); @@ -55,6 +66,7 @@ void RenderableTextEntityItem::render(RenderArgs* args) { glm::vec2 bounds = glm::vec2(dimensions.x - 2.0f * leftMargin, dimensions.y - 2.0f * topMargin); _textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, _text, textColor, bounds / scale); + } diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index da5f96f503..74b7a36504 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -54,6 +54,7 @@ EntityItem::EntityItem(const EntityItemID& entityItemID) : _friction(ENTITY_ITEM_DEFAULT_FRICTION), _lifetime(ENTITY_ITEM_DEFAULT_LIFETIME), _script(ENTITY_ITEM_DEFAULT_SCRIPT), + _scriptTimestamp(ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), _collisionSoundURL(ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), _registrationPoint(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT), _angularVelocity(ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY), @@ -107,6 +108,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_FRICTION; requestedProperties += PROP_LIFETIME; requestedProperties += PROP_SCRIPT; + requestedProperties += PROP_SCRIPT_TIMESTAMP; requestedProperties += PROP_COLLISION_SOUND_URL; requestedProperties += PROP_REGISTRATION_POINT; requestedProperties += PROP_ANGULAR_VELOCITY; @@ -238,6 +240,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_FRICTION, getFriction()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, getScript()); + APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, getAngularVelocity()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_DAMPING, getAngularDamping()); @@ -555,6 +558,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_FRICTION, float, updateFriction); READ_ENTITY_PROPERTY(PROP_LIFETIME, float, updateLifetime); READ_ENTITY_PROPERTY(PROP_SCRIPT, QString, setScript); + READ_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocity); //READ_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, glm::vec3, updateAngularVelocityInDegrees); @@ -901,6 +905,7 @@ EntityItemProperties EntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(created, getCreated); COPY_ENTITY_PROPERTY_TO_PROPERTIES(lifetime, getLifetime); COPY_ENTITY_PROPERTY_TO_PROPERTIES(script, getScript); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(scriptTimestamp, getScriptTimestamp); COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionSoundURL, getCollisionSoundURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(registrationPoint, getRegistrationPoint); COPY_ENTITY_PROPERTY_TO_PROPERTIES(angularVelocity, getAngularVelocity); @@ -967,6 +972,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { // non-simulation properties below SET_ENTITY_PROPERTY_FROM_PROPERTIES(script, setScript); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(scriptTimestamp, setScriptTimestamp); SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionSoundURL, setCollisionSoundURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(glowLevel, setGlowLevel); SET_ENTITY_PROPERTY_FROM_PROPERTIES(localRenderAlpha, setLocalRenderAlpha); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 73f9127361..3ce3a8e269 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -276,6 +276,10 @@ public: const QString& getScript() const { return _script; } void setScript(const QString& value) { _script = value; } + + quint64 getScriptTimestamp() const { return _scriptTimestamp; } + void setScriptTimestamp(const quint64 value) { _scriptTimestamp = value; } + const QString& getCollisionSoundURL() const { return _collisionSoundURL; } void setCollisionSoundURL(const QString& value) { _collisionSoundURL = value; } @@ -313,7 +317,7 @@ public: const QString& getUserData() const { return _userData; } void setUserData(const QString& value) { _userData = value; } - + QUuid getSimulatorID() const { return _simulatorID; } void setSimulatorID(const QUuid& value); void updateSimulatorID(const QUuid& value); @@ -412,6 +416,7 @@ protected: float _friction; float _lifetime; QString _script; + quint64 _scriptTimestamp; QString _collisionSoundURL; glm::vec3 _registrationPoint; glm::vec3 _angularVelocity; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index cbb3b1dc31..f755cd49fb 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -51,6 +51,7 @@ CONSTRUCT_PROPERTY(friction, ENTITY_ITEM_DEFAULT_FRICTION), CONSTRUCT_PROPERTY(lifetime, ENTITY_ITEM_DEFAULT_LIFETIME), CONSTRUCT_PROPERTY(created, UNKNOWN_CREATED_TIME), CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT), +CONSTRUCT_PROPERTY(scriptTimestamp, ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP), CONSTRUCT_PROPERTY(collisionSoundURL, ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL), CONSTRUCT_PROPERTY(color, ), CONSTRUCT_PROPERTY(modelURL, ""), @@ -98,6 +99,7 @@ CONSTRUCT_PROPERTY(backgroundMode, BACKGROUND_MODE_INHERIT), CONSTRUCT_PROPERTY(sourceUrl, ""), CONSTRUCT_PROPERTY(lineWidth, LineEntityItem::DEFAULT_LINE_WIDTH), CONSTRUCT_PROPERTY(linePoints, QVector()), +CONSTRUCT_PROPERTY(faceCamera, TextEntityItem::DEFAULT_FACE_CAMERA), _id(UNKNOWN_ENTITY_ID), @@ -299,6 +301,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_FRICTION, friction); CHECK_PROPERTY_CHANGE(PROP_LIFETIME, lifetime); CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script); + CHECK_PROPERTY_CHANGE(PROP_SCRIPT_TIMESTAMP, scriptTimestamp); CHECK_PROPERTY_CHANGE(PROP_COLLISION_SOUND_URL, collisionSoundURL); CHECK_PROPERTY_CHANGE(PROP_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL); @@ -349,6 +352,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_LINE_POINTS, linePoints); CHECK_PROPERTY_CHANGE(PROP_HREF, href); CHECK_PROPERTY_CHANGE(PROP_DESCRIPTION, description); + CHECK_PROPERTY_CHANGE(PROP_FACE_CAMERA, faceCamera); changedProperties += _stage.getChangedProperties(); changedProperties += _atmosphere.getChangedProperties(); @@ -391,6 +395,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(created, created.toString(Qt::ISODate)); COPY_PROPERTY_TO_QSCRIPTVALUE(script); + COPY_PROPERTY_TO_QSCRIPTVALUE(scriptTimestamp); COPY_PROPERTY_TO_QSCRIPTVALUE(registrationPoint); COPY_PROPERTY_TO_QSCRIPTVALUE(angularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(angularDamping); @@ -444,6 +449,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(linePoints); COPY_PROPERTY_TO_QSCRIPTVALUE(href); COPY_PROPERTY_TO_QSCRIPTVALUE(description); + COPY_PROPERTY_TO_QSCRIPTVALUE(faceCamera); // Sitting properties support if (!skipDefaults) { @@ -503,6 +509,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(friction, float, setFriction); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifetime, float, setLifetime); COPY_PROPERTY_FROM_QSCRIPTVALUE(script, QString, setScript); + COPY_PROPERTY_FROM_QSCRIPTVALUE(scriptTimestamp, quint64, setScriptTimestamp); COPY_PROPERTY_FROM_QSCRIPTVALUE(registrationPoint, glmVec3, setRegistrationPoint); COPY_PROPERTY_FROM_QSCRIPTVALUE(angularVelocity, glmVec3, setAngularVelocity); COPY_PROPERTY_FROM_QSCRIPTVALUE(angularDamping, float, setAngularDamping); @@ -555,7 +562,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(linePoints, qVectorVec3, setLinePoints); COPY_PROPERTY_FROM_QSCRIPTVALUE(href, QString, setHref); COPY_PROPERTY_FROM_QSCRIPTVALUE(description, QString, setDescription); - + COPY_PROPERTY_FROM_QSCRIPTVALUE(faceCamera, bool, setFaceCamera); if (!honorReadOnly) { // this is used by the json reader to set things that we don't want javascript to able to affect. @@ -710,6 +717,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_FRICTION, properties.getFriction()); APPEND_ENTITY_PROPERTY(PROP_LIFETIME, properties.getLifetime()); APPEND_ENTITY_PROPERTY(PROP_SCRIPT, properties.getScript()); + APPEND_ENTITY_PROPERTY(PROP_SCRIPT_TIMESTAMP, properties.getScriptTimestamp()); APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_REGISTRATION_POINT, properties.getRegistrationPoint()); APPEND_ENTITY_PROPERTY(PROP_ANGULAR_VELOCITY, properties.getAngularVelocity()); @@ -723,6 +731,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_HREF, properties.getHref()); APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription()); + if (properties.getType() == EntityTypes::Web) { APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl()); } @@ -732,6 +741,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, properties.getBackgroundColor()); + APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, properties.getFaceCamera()); } if (properties.getType() == EntityTypes::Model) { @@ -961,7 +971,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RESTITUTION, float, setRestitution); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FRICTION, float, setFriction); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LIFETIME, float, setLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT,QString, setScript); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT, QString, setScript); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SCRIPT_TIMESTAMP, quint64, setScriptTimestamp); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, xColor, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_REGISTRATION_POINT, glm::vec3, setRegistrationPoint); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANGULAR_VELOCITY, glm::vec3, setAngularVelocity); @@ -975,6 +986,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HREF, QString, setHref); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription); + if (properties.getType() == EntityTypes::Web) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl); } @@ -984,6 +996,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, xColor, setTextColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_COLOR, xColor, setBackgroundColor); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FACE_CAMERA, bool, setFaceCamera); } if (properties.getType() == EntityTypes::Model) { @@ -1099,6 +1112,7 @@ void EntityItemProperties::markAllChanged() { _userDataChanged = true; _simulatorIDChanged = true; _scriptChanged = true; + _scriptTimestampChanged = true; _collisionSoundURLChanged = true; _registrationPointChanged = true; _angularVelocityChanged = true; @@ -1161,7 +1175,9 @@ void EntityItemProperties::markAllChanged() { _hrefChanged = true; _descriptionChanged = true; - + + _faceCameraChanged = true; + } /// The maximum bounding cube for the entity, independent of it's rotation. diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 5f33da52f8..fdf01c4761 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -98,6 +98,7 @@ public: DEFINE_PROPERTY(PROP_LIFETIME, Lifetime, lifetime, float); DEFINE_PROPERTY(PROP_CREATED, Created, created, quint64); DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString); + DEFINE_PROPERTY(PROP_SCRIPT_TIMESTAMP, ScriptTimestamp, scriptTimestamp, quint64); DEFINE_PROPERTY_REF(PROP_COLLISION_SOUND_URL, CollisionSoundURL, collisionSoundURL, QString); DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor); DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString); @@ -150,7 +151,8 @@ public: DEFINE_PROPERTY_REF(LINE_POINTS, LinePoints, linePoints, QVector); DEFINE_PROPERTY_REF(PROP_HREF, Href, href, QString); DEFINE_PROPERTY_REF(PROP_DESCRIPTION, Description, description, QString); - + DEFINE_PROPERTY(PROP_FACE_CAMERA, FaceCamera, faceCamera, bool); + static QString getBackgroundModeString(BackgroundMode mode); @@ -261,6 +263,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, Friction, friction, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Lifetime, lifetime, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, ScriptTimestamp, scriptTimestamp, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionSoundURL, collisionSoundURL, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 11341b160c..0cdfe73040 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -32,6 +32,7 @@ const float ENTITY_ITEM_DEFAULT_GLOW_LEVEL = 0.0f; const bool ENTITY_ITEM_DEFAULT_VISIBLE = true; const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString(""); +const quint64 ENTITY_ITEM_DEFAULT_SCRIPT_TIMESTAMP = 0; const QString ENTITY_ITEM_DEFAULT_COLLISION_SOUND_URL = QString(""); const glm::vec3 ENTITY_ITEM_DEFAULT_REGISTRATION_POINT = ENTITY_ITEM_HALF_VEC3; // center @@ -66,6 +67,7 @@ const float ENTITY_ITEM_DEFAULT_FRICTION = 0.5f; const bool ENTITY_ITEM_DEFAULT_IGNORE_FOR_COLLISIONS = false; const bool ENTITY_ITEM_DEFAULT_COLLISIONS_WILL_MOVE = false; +const bool ENTITY_ITEM_DEFAULT_BILLBOARDED = false; const float ENTITY_ITEM_DEFAULT_CUTOFF = PI / 2; diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 0f3459e5d2..d4fcfa8cab 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -92,6 +92,7 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const glm::vec3& v) { r inline QScriptValue convertScriptValue(QScriptEngine* e, float v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, int v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, quint32 v) { return QScriptValue(v); } +inline QScriptValue convertScriptValue(QScriptEngine* e, quint64 v) { return QScriptValue((qsreal)v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const QString& v) { return QScriptValue(v); } inline QScriptValue convertScriptValue(QScriptEngine* e, const xColor& v) { return xColorToScriptValue(e, v); } @@ -134,6 +135,7 @@ typedef glm::vec3 glmVec3; typedef glm::quat glmQuat; typedef QVector qVectorVec3; inline float float_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toFloat(&isValid); } +inline quint64 quint64_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toULongLong(&isValid); } inline uint16_t uint16_t_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } inline int int_convertFromScriptValue(const QScriptValue& v, bool& isValid) { return v.toVariant().toInt(&isValid); } inline bool bool_convertFromScriptValue(const QScriptValue& v, bool& isValid) { isValid = true; return v.toVariant().toBool(); } diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index f1ebdb8a1f..9f4cc27cf3 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -55,6 +55,7 @@ enum EntityPropertyList { PROP_DAMPING, PROP_LIFETIME, PROP_SCRIPT, + PROP_SCRIPT_TIMESTAMP, // these properties are supported by some derived classes PROP_COLOR, @@ -122,6 +123,8 @@ enum EntityPropertyList { PROP_HREF, PROP_DESCRIPTION, + PROP_FACE_CAMERA, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 363f3c56d7..752082932b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -185,6 +185,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI // else client accepts what the server says QString entityScriptBefore = entity->getScript(); + quint64 entityScriptTimestampBefore = entity->getScriptTimestamp(); QString collisionSoundURLBefore = entity->getCollisionSoundURL(); uint32_t preFlags = entity->getDirtyFlags(); UpdateEntityOperator theOperator(this, containingElement, entity, properties); @@ -206,8 +207,10 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI } QString entityScriptAfter = entity->getScript(); - if (entityScriptBefore != entityScriptAfter) { - emitEntityScriptChanging(entity->getEntityItemID()); // the entity script has changed + quint64 entityScriptTimestampAfter = entity->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + if (entityScriptBefore != entityScriptAfter || reload) { + emitEntityScriptChanging(entity->getEntityItemID(), reload); // the entity script has changed } maybeNotifyNewCollisionSoundURL(collisionSoundURLBefore, entity->getCollisionSoundURL()); } @@ -266,9 +269,10 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti return result; } -void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID) { - emit entityScriptChanging(entityItemID); +void EntityTree::emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload) { + emit entityScriptChanging(entityItemID, reload); } + void EntityTree::maybeNotifyNewCollisionSoundURL(const QString& previousCollisionSoundURL, const QString& nextCollisionSoundURL) { if (!nextCollisionSoundURL.isEmpty() && (nextCollisionSoundURL != previousCollisionSoundURL)) { emit newCollisionSoundURL(QUrl(nextCollisionSoundURL)); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 1ad3ef6b1c..9c4be9a86f 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -155,7 +155,7 @@ public: void entityChanged(EntityItemPointer entity); - void emitEntityScriptChanging(const EntityItemID& entityItemID); + void emitEntityScriptChanging(const EntityItemID& entityItemID, const bool reload); void setSimulation(EntitySimulation* simulation); EntitySimulation* getSimulation() const { return _simulation; } @@ -171,7 +171,7 @@ public: signals: void deletingEntity(const EntityItemID& entityID); void addingEntity(const EntityItemID& entityID); - void entityScriptChanging(const EntityItemID& entityItemID); + void entityScriptChanging(const EntityItemID& entityItemID, const bool reload); void newCollisionSoundURL(const QUrl& url); void clearingEntities(); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 80333a44e5..4bc81e1da6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -733,6 +733,7 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int // 3) remember the old cube for the entity so we can mark it as dirty if (entityItem) { QString entityScriptBefore = entityItem->getScript(); + quint64 entityScriptTimestampBefore = entityItem->getScriptTimestamp(); bool bestFitBefore = bestFitEntityBounds(entityItem); EntityTreeElement* currentContainingElement = _myTree->getContainingElement(entityItemID); @@ -755,8 +756,10 @@ int EntityTreeElement::readElementDataFromBuffer(const unsigned char* data, int } QString entityScriptAfter = entityItem->getScript(); - if (entityScriptBefore != entityScriptAfter) { - _myTree->emitEntityScriptChanging(entityItemID); // the entity script has changed + quint64 entityScriptTimestampAfter = entityItem->getScriptTimestamp(); + bool reload = entityScriptTimestampBefore != entityScriptTimestampAfter; + if (entityScriptBefore != entityScriptAfter || reload) { + _myTree->emitEntityScriptChanging(entityItemID, reload); // the entity script has changed } } else { diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 5718fe8c12..31ee9e6676 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -27,6 +27,7 @@ const QString TextEntityItem::DEFAULT_TEXT(""); const float TextEntityItem::DEFAULT_LINE_HEIGHT = 0.1f; const xColor TextEntityItem::DEFAULT_TEXT_COLOR = { 255, 255, 255 }; const xColor TextEntityItem::DEFAULT_BACKGROUND_COLOR = { 0, 0, 0}; +const bool TextEntityItem::DEFAULT_FACE_CAMERA = false; EntityItemPointer TextEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return EntityItemPointer(new TextEntityItem(entityID, properties)); @@ -54,6 +55,7 @@ EntityItemProperties TextEntityItem::getProperties() const { COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight); COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColorX); COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundColor, getBackgroundColorX); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); return properties; } @@ -65,6 +67,7 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight); SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor); SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundColor, setBackgroundColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); if (somethingChanged) { bool wantDebug = false; @@ -91,7 +94,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight); READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, rgbColor, setTextColor); READ_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, rgbColor, setBackgroundColor); - + READ_ENTITY_PROPERTY(PROP_FACE_CAMERA, bool, setFaceCamera); + return bytesRead; } @@ -103,6 +107,7 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_LINE_HEIGHT; requestedProperties += PROP_TEXT_COLOR; requestedProperties += PROP_BACKGROUND_COLOR; + requestedProperties += PROP_FACE_CAMERA; return requestedProperties; } @@ -120,6 +125,8 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, getBackgroundColor()); + APPEND_ENTITY_PROPERTY(PROP_FACE_CAMERA, getFaceCamera()); + } diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 6d72896047..bf03c192c7 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -79,12 +79,17 @@ public: _backgroundColor[GREEN_INDEX] = value.green; _backgroundColor[BLUE_INDEX] = value.blue; } + + static const bool DEFAULT_FACE_CAMERA; + bool getFaceCamera() const { return _faceCamera; } + void setFaceCamera(bool value) { _faceCamera = value; } protected: QString _text; float _lineHeight; rgbColor _textColor; rgbColor _backgroundColor; + bool _faceCamera; }; #endif // hifi_TextEntityItem_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 78bb3f11e1..f989c417f0 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -73,7 +73,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketTypeEntityAdd: case PacketTypeEntityEdit: case PacketTypeEntityData: - return VERSION_ENTITIES_LINE_POINTS; + return VERSION_ENTITIES_SCRIPT_TIMESTAMP; case PacketTypeEntityErase: return 2; case PacketTypeAudioStreamStats: diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 228df1cdde..ade4fc8bd9 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -183,5 +183,7 @@ const PacketVersion VERSION_ENTITIES_HAVE_FRICTION = 26; const PacketVersion VERSION_NO_ENTITY_ID_SWAP = 27; const PacketVersion VERSION_ENTITIES_PARTICLE_FIX = 28; const PacketVersion VERSION_ENTITIES_LINE_POINTS = 29; +const PacketVersion VERSION_ENTITIES_FACE_CAMERA = 30; +const PacketVersion VERSION_ENTITIES_SCRIPT_TIMESTAMP = 31; #endif // hifi_PacketHeaders_h diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 8f854cfdc3..2047442ce6 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -16,18 +16,22 @@ #include #include +#include #include #include -#include "ScriptEngineLogging.h" + #include "ScriptCache.h" +#include "ScriptEngineLogging.h" ScriptCache::ScriptCache(QObject* parent) { // nothing to do here... } -QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending) { +QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool reload) { + assert(!_scriptCache.contains(url) || !reload); + QString scriptContents; - if (_scriptCache.contains(url)) { + if (_scriptCache.contains(url) && !reload) { qCDebug(scriptengine) << "Found script in cache:" << url.toString(); scriptContents = _scriptCache[url]; scriptUser->scriptContentsAvailable(url, scriptContents); @@ -43,8 +47,13 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + if (reload) { + networkRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork); + qCDebug(scriptengine) << "Redownloading script at:" << url.toString(); + } else { + qCDebug(scriptengine) << "Downloading script at:" << url.toString(); + } - qCDebug(scriptengine) << "Downloading script at" << url.toString(); QNetworkReply* reply = networkAccessManager.get(networkRequest); connect(reply, &QNetworkReply::finished, this, &ScriptCache::scriptDownloaded); } @@ -52,6 +61,13 @@ QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& is return scriptContents; } +void ScriptCache::deleteScript(const QUrl& url) { + if (_scriptCache.contains(url)) { + qCDebug(scriptengine) << "Delete script from cache:" << url.toString(); + _scriptCache.remove(url); + } +} + void ScriptCache::scriptDownloaded() { QNetworkReply* reply = qobject_cast(sender()); QUrl url = reply->url(); diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 16bf04c53e..25a36c04d8 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -26,7 +26,8 @@ class ScriptCache : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending); + QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending, bool redownload = false); + void deleteScript(const QUrl& url); void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); } bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); } diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0945a556f2..55cf9e9890 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,6 +94,7 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _vec3Library(), _uuidLibrary(), _isUserLoaded(false), + _isReloading(false), _arrayBufferClass(new ArrayBufferClass(this)) { _allScriptsMutex.lock(); @@ -251,12 +252,13 @@ bool ScriptEngine::setScriptContents(const QString& scriptContents, const QStrin return true; } -void ScriptEngine::loadURL(const QUrl& scriptURL) { +void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) { if (_isRunning) { return; } _fileNameString = scriptURL.toString(); + _isReloading = reload; QUrl url(scriptURL); @@ -283,8 +285,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL) { } else { bool isPending; auto scriptCache = DependencyManager::get(); - scriptCache->getScript(url, this, isPending); - + scriptCache->getScript(url, this, isPending, reload); } } } @@ -904,7 +905,13 @@ void ScriptEngine::load(const QString& loadFile) { } QUrl url = resolvePath(loadFile); - emit loadScript(url.toString(), false); + if (_isReloading) { + auto scriptCache = DependencyManager::get(); + scriptCache->deleteScript(url.toString()); + emit reloadScript(url.toString(), false); + } else { + emit loadScript(url.toString(), false); + } } void ScriptEngine::nodeKilled(SharedNodePointer node) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index ec28804049..a96042184d 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -104,7 +104,7 @@ public: Q_INVOKABLE void removeEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler); public slots: - void loadURL(const QUrl& scriptURL); + void loadURL(const QUrl& scriptURL, bool reload); void stop(); QScriptValue evaluate(const QString& program, const QString& fileName = QString(), int lineNumber = 1); @@ -132,6 +132,7 @@ signals: void runningStateChanged(); void evaluationFinished(QScriptValue result, bool isException); void loadScript(const QString& scriptName, bool isUserLoaded); + void reloadScript(const QString& scriptName, bool isUserLoaded); void doneRunning(); protected: @@ -165,6 +166,7 @@ private: Vec3 _vec3Library; ScriptUUID _uuidLibrary; bool _isUserLoaded; + bool _isReloading; ArrayBufferClass* _arrayBufferClass;