diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index d9b8dd997a..bfe291f8ef 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -21,6 +21,7 @@ #include "EntityTree.h" #include "LightEntityItem.h" #include "ModelEntityItem.h" +#include "QVariantGLM.h" #include "SimulationOwner.h" #include "ZoneEntityItem.h" @@ -550,14 +551,6 @@ QVector EntityScriptingInterface::findEntities(const glm::vec3& center, f return result; } -QVector EntityScriptingInterface::findEntitiesInView(const glm::vec3& center, float radius) const { - QVector result; - - // TODO - - return result; -} - QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const { QVector result; if (_entityTree) { @@ -574,6 +567,48 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } +QVector EntityScriptingInterface::findEntitiesInFrustum(QVariantMap frustum) const { + QVector result; + + const QString POSITION_PROPERTY = "position"; + bool positionOK = frustum.contains(POSITION_PROPERTY); + glm::vec3 position = positionOK ? qMapToGlmVec3(frustum[POSITION_PROPERTY]) : glm::vec3(); + + const QString ORIENTATION_PROPERTY = "orientation"; + bool orientationOK = frustum.contains(ORIENTATION_PROPERTY); + glm::quat orientation = orientationOK ? qMapToGlmQuat(frustum[ORIENTATION_PROPERTY]) : glm::quat(); + + const QString PROJECTION_PROPERTY = "projection"; + bool projectionOK = frustum.contains(PROJECTION_PROPERTY); + glm::mat4 projection = projectionOK ? qMapToGlmMat4(frustum[PROJECTION_PROPERTY]) : glm::mat4(); + + const QString CENTER_RADIUS_PROPERTY = "centerRadius"; + bool centerRadiusOK = frustum.contains(CENTER_RADIUS_PROPERTY); + float centerRadius = centerRadiusOK ? frustum[CENTER_RADIUS_PROPERTY].toFloat() : 0.0f; + + if (positionOK && orientationOK && projectionOK && centerRadiusOK) { + ViewFrustum viewFrustum; + viewFrustum.setPosition(position); + viewFrustum.setOrientation(orientation); + viewFrustum.setProjection(projection); + viewFrustum.setCenterRadius(centerRadius); + viewFrustum.calculate(); + + if (_entityTree) { + QVector entities; + _entityTree->withReadLock([&] { + _entityTree->findEntities(viewFrustum, entities); + }); + + foreach(EntityItemPointer entity, entities) { + result << entity->getEntityItemID(); + } + } + } + + return result; +} + RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 0b9fb5383f..03c2f772b5 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -127,14 +127,19 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; - /// finds models within the search sphere specified by the center point and radius - /// this function will not find any models in script engine contexts which don't have access to models - Q_INVOKABLE QVector findEntitiesInView(const glm::vec3& center, float radius) const; - /// finds models within the box specified by the corner and dimensions /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; + /// finds models within the frustum + /// the frustum must have the following properties: + /// - position + /// - orientation + /// - projection + /// - centerRadius + /// this function will not find any models in script engine contexts which don't have access to models + Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; + /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 4cdebc364c..89f469037e 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -688,6 +688,31 @@ void EntityTree::findEntities(const AABox& box, QVector& foun foundEntities.swap(args._foundEntities); } +class FindInFrustumArgs { +public: + ViewFrustum frustum; + QVector entities; +}; + +bool EntityTree::findInFrustumOperation(OctreeElementPointer element, void* extraData) { + FindInFrustumArgs* args = static_cast(extraData); + if (element->isInView(args->frustum)) { + EntityTreeElementPointer entityTreeElement = std::static_pointer_cast(element); + entityTreeElement->getEntities(args->frustum, args->entities); + return true; + } + return false; +} + +// NOTE: assumes caller has handled locking +void EntityTree::findEntities(const ViewFrustum& frustum, QVector& foundEntities) { + FindInFrustumArgs args = { frustum, QVector() }; + // NOTE: This should use recursion, since this is a spatial operation + recurseTreeWithOperation(findInFrustumOperation, &args); + // swap the two lists of entity pointers instead of copy + foundEntities.swap(args.entities); +} + EntityItemPointer EntityTree::findEntityByID(const QUuid& id) { EntityItemID entityID(id); return findEntityByEntityItemID(entityID); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 7dc999aac2..917b9333a5 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -153,6 +153,11 @@ public: /// \remark Side effect: any initial contents in entities will be lost void findEntities(const AABox& box, QVector& foundEntities); + /// finds all entities within a frustum + /// \parameter frustum the query frustum + /// \param foundEntities[out] vector of EntityItemPointer + void findEntities(const ViewFrustum& frustum, QVector& foundEntities); + void addNewlyCreatedHook(NewlyCreatedEntityHook* hook); void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook); @@ -276,6 +281,7 @@ protected: static bool findInSphereOperation(OctreeElementPointer element, void* extraData); static bool findInCubeOperation(OctreeElementPointer element, void* extraData); static bool findInBoxOperation(OctreeElementPointer element, void* extraData); + static bool findInFrustumOperation(OctreeElementPointer element, void* extraData); static bool sendEntitiesOperation(OctreeElementPointer element, void* extraData); void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode); diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 657e0b286b..29274d2e72 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -796,6 +796,17 @@ void EntityTreeElement::getEntities(const AABox& box, QVector }); } +void EntityTreeElement::getEntities(const ViewFrustum& frustum, QVector& foundEntities) { + forEachEntity([&](EntityItemPointer entity) { + bool success; + AABox entityBox = entity->getAABox(success); + // FIXME - See FIXMEs for similar methods above. + if (!success || frustum.boxIntersectsFrustum(entityBox) || frustum.boxIntersectsKeyhole(entityBox)) { + foundEntities.push_back(entity); + } + }); +} + EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemID& id) const { EntityItemPointer foundEntity = NULL; withReadLock([&] { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 4875e258da..d92dfa52dc 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -194,6 +194,11 @@ public: /// \param entities[out] vector of non-const EntityItemPointer void getEntities(const AABox& box, QVector& foundEntities); + /// finds all entities that touch a frustum + /// \param frustum the query frustum + /// \param entities[out] vector of non-const EntityItemPointer + void getEntities(const ViewFrustum& frustum, QVector& foundEntities); + EntityItemPointer getEntityWithID(uint32_t id) const; EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const; void getEntitiesInside(const AACube& box, QVector& foundEntities); diff --git a/libraries/shared/src/QVariantGLM.cpp b/libraries/shared/src/QVariantGLM.cpp index 7a3ab92cca..b5b840c2ca 100644 --- a/libraries/shared/src/QVariantGLM.cpp +++ b/libraries/shared/src/QVariantGLM.cpp @@ -62,3 +62,48 @@ void qListtoRgbColor(const QVariant& q, rgbColor& returnValue) { returnValue[GREEN_INDEX] = qList[GREEN_INDEX].toInt(); returnValue[BLUE_INDEX] = qList[BLUE_INDEX].toInt(); } + + +glm::vec3 qMapToGlmVec3(const QVariant& q) { + QVariantMap qMap = q.toMap(); + if (qMap.contains("x") && qMap.contains("y") && qMap.contains("y")) { + return glm::vec3( + qMap["x"].toFloat(), + qMap["y"].toFloat(), + qMap["z"].toFloat() + ); + } else { + return glm::vec3(); + } +} + +glm::quat qMapToGlmQuat(const QVariant& q) { + QVariantMap qMap = q.toMap(); + if (qMap.contains("w") && qMap.contains("x") && qMap.contains("y") && qMap.contains("z")) { + return glm::quat( + qMap["w"].toFloat(), + qMap["x"].toFloat(), + qMap["y"].toFloat(), + qMap["z"].toFloat() + ); + } else { + return glm::quat(); + } +} + +glm::mat4 qMapToGlmMat4(const QVariant& q) { + QVariantMap qMap = q.toMap(); + if (qMap.contains("r0c0") && qMap.contains("r1c0") && qMap.contains("r2c0") && qMap.contains("r3c0") + && qMap.contains("r0c1") && qMap.contains("r1c1") && qMap.contains("r2c1") && qMap.contains("r3c1") + && qMap.contains("r0c2") && qMap.contains("r1c2") && qMap.contains("r2c2") && qMap.contains("r3c2") + && qMap.contains("r0c3") && qMap.contains("r1c3") && qMap.contains("r2c3") && qMap.contains("r3c3")) { + return glm::mat4( + qMap["r0c0"].toFloat(), qMap["r1c0"].toFloat(), qMap["r2c0"].toFloat(), qMap["r3c0"].toFloat(), + qMap["r0c1"].toFloat(), qMap["r1c1"].toFloat(), qMap["r2c1"].toFloat(), qMap["r3c1"].toFloat(), + qMap["r0c2"].toFloat(), qMap["r1c2"].toFloat(), qMap["r2c2"].toFloat(), qMap["r3c2"].toFloat(), + qMap["r0c3"].toFloat(), qMap["r1c3"].toFloat(), qMap["r2c3"].toFloat(), qMap["r3c3"].toFloat() + ); + } else { + return glm::mat4(); + } +} diff --git a/libraries/shared/src/QVariantGLM.h b/libraries/shared/src/QVariantGLM.h index 3a91110250..314889d5dd 100644 --- a/libraries/shared/src/QVariantGLM.h +++ b/libraries/shared/src/QVariantGLM.h @@ -27,3 +27,7 @@ QVariantMap glmToQMap(const glm::quat& glmQuat); glm::vec3 qListToGlmVec3(const QVariant& q); glm::quat qListToGlmQuat(const QVariant& q); void qListtoRgbColor(const QVariant& q, rgbColor& returnValue); + +glm::vec3 qMapToGlmVec3(const QVariant& q); +glm::quat qMapToGlmQuat(const QVariant& q); +glm::mat4 qMapToGlmMat4(const QVariant& q);