14

I have a very large number of entities (units). On each step, each unit needs to know the positions of all units near it (distance is less then given constant R). All units move continuously. This is in 3D.

On average, there will be 1% of the total unit count near any other given unit with the given constraints.

How can I do this efficiently, without bruteforcing?

Tetrad
  • 30,124
  • 12
  • 94
  • 143
OCyril
  • 304
  • 3
  • 7
  • 7
    You'll want some kind of spacial partitioning system: http://en.wikipedia.org/wiki/Space_partitioning – Tetrad Jan 01 '12 at 14:48

4 Answers4

15

Use one of the common space partitioning algorithms, such as a Quadtree, Octree, BSP tree, or even a simple Grid System. Each has their own pros and cons for each specific scenario. You may read more about them in these books.

Generally (or so I've heard, I'm not too familiar with the reasoning behind this) a Quadtree or Octree is better fit for outdoor environments, while the BSP tree fits indoor scenes better. And the choice between using a Quadtree or an Octree depends on how flat your world is. If there's little variation in the Y axis using an Octree would be wasteful. An Octree is basically a Quadtree with an additional dimension.

Finally, don't disregard the simplicity of the Grid solution. Many people ignore that a simple grid can sometimes be enough (and even more efficient) for their problems, and jump straight to a more complex solution instead.

Using a grid consists simply in dividing the world into evenly spaced regions and storing the entities in the appropriate region of the world. Then, given a position, finding the neighbouring entities would be a matter of iterating over the regions that intersect your radius of search.

Let's say your world ranged from (-1000, -1000) to (1000, 1000) in the XZ plane. You could for instance divide it into a 10x10 grid, like so:

var grid = new List<Entity>[10, 10];

Then you'd place entities into their appropriate cells in the grid. For instance an entity with XZ (-1000, -1000) would fall on cell (0,0) while an entity with XZ (1000, 1000) would fall on cell (9, 9). Then given a position and a radius in the world, you could determine which cells are intersected by this "circle" and iterate only over those, with a simple double for.

Anyway, research all of the alternatives and pick the one that seems to fit your game better. I admit I'm still not knowledgeable enough on the subject to decide which of the algorithms would be best for you.

Edit Found this on another forum and it might help you with the decision:

Grids work best when the vast majority objects fit within a grid square, and the distribution is fairly homogenous. Conversely, quadtrees work when the objects have variable sizes or are clustered in small areas.

Given your vague description of the problem, I'm leaning against the grid solution too (which is, assuming units are small and fairly homogeneously distributed).

David Gouveia
  • 24,875
  • 5
  • 84
  • 124
0

I wrote this some time back. It's now on a commercial site, but you can get the source for personal use for free. It may be overkill and it's written in Java, but it's well documented so it should not be too hard to trim and rewrite in another language. It basically uses an Octree, with tweaks to handle really large objects and multi-threading.

I found an Octree offered the best combination of flexibility and efficiency. I started out with a grid, but it was impossible to size the squares properly and large patches of empty squares used up space and computing power for nothing. (And that was just in 2 dimensions.) My code handles queries from multiple threads, which adds a lot to the complexity, but the documentation should help you work around that if you don't need it.

RalphChapin
  • 226
  • 2
  • 5
0

To boost your efficiency, try to trivially reject the 99% of "units" that are not near the target unit using a very inexpensive bounding box check. And I would hope you could do this without structuring your data spatially. So if all your units are stored in a flat data structure you could try to race through it from start to finish and firstly check is the current unit outside the bounding box of the unit of interest.

Define an oversized bounding box for the unit of interest such that it can safely reject items that have no chance of being considered "near" it. The check for exclusion from a bounding box could be made cheaper than a radius check. However on some systems where this was tested it was found not to be the case. The two perform almost equally. This edited after much debate below.

First: 2D bounding box clip.

// returns true if the circle supplied is completely OUTSIDE the bounding box, rectClip
bool canTrivialRejectCircle(Vertex2D& vCentre, WorldUnit radius, Rect& rectClip) {
  if (vCentre.x + radius < rectClip.l ||
    vCentre.x - radius > rectClip.r ||
    vCentre.y + radius < rectClip.b ||
    vCentre.y - radius > rectClip.t)
    return true;
  else
    return false;
}

Compared with something like this (in 3D):

BOOL bSphereTest(CObject3D* obj1, CObject3D* obj2 )
{
  D3DVECTOR relPos = obj1->prPosition - obj2->prPosition;
  float dist = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z;
  float minDist = obj1->fRadius + obj2->fRadius;
  return dist <= minDist * minDist;
}.

If the object is not trivially rejected then you perform a more expensive and accurate collision test. But you are just looking for nearnesss so the sphere test is suitable for that, but only for the 1% of objects that survive the trivial rejection.

This article supports the box for trivial rejection. http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3

If this linear approach doesn't give you the performance you need, then a hierarchical data structure may be required such as the other posters have been talking about. R-Trees are worth considering. They support dynamic changes. They are the BTrees of the spatial world.

I just didn't want you going to all that trouble of introducing such complexity if you could avoid it. Plus what about the cost of keeping this complex data strucure up to date as objects move around several times per second?

Bear in mind that a grid is a one level deep spatial data structure. This limit means that it is not truly scaleable. As the world grows in size, so do the number of cells you need to cover. Eventually that number of cells itself becomes a performance problem. For a certain sized world however, it will give you a massive performance boost over no spatial partitioning.

Ciaran
  • 401
  • 3
  • 13
  • 1
    The OP specifically said he wants to avoid a brute force approach, which is exactly what you describe in your first paragraph. Also, how do you figure a bounding box check is cheaper than a bounding sphere check?! That's just wrong. – notlesh Jan 01 '12 at 20:40
  • Yes I know he wants to avoid brute force which would be avoided by going to the effort of introducing a hierarchical data structure to his application. But that could be a lot of effort. If he doesnt want to do that yet, he can try the linear approach which is brute force but may not perform so bad if his list isn't very big. I will try to edit the code above to put in my 2D bounding box trivial reject function. I don't think I was wrong. – Ciaran Jan 01 '12 at 21:11
  • The link to GDnet is broken, but the canonical sphere test is very simple, very cheap and doesn't branch: inside = (dot(p-p0, p-p0) <= r*r) – Lars Viklund Jan 02 '12 at 10:31
  • I have pasted the code up above instead. It looks anything but cheap compared to bounding box. – Ciaran Jan 02 '12 at 11:34
  • And this article supports my argument that box is cheaper than circle/sphere for trivial rejection. The expensive part is computing the distance between the two circle centres: http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3 – Ciaran Jan 02 '12 at 11:57
  • You do realize that your code is an expanded version of mine, right? The author didn't mention what platform he was on, but if a vector subtraction and a single dot product is expensive, he's in deep trouble. – Lars Viklund Jan 02 '12 at 13:51
  • Of course I realize that. stephelton and myself were comparing sphere collision with BB. Performance comparison was the discussion. So sphere collision detection may not be expensive, it just has to be more expensive than BB, which it is. I don't care how fast or slow his hardware platform is. If the software is to be scaleable (for many many objects), then the relative performance of these two techniques becomes important. – Ciaran Jan 02 '12 at 14:25
  • @Ciaran The tests you did there are entirely platform dependent. Flash sometimes performs better if you split up multiple conditions into multiple if statements. In C++ for example this would introduce a lot of branching, which is something you should avoid if you need good performance! If your BB collision would be resolved on the last if statement instead of the first, the performance would go down the drain... – bummzack Jan 03 '12 at 08:38
  • @bummzack Yes, with the BB, the more comparisons it does before rejecting the worse it performs. This could be optimized further. As you say, the code should be written carefully because of the branching. I am interested mainly in C++. However, the article I reference above (not my tests by the way) use ActionScript. They restructured their BB test into seperate if statements which yielded better performance. Their measurements indicate that the BB performs better than the circle test, which is the debate in these comments. On the same site, a C version was mentioned that also favours BB. – Ciaran Jan 03 '12 at 10:21
  • 1
    @Ciaran Quite honestly, that article seems really bad. First of all it doesn't do the tests with realistic data, but rather uses the same values over and over again. Not something you will encounter in a real scenario. And no, according to the article the BB is only faster when there's no collision (eg. the check fails at the first if statement). Also not very realistic. But quite honestly, if you're beginning to optimize things like this, then you're definitely starting at the wrong place. – bummzack Jan 03 '12 at 10:40
  • @bummzack I may revisit the article. In my case, realistic data would be 1000s of Asteroids rocks, one ship and me trying to clip the irrelvant rocks both from drawing and before doing a further expensive and accurate polygon collision test. I didn't measure the performance of the circle intersection but the code does look slower. You say I'm starting at the wrong place. Can you elaborate on that? Whats wrong with developing with performance and scalability in mind as you go along? – Ciaran Jan 03 '12 at 11:01
  • @Ciaran Because the performance difference between a AABB test and a circle vs. circle test is neglectable. The optimization should go where you get the greatest benefits (and where you detect a bottleneck while profiling!). In your case that might be a fast clipping algorithm or an optimized polygon collision test. Also: If your asteroids have a shape that is closer to a circle than a rectangle, then your broad-phase is going to be better with circle vs. circle, as you won't get as many false positives. The opposite is true, when your "average" shape is closer to a rectangle than a circle. – bummzack Jan 03 '12 at 11:08
  • @bummzack By the way, why don't you write a realistic test yourself to try to disprove the published one. – Ciaran Jan 03 '12 at 11:09
  • @bummzack This optimization did yield large benefits because previously I had no clipping. Negligible: we can't say that until you have measured it. Either way, I go with the faster one. I am not concerned with the shape of the clip area. It is irrelevant, as less then 1% of the objects make it to the polygon test. I've just got a warning about extended discussions! We should close this off. – Ciaran Jan 03 '12 at 11:15
  • @Ciaran As a closing note, let me point you to this small test I wrote. It checks 5000 randomly sized objects against each other, which is not very realistic either, but much better than always using the same values as in the article you reference. I did so with circles, and with rectangles. Over 100 tests with 12'497'500 checks for every test, the results are basically even, with a slightly better performance for circle vs. circle (~1ms faster). So I think I proved the fact that AABB or circle vs. circle is negligible. – bummzack Jan 03 '12 at 14:34
  • @bummzack Thanks for the effort. Can you change it to count rejects instead. It means restructuring your if statement for the rect check so that it checks for outside the box instead of inside. I am trying that here. Your time code code does not compile on Windows :-) – Ciaran Jan 03 '12 at 14:51
  • @Ciaran, I think you've REALLY missed the point here. The difference between sphere / BB intersection testing is extremely negligible. If you're have a problem with your intersection detection code, you're WAY better off reducing the number of tests than forcing everything (!!) to use BB or spheres for broad phase. If your data says you have a sphere test that is 1 ns to calculate * 10k tests and it's problematic, you're better off reducing that to 1k tests than reducing the cost to 0.99 ns. Hence my original point (first comment) that brute force is the problem with your answer. – notlesh Jan 08 '12 at 01:16
  • @stephelton Yep. Turns out the difference is surprisingly negligible on some platforms and the solution is kind of brute force. Re: spatial data structures, I already am aware that they result in huge savings. My point was they are more effort to implement and introduce more complexity. I would say most hobbyists avoid using them unless necessary or unless they want the challenge. In fact I wonder how many game developers probably don't even have a broad phase of any kind in their collision or drawing. Thats even more brute force. – Ciaran Jan 08 '12 at 10:39
0

I have to make this an answer because I don't have the points to comment or upvote. For 99% of the people who ask this question, a bounding box is the solution, as described by Ciaran. In a compiled language, it will reject 100,000 irrelevent units in the blink of an eye. There's a lot of overhead involved with non-brute-force solutions; with smaller numbers (say under 1000) they'll be more expensive in terms of processing time than brute force checking. And they'll take vastly more programming time.

I'm not sure what "a very large number" in the question means, or what other people looking here for answers will mean by it. I suspect my numbers above are conservative and could be multiplied by 10; I am personally quite prejudiced against brute force techniques and am seriously annoyed at how well they work. But I wouldn't want someone with, say, 10,000 units to waste time with a fancy solution when a few quick lines of code will do the trick. They can always get fancy later if they need to.

Also, I would note that a bounding sphere check requires multiplication where the bounding box does not. Multiplication, by its nature, takes several times as long as addition and comparisons. There's bound to be some combination of language, OS, and hardware where the sphere check will be faster than a box check, but in most places and times the box check has to be faster, even if the sphere does reject a few irrelevant units the box accepts. (And where the sphere is faster, a new release of the compiler/interpreter/optimizer is very likely to change that.)

RalphChapin
  • 226
  • 2
  • 5
  • While there's nothing wrong with your answer, you're not answering the question. It was specifically asked for a "non bruteforce" approach. Also you seem to repeat what Ciaran already wrote and we had a lengthy comment-discussion about AABB vs. circle tests. The performance difference is simply irrelevant. Better choose a bounding volume that fits most of your collision candidates, as it will reduce the amount of actual narrow-phase tests.. which will have a greater impact on performance overall. – bummzack Jan 04 '12 at 16:20