From a15c5999b5ae6845457ec386c9fbb8a496ea0bf1 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Wed, 10 May 2017 16:58:51 -0700 Subject: [PATCH 01/11] first cut at octree storage of Triangle Set --- interface/src/Application.cpp | 2 +- libraries/render-utils/src/Model.cpp | 4 +- libraries/shared/src/AABox.cpp | 41 +++++++ libraries/shared/src/AABox.h | 15 +++ libraries/shared/src/TriangleSet.cpp | 159 ++++++++++++++++++++++++++- libraries/shared/src/TriangleSet.h | 66 ++++++++++- 6 files changed, 274 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8220b3e891..01b855bb73 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -336,7 +336,7 @@ public: // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code - #ifdef NDEBUG + #if 0 //def NDEBUG deadlockDetectionCrash(); #endif } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index acc84646c5..2c338674eb 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); - for (const auto& triangleSet : _modelSpaceMeshTriangleSets) { + for (auto& triangleSet : _modelSpaceMeshTriangleSets) { float triangleSetDistance = 0.0f; BoxFace triangleSetFace; glm::vec3 triangleSetNormal; @@ -462,6 +462,8 @@ bool Model::convexHullContains(glm::vec3 point) { void Model::calculateTriangleSets() { PROFILE_RANGE(render, __FUNCTION__); + qDebug() << __FUNCTION__ << "url:" << _url; + const FBXGeometry& geometry = getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 3f3146cc04..cea0a83d52 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -114,6 +114,10 @@ static bool isWithin(float value, float corner, float size) { return value >= corner && value <= corner + size; } +bool AABox::contains(const Triangle& triangle) const { + return contains(triangle.v0) && contains(triangle.v1) && contains(triangle.v2); +} + bool AABox::contains(const glm::vec3& point) const { return isWithin(point.x, _corner.x, _scale.x) && isWithin(point.y, _corner.y, _scale.y) && @@ -622,3 +626,40 @@ void AABox::transform(const glm::mat4& matrix) { _corner = newCenter - newDir; _scale = newDir * 2.0f; } + +AABox AABox::getOctreeChild(OctreeChild child) const { + AABox result(*this); // self + switch (child) { + case topLeftNear: + result._corner.y += _scale.y / 2.0f; + break; + case topLeftFar: + result._corner.y += _scale.y / 2.0f; + result._corner.z += _scale.z / 2.0f; + break; + case topRightNear: + result._corner.y += _scale.y / 2.0f; + result._corner.x += _scale.x / 2.0f; + break; + case topRightFar: + result._corner.y += _scale.y / 2.0f; + result._corner.x += _scale.x / 2.0f; + result._corner.z += _scale.z / 2.0f; + break; + case bottomLeftNear: + // _corner = same as parent + break; + case bottomLeftFar: + result._corner.z += _scale.z / 2.0f; + break; + case bottomRightNear: + result._corner.x += _scale.x / 2.0f; + break; + case bottomRightFar: + result._corner.x += _scale.x / 2.0f; + result._corner.z += _scale.z / 2.0f; + break; + } + result._scale /= 2.0f; // everything is half the scale + return result; +} diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index a53cc26163..eef83974ea 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -20,6 +20,7 @@ #include <QDebug> #include "BoxBase.h" +#include "GeometryUtil.h" #include "StreamUtils.h" class AACube; @@ -58,6 +59,7 @@ public: const glm::vec3& getMinimumPoint() const { return _corner; } glm::vec3 getMaximumPoint() const { return calcTopFarLeft(); } + bool contains(const Triangle& triangle) const; bool contains(const glm::vec3& point) const; bool contains(const AABox& otherBox) const; bool touches(const AABox& otherBox) const; @@ -112,6 +114,19 @@ public: void clear() { _corner = INFINITY_VECTOR; _scale = glm::vec3(0.0f); } + typedef enum { + topLeftNear, + topLeftFar, + topRightNear, + topRightFar, + bottomLeftNear, + bottomLeftFar, + bottomRightNear, + bottomRightFar + } OctreeChild; + + AABox getOctreeChild(OctreeChild child) const; // returns the AABox of the would be octree child of this AABox + private: glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const; glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index cdb3fd6b2c..e13d8b126f 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -12,7 +12,22 @@ #include "GLMHelpers.h" #include "TriangleSet.h" -void TriangleSet::insert(const Triangle& t) { + + +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) { + + if (!_isBalanced) { + balanceOctree(); + } + + int trianglesTouched = 0; + auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + return result; +} + +void InternalTriangleSet::insert(const Triangle& t) { _triangles.push_back(t); _bounds += t.v0; @@ -20,15 +35,15 @@ void TriangleSet::insert(const Triangle& t) { _bounds += t.v2; } -void TriangleSet::clear() { +void InternalTriangleSet::clear() { _triangles.clear(); _bounds.clear(); } // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const { +bool InternalTriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { bool intersectedSomething = false; float boxDistance = std::numeric_limits<float>::max(); @@ -38,6 +53,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& if (precision) { for (const auto& triangle : _triangles) { float thisTriangleDistance; + trianglesTouched++; if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { if (thisTriangleDistance < bestDistance) { bestDistance = thisTriangleDistance; @@ -57,7 +73,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& } -bool TriangleSet::convexHullContains(const glm::vec3& point) const { +bool InternalTriangleSet::convexHullContains(const glm::vec3& point) const { if (!_bounds.contains(point)) { return false; } @@ -74,3 +90,136 @@ bool TriangleSet::convexHullContains(const glm::vec3& point) const { return insideMesh; } +void TriangleSet::debugDump() { + qDebug() << __FUNCTION__; + qDebug() << "bounds:" << getBounds(); + qDebug() << "triangles:" << size() << "at top level...."; + qDebug() << "----- _triangleOctree -----"; + _triangleOctree.debugDump(); +} + +void TriangleSet::balanceOctree() { + _triangleOctree.reset(_bounds, 0); + for (const auto& triangle : _triangles) { + _triangleOctree.insert(triangle); + } + _isBalanced = true; +} + +static const int MAX_DEPTH = 3; // for now +static const int MAX_CHILDREN = 8; + +TriangleOctreeCell::TriangleOctreeCell(const AABox& bounds, int depth) { + reset(bounds, depth); +} + +void TriangleOctreeCell::clear() { + _triangleSet.clear(); + _population = 0; +} + +void TriangleOctreeCell::reset(const AABox& bounds, int depth) { + //qDebug() << __FUNCTION__ << "bounds:" << bounds << "depth:" << depth; + clear(); + _triangleSet._bounds = bounds; + _depth = depth; + if (depth <= MAX_DEPTH) { + int childDepth = depth + 1; + _children.clear(); + for (int child = 0; child < MAX_CHILDREN; child++) { + AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); + /* + qDebug() << __FUNCTION__ << "bounds:" << bounds << "depth:" << depth + << "child:" << child << "childBounds:" << childBounds << "childDepth:" << childDepth; + */ + + _children.push_back(TriangleOctreeCell(childBounds, childDepth)); + } + } +} + +void TriangleOctreeCell::debugDump() { + qDebug() << __FUNCTION__; + qDebug() << "bounds:" << getBounds(); + qDebug() << "depth:" << _depth; + qDebug() << "triangleSet:" << _triangleSet.size() << "at this level"; + qDebug() << "population:" << _population << "this level or below"; + if (_depth < MAX_DEPTH) { + int childNum = 0; + for (auto& child : _children) { + qDebug() << "child:" << childNum; + child.debugDump(); + childNum++; + } + } +} + +void TriangleOctreeCell::insert(const Triangle& t) { + _population++; + // if we're not yet at the max depth, then check which child the triangle fits in + if (_depth < MAX_DEPTH) { + for (auto& child : _children) { + if (child.getBounds().contains(t)) { + child.insert(t); + return; + } + } + } + // either we're at max depth, or the triangle doesn't fit in one of our + // children and so we want to just record it here + _triangleSet.insert(t); +} + +bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { + + if (_population < 1) { + return false; // no triangles below here, so we can't intersect + } + + float bestLocalDistance = std::numeric_limits<float>::max(); + BoxFace bestLocalFace; + glm::vec3 bestLocalNormal; + bool intersects = false; + + // if the ray intersects our bounding box, then continue + if (getBounds().findRayIntersection(origin, direction, bestLocalDistance, bestLocalFace, bestLocalNormal)) { + bestLocalDistance = std::numeric_limits<float>::max(); + + float childDistance = std::numeric_limits<float>::max(); + BoxFace childFace; + glm::vec3 childNormal; + + // if we're not yet at the max depth, then check which child the triangle fits in + if (_depth < MAX_DEPTH) { + for (auto& child : _children) { + // check each child, if there's an intersection, it will return some distance that we need + // to compare against the other results, because there might be multiple intersections and + // we will always choose the best (shortest) intersection + if (child.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalNormal = childNormal; + intersects = true; + } + } + } + } + // also check our local triangle set + if (_triangleSet.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalNormal = childNormal; + intersects = true; + } + } + } + if (intersects) { + distance = bestLocalDistance; + face = bestLocalFace; + surfaceNormal = bestLocalNormal; + } + return intersects; +} diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index b54f1a642a..c8703c6445 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -14,20 +14,21 @@ #include "AABox.h" #include "GeometryUtil.h" -class TriangleSet { +class InternalTriangleSet { public: + InternalTriangleSet() { } + void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles size_t size() const { return _triangles.size(); } - const Triangle& getTriangle(size_t t) const { return _triangles[t]; } - - void insert(const Triangle& t); + virtual void insert(const Triangle& t); void clear(); // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an // intersection occurs, the distance and surface normal will be provided. + // note: this might side-effect internal structures bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) const; + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); // Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to // determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a @@ -35,7 +36,60 @@ public: bool convexHullContains(const glm::vec3& point) const; const AABox& getBounds() const { return _bounds; } -private: +protected: std::vector<Triangle> _triangles; AABox _bounds; + + friend class TriangleOctreeCell; +}; + +class TriangleOctreeCell { +public: + TriangleOctreeCell() { } + + void insert(const Triangle& t); + void reset(const AABox& bounds, int depth = 0); + void clear(); + + // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an + // intersection occurs, the distance and surface normal will be provided. + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); + + const AABox& getBounds() const { return _triangleSet.getBounds(); } + + void debugDump(); + +protected: + TriangleOctreeCell(const AABox& bounds, int depth); + + InternalTriangleSet _triangleSet; + std::vector<TriangleOctreeCell> _children; + int _depth { 0 }; + int _population { 0 }; + + friend class TriangleSet; +}; + +class TriangleSet : public InternalTriangleSet { + // pass through public implementation all the features of InternalTriangleSet +public: + TriangleSet() { } + + void debugDump(); + + virtual void insert(const Triangle& t) { + _isBalanced = false; + InternalTriangleSet::insert(t); + } + + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision); + + void balanceOctree(); + +protected: + + bool _isBalanced { false }; + TriangleOctreeCell _triangleOctree; }; From 6d7e98274b0a18d8ca62d8cccc54cac29ed4c884 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 17:07:00 -0700 Subject: [PATCH 02/11] revert debug change --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ded6d6b628..f4699009ac 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -336,7 +336,7 @@ public: // Don't actually crash in debug builds, in case this apparent deadlock is simply from // the developer actively debugging code - #if 0 //def NDEBUG + #ifdef NDEBUG deadlockDetectionCrash(); #endif } From eb3b27849c74e111e16cc831bfade32553c563aa Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 17:07:47 -0700 Subject: [PATCH 03/11] only keep one copy of Triangles --- libraries/render-utils/src/Model.cpp | 2 - libraries/shared/src/TriangleSet.cpp | 153 ++++++++++++++++----------- libraries/shared/src/TriangleSet.h | 61 +++++++---- 3 files changed, 132 insertions(+), 84 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 2c338674eb..9af1c6802e 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -462,8 +462,6 @@ bool Model::convexHullContains(glm::vec3 point) { void Model::calculateTriangleSets() { PROFILE_RANGE(render, __FUNCTION__); - qDebug() << __FUNCTION__ << "url:" << _url; - const FBXGeometry& geometry = getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index e13d8b126f..ef8f3ab91e 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -13,6 +13,22 @@ #include "TriangleSet.h" +void TriangleSet::insert(const Triangle& t) { + _isBalanced = false; + + _triangles.push_back(t); + _bounds += t.v0; + _bounds += t.v1; + _bounds += t.v2; +} + +void TriangleSet::clear() { + _triangles.clear(); + _bounds.clear(); + _isBalanced = false; + + // delete the octree? +} bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) { @@ -27,53 +43,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& return result; } -void InternalTriangleSet::insert(const Triangle& t) { - _triangles.push_back(t); - - _bounds += t.v0; - _bounds += t.v1; - _bounds += t.v2; -} - -void InternalTriangleSet::clear() { - _triangles.clear(); - _bounds.clear(); -} - -// Determine of the given ray (origin/direction) in model space intersects with any triangles -// in the set. If an intersection occurs, the distance and surface normal will be provided. -bool InternalTriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { - - bool intersectedSomething = false; - float boxDistance = std::numeric_limits<float>::max(); - float bestDistance = std::numeric_limits<float>::max(); - - if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) { - if (precision) { - for (const auto& triangle : _triangles) { - float thisTriangleDistance; - trianglesTouched++; - if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - surfaceNormal = triangle.getNormal(); - distance = bestDistance; - } - } - } - } else { - intersectedSomething = true; - distance = boxDistance; - } - } - - return intersectedSomething; -} - - -bool InternalTriangleSet::convexHullContains(const glm::vec3& point) const { +bool TriangleSet::convexHullContains(const glm::vec3& point) const { if (!_bounds.contains(point)) { return false; } @@ -100,16 +70,76 @@ void TriangleSet::debugDump() { void TriangleSet::balanceOctree() { _triangleOctree.reset(_bounds, 0); - for (const auto& triangle : _triangles) { - _triangleOctree.insert(triangle); + + // insert all the triangles + + for (int i = 0; i < _triangles.size(); i++) { + _triangleOctree.insert(i); } + + // prune the empty cells + _triangleOctree.prune(); + _isBalanced = true; } + + +void InternalTriangleSet::insert(int triangleIndex) { + auto& triangle = _allTriangles[triangleIndex]; + + _triangleIndices.push_back(triangleIndex); + + _bounds += triangle.v0; + _bounds += triangle.v1; + _bounds += triangle.v2; +} + +void InternalTriangleSet::clear() { + _triangleIndices.clear(); + _bounds.clear(); +} + +// Determine of the given ray (origin/direction) in model space intersects with any triangles +// in the set. If an intersection occurs, the distance and surface normal will be provided. +bool InternalTriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { + + bool intersectedSomething = false; + float boxDistance = std::numeric_limits<float>::max(); + float bestDistance = std::numeric_limits<float>::max(); + + if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) { + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& triangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findRayTriangleIntersection(origin, direction, triangle, thisTriangleDistance)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + intersectedSomething = true; + surfaceNormal = triangle.getNormal(); + distance = bestDistance; + } + } + } + } else { + intersectedSomething = true; + distance = boxDistance; + } + } + + return intersectedSomething; +} + static const int MAX_DEPTH = 3; // for now static const int MAX_CHILDREN = 8; -TriangleOctreeCell::TriangleOctreeCell(const AABox& bounds, int depth) { +TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) : + _allTriangles(allTriangles), + _triangleSet(allTriangles) +{ reset(bounds, depth); } @@ -118,8 +148,11 @@ void TriangleOctreeCell::clear() { _population = 0; } +void TriangleOctreeCell::prune() { + // do nothing yet... +} + void TriangleOctreeCell::reset(const AABox& bounds, int depth) { - //qDebug() << __FUNCTION__ << "bounds:" << bounds << "depth:" << depth; clear(); _triangleSet._bounds = bounds; _depth = depth; @@ -128,12 +161,7 @@ void TriangleOctreeCell::reset(const AABox& bounds, int depth) { _children.clear(); for (int child = 0; child < MAX_CHILDREN; child++) { AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); - /* - qDebug() << __FUNCTION__ << "bounds:" << bounds << "depth:" << depth - << "child:" << child << "childBounds:" << childBounds << "childDepth:" << childDepth; - */ - - _children.push_back(TriangleOctreeCell(childBounds, childDepth)); + _children.push_back(TriangleOctreeCell(_allTriangles, childBounds, childDepth)); } } } @@ -142,7 +170,7 @@ void TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "depth:" << _depth; - qDebug() << "triangleSet:" << _triangleSet.size() << "at this level"; + //qDebug() << "triangleSet:" << _triangleSet.size() << "at this level"; qDebug() << "population:" << _population << "this level or below"; if (_depth < MAX_DEPTH) { int childNum = 0; @@ -154,20 +182,21 @@ void TriangleOctreeCell::debugDump() { } } -void TriangleOctreeCell::insert(const Triangle& t) { +void TriangleOctreeCell::insert(int triangleIndex) { + const Triangle& triangle = _allTriangles[triangleIndex]; _population++; // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { for (auto& child : _children) { - if (child.getBounds().contains(t)) { - child.insert(t); + if (child.getBounds().contains(triangle)) { + child.insert(triangleIndex); return; } } } // either we're at max depth, or the triangle doesn't fit in one of our // children and so we want to just record it here - _triangleSet.insert(t); + _triangleSet.insert(triangleIndex); } bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index c8703c6445..d98ba86cb5 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -14,14 +14,15 @@ #include "AABox.h" #include "GeometryUtil.h" + + class InternalTriangleSet { public: - InternalTriangleSet() { } + InternalTriangleSet(std::vector<Triangle>& allTriangles) : + _allTriangles(allTriangles) + { } - void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles - size_t size() const { return _triangles.size(); } - - virtual void insert(const Triangle& t); + virtual void insert(int triangleIndex); void clear(); // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an @@ -30,14 +31,11 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); - // Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to - // determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a - // convex hull, the result of this method is meaningless and undetermined. - bool convexHullContains(const glm::vec3& point) const; const AABox& getBounds() const { return _bounds; } protected: - std::vector<Triangle> _triangles; + std::vector<Triangle>& _allTriangles; + std::vector<int> _triangleIndices; AABox _bounds; friend class TriangleOctreeCell; @@ -45,11 +43,16 @@ protected: class TriangleOctreeCell { public: - TriangleOctreeCell() { } + TriangleOctreeCell(std::vector<Triangle>& allTriangles) : + _allTriangles(allTriangles), + _triangleSet(allTriangles) + { } - void insert(const Triangle& t); + + void insert(int triangleIndex); void reset(const AABox& bounds, int depth = 0); void clear(); + void prune(); // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an // intersection occurs, the distance and surface normal will be provided. @@ -61,8 +64,9 @@ public: void debugDump(); protected: - TriangleOctreeCell(const AABox& bounds, int depth); + TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth); + std::vector<Triangle>& _allTriangles; InternalTriangleSet _triangleSet; std::vector<TriangleOctreeCell> _children; int _depth { 0 }; @@ -71,25 +75,42 @@ protected: friend class TriangleSet; }; -class TriangleSet : public InternalTriangleSet { +class TriangleSet { // pass through public implementation all the features of InternalTriangleSet public: - TriangleSet() { } + TriangleSet() : + _triangleOctree(_triangles) + {} void debugDump(); - virtual void insert(const Triangle& t) { - _isBalanced = false; - InternalTriangleSet::insert(t); - } + void insert(const Triangle& t); bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision); void balanceOctree(); + void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles + size_t size() const { return _triangles.size(); } + void clear(); + + // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an + // intersection occurs, the distance and surface normal will be provided. + // note: this might side-effect internal structures + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); + + // Determine if a point is "inside" all the triangles of a convex hull. It is the responsibility of the caller to + // determine that the triangle set is indeed a convex hull. If the triangles added to this set are not in fact a + // convex hull, the result of this method is meaningless and undetermined. + bool convexHullContains(const glm::vec3& point) const; + const AABox& getBounds() const { return _bounds; } + protected: - bool _isBalanced { false }; + bool _isBalanced{ false }; TriangleOctreeCell _triangleOctree; + std::vector<Triangle> _triangles; + AABox _bounds; }; From 89b6a79f6888e9e797c6dd2a9daa0f0a4f53c875 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 17:56:45 -0700 Subject: [PATCH 04/11] make sure to exit early if box intersection is already larger than best triangle intersection --- libraries/shared/src/TriangleSet.cpp | 63 ++++++++++++++++++++-------- libraries/shared/src/TriangleSet.h | 3 +- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index ef8f3ab91e..f9dea7bcc6 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -33,13 +33,16 @@ void TriangleSet::clear() { bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision) { + // reset our distance to be the max possible, lower level tests will store best distance here + distance = std::numeric_limits<float>::max(); + if (!_isBalanced) { balanceOctree(); } int trianglesTouched = 0; auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched); - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + //qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; return result; } @@ -77,10 +80,9 @@ void TriangleSet::balanceOctree() { _triangleOctree.insert(i); } - // prune the empty cells - _triangleOctree.prune(); - _isBalanced = true; + + //debugDump(); } @@ -106,10 +108,17 @@ bool InternalTriangleSet::findRayIntersection(const glm::vec3& origin, const glm float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { bool intersectedSomething = false; - float boxDistance = std::numeric_limits<float>::max(); - float bestDistance = std::numeric_limits<float>::max(); + float boxDistance = distance; // std::numeric_limits<float>::max(); + float bestDistance = distance; // std::numeric_limits<float>::max(); if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) { + + // if our bounding box intersects at a distance greater than the current known + // best distance, than we can safely not check any of our triangles + if (boxDistance > bestDistance) { + return false; + } + if (precision) { for (const auto& triangleIndex : _triangleIndices) { const auto& triangle = _allTriangles[triangleIndex]; @@ -148,10 +157,6 @@ void TriangleOctreeCell::clear() { _population = 0; } -void TriangleOctreeCell::prune() { - // do nothing yet... -} - void TriangleOctreeCell::reset(const AABox& bounds, int depth) { clear(); _triangleSet._bounds = bounds; @@ -159,10 +164,6 @@ void TriangleOctreeCell::reset(const AABox& bounds, int depth) { if (depth <= MAX_DEPTH) { int childDepth = depth + 1; _children.clear(); - for (int child = 0; child < MAX_CHILDREN; child++) { - AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); - _children.push_back(TriangleOctreeCell(_allTriangles, childBounds, childDepth)); - } } } @@ -170,8 +171,9 @@ void TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "depth:" << _depth; - //qDebug() << "triangleSet:" << _triangleSet.size() << "at this level"; qDebug() << "population:" << _population << "this level or below"; + qDebug() << "triangleSet:" << _triangleSet.size() << "in this cell"; + qDebug() << "child cells:" << _children.size(); if (_depth < MAX_DEPTH) { int childNum = 0; for (auto& child : _children) { @@ -187,12 +189,30 @@ void TriangleOctreeCell::insert(int triangleIndex) { _population++; // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { + + // check existing children to see if this triangle fits them... for (auto& child : _children) { if (child.getBounds().contains(triangle)) { child.insert(triangleIndex); return; } } + + // if it doesn't exist in an existing child, then check for new possible children + // note: this will actually re-check the bounds of all the existing children as well, hmmm + for (int child = 0; child < MAX_CHILDREN; child++) { + AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); + if (childBounds.contains(triangle)) { + + // create a child node + auto child = TriangleOctreeCell(_allTriangles, childBounds, _depth + 1); + _children.push_back(child); + + // insert this triangle into it + child.insert(triangleIndex); + return; + } + } } // either we're at max depth, or the triangle doesn't fit in one of our // children and so we want to just record it here @@ -206,16 +226,23 @@ bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm: return false; // no triangles below here, so we can't intersect } - float bestLocalDistance = std::numeric_limits<float>::max(); + float bestLocalDistance = distance; // std::numeric_limits<float>::max(); BoxFace bestLocalFace; glm::vec3 bestLocalNormal; bool intersects = false; // if the ray intersects our bounding box, then continue if (getBounds().findRayIntersection(origin, direction, bestLocalDistance, bestLocalFace, bestLocalNormal)) { - bestLocalDistance = std::numeric_limits<float>::max(); - float childDistance = std::numeric_limits<float>::max(); + // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) + // then we know that none of our triangles can represent a better intersection and we can return + if (bestLocalDistance > distance) { + return false; + } + + bestLocalDistance = distance; // std::numeric_limits<float>::max(); + + float childDistance = distance; // std::numeric_limits<float>::max(); BoxFace childFace; glm::vec3 childNormal; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index d98ba86cb5..c5e4c719cf 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -33,6 +33,8 @@ public: const AABox& getBounds() const { return _bounds; } + size_t size() const { return _triangleIndices.size(); } + protected: std::vector<Triangle>& _allTriangles; std::vector<int> _triangleIndices; @@ -52,7 +54,6 @@ public: void insert(int triangleIndex); void reset(const AABox& bounds, int depth = 0); void clear(); - void prune(); // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an // intersection occurs, the distance and surface normal will be provided. From f1bd06cfa09f5065d4ea2d820242a9f068c67ce7 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 18:33:28 -0700 Subject: [PATCH 05/11] cleanup some unneeded class --- libraries/shared/src/TriangleSet.cpp | 42 +++++++++++----------------- libraries/shared/src/TriangleSet.h | 40 ++++++-------------------- 2 files changed, 24 insertions(+), 58 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index f9dea7bcc6..99b2e2fc79 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -42,7 +42,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& int trianglesTouched = 0; auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched); - //qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; return result; } @@ -82,29 +82,13 @@ void TriangleSet::balanceOctree() { _isBalanced = true; - //debugDump(); + debugDump(); } - -void InternalTriangleSet::insert(int triangleIndex) { - auto& triangle = _allTriangles[triangleIndex]; - - _triangleIndices.push_back(triangleIndex); - - _bounds += triangle.v0; - _bounds += triangle.v1; - _bounds += triangle.v2; -} - -void InternalTriangleSet::clear() { - _triangleIndices.clear(); - _bounds.clear(); -} - // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool InternalTriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { bool intersectedSomething = false; @@ -146,20 +130,20 @@ static const int MAX_DEPTH = 3; // for now static const int MAX_CHILDREN = 8; TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) : - _allTriangles(allTriangles), - _triangleSet(allTriangles) + _allTriangles(allTriangles) { reset(bounds, depth); } void TriangleOctreeCell::clear() { - _triangleSet.clear(); _population = 0; + _triangleIndices.clear(); + _bounds.clear(); } void TriangleOctreeCell::reset(const AABox& bounds, int depth) { clear(); - _triangleSet._bounds = bounds; + _bounds = bounds; _depth = depth; if (depth <= MAX_DEPTH) { int childDepth = depth + 1; @@ -172,7 +156,7 @@ void TriangleOctreeCell::debugDump() { qDebug() << "bounds:" << getBounds(); qDebug() << "depth:" << _depth; qDebug() << "population:" << _population << "this level or below"; - qDebug() << "triangleSet:" << _triangleSet.size() << "in this cell"; + qDebug() << "triangleIndices:" << _triangleIndices.size() << "in this cell"; qDebug() << "child cells:" << _children.size(); if (_depth < MAX_DEPTH) { int childNum = 0; @@ -216,7 +200,13 @@ void TriangleOctreeCell::insert(int triangleIndex) { } // either we're at max depth, or the triangle doesn't fit in one of our // children and so we want to just record it here - _triangleSet.insert(triangleIndex); + _triangleIndices.push_back(triangleIndex); + + /* + _bounds += triangle.v0; + _bounds += triangle.v1; + _bounds += triangle.v2; + */ } bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -263,7 +253,7 @@ bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm: } } // also check our local triangle set - if (_triangleSet.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { + if (findRayIntersectionInternal(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { if (childDistance < bestLocalDistance) { bestLocalDistance = childDistance; bestLocalFace = childFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index c5e4c719cf..65033976cf 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -16,38 +16,10 @@ -class InternalTriangleSet { -public: - InternalTriangleSet(std::vector<Triangle>& allTriangles) : - _allTriangles(allTriangles) - { } - - virtual void insert(int triangleIndex); - void clear(); - - // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an - // intersection occurs, the distance and surface normal will be provided. - // note: this might side-effect internal structures - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); - - const AABox& getBounds() const { return _bounds; } - - size_t size() const { return _triangleIndices.size(); } - -protected: - std::vector<Triangle>& _allTriangles; - std::vector<int> _triangleIndices; - AABox _bounds; - - friend class TriangleOctreeCell; -}; - class TriangleOctreeCell { public: TriangleOctreeCell(std::vector<Triangle>& allTriangles) : - _allTriangles(allTriangles), - _triangleSet(allTriangles) + _allTriangles(allTriangles) { } @@ -60,24 +32,28 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); - const AABox& getBounds() const { return _triangleSet.getBounds(); } + const AABox& getBounds() const { return _bounds; } void debugDump(); protected: TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth); + // checks our internal list of triangles + bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); + std::vector<Triangle>& _allTriangles; - InternalTriangleSet _triangleSet; std::vector<TriangleOctreeCell> _children; int _depth { 0 }; int _population { 0 }; + AABox _bounds; + std::vector<int> _triangleIndices; friend class TriangleSet; }; class TriangleSet { - // pass through public implementation all the features of InternalTriangleSet public: TriangleSet() : _triangleOctree(_triangles) From 4af3c760e6c304fbabce613ab2a4a80a6f2eaf09 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 18:57:33 -0700 Subject: [PATCH 06/11] more cleanup --- libraries/shared/src/TriangleSet.cpp | 34 +++++++------ libraries/shared/src/TriangleSet.h | 74 +++++++++++++--------------- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 99b2e2fc79..2b6e12f005 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -27,7 +27,7 @@ void TriangleSet::clear() { _bounds.clear(); _isBalanced = false; - // delete the octree? + _triangleOctree.clear(); } bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -42,7 +42,12 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& int trianglesTouched = 0; auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, surfaceNormal, precision, trianglesTouched); - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + + #if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + } + #endif return result; } @@ -82,13 +87,15 @@ void TriangleSet::balanceOctree() { _isBalanced = true; + #if WANT_DEBUGGING debugDump(); + #endif } // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { bool intersectedSomething = false; @@ -126,22 +133,23 @@ bool TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, co return intersectedSomething; } -static const int MAX_DEPTH = 3; // for now +static const int MAX_DEPTH = 4; // for now static const int MAX_CHILDREN = 8; -TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) : +TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) { reset(bounds, depth); } -void TriangleOctreeCell::clear() { +void TriangleSet::TriangleOctreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); + _children.clear(); } -void TriangleOctreeCell::reset(const AABox& bounds, int depth) { +void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { clear(); _bounds = bounds; _depth = depth; @@ -151,7 +159,7 @@ void TriangleOctreeCell::reset(const AABox& bounds, int depth) { } } -void TriangleOctreeCell::debugDump() { +void TriangleSet::TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "depth:" << _depth; @@ -168,7 +176,7 @@ void TriangleOctreeCell::debugDump() { } } -void TriangleOctreeCell::insert(int triangleIndex) { +void TriangleSet::TriangleOctreeCell::insert(int triangleIndex) { const Triangle& triangle = _allTriangles[triangleIndex]; _population++; // if we're not yet at the max depth, then check which child the triangle fits in @@ -201,15 +209,9 @@ void TriangleOctreeCell::insert(int triangleIndex) { // either we're at max depth, or the triangle doesn't fit in one of our // children and so we want to just record it here _triangleIndices.push_back(triangleIndex); - - /* - _bounds += triangle.v0; - _bounds += triangle.v1; - _bounds += triangle.v2; - */ } -bool TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { if (_population < 1) { diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 65033976cf..bedb038c58 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -14,46 +14,42 @@ #include "AABox.h" #include "GeometryUtil.h" - - -class TriangleOctreeCell { -public: - TriangleOctreeCell(std::vector<Triangle>& allTriangles) : - _allTriangles(allTriangles) - { } - - - void insert(int triangleIndex); - void reset(const AABox& bounds, int depth = 0); - void clear(); - - // Determine if the given ray (origin/direction) in model space intersects with any triangles in the set. If an - // intersection occurs, the distance and surface normal will be provided. - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); - - const AABox& getBounds() const { return _bounds; } - - void debugDump(); - -protected: - TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth); - - // checks our internal list of triangles - bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); - - std::vector<Triangle>& _allTriangles; - std::vector<TriangleOctreeCell> _children; - int _depth { 0 }; - int _population { 0 }; - AABox _bounds; - std::vector<int> _triangleIndices; - - friend class TriangleSet; -}; - class TriangleSet { + + class TriangleOctreeCell { + public: + TriangleOctreeCell(std::vector<Triangle>& allTriangles) : + _allTriangles(allTriangles) + { } + + void insert(int triangleIndex); + void reset(const AABox& bounds, int depth = 0); + void clear(); + + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); + + const AABox& getBounds() const { return _bounds; } + + void debugDump(); + + protected: + TriangleOctreeCell(std::vector<Triangle>& allTriangles, const AABox& bounds, int depth); + + // checks our internal list of triangles + bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); + + std::vector<Triangle>& _allTriangles; + std::vector<TriangleOctreeCell> _children; + int _depth{ 0 }; + int _population{ 0 }; + AABox _bounds; + std::vector<int> _triangleIndices; + + friend class TriangleSet; + }; + public: TriangleSet() : _triangleOctree(_triangles) From dcb70aa50467dbdf6264cb00f3f79dd7e8da0882 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Fri, 12 May 2017 19:03:08 -0700 Subject: [PATCH 07/11] more cleanup --- libraries/shared/src/TriangleSet.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 2b6e12f005..cd3f829d93 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -99,8 +99,8 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched) { bool intersectedSomething = false; - float boxDistance = distance; // std::numeric_limits<float>::max(); - float bestDistance = distance; // std::numeric_limits<float>::max(); + float boxDistance = distance; + float bestDistance = distance; if (_bounds.findRayIntersection(origin, direction, boxDistance, face, surfaceNormal)) { @@ -218,7 +218,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi return false; // no triangles below here, so we can't intersect } - float bestLocalDistance = distance; // std::numeric_limits<float>::max(); + float bestLocalDistance = distance; BoxFace bestLocalFace; glm::vec3 bestLocalNormal; bool intersects = false; @@ -232,9 +232,9 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi return false; } - bestLocalDistance = distance; // std::numeric_limits<float>::max(); + bestLocalDistance = distance; - float childDistance = distance; // std::numeric_limits<float>::max(); + float childDistance = distance; BoxFace childFace; glm::vec3 childNormal; From da404ce2ce573358344e5efb253e8edcda1659ce Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Sat, 13 May 2017 09:29:32 -0700 Subject: [PATCH 08/11] fix warning --- libraries/shared/src/TriangleSet.cpp | 8 ++------ libraries/shared/src/TriangleSet.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index cd3f829d93..d880b43a20 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -81,7 +81,7 @@ void TriangleSet::balanceOctree() { // insert all the triangles - for (int i = 0; i < _triangles.size(); i++) { + for (size_t i = 0; i < _triangles.size(); i++) { _triangleOctree.insert(i); } @@ -153,10 +153,6 @@ void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { clear(); _bounds = bounds; _depth = depth; - if (depth <= MAX_DEPTH) { - int childDepth = depth + 1; - _children.clear(); - } } void TriangleSet::TriangleOctreeCell::debugDump() { @@ -176,7 +172,7 @@ void TriangleSet::TriangleOctreeCell::debugDump() { } } -void TriangleSet::TriangleOctreeCell::insert(int triangleIndex) { +void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { const Triangle& triangle = _allTriangles[triangleIndex]; _population++; // if we're not yet at the max depth, then check which child the triangle fits in diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index bedb038c58..18494d270e 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -22,7 +22,7 @@ class TriangleSet { _allTriangles(allTriangles) { } - void insert(int triangleIndex); + void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); void clear(); From 5065c7c5c45712f058e5c7c3c762eab7f6bb9a1c Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Sat, 13 May 2017 10:17:59 -0700 Subject: [PATCH 09/11] more warning fixes --- libraries/shared/src/TriangleSet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 18494d270e..3b39b935af 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -45,7 +45,7 @@ class TriangleSet { int _depth{ 0 }; int _population{ 0 }; AABox _bounds; - std::vector<int> _triangleIndices; + std::vector<size_t> _triangleIndices; friend class TriangleSet; }; From 149750fe216b96f067287a1d908e4010e7362fe6 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Sun, 14 May 2017 08:36:54 -0700 Subject: [PATCH 10/11] make the octree sparse --- libraries/shared/src/TriangleSet.cpp | 40 +++++++++++++--------------- libraries/shared/src/TriangleSet.h | 2 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index d880b43a20..aa21aa5cc0 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -45,7 +45,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population; + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); } #endif return result; @@ -80,7 +80,6 @@ void TriangleSet::balanceOctree() { _triangleOctree.reset(_bounds, 0); // insert all the triangles - for (size_t i = 0; i < _triangles.size(); i++) { _triangleOctree.insert(i); } @@ -159,14 +158,15 @@ void TriangleSet::TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "depth:" << _depth; - qDebug() << "population:" << _population << "this level or below"; - qDebug() << "triangleIndices:" << _triangleIndices.size() << "in this cell"; + qDebug() << "population:" << _population << "this level or below" + << " ---- triangleIndices:" << _triangleIndices.size() << "in this cell"; + qDebug() << "child cells:" << _children.size(); if (_depth < MAX_DEPTH) { int childNum = 0; for (auto& child : _children) { qDebug() << "child:" << childNum; - child.debugDump(); + child.second.debugDump(); childNum++; } } @@ -178,26 +178,21 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { - // check existing children to see if this triangle fits them... - for (auto& child : _children) { - if (child.getBounds().contains(triangle)) { - child.insert(triangleIndex); - return; - } - } - - // if it doesn't exist in an existing child, then check for new possible children - // note: this will actually re-check the bounds of all the existing children as well, hmmm for (int child = 0; child < MAX_CHILDREN; child++) { AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); + + + // if the child AABox would contain the triangle... if (childBounds.contains(triangle)) { + // if the child cell doesn't yet exist, create it... + if (_children.find((AABox::OctreeChild)child) == _children.end()) { + _children.insert( + std::pair<AABox::OctreeChild, TriangleOctreeCell> + ((AABox::OctreeChild)child, TriangleOctreeCell(_allTriangles, childBounds, _depth + 1))); + } - // create a child node - auto child = TriangleOctreeCell(_allTriangles, childBounds, _depth + 1); - _children.push_back(child); - - // insert this triangle into it - child.insert(triangleIndex); + // insert the triangleIndex in the child cell + _children.at((AABox::OctreeChild)child).insert(triangleIndex); return; } } @@ -224,6 +219,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) // then we know that none of our triangles can represent a better intersection and we can return + if (bestLocalDistance > distance) { return false; } @@ -240,7 +236,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi // check each child, if there's an intersection, it will return some distance that we need // to compare against the other results, because there might be multiple intersections and // we will always choose the best (shortest) intersection - if (child.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { + if (child.second.findRayIntersection(origin, direction, childDistance, childFace, childNormal, precision, trianglesTouched)) { if (childDistance < bestLocalDistance) { bestLocalDistance = childDistance; bestLocalFace = childFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 3b39b935af..6cedc4da7e 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -41,7 +41,7 @@ class TriangleSet { float& distance, BoxFace& face, glm::vec3& surfaceNormal, bool precision, int& trianglesTouched); std::vector<Triangle>& _allTriangles; - std::vector<TriangleOctreeCell> _children; + std::map<AABox::OctreeChild, TriangleOctreeCell> _children; int _depth{ 0 }; int _population{ 0 }; AABox _bounds; From 9c81a89ac3e43b0adc671e3233593c233b2ddcb1 Mon Sep 17 00:00:00 2001 From: ZappoMan <brad@highfidelity.io> Date: Sun, 14 May 2017 08:37:26 -0700 Subject: [PATCH 11/11] add test script --- .../tests/performance/rayPickPerformance.js | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 scripts/developer/tests/performance/rayPickPerformance.js diff --git a/scripts/developer/tests/performance/rayPickPerformance.js b/scripts/developer/tests/performance/rayPickPerformance.js new file mode 100644 index 0000000000..b4faf4c1be --- /dev/null +++ b/scripts/developer/tests/performance/rayPickPerformance.js @@ -0,0 +1,131 @@ +// +// rayPickingPerformance.js +// examples +// +// Created by Brad Hefta-Gaub on 5/13/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + + +var MIN_RANGE = -3; +var MAX_RANGE = 3; +var RANGE_DELTA = 0.5; +var OUTER_LOOPS = 10; + +// NOTE: These expected results depend completely on the model, and the range settings above +var EXPECTED_TESTS = 1385 * OUTER_LOOPS; +var EXPECTED_INTERSECTIONS = 1286 * OUTER_LOOPS; + + +var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation()))); +var model_url = "http://hifi-content.s3.amazonaws.com/caitlyn/production/Scansite/buddhaReduced.fbx"; + +var rayPickOverlays = Array(); + +var modelEntity = Entities.addEntity({ + type: "Model", + modelURL: model_url, + dimensions: { + x: 0.671, + y: 1.21, + z: 0.938 + }, + position: center +}); + +function rayCastTest() { + var tests = 0; + var intersections = 0; + + var testStart = Date.now(); + for (var t = 0; t < OUTER_LOOPS; t++) { + print("beginning loop:" + t); + for (var x = MIN_RANGE; x < MAX_RANGE; x += RANGE_DELTA) { + for (var y = MIN_RANGE; y < MAX_RANGE; y += RANGE_DELTA) { + for (var z = MIN_RANGE; z < MAX_RANGE; z += RANGE_DELTA) { + if ((x <= -2 || x >= 2) || + (y <= -2 || y >= 2) || + (z <= -2 || z >= 2)) { + + tests++; + + var origin = { x: center.x + x, + y: center.y + y, + z: center.z + z }; + var direction = Vec3.subtract(center, origin); + + var pickRay = { + origin: origin, + direction: direction + }; + + var pickResults = Entities.findRayIntersection(pickRay, true); + + var color; + var visible; + + if (pickResults.intersects && pickResults.entityID == modelEntity) { + intersections++; + color = { + red: 0, + green: 255, + blue: 0 + }; + visible = false; + + } else { + /* + print("NO INTERSECTION?"); + Vec3.print("origin:", origin); + Vec3.print("direction:", direction); + */ + + color = { + red: 255, + green: 0, + blue: 0 + }; + visible = true; + } + + var overlayID = Overlays.addOverlay("line3d", { + color: color, + alpha: 1, + visible: visible, + lineWidth: 2, + start: origin, + end: Vec3.sum(origin,Vec3.multiply(5,direction)) + }); + + rayPickOverlays.push(overlayID); + + } + } + } + } + print("ending loop:" + t); + } + var testEnd = Date.now(); + var testElapsed = testEnd - testStart; + + + print("EXPECTED tests:" + EXPECTED_TESTS + " intersections:" + EXPECTED_INTERSECTIONS); + print("ACTUAL tests:" + tests + " intersections:" + intersections); + print("ELAPSED TIME:" + testElapsed + " ms"); + +} + +function cleanup() { + Entities.deleteEntity(modelEntity); + rayPickOverlays.forEach(function(item){ + Overlays.deleteOverlay(item); + }); +} + +Script.scriptEnding.connect(cleanup); + +rayCastTest(); // run ray cast test immediately \ No newline at end of file