diff --git a/examples/entityScripts/changeColorOnHover.js b/examples/entityScripts/changeColorOnHover.js index 638c1bece4..0cf08adf1d 100644 --- a/examples/entityScripts/changeColorOnHover.js +++ b/examples/entityScripts/changeColorOnHover.js @@ -13,34 +13,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -(function(){ - this.oldColor = {}; - this.oldColorKnown = false; - this.storeOldColor = function(entityID) { - var oldProperties = Entities.getEntityProperties(entityID); - this.oldColor = oldProperties.color; - this.oldColorKnown = true; - print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); - }; - - this.preload = function(entityID) { - print("preload"); - this.storeOldColor(entityID); - }; - - this.hoverEnterEntity = function(entityID, mouseEvent) { - print("hoverEnterEntity"); - if (!this.oldColorKnown) { - this.storeOldColor(entityID); - } - Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); - }; - this.hoverLeaveEntity = function(entityID, mouseEvent) { - print("hoverLeaveEntity"); - if (this.oldColorKnown) { - print("leave restoring old color... this.oldColor=" - + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); - Entities.editEntity(entityID, { color: this.oldColor }); - } - }; -}) \ No newline at end of file +(function() { + Script.include("changeColorOnHoverClass.js"); + return new ChangeColorOnHover(); +}) diff --git a/examples/entityScripts/changeColorOnHoverClass.js b/examples/entityScripts/changeColorOnHoverClass.js new file mode 100644 index 0000000000..231469f7e8 --- /dev/null +++ b/examples/entityScripts/changeColorOnHoverClass.js @@ -0,0 +1,55 @@ +// +// changeColorOnHover.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to a non-model entity like a box or sphere, will +// change the color of the entity when you hover over it. This script uses the JavaScript prototype/class functionality +// to construct an object that has methods for hoverEnterEntity and hoverLeaveEntity; +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +ChangeColorOnHover = function(){ + this.oldColor = {}; + this.oldColorKnown = false; +}; + +ChangeColorOnHover.prototype = { + + storeOldColor: function(entityID) { + var oldProperties = Entities.getEntityProperties(entityID); + this.oldColor = oldProperties.color; + this.oldColorKnown = true; + print("storing old color... this.oldColor=" + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + }, + + preload: function(entityID) { + print("preload"); + this.storeOldColor(entityID); + }, + + hoverEnterEntity: function(entityID, mouseEvent) { + print("hoverEnterEntity"); + if (!this.oldColorKnown) { + this.storeOldColor(entityID); + } + Entities.editEntity(entityID, { color: { red: 0, green: 255, blue: 255} }); + }, + + + hoverLeaveEntity: function(entityID, mouseEvent) { + print("hoverLeaveEntity"); + if (this.oldColorKnown) { + print("leave restoring old color... this.oldColor=" + + this.oldColor.red + "," + this.oldColor.green + "," + this.oldColor.blue); + Entities.editEntity(entityID, { color: this.oldColor }); + } + } +}; + + diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js index f82c05c580..b95e35ab1d 100644 --- a/examples/entityScripts/playSoundOnEnterOrLeave.js +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -27,10 +27,12 @@ }; this.enterEntity = function(entityID) { + print("enterEntity("+entityID.id+")"); playSound(); }; this.leaveEntity = function(entityID) { + print("leaveEntity("+entityID.id+")"); playSound(); }; }) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 7253b96480..9f1185cfb9 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -62,6 +62,7 @@ EntityTreeRenderer::~EntityTreeRenderer() { } void EntityTreeRenderer::clear() { + leaveAllEntities(); foreach (const EntityItemID& entityID, _entityScripts.keys()) { checkAndCallUnload(entityID); } @@ -82,8 +83,7 @@ void EntityTreeRenderer::init() { // make sure our "last avatar position" is something other than our current position, so that on our // first chance, we'll check for enter/leave entity events. - glm::vec3 avatarPosition = _viewState->getAvatarPosition(); - _lastAvatarPosition = avatarPosition + glm::vec3(1.0f, 1.0f, 1.0f); + _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f); connect(entityTree, &EntityTree::deletingEntity, this, &EntityTreeRenderer::deletingEntity); connect(entityTree, &EntityTree::addingEntity, this, &EntityTreeRenderer::checkAndCallPreload); @@ -97,13 +97,15 @@ QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItem } -QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText) { +QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL) { QUrl url(scriptMaybeURLorText); // If the url is not valid, this must be script text... if (!url.isValid()) { + isURL = false; return scriptMaybeURLorText; } + isURL = true; QString scriptContents; // assume empty @@ -173,7 +175,8 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { return QScriptValue(); // no script } - QString scriptContents = loadScriptContents(entityScript); + bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text. + QString scriptContents = loadScriptContents(entityScript, isURL); QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents); if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) { @@ -184,6 +187,9 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { return QScriptValue(); // invalid script } + if (isURL) { + _entitiesScriptEngine->setParentURL(entity->getScript()); + } QScriptValue entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents); if (!entityScriptConstructor.isFunction()) { @@ -197,6 +203,10 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) { EntityScriptDetails newDetails = { entityScript, entityScriptObject }; _entityScripts[entityID] = newDetails; + if (isURL) { + _entitiesScriptEngine->setParentURL(""); + } + return entityScriptObject; // newly constructed } @@ -287,6 +297,27 @@ void EntityTreeRenderer::checkEnterLeaveEntities() { } } +void EntityTreeRenderer::leaveAllEntities() { + if (_tree) { + _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 + foreach(const EntityItemID& entityID, _currentEntitiesInside) { + emit leaveEntity(entityID); + QScriptValueList entityArgs = createEntityArgs(entityID); + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("leaveEntity").isValid()) { + entityScript.property("leaveEntity").call(entityScript, entityArgs); + } + } + _currentEntitiesInside.clear(); + + // make sure our "last avatar position" is something other than our current position, so that on our + // first chance, we'll check for enter/leave entity events. + _lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3(1.0f, 1.0f, 1.0f); + _tree->unlock(); + } +} void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::RenderSide renderSide) { if (_tree) { Model::startScene(renderSide); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index f23c5286c1..92cc2c4dcc 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -129,6 +129,7 @@ private: QScriptValueList createEntityArgs(const EntityItemID& entityID); void checkEnterLeaveEntities(); + void leaveAllEntities(); glm::vec3 _lastAvatarPosition; QVector _currentEntitiesInside; @@ -138,7 +139,7 @@ private: QScriptValue loadEntityScript(EntityItem* entity); QScriptValue loadEntityScript(const EntityItemID& entityItemID); QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID); - QString loadScriptContents(const QString& scriptMaybeURLorText); + QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID); QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7cd6ea07ee..ac6291469e 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -601,16 +601,20 @@ void ScriptEngine::stopTimer(QTimer *timer) { } QUrl ScriptEngine::resolvePath(const QString& include) const { - // first lets check to see if it's already a full URL QUrl url(include); + // first lets check to see if it's already a full URL 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); - + QUrl parentURL; + if (_parentURL.isEmpty()) { + parentURL = QUrl(_fileNameString); + } else { + parentURL = QUrl(_parentURL); + } // if the parent URL's scheme is empty, then this is probably a local file... if (parentURL.scheme().isEmpty()) { parentURL = QUrl::fromLocalFile(_fileNameString); @@ -643,6 +647,7 @@ void ScriptEngine::include(const QString& includeFile) { #else QString fileName = url.toLocalFile(); #endif + QFile scriptFile(fileName); if (scriptFile.open(QFile::ReadOnly | QFile::Text)) { qDebug() << "Including file:" << fileName; diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 3b074d7c73..7474ac8725 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -89,6 +89,8 @@ public: void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; } + void setParentURL(const QString& parentURL) { _parentURL = parentURL; } + public slots: void loadURL(const QUrl& scriptURL); void stop(); @@ -120,6 +122,7 @@ signals: protected: QString _scriptContents; + QString _parentURL; bool _isFinished; bool _isRunning; bool _isInitialized;