diff --git a/examples/painting/whiteboard/whiteboardSpawner.js b/examples/painting/whiteboard/whiteboardSpawner.js index f3005495ec..f6c7aa77ca 100644 --- a/examples/painting/whiteboard/whiteboardSpawner.js +++ b/examples/painting/whiteboard/whiteboardSpawner.js @@ -247,4 +247,4 @@ function cleanup() { // Uncomment this line to delete whiteboard and all associated entity on script close -//Script.scriptEnding.connect(cleanup); +// Script.scriptEnding.connect(cleanup); diff --git a/examples/rayPickingFilterExample.js b/examples/rayPickingFilterExample.js new file mode 100644 index 0000000000..73d151847b --- /dev/null +++ b/examples/rayPickingFilterExample.js @@ -0,0 +1,73 @@ + // + // rayPickingFilterExample.js + // examples + // + // Created by Eric Levin on 12/24/2015 + // Copyright 2015 High Fidelity, Inc. + // + // This is an example script that demonstrates the use of filtering entities for ray picking + // + // Distributed under the Apache License, Version 2.0. + // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + // + + + var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); + + var whiteListBox = Entities.addEntity({ + type: "Box", + color: { + red: 10, + green: 200, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: center + }); + + var blackListBox = Entities.addEntity({ + type: "Box", + color: { + red: 100, + green: 10, + blue: 10 + }, + dimensions: { + x: 0.2, + y: 0.2, + z: 0.2 + }, + position: Vec3.sum(center, { + x: 0, + y: 0.3, + z: 0 + }) + }); + + + function castRay(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + // In this example every entity will be pickable except the entities in the blacklist array + // the third argument is the whitelist array,and the fourth and final is the blacklist array + var pickResults = Entities.findRayIntersection(pickRay, true, [], [blackListBox]); + + // With below example, only entities added to whitelist will be pickable + // var pickResults = Entities.findRayIntersection(pickRay, true, [whiteListBox], []); + + if (pickResults.intersects) { + print("INTERSECTION!"); + } + + } + + function cleanup() { + Entities.deleteEntity(whiteListBox); + Entities.deleteEntity(blackListBox); + } + + Script.scriptEnding.connect(cleanup); + Controller.mousePressEvent.connect(castRay); \ No newline at end of file diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 996a8f3e1d..f517c49b00 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -487,7 +487,8 @@ void EntityTreeRenderer::deleteReleasedModels() { } RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; if (_tree) { EntityTreePointer entityTree = std::static_pointer_cast(_tree); @@ -495,7 +496,7 @@ RayToEntityIntersectionResult EntityTreeRenderer::findRayIntersectionWorker(cons OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, - result.face, result.surfaceNormal, entityIdsToInclude, + result.face, result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 076fe26d6f..2c205336c0 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -130,7 +130,8 @@ private: QList _releasedModels; RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude = QVector()); + bool precisionPicking, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector()); EntityItemID _currentHoverOverEntityID; EntityItemID _currentClickingOnEntityID; diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a0a6719521..0746c2a824 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -357,19 +357,21 @@ QVector EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corn return result; } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - QVector entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + QVector entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking, entitiesToInclude, entitiesToDiscard); } -RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude) { - const QVector& entities = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); - return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entities); +RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking, const QScriptValue& entityIdsToInclude, const QScriptValue& entityIdsToDiscard) { + const QVector& entitiesToInclude = qVectorEntityItemIDFromScriptValue(entityIdsToInclude); + const QVector entitiesToDiscard = qVectorEntityItemIDFromScriptValue(entityIdsToDiscard); + return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking, entitiesToInclude, entitiesToDiscard); } RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude) { + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard) { RayToEntityIntersectionResult result; @@ -377,7 +379,7 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke OctreeElementPointer element; EntityItemPointer intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, - result.surfaceNormal, entityIdsToInclude, (void**)&intersectedEntity, lockType, &result.accurate, + result.surfaceNormal, entityIdsToInclude, entityIdsToDiscard, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { result.entityID = intersectedEntity->getEntityItemID(); diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index f745b6b644..d08a1b7e36 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -112,11 +112,11 @@ public slots: /// 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. - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersection(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); Q_INVOKABLE void setLightsArePickable(bool value); Q_INVOKABLE bool getLightsArePickable() const; @@ -189,7 +189,7 @@ private: /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode RayToEntityIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, - bool precisionPicking, const QVector& entityIdsToInclude); + bool precisionPicking, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard); EntityTreePointer _entityTree; EntitiesScriptEngineProvider* _entitiesScriptEngine = nullptr; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba6294f8a8..f0a03623c2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -498,6 +498,7 @@ public: BoxFace& face; glm::vec3& surfaceNormal; const QVector& entityIdsToInclude; + const QVector& entityIdsToDiscard; void** intersectedObject; bool found; bool precisionPicking; @@ -510,7 +511,7 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { EntityTreeElementPointer entityTreeElementPointer = std::dynamic_pointer_cast(element); if (entityTreeElementPointer ->findRayIntersection(args->origin, args->direction, keepSearching, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, - args->intersectedObject, args->precisionPicking)) { + args->entityIdsToDiscard, args->intersectedObject, args->precisionPicking)) { args->found = true; } return keepSearching; @@ -518,9 +519,9 @@ bool findRayIntersectionOp(OctreeElementPointer element, void* extraData) { bool EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, void** intersectedObject, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, void** intersectedObject, Octree::lockType lockType, bool* accurateResult, bool precisionPicking) { - RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, intersectedObject, false, precisionPicking }; + RayArgs args = { origin, direction, element, distance, face, surfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, false, precisionPicking }; distance = FLT_MAX; bool requireLock = lockType == Octree::Lock; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5e54e562a0..f68e2d59e9 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -84,6 +84,7 @@ public: virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& node, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude = QVector(), + const QVector& entityIdsToDiscard = QVector(), void** intersectedObject = NULL, Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL, diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ff2e97e8fe..8944c95cbc 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -475,8 +475,8 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - void** intersectedObject, bool precisionPicking) { + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking) { keepSearching = true; // assume that we will continue searching after this. @@ -501,7 +501,7 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: if (_cube.contains(origin) || distanceToElementCube < distance) { if (findDetailedRayIntersection(origin, direction, keepSearching, element, distanceToElementDetails, - face, localSurfaceNormal, entityIdsToInclude, intersectedObject, precisionPicking, distanceToElementCube)) { + face, localSurfaceNormal, entityIdsToInclude, entityIdsToDiscard, intersectedObject, precisionPicking, distanceToElementCube)) { if (distanceToElementDetails < distance) { distance = distanceToElementDetails; @@ -516,13 +516,13 @@ bool EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm:: bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, - const QVector& entityIdsToInclude, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { + const QVector& entityIdsToInclude, const QVector& entityIDsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube) { // only called if we do intersect our bounding cube, but find if we actually intersect with entities... int entityNumber = 0; bool somethingIntersected = false; forEachEntity([&](EntityItemPointer entity) { - if (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) { + if ( (entityIdsToInclude.size() > 0 && !entityIdsToInclude.contains(entity->getID())) || (entityIDsToDiscard.size() > 0 && entityIDsToDiscard.contains(entity->getID())) ) { return; } diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d8a182156d..aa05438bde 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -144,11 +144,13 @@ public: virtual bool canRayIntersect() const { return hasEntities(); } virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& node, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject = NULL, bool precisionPicking = false); virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, + const QVector& entityIdsToDiscard, void** intersectedObject, bool precisionPicking, float distanceToElementCube); virtual bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject) const;