1

I am trying to implement a kd-tree for my C++ (DirectX) project to speed up my collision detection. My implementation is a really primitive recursive function. The nth_element seems to be working okay (only 1 fps difference if i comment it out). I am not quite sure where the culprit it comming from.

KDTreeNode Box::buildKDTree(std::vector<Ball> balls, int depth) {
    if (balls.size() < 3) {
        return KDTreeNode(balls[0].getPos(), KDTreeLeaf(), KDTreeLeaf());
    }

    Variables::currAxis = depth % 3;
    size_t n = (balls.size() / 2);
std::nth_element(balls.begin(), balls.begin() + n, balls.end()); // SORTS FOR THE ACCORDING AXIS - SEE BALL.CPP FOR IMPLEMENTATION
    std::vector<Ball> leftSide(balls.begin(), balls.begin() + n);
    std::vector<Ball> rightSide(balls.begin() + n, balls.end());
    return KDTreeNode(balls[n].getPos(), this->buildKDTree(leftSide, depth + 1), this->buildKDTree(rightSide, depth + 1));
}

I have overwritten the bool operator in the Ball class:

bool Ball::operator < (Ball& ball)
{
    if (Variables::currAxis == 0) {
        return (XMVectorGetX(this->getPos()) < XMVectorGetX(ball.getPos()));
    } else if (Variables::currAxis == 1) {
        return (XMVectorGetY(this->getPos()) < XMVectorGetY(ball.getPos()));
    } else {
        return (XMVectorGetZ(this->getPos()) < XMVectorGetZ(ball.getPos()));
    }
}

I am pretty sure that this is not an optimal way to handle the construction in real time. Maybe you can help me to get on the right track.

There is one other thing what i am really wondering about: Say i have a lot of spheres in the scene and i use a kd-tree. How do i determine in what leaf they belong? Because at the contruction i am only using the center position, but not their actual diameter? How do i go about this then? Thanks

EDIT: I've implemented all the suggested changes and it runs very good now. Thanks! Here is what i did:

KDTreeNode Box::buildKDTree(std::vector<Ball>::iterator start, std::vector<Ball>::iterator end, int depth) {
    if ((end-start) == 1) {
        return KDTreeNode(balls[0].getPos(), &KDTreeLeaf(), &KDTreeLeaf());
    }

    Variables::currAxis = depth % 3;
    size_t n = (abs(end-start) / 2);
    std::nth_element(start, start + n, end); // SORTS FOR THE ACCORDING AXIS - SEE BALL.CPP FOR IMPLEMENTATION
    return KDTreeNode(balls[n].getPos(), &this->buildKDTree(start, (start+n), depth + 1), &this->buildKDTree((start+n), end, depth + 1));
}

As you can see i am not copying the vectors anymore and i am also passing the left and right child as reference so that they are not copied.

puelo
  • 3,607
  • 2
  • 30
  • 48
  • You might try [std::nth_element](http://stackoverflow.com/a/1719155/572743) instead of sorting. The two halves will not be perfectly sorted, but you don't need that for your kd-tree. The one element you are interested in is in the correct place, and anything lower will be in the lower half. That's really all you care about. – Damon Dec 08 '13 at 23:36
  • Sadly this does not solve my problem. My frame-rate got a little bit better, but i am still unable to move the camera etc. (1.24fps). I guess my main problem is that i have to compare Objects or maybe the XMVectorGetX etc. Not quite sure. I maybe could store just the positions in a vector and compare those. – puelo Dec 08 '13 at 23:46
  • I am stupid. It is not the sort that slows it down. It has to be something different. I just commented it out and it was still very slow (only 1 fps better). – puelo Dec 08 '13 at 23:49
  • [CGAL kd-tree](http://doc.cgal.org/latest/Spatial_searching/classCGAL_1_1Kd__tree.html) – gongzhitaao Dec 08 '13 at 23:52
  • How does your node look like? Do you actually copy all the data to it instead of using pointers? – Jaa-c Dec 09 '13 at 01:43
  • You are creating copies of the data at each iteration (leftSide and rightSide). Use iterators instead. This (in combination with nth_element) will speed it up dramatically. – gvd Dec 09 '13 at 05:55
  • @Jaa-c if i use a reference in the Node class for the left and right child are they not going to be out of scope as soon as i finish the construction of the tree? See my edit. – puelo Dec 09 '13 at 12:41
  • 1
    After using `nth_element` and eliding all those copies, the next obvious optimization would be not to run the tree construction and simulation with collision detection every frame, but to put it in another thread that runs simulations asynchronously on roughly a 10-frame interval. Interpolate between two simulated states for animation in your render thread. That way you avoid these calculations from stalling your rendering. What is on screen will only be 99% correct, but you will not be able to tell the difference. – Damon Dec 09 '13 at 12:53
  • Thanks for this information @Damon. I will consider this! – puelo Dec 09 '13 at 13:04

1 Answers1

1

I see two possible problems:

  1. Passing the vector to the function as a value (this effectively copies the whole vector)
  2. Creating new vectors for the smaller and bigger elements, instead of some in-place processing

Basically the function copies all balls in the initial vector for every level of your kd-tree twice. This should cause some serious slow down, so try to avoid requesting so much memory.

One way to solve it would be to access the data of the vector directly, use nth_element etc. and only pass the indices of the subvectors to the recursive call.

Sigroad
  • 1,583
  • 2
  • 19
  • 17
  • Thanks alot. It runs smooth now. Can you verify that my implementation is correct? I am not quite sure if i can pass those Node and Leaf objects as reference. Will they go out of scope? – puelo Dec 09 '13 at 12:01
  • 1
    @puelo Yes, you are right, they will go out of scope. Hard to say anything without seeing the `KDTreeNode` class but the standard solution would be to create them on the heap with `new` and use pointers later. – Sigroad Dec 09 '13 at 12:43