From 4aa96e1fbe9cbbd2c4bffca3be0c9bff28a7957d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 29 Apr 2014 15:10:28 -0700 Subject: [PATCH] added support for returning accuracy of ray intersections and other octree tests that can fail due to getting lock --- examples/rayPickExample.js | 6 +++ libraries/octree/src/Octree.cpp | 49 ++++++++++++++++--- libraries/octree/src/Octree.h | 17 ++++--- libraries/script-engine/src/LocalVoxels.cpp | 11 ++++- libraries/script-engine/src/LocalVoxels.h | 11 ++++- libraries/voxels/src/VoxelDetail.cpp | 3 ++ libraries/voxels/src/VoxelDetail.h | 1 + .../voxels/src/VoxelsScriptingInterface.cpp | 13 ++++- .../voxels/src/VoxelsScriptingInterface.h | 30 +++++++----- 9 files changed, 112 insertions(+), 29 deletions(-) diff --git a/examples/rayPickExample.js b/examples/rayPickExample.js index c09a7b1e57..9b08a76a2a 100644 --- a/examples/rayPickExample.js +++ b/examples/rayPickExample.js @@ -19,6 +19,12 @@ function mouseMoveEvent(event) { print("computePickRay direction=" + pickRay.direction.x + ", " + pickRay.direction.y + ", " + pickRay.direction.z); var pickRay = Camera.computePickRay(event.x, event.y); var intersection = Voxels.findRayIntersection(pickRay); + if (!intersection.accurate) { + print(">>> NOTE: intersection not accurate. will try calling Voxels.findRayIntersectionBlocking()"); + intersection = Voxels.findRayIntersectionBlocking(pickRay); + print(">>> AFTER BLOCKING CALL intersection.accurate=" + intersection.accurate); + } + if (intersection.intersects) { print("intersection voxel.red/green/blue=" + intersection.voxel.red + ", " + intersection.voxel.green + ", " + intersection.voxel.blue); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5e812c06c3..266447e27e 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -594,8 +594,9 @@ bool findRayIntersectionOp(OctreeElement* node, void* extraData) { } bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElement*& node, float& distance, BoxFace& face, Octree::lockType lockType) { - RayArgs args = { origin / (float)(TREE_SCALE), direction, node, distance, face }; + OctreeElement*& node, float& distance, BoxFace& face, + Octree::lockType lockType, bool* accurateResult) { + RayArgs args = { origin / (float)(TREE_SCALE), direction, node, distance, face, false}; bool gotLock = false; if (lockType == Octree::Lock) { @@ -604,6 +605,9 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc } else if (lockType == Octree::TryLock) { gotLock = tryLockForRead(); if (!gotLock) { + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.found; // if we wanted to tryLock, and we couldn't then just bail... } } @@ -614,6 +618,9 @@ bool Octree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc unlock(); } + if (accurateResult) { + *accurateResult = true; // if user asked to accuracy or result, let them know this is accurate + } return args.found; } @@ -640,7 +647,8 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { if (element->hasContent()) { glm::vec3 elementPenetration; if (element->findSpherePenetration(args->center, args->radius, elementPenetration, &args->penetratedObject)) { - // NOTE: it is possible for this penetration accumulation algorithm to produce a final penetration vector with zero length. + // NOTE: it is possible for this penetration accumulation algorithm to produce a + // final penetration vector with zero length. args->penetration = addPenetrations(args->penetration, elementPenetration * (float)(TREE_SCALE)); args->found = true; } @@ -649,7 +657,7 @@ bool findSpherePenetrationOp(OctreeElement* element, void* extraData) { } bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, - void** penetratedObject, Octree::lockType lockType) { + void** penetratedObject, Octree::lockType lockType, bool* accurateResult) { SphereArgs args = { center / (float)(TREE_SCALE), @@ -666,6 +674,9 @@ bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::v } else if (lockType == Octree::TryLock) { gotLock = tryLockForRead(); if (!gotLock) { + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.found; // if we wanted to tryLock, and we couldn't then just bail... } } @@ -679,6 +690,9 @@ bool Octree::findSpherePenetration(const glm::vec3& center, float radius, glm::v unlock(); } + if (accurateResult) { + *accurateResult = true; // if user asked to accuracy or result, let them know this is accurate + } return args.found; } @@ -741,7 +755,7 @@ bool findShapeCollisionsOp(OctreeElement* node, void* extraData) { } bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, - glm::vec3& penetration, Octree::lockType lockType) { + glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) { CapsuleArgs args = { start / (float)(TREE_SCALE), @@ -758,6 +772,9 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end } else if (lockType == Octree::TryLock) { gotLock = tryLockForRead(); if (!gotLock) { + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.found; // if we wanted to tryLock, and we couldn't then just bail... } } @@ -767,10 +784,15 @@ bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end if (gotLock) { unlock(); } + + if (accurateResult) { + *accurateResult = true; // if user asked to accuracy or result, let them know this is accurate + } return args.found; } -bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType lockType) { +bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, + Octree::lockType lockType, bool* accurateResult) { ShapeArgs args = { shape, collisions, false }; @@ -781,6 +803,9 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, } else if (lockType == Octree::TryLock) { gotLock = tryLockForRead(); if (!gotLock) { + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.found; // if we wanted to tryLock, and we couldn't then just bail... } } @@ -790,6 +815,10 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions, if (gotLock) { unlock(); } + + if (accurateResult) { + *accurateResult = true; // if user asked to accuracy or result, let them know this is accurate + } return args.found; } @@ -816,7 +845,7 @@ bool getElementEnclosingOperation(OctreeElement* element, void* extraData) { return true; // keep looking } -OctreeElement* Octree::getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType) { +OctreeElement* Octree::getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType, bool* accurateResult) { GetElementEnclosingArgs args; args.point = point; args.element = NULL; @@ -828,6 +857,9 @@ OctreeElement* Octree::getElementEnclosingPoint(const glm::vec3& point, Octree:: } else if (lockType == Octree::TryLock) { gotLock = tryLockForRead(); if (!gotLock) { + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.element; // if we wanted to tryLock, and we couldn't then just bail... } } @@ -838,6 +870,9 @@ OctreeElement* Octree::getElementEnclosingPoint(const glm::vec3& point, Octree:: unlock(); } + if (accurateResult) { + *accurateResult = false; // if user asked to accuracy or result, let them know this is inaccurate + } return args.element; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a11e73ab04..56e8d9d08c 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -249,17 +249,20 @@ public: } lockType; bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - OctreeElement*& node, float& distance, BoxFace& face, Octree::lockType lockType = Octree::TryLock); + OctreeElement*& node, float& distance, BoxFace& face, + Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, - void** penetratedObject = NULL, Octree::lockType lockType = Octree::TryLock); + bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration, void** penetratedObject = NULL, + Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, - glm::vec3& penetration, Octree::lockType lockType = Octree::TryLock); + bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration, + Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); - bool findShapeCollisions(const Shape* shape, CollisionList& collisions, Octree::lockType = Octree::TryLock); + bool findShapeCollisions(const Shape* shape, CollisionList& collisions, + Octree::lockType = Octree::TryLock, bool* accurateResult = NULL); - OctreeElement* getElementEnclosingPoint(const glm::vec3& point, Octree::lockType lockType = Octree::TryLock); + OctreeElement* getElementEnclosingPoint(const glm::vec3& point, + Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL); // Note: this assumes the fileFormat is the HIO individual voxels code files void loadOctreeFile(const char* fileName, bool wantColorRandomizer); diff --git a/libraries/script-engine/src/LocalVoxels.cpp b/libraries/script-engine/src/LocalVoxels.cpp index 1645c229d6..27952b0998 100644 --- a/libraries/script-engine/src/LocalVoxels.cpp +++ b/libraries/script-engine/src/LocalVoxels.cpp @@ -119,10 +119,19 @@ void LocalVoxels::pasteFrom(float x, float y, float z, float scale, const QStrin } RayToVoxelIntersectionResult LocalVoxels::findRayIntersection(const PickRay& ray) { + return findRayIntersectionWorker(ray, Octree::TryLock); +} + +RayToVoxelIntersectionResult LocalVoxels::findRayIntersectionBlocking(const PickRay& ray) { + return findRayIntersectionWorker(ray, Octree::Lock); +} + +RayToVoxelIntersectionResult LocalVoxels::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType) { RayToVoxelIntersectionResult result; if (_tree) { OctreeElement* element; - result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face); + result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, + lockType, &result.accurate); if (result.intersects) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; result.voxel.x = voxel->getCorner().x; diff --git a/libraries/script-engine/src/LocalVoxels.h b/libraries/script-engine/src/LocalVoxels.h index c382d35c09..f6f52aa264 100644 --- a/libraries/script-engine/src/LocalVoxels.h +++ b/libraries/script-engine/src/LocalVoxels.h @@ -76,13 +76,22 @@ public: /// \param source LocalVoxels' source tree Q_INVOKABLE void pasteFrom(float x, float y, float z, float scale, const QString source); - /// If the scripting context has visible voxels, this will determine a ray intersection + /// If the scripting context has visible voxels, this will determine a ray intersection, the results + /// may be inaccurate if the engine is unable to access the visible voxels, in which case result.accurate + /// will be false. Q_INVOKABLE RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray); + + /// If the scripting context has visible voxels, this will determine a ray intersection, and will block in + /// order to return an accurate result + Q_INVOKABLE RayToVoxelIntersectionResult findRayIntersectionBlocking(const PickRay& ray); /// returns a voxel space axis aligned vector for the face, useful in doing voxel math Q_INVOKABLE glm::vec3 getFaceVector(const QString& face); private: + /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode + RayToVoxelIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType); + QString _name; StrongVoxelTreePointer _tree; }; diff --git a/libraries/voxels/src/VoxelDetail.cpp b/libraries/voxels/src/VoxelDetail.cpp index f1855f5f81..c1f7497d18 100644 --- a/libraries/voxels/src/VoxelDetail.cpp +++ b/libraries/voxels/src/VoxelDetail.cpp @@ -41,6 +41,7 @@ void voxelDetailFromScriptValue(const QScriptValue &object, VoxelDetail& voxelDe RayToVoxelIntersectionResult::RayToVoxelIntersectionResult() : intersects(false), + accurate(true), // assume it's accurate voxel(), distance(0), face() @@ -50,6 +51,7 @@ RayToVoxelIntersectionResult::RayToVoxelIntersectionResult() : QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, const RayToVoxelIntersectionResult& value) { QScriptValue obj = engine->newObject(); obj.setProperty("intersects", value.intersects); + obj.setProperty("accurate", value.accurate); QScriptValue voxelValue = voxelDetailToScriptValue(engine, value.voxel); obj.setProperty("voxel", voxelValue); obj.setProperty("distance", value.distance); @@ -88,6 +90,7 @@ QScriptValue rayToVoxelIntersectionResultToScriptValue(QScriptEngine* engine, co void rayToVoxelIntersectionResultFromScriptValue(const QScriptValue& object, RayToVoxelIntersectionResult& value) { value.intersects = object.property("intersects").toVariant().toBool(); + value.accurate = object.property("accurate").toVariant().toBool(); QScriptValue voxelValue = object.property("voxel"); if (voxelValue.isValid()) { voxelDetailFromScriptValue(voxelValue, value.voxel); diff --git a/libraries/voxels/src/VoxelDetail.h b/libraries/voxels/src/VoxelDetail.h index d30255bbb1..76657cf10b 100644 --- a/libraries/voxels/src/VoxelDetail.h +++ b/libraries/voxels/src/VoxelDetail.h @@ -39,6 +39,7 @@ class RayToVoxelIntersectionResult { public: RayToVoxelIntersectionResult(); bool intersects; + bool accurate; VoxelDetail voxel; float distance; BoxFace face; diff --git a/libraries/voxels/src/VoxelsScriptingInterface.cpp b/libraries/voxels/src/VoxelsScriptingInterface.cpp index 15503db454..3f69867530 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.cpp +++ b/libraries/voxels/src/VoxelsScriptingInterface.cpp @@ -122,12 +122,21 @@ void VoxelsScriptingInterface::eraseVoxel(float x, float y, float z, float scale } } - RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersection(const PickRay& ray) { + return findRayIntersectionWorker(ray, Octree::TryLock); +} + +RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersectionBlocking(const PickRay& ray) { + return findRayIntersectionWorker(ray, Octree::Lock); +} + +RayToVoxelIntersectionResult VoxelsScriptingInterface::findRayIntersectionWorker(const PickRay& ray, + Octree::lockType lockType) { RayToVoxelIntersectionResult result; if (_tree) { OctreeElement* element; - result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face); + result.intersects = _tree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, + lockType, &result.accurate); if (result.intersects) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; result.voxel.x = voxel->getCorner().x; diff --git a/libraries/voxels/src/VoxelsScriptingInterface.h b/libraries/voxels/src/VoxelsScriptingInterface.h index d5322fdc4d..e5f2abf629 100644 --- a/libraries/voxels/src/VoxelsScriptingInterface.h +++ b/libraries/voxels/src/VoxelsScriptingInterface.h @@ -35,17 +35,16 @@ public: void setVoxelTree(VoxelTree* tree) { _tree = tree; } void setUndoStack(QUndoStack* undoStack) { _undoStack = undoStack; } -public slots: - +public: /// provide the world scale - const int getTreeScale() const { return TREE_SCALE; } + Q_INVOKABLE const int getTreeScale() const { return TREE_SCALE; } /// checks the local voxel tree for a voxel at the specified location and scale /// \param x the x-coordinate of the voxel (in meter units) /// \param y the y-coordinate of the voxel (in meter units) /// \param z the z-coordinate of the voxel (in meter units) /// \param scale the scale of the voxel (in meter units) - VoxelDetail getVoxelAt(float x, float y, float z, float scale); + Q_INVOKABLE VoxelDetail getVoxelAt(float x, float y, float z, float scale); /// queues the creation of a voxel which will be sent by calling process on the PacketSender /// \param x the x-coordinate of the voxel (in meter units) @@ -55,7 +54,7 @@ public slots: /// \param red the R value for RGB color of voxel /// \param green the G value for RGB color of voxel /// \param blue the B value for RGB color of voxel - void setVoxelNonDestructive(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + Q_INVOKABLE void setVoxelNonDestructive(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); /// queues the destructive creation of a voxel which will be sent by calling process on the PacketSender /// \param x the x-coordinate of the voxel (in meter units) @@ -65,27 +64,36 @@ public slots: /// \param red the R value for RGB color of voxel /// \param green the G value for RGB color of voxel /// \param blue the B value for RGB color of voxel - void setVoxel(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); + Q_INVOKABLE void setVoxel(float x, float y, float z, float scale, uchar red, uchar green, uchar blue); /// queues the deletion of a voxel, sent by calling process on the PacketSender /// \param x the x-coordinate of the voxel (in meter units) /// \param y the y-coordinate of the voxel (in meter units) /// \param z the z-coordinate of the voxel (in meter units) /// \param scale the scale of the voxel (in meter units) - void eraseVoxel(float x, float y, float z, float scale); + Q_INVOKABLE void eraseVoxel(float x, float y, float z, float scale); - /// If the scripting context has visible voxels, this will determine a ray intersection - RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray); + /// If the scripting context has visible voxels, this will determine a ray intersection, the results + /// may be inaccurate if the engine is unable to access the visible voxels, in which case result.accurate + /// will be false. + Q_INVOKABLE RayToVoxelIntersectionResult findRayIntersection(const PickRay& ray); + + /// If the scripting context has visible voxels, this will determine a ray intersection, and will block in + /// order to return an accurate result + Q_INVOKABLE RayToVoxelIntersectionResult findRayIntersectionBlocking(const PickRay& ray); /// returns a voxel space axis aligned vector for the face, useful in doing voxel math - glm::vec3 getFaceVector(const QString& face); + Q_INVOKABLE glm::vec3 getFaceVector(const QString& face); /// checks the local voxel tree for the smallest voxel enclosing the point /// \param point the x,y,z coordinates of the point (in meter units) /// \return VoxelDetail - if no voxel encloses the point then VoxelDetail items will be 0 - VoxelDetail getVoxelEnclosingPoint(const glm::vec3& point); + Q_INVOKABLE VoxelDetail getVoxelEnclosingPoint(const glm::vec3& point); private: + /// actually does the work of finding the ray intersection, can be called in locking mode or tryLock mode + RayToVoxelIntersectionResult findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType); + void queueVoxelAdd(PacketType addPacketType, VoxelDetail& addVoxelDetails); VoxelTree* _tree; QUndoStack* _undoStack;