From e12e3b05e10b72848ed131cfc413cf9ef484f9d1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:33:01 -0800 Subject: [PATCH 1/4] add support for enter/leave entity events for when the avatar enters or leaves the bounds of an entity --- .../entityScripts/playSoundOnEnterOrLeave.js | 32 +++++++++ interface/src/entities/EntityTreeRenderer.cpp | 66 +++++++++++++++++++ interface/src/entities/EntityTreeRenderer.h | 9 +++ libraries/entities/src/EntityItem.h | 1 + .../entities/src/EntityScriptingInterface.h | 3 + libraries/entities/src/SphereEntityItem.h | 3 + 6 files changed, 114 insertions(+) create mode 100644 examples/entityScripts/playSoundOnEnterOrLeave.js diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js new file mode 100644 index 0000000000..6f2ec830c6 --- /dev/null +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -0,0 +1,32 @@ +// +// playSoundOnEnterOrLeave.js +// examples/entityScripts +// +// Created by Brad Hefta-Gaub on 11/3/14. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example of an entity script which when assigned to an entity, that entity will play a sound in world when +// your avatar enters or leaves the bounds of the entity. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +(function(){ + var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); + + function playSound(entityID) { + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(bird, options); + }; + + this.enterEntity = function(entityID) { + playSound(); + }; + + this.leaveEntity = function(entityID) { + playSound(); + }; +}) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index df2c51104b..4963c8baca 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -72,6 +72,11 @@ void EntityTreeRenderer::init() { Application::getInstance()->getControllerScriptingInterface()); Application::getInstance()->registerScriptEngineWithApplicationServices(_entitiesScriptEngine); } + + // 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 = Application::getInstance()->getAvatar()->getPosition(); + _lastAvatarPosition = avatarPosition + glm::vec3(1.f,1.f,1.f); } QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) { @@ -183,6 +188,58 @@ void EntityTreeRenderer::update() { if (_tree) { EntityTree* tree = static_cast(_tree); tree->update(); + + // check to see if the avatar has moved and if we need to handle enter/leave entity logic + checkEnterLeaveEntities(); + } +} + +void EntityTreeRenderer::checkEnterLeaveEntities() { + if (_tree) { + glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float)TREE_SCALE; + + if (avatarPosition != _lastAvatarPosition) { + float radius = 1.0f / (float)TREE_SCALE; // for now, assume 1 meter radius + QVector foundEntities; + QVector entitiesContainingAvatar; + + // find the entities near us + static_cast(_tree)->findEntities(avatarPosition, radius, foundEntities); + + // create a list of entities that actually contain the avatar's position + foreach(const EntityItem* entity, foundEntities) { + if (entity->contains(avatarPosition)) { + entitiesContainingAvatar << entity->getEntityItemID(); + } + } + + // for all of our previous containing entities, if they are no longer containing then send them a leave event + foreach(const EntityItemID& entityID, _currentEntitiesInside) { + if (!entitiesContainingAvatar.contains(entityID)) { + emit leaveEntity(entityID); + QScriptValueList entityArgs = createEntityArgs(entityID); + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("leaveEntity").isValid()) { + entityScript.property("leaveEntity").call(entityScript, entityArgs); + } + + } + } + + // for all of our new containing entities, if they weren't previously containing then send them an enter event + foreach(const EntityItemID& entityID, entitiesContainingAvatar) { + if (!_currentEntitiesInside.contains(entityID)) { + emit enterEntity(entityID); + QScriptValueList entityArgs = createEntityArgs(entityID); + QScriptValue entityScript = loadEntityScript(entityID); + if (entityScript.property("enterEntity").isValid()) { + entityScript.property("enterEntity").call(entityScript, entityArgs); + } + } + } + _currentEntitiesInside = entitiesContainingAvatar; + _lastAvatarPosition = avatarPosition; + } } } @@ -547,6 +604,9 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::hoverEnterEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); connect(this, &EntityTreeRenderer::hoverOverEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); connect(this, &EntityTreeRenderer::hoverLeaveEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + + connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); + connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); } QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { @@ -556,6 +616,12 @@ QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& en return args; } +QScriptValueList EntityTreeRenderer::createEntityArgs(const EntityItemID& entityID) { + QScriptValueList args; + args << entityID.toScriptValue(_entitiesScriptEngine); + return args; +} + void EntityTreeRenderer::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); PickRay ray = computePickRay(event->x(), event->y()); diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 6c3f948594..47a4836738 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -104,6 +104,9 @@ signals: void hoverEnterEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + + void enterEntity(const EntityItemID& entityItemID); + void leaveEntity(const EntityItemID& entityItemID); private: QList _releasedModels; @@ -113,6 +116,12 @@ private: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; + + + QScriptValueList createEntityArgs(const EntityItemID& entityID); + void checkEnterLeaveEntities(); + glm::vec3 _lastAvatarPosition; + QVector _currentEntitiesInside; bool _wantScripts; ScriptEngine* _entitiesScriptEngine; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 65da3c964a..2117ec25b0 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -253,6 +253,7 @@ public: void applyHardCollision(const CollisionInfo& collisionInfo); virtual const Shape& getCollisionShapeInMeters() const { return _collisionShape; } + virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); } protected: virtual void initFromEntityItemID(const EntityItemID& entityItemID); // maybe useful to allow subclasses to init diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 1152ccbd03..2150fa51da 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -115,6 +115,9 @@ signals: void hoverOverEntity(const EntityItemID& entityItemID, const MouseEvent& event); void hoverLeaveEntity(const EntityItemID& entityItemID, const MouseEvent& event); + void enterEntity(const EntityItemID& entityItemID); + void leaveEntity(const EntityItemID& entityItemID); + private: void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index d57ada760b..21cb58223b 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -53,6 +53,9 @@ public: virtual const Shape& getCollisionShapeInMeters() const { return _sphereShape; } + // TODO: implement proper contains for 3D ellipsoid + //virtual bool contains(const glm::vec3& point) const; + protected: virtual void recalculateCollisionShape(); From bc329a63945ab2ef3775e3fd999354400d09766f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:34:30 -0800 Subject: [PATCH 2/4] remove blank line --- interface/src/entities/EntityTreeRenderer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/entities/EntityTreeRenderer.h b/interface/src/entities/EntityTreeRenderer.h index 47a4836738..0a725c8294 100644 --- a/interface/src/entities/EntityTreeRenderer.h +++ b/interface/src/entities/EntityTreeRenderer.h @@ -117,7 +117,6 @@ private: EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; - QScriptValueList createEntityArgs(const EntityItemID& entityID); void checkEnterLeaveEntities(); glm::vec3 _lastAvatarPosition; From 2dae78b82c8e27b01228292ee456f6f137370a0d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:40:09 -0800 Subject: [PATCH 3/4] CR feedback --- interface/src/entities/EntityTreeRenderer.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 4963c8baca..693b367d73 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -196,10 +196,10 @@ void EntityTreeRenderer::update() { void EntityTreeRenderer::checkEnterLeaveEntities() { if (_tree) { - glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float)TREE_SCALE; + glm::vec3 avatarPosition = Application::getInstance()->getAvatar()->getPosition() / (float) TREE_SCALE; if (avatarPosition != _lastAvatarPosition) { - float radius = 1.0f / (float)TREE_SCALE; // for now, assume 1 meter radius + float radius = 1.0f / (float) TREE_SCALE; // for now, assume 1 meter radius QVector foundEntities; QVector entitiesContainingAvatar; @@ -276,8 +276,8 @@ const Model* EntityTreeRenderer::getModelForEntityItem(const EntityItem* entityI } void renderElementProxy(EntityTreeElement* entityTreeElement) { - glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float)TREE_SCALE; - float elementSize = entityTreeElement->getScale() * (float)TREE_SCALE; + glm::vec3 elementCenter = entityTreeElement->getAACube().calcCenter() * (float) TREE_SCALE; + float elementSize = entityTreeElement->getScale() * (float) TREE_SCALE; glColor3f(1.0f, 0.0f, 0.0f); glPushMatrix(); glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z); @@ -350,9 +350,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg AACube minCube = entity->getMinimumAACube(); AABox entityBox = entity->getAABox(); - maxCube.scale((float)TREE_SCALE); - minCube.scale((float)TREE_SCALE); - entityBox.scale((float)TREE_SCALE); + maxCube.scale((float) TREE_SCALE); + minCube.scale((float) TREE_SCALE); + entityBox.scale((float) TREE_SCALE); glm::vec3 maxCenter = maxCube.calcCenter(); glm::vec3 minCenter = minCube.calcCenter(); @@ -382,9 +382,9 @@ void EntityTreeRenderer::renderProxies(const EntityItem* entity, RenderArgs* arg glPopMatrix(); - glm::vec3 position = entity->getPosition() * (float)TREE_SCALE; - glm::vec3 center = entity->getCenter() * (float)TREE_SCALE; - glm::vec3 dimensions = entity->getDimensions() * (float)TREE_SCALE; + glm::vec3 position = entity->getPosition() * (float) TREE_SCALE; + glm::vec3 center = entity->getCenter() * (float) TREE_SCALE; + glm::vec3 dimensions = entity->getDimensions() * (float) TREE_SCALE; glm::quat rotation = entity->getRotation(); glColor4f(1.0f, 0.0f, 1.0f, 1.0f); From 60182a78cb0050cd996c0d61cba396b9ae496b56 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 3 Nov 2014 15:42:40 -0800 Subject: [PATCH 4/4] CR feedback --- examples/entityScripts/playSoundOnEnterOrLeave.js | 10 +++++----- interface/src/entities/EntityTreeRenderer.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/entityScripts/playSoundOnEnterOrLeave.js b/examples/entityScripts/playSoundOnEnterOrLeave.js index 6f2ec830c6..228a8a36d0 100644 --- a/examples/entityScripts/playSoundOnEnterOrLeave.js +++ b/examples/entityScripts/playSoundOnEnterOrLeave.js @@ -15,11 +15,11 @@ var bird = new Sound("http://s3.amazonaws.com/hifi-public/sounds/Animals/bushtit_1.raw"); function playSound(entityID) { - var options = new AudioInjectionOptions(); - var position = MyAvatar.position; - options.position = position; - options.volume = 0.5; - Audio.playSound(bird, options); + var options = new AudioInjectionOptions(); + var position = MyAvatar.position; + options.position = position; + options.volume = 0.5; + Audio.playSound(bird, options); }; this.enterEntity = function(entityID) { diff --git a/interface/src/entities/EntityTreeRenderer.cpp b/interface/src/entities/EntityTreeRenderer.cpp index 693b367d73..e447c703fb 100644 --- a/interface/src/entities/EntityTreeRenderer.cpp +++ b/interface/src/entities/EntityTreeRenderer.cpp @@ -76,7 +76,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 = Application::getInstance()->getAvatar()->getPosition(); - _lastAvatarPosition = avatarPosition + glm::vec3(1.f,1.f,1.f); + _lastAvatarPosition = avatarPosition + glm::vec3(1.f, 1.f, 1.f); } QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {