//------------------------------------------------------------------------ float KdTreeNode::cost(const AxisAlignedBoundingBox& aabb, unsigned int lowerCount, unsigned int greaterCount, float distance, Vector3::AXIS direction) const { float lowerSurfaceArea = aabb.getLowerSubAABB(distance, direction).getSurfaceArea(); float greaterSurfaceArea = aabb.getGreaterSubAABB(distance, direction).getSurfaceArea(); return 2.f + 10.f * (lowerSurfaceArea * lowerCount + greaterSurfaceArea * greaterCount); }
//------------------------------------------------------------------------ void KdTreeNode::build(KdTree* tree, const AxisAlignedBoundingBox& aabb, unsigned int depth) { if (hasToStopBuilding(depth)) { return; } float subcost = std::numeric_limits<float>::max(); float distance = std::numeric_limits<float>::max(); Vector3::AXIS axis = aabb.getDirection(); float lowerDistance = aabb.getLowerCorner().get(axis); float greaterDistance = aabb.getGreaterCorner().get(axis); // m_Spheres are sorted along axis to simplify the splitplane research std::sort(m_Spheres->begin(), m_Spheres->end(), [&](const Sphere* s1, const Sphere* s2) { return s1->getCenter().get(axis) < s2->getCenter().get(axis); }); unsigned int greaterCount = unsigned int(m_Spheres->size()); unsigned int lowerCount = 0; /////////////////////////// // Find best split plane // /////////////////////////// for (auto it = m_Spheres->begin(); it != m_Spheres->end(); it++) { const Sphere* pSphere = *it; const AxisAlignedBoundingBox& sphereAABB = pSphere->getAABB(); float newDistance; float newSubcost; newDistance = sphereAABB.getLowerCorner().get(axis); if (lowerDistance < newDistance && newDistance < greaterDistance) { newSubcost = cost(aabb, lowerCount, greaterCount, newDistance, axis); if (newSubcost < subcost) { distance = newDistance; subcost = newSubcost; } } greaterCount--; lowerCount++; newDistance = sphereAABB.getGreaterCorner().get(axis); if (lowerDistance < newDistance && newDistance < greaterDistance) { newSubcost = cost(aabb, lowerCount, greaterCount, newDistance, axis); if (newSubcost < subcost) { distance = newDistance; subcost = newSubcost; } } } // if there is no interesting split if (distance <= lowerDistance || greaterDistance <= distance) { return; } //////////////////// // Split the Node // //////////////////// m_Axis = axis; m_SplitDistance = distance; AxisAlignedBoundingBox boxA = aabb.getLowerSubAABB(distance, axis); AxisAlignedBoundingBox boxB = aabb.getGreaterSubAABB(distance, axis); m_Nodes = tree->getNextNode(); tree->getNextNode(); // we allocate a pair, they are next to each other in memory for (auto it = m_Spheres->begin(); it != m_Spheres->end(); it++) { const Sphere* pSphere = *it; if (boxA.intersect(*pSphere)) { getA()->add(pSphere); } if (boxB.intersect(*pSphere)) { getB()->add(pSphere); } } getA()->build(tree, boxA, depth + 1); getB()->build(tree, boxB, depth + 1); delete m_Spheres; m_Spheres = nullptr; }