diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index feaf8562d7..79f4b2a269 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -96,6 +96,9 @@ void Scene::processPendingChangesQueue() { // removes removeItems(consolidatedPendingChanges._removedItems); + // Update the numItemsAtomic counter AFTER the pending changes went through + _numAllocatedItems.exchange(maxID); + // ready to go back to rendering activities _itemsMutex.unlock(); } diff --git a/libraries/render/src/render/SpatialTree.cpp b/libraries/render/src/render/SpatialTree.cpp index b09b6f4778..1bb3538521 100644 --- a/libraries/render/src/render/SpatialTree.cpp +++ b/libraries/render/src/render/SpatialTree.cpp @@ -264,12 +264,33 @@ Octree::Index Octree::accessCellBrick(Index cellID, const CellBrickAccessor& acc return brickID; } +Octree::Location ItemSpatialTree::evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const { + minCoordf = evalCoordf(bound.getMinimumPoint()); + maxCoordf = evalCoordf(bound.getMaximumPoint()); + + // If the bound crosses any of the octree volume limit, then return root cell + if ( (minCoordf.x < 0.0f) + || (minCoordf.y < 0.0f) + || (minCoordf.z < 0.0f) + || (maxCoordf.x >= _size) + || (maxCoordf.y >= _size) + || (maxCoordf.z >= _size)) { + return Location(); + } + + Coord3 minCoord(minCoordf); + Coord3 maxCoord(maxCoordf); + return Location::evalFromRange(minCoord, maxCoord); +} + Octree::Locations ItemSpatialTree::evalLocations(const ItemBounds& bounds) const { Locations locations; + Coord3f minCoordf, maxCoordf; + locations.reserve(bounds.size()); for (auto& bound : bounds) { if (!bound.bound.isNull()) { - locations.emplace_back(evalLocation(bound.bound)); + locations.emplace_back(evalLocation(bound.bound, minCoordf, maxCoordf)); } else { locations.emplace_back(Location()); } @@ -344,11 +365,8 @@ bool ItemSpatialTree::removeItem(Index cellIdx, const ItemKey& key, const ItemID ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& oldKey, const AABox& bound, const ItemID& item, ItemKey& newKey) { auto newCell = INVALID_CELL; if (!newKey.isViewSpace()) { - auto minCoordf = evalCoordf(bound.getMinimumPoint()); - auto maxCoordf = evalCoordf(bound.getMaximumPoint()); - Coord3 minCoord(minCoordf); - Coord3 maxCoord(maxCoordf); - auto location = Location::evalFromRange(minCoord, maxCoord); + Coord3f minCoordf, maxCoordf; + auto location = evalLocation(bound, minCoordf, maxCoordf); // Compare range size vs cell location size and tag itemKey accordingly // If Item bound fits in sub cell then tag as small @@ -403,7 +421,21 @@ ItemSpatialTree::Index ItemSpatialTree::resetItem(Index oldCell, const ItemKey& int Octree::select(CellSelection& selection, const FrustumSelector& selector) const { Index cellID = ROOT_CELL; - return selectTraverse(cellID, selection, selector); + auto cell = getConcreteCell(cellID); + int numSelectedsIn = (int)selection.size(); + + // Always include the root cell partially containing potentially outer objects + selectCellBrick(cellID, selection, false); + + // then traverse deeper + for (int i = 0; i < NUM_OCTANTS; i++) { + Index subCellID = cell.child((Link)i); + if (subCellID != INVALID_CELL) { + selectTraverse(subCellID, selection, selector); + } + } + + return (int)selection.size() - numSelectedsIn; } diff --git a/libraries/render/src/render/SpatialTree.h b/libraries/render/src/render/SpatialTree.h index a5dbb29544..a89b9847e6 100644 --- a/libraries/render/src/render/SpatialTree.h +++ b/libraries/render/src/render/SpatialTree.h @@ -117,7 +117,6 @@ namespace render { return depth; } - class Location { void assertValid() { assert((pos.x >= 0) && (pos.y >= 0) && (pos.z >= 0)); @@ -157,6 +156,7 @@ namespace render { // Eval the location best fitting the specified range static Location evalFromRange(const Coord3& minCoord, const Coord3& maxCoord, Depth rangeDepth = MAX_DEPTH); + // Eval the intersection test against a frustum enum Intersection { Outside = 0, @@ -367,7 +367,7 @@ namespace render { // An octree of Items organizing them efficiently for culling // The octree only cares about the bound & the key of an item to store it a the right cell location class ItemSpatialTree : public Octree { - float _size { 32768.0f }; + float _size{ 32768.0f }; float _invSize { 1.0f / _size }; glm::vec3 _origin { -16384.0f }; @@ -398,10 +398,26 @@ namespace render { return getOrigin() + glm::vec3(coord) * cellWidth; } + + // Clamp a 3D relative position to make sure it is in the valid range space of the octree + glm::vec3 clampRelPosToTreeRange(const glm::vec3& pos) const { + const float EPSILON = 0.0001f; + return glm::vec3( + std::min(std::max(pos.x, 0.0f), _size - EPSILON), + std::min(std::max(pos.y, 0.0f), _size - EPSILON), + std::min(std::max(pos.z, 0.0f), _size - EPSILON)); + } + + // Eval an integer cell coordinate (at the specified deepth) from a given 3d position + // If the 3D position is out of the octree volume, then the position is clamped + // so the integer coordinate is meaningfull Coord3 evalCoord(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const { - auto npos = (pos - getOrigin()); + auto npos = clampRelPosToTreeRange((pos - getOrigin())); return Coord3(npos * getInvCellWidth(depth)); // Truncate fractional part } + + // Eval a real cell coordinate (at the specified deepth) from a given 3d position + // Position is NOT clamped to the boundaries of the octree so beware of conversion to a Coord3! Coord3f evalCoordf(const glm::vec3& pos, Depth depth = Octree::METRIC_COORD_DEPTH) const { auto npos = (pos - getOrigin()); return Coord3f(npos * getInvCellWidth(depth)); @@ -412,9 +428,10 @@ namespace render { float cellWidth = getCellWidth(loc.depth); return AABox(evalPos(loc.pos, cellWidth), cellWidth); } - Location evalLocation(const AABox& bound) const { - return Location::evalFromRange(evalCoord(bound.getMinimumPoint()), evalCoord(bound.getMaximumPoint())); - } + + // Eval the cell location for a given arbitrary Bound, + // if the Bound crosses any of the Octree planes then the root cell is returned + Location evalLocation(const AABox& bound, Coord3f& minCoordf, Coord3f& maxCoordf) const; Locations evalLocations(const ItemBounds& bounds) const; // Managing itemsInserting items in cells