51 PH_ASSERT_GT(m_numNodes, 0);
61 constexpr int MAX_STACK_HEIGHT = 64;
64 if(!m_rootAABB.isIntersectingVolume(segment, &minT, &maxT))
68 TLineSegment<real> intersectSegment(segment.getOrigin(), segment.getDir(), minT, maxT);
70 const Vector3R rcpRayDir(segment.getDir().rcp());
72 std::array<NodeState, MAX_STACK_HEIGHT> nodeStack;
75 const Node* currentNode = &(m_nodeBuffer[0]);
78 if(!currentNode->isLeaf())
80 const auto splitAxis = currentNode->getSplitAxis();
81 const real splitPlaneT = (currentNode->getSplitPos() - segment.getOrigin()[splitAxis]) * rcpRayDir[splitAxis];
83 const Node* nearHitNode;
84 const Node* farHitNode;
86 (segment.getOrigin()[splitAxis] < currentNode->getSplitPos()) ||
87 (segment.getOrigin()[splitAxis] == currentNode->getSplitPos() &&
88 segment.getDir()[splitAxis] <= 0))
90 nearHitNode = currentNode + 1;
91 farHitNode = &(m_nodeBuffer[currentNode->getPositiveChildIndex()]);
95 nearHitNode = &(m_nodeBuffer[currentNode->getPositiveChildIndex()]);
96 farHitNode = currentNode + 1;
101 if(splitPlaneT > maxT || splitPlaneT < 0)
103 currentNode = nearHitNode;
107 else if(splitPlaneT < minT)
109 currentNode = farHitNode;
115 PH_ASSERT_LT(stackHeight, MAX_STACK_HEIGHT);
117 nodeStack[stackHeight].node = farHitNode;
118 nodeStack[stackHeight].minT = splitPlaneT;
119 nodeStack[stackHeight].maxT = maxT;
122 currentNode = nearHitNode;
129 const std::size_t numItems = currentNode->numItems();
133 const auto itemIndex = currentNode->getSingleItemDirectIndex();
134 const Item& item = getItem(itemIndex);
136 std::optional<real> hitT;
137 if constexpr(TEST_WITH_ITEM_IDX)
139 hitT = intersectionTester(item, intersectSegment, itemIndex);
143 hitT = intersectionTester(item, intersectSegment);
148 intersectSegment.setMaxT(*hitT);
154 for(std::size_t i = 0; i < numItems; ++i)
156 const Index itemIndex = m_itemIndices[currentNode->getIndexBufferOffset() + i];
157 const Item& item = getItem(itemIndex);
159 std::optional<real> hitT;
160 if constexpr(TEST_WITH_ITEM_IDX)
162 hitT = intersectionTester(item, intersectSegment, itemIndex);
166 hitT = intersectionTester(item, intersectSegment);
171 intersectSegment.setMaxT(*hitT);
180 currentNode = nodeStack[stackHeight].node;
181 minT = nodeStack[stackHeight].minT;
182 maxT = nodeStack[stackHeight].maxT;
185 if(intersectSegment.getMaxT() < minT)
201 typename IndexToItem,
204inline auto TIndexedKdtree<IndexToItem, ItemToAABB, Index>
208 PH_ASSERT(!isEmpty());
214 typename IndexToItem,
221 return m_numItems == 0;
225 typename IndexToItem,
232 PH_ASSERT_LT(idx, m_numItems);
234 return m_indexToItem(lossless_cast<Index>(idx));
238 typename IndexToItem,
244 PH_ASSERT_GT(m_numItems, 0);
246 const auto maxNodeDepth =
static_cast<std::size_t
>(8 + 1.3 * std::log2(m_numItems) + 0.5);
249 std::vector<AABB3D> itemAABBs;
250 m_rootAABB = itemToAABB(getItem(0));
251 for(std::size_t i = 0; i < m_numItems; ++i)
253 const AABB3D aabb = itemToAABB(getItem(i));
255 itemAABBs.push_back(aabb);
256 m_rootAABB.unionWith(aabb);
259 std::unique_ptr<Index[]> negativeItemIndicesCache(
new Index[m_numItems]);
260 std::unique_ptr<Index[]> positiveItemIndicesCache(
new Index[m_numItems * maxNodeDepth]);
262 PH_ASSERT_LE(m_numItems - 1, std::numeric_limits<Index>::max());
263 for(std::size_t i = 0; i < m_numItems; ++i)
265 negativeItemIndicesCache[i] = lossless_cast<Index>(i);
268 std::array<std::unique_ptr<ItemEndpoint[]>, 3> endPointsCache;
269 for(
auto&& cache : endPointsCache)
271 cache = std::unique_ptr<ItemEndpoint[]>(
new ItemEndpoint[m_numItems * 2]);
277 negativeItemIndicesCache.get(),
284 negativeItemIndicesCache.get(),
285 positiveItemIndicesCache.get(),
290 typename IndexToItem,
293inline void TIndexedKdtree<IndexToItem, ItemToAABB, Index>
295 std::size_t nodeIndex,
297 const Index* nodeItemIndices,
298 std::size_t numNodeItems,
299 std::size_t currentNodeDepth,
300 std::size_t currentBadRefines,
301 const std::vector<AABB3D>& itemAABBs,
302 const std::size_t maxNodeDepth,
303 IndexedKdtreeParams params,
304 Index* negativeItemIndicesCache,
305 Index* positiveItemIndicesCache,
306 std::array<std::unique_ptr<ItemEndpoint[]>, 3>& endpointsCache)
309 if(m_numNodes > m_nodeBuffer.size())
311 m_nodeBuffer.resize(m_numNodes * 2);
313 PH_ASSERT_LT(nodeIndex, m_nodeBuffer.size());
315 if(currentNodeDepth == maxNodeDepth || numNodeItems <= params.getMaxNodeItems())
317 m_nodeBuffer[nodeIndex] = Node::makeLeaf({nodeItemIndices, numNodeItems}, m_itemIndices);
321 const real noSplitCost = params.getInteractCost() *
static_cast<real
>(numNodeItems);
322 const real rcpNodeSurfaceArea = 1.0_r / nodeAABB.getSurfaceArea();
323 const Vector3R nodeExtents = nodeAABB.getExtents();
325 real bestSplitCost = std::numeric_limits<real>::max();
327 std::size_t bestEndpointIndex = std::numeric_limits<std::size_t>::max();
328 int axis =
static_cast<int>(nodeExtents.maxDimension());
329 int numSplitTrials = 0;
330 while(bestAxis == -1 && numSplitTrials < 3)
332 for(std::size_t i = 0; i < numNodeItems; ++i)
334 const Index itemIndex = nodeItemIndices[i];
335 const AABB3D& itemAABB = itemAABBs[itemIndex];
337 endpointsCache[axis][2 * i + 1] = ItemEndpoint{itemAABB.getMaxVertex()[axis], itemIndex,
EEndpoint::MAX};
340 std::sort(&(endpointsCache[axis][0]), &(endpointsCache[axis][2 * numNodeItems]),
341 [](
const ItemEndpoint& a,
const ItemEndpoint& b) ->
bool
343 return a.position != b.position ? a.position < b.position
347 std::size_t numNegativeItems = 0;
348 std::size_t numPositiveItems = numNodeItems;
349 for(std::size_t e = 0; e < 2 * numNodeItems; ++e)
357 real endpoint = endpointsCache[axis][e].position;
358 if(endpoint > nodeAABB.getMinVertex()[axis] &&
359 endpoint < nodeAABB.getMaxVertex()[axis])
361 Vector3R endpointMinVertex = nodeAABB.getMinVertex();
362 Vector3R endpointMaxVertex = nodeAABB.getMaxVertex();
363 endpointMinVertex[axis] = endpoint;
364 endpointMaxVertex[axis] = endpoint;
366 const real probNegative =
AABB3D(nodeAABB.getMinVertex(), endpointMaxVertex).
getSurfaceArea() * rcpNodeSurfaceArea;
367 const real probPositive =
AABB3D(endpointMinVertex, nodeAABB.getMaxVertex()).
getSurfaceArea() * rcpNodeSurfaceArea;
368 const real emptyBonus = (numNegativeItems == 0 || numPositiveItems == 0) ? params.getEmptyBonus() : 0.0_r;
369 const real currentSplitCost = params.getTraversalCost() + (1.0_r - emptyBonus) * params.getInteractCost() *
370 (probNegative *
static_cast<real
>(numNegativeItems) + probPositive *
static_cast<real
>(numPositiveItems));
372 if(currentSplitCost < bestSplitCost)
374 bestSplitCost = currentSplitCost;
376 bestEndpointIndex = e;
387 axis = (axis + 1) % 3;
390 std::size_t newNumBadRefines = currentBadRefines;
391 if(bestSplitCost > noSplitCost)
396 if((bestSplitCost > 4 * noSplitCost && numNodeItems < 16) ||
398 newNumBadRefines == 3)
400 m_nodeBuffer[nodeIndex] = Node::makeLeaf({nodeItemIndices, numNodeItems}, m_itemIndices);
404 std::size_t numNegativeItems = 0;
405 for(std::size_t e = 0; e < bestEndpointIndex; ++e)
409 negativeItemIndicesCache[numNegativeItems++] = endpointsCache[bestAxis][e].index;
412 std::size_t numPositiveItems = 0;
413 for(std::size_t e = bestEndpointIndex + 1; e < 2 * numNodeItems; ++e)
417 positiveItemIndicesCache[numPositiveItems++] = endpointsCache[bestAxis][e].index;
420 PH_ASSERT(numNegativeItems <= numNodeItems && numPositiveItems <= numNodeItems);
422 const real bestSplitPos = endpointsCache[bestAxis][bestEndpointIndex].position;
424 Vector3R splitPosMinVertex = nodeAABB.getMinVertex();
425 Vector3R splitPosMaxVertex = nodeAABB.getMaxVertex();
426 splitPosMinVertex[bestAxis] = bestSplitPos;
427 splitPosMaxVertex[bestAxis] = bestSplitPos;
428 const AABB3D negativeNodeAABB(nodeAABB.getMinVertex(), splitPosMaxVertex);
429 const AABB3D positiveNodeAABB(splitPosMinVertex, nodeAABB.getMaxVertex());
434 negativeItemIndicesCache,
436 currentNodeDepth + 1,
441 negativeItemIndicesCache,
442 positiveItemIndicesCache + numNodeItems,
445 const std::size_t positiveChildIndex = m_numNodes;
446 m_nodeBuffer[nodeIndex] = Node::makeInner(bestSplitPos, bestAxis, positiveChildIndex);
451 positiveItemIndicesCache,
453 currentNodeDepth + 1,
458 negativeItemIndicesCache,
459 positiveItemIndicesCache + numNodeItems,