void dGeomEnable (dGeomID);
void dGeomDisable (dGeomID);
int dGeomIsEnabled (dGeomID);
Enable and disable a geom. Disabled geoms are completely ignored by
dSpaceCollide and
dSpaceCollide2, although they can still be members of a space.
dGeomIsEnabled() returns 1 if a geom is enabled or 0 if it is disabled. New geoms are created in the enabled state.
A collision detection ``world'' is created by making a space and then adding geoms to that space. At every time step we want to generate a list of contacts for all the geoms that intersect each other. Three functions are used to do this:
- dCollide intersects two geoms and generates contact points.
- dSpaceCollide determines which pairs of geoms in a space may potentially intersect, and calls a callback function with each candidate pair. This does not generate contact points directly, because the user may want to handle some pairs specially - for example by ignoring them or using different contact generating strategies. Such decisions are made in the callback function, which can choose whether or not to call dCollide for each pair.
- dSpaceCollide2 determines which geoms from one space may potentially intersect with geoms from another space, and calls a callback function with each candidate pair. It can also test a single non-space geom against a space. This function is useful when there is a collision hierarchy, i.e. when there are spaces that contain other spaces.
The collision system has been designed to give the user maximum flexibility to decide which objects will be tested against each other. This is why are there are three collision functions instead of, for example, one function that just generates all the contact points.
Spaces may contain other spaces. These sub-spaces will typically represent a collection of geoms (or other spaces) that are located near each other. This is useful for gaining extra collision performance by dividing the collision world into hierarchies. Here is an example of where this is useful:
Suppose you have two cars driving over some terrain. Each car is made up of many geoms. If all these geoms were inserted into the same space, the collision computation time between the two cars would always be proportional to the total number of geoms (or even to the square of this number, depending on which space type is used).
To speed up collision a separate space is created to represent each car. The car geoms are inserted into the car-spaces, and the car-spaces are inserted into the top level space. At each time step dSpaceCollide is called for the top level space. This will do a single intersection test between the car-spaces (actually between their bounding boxes) and call the callback if they touch. The callback can then test the geoms in the car-spaces against each other using dSpaceCollide2. If the cars are not near each other then the callback is not called and no time is wasted performing unnecessary tests.
If space hierarchies are being used then the callback function may be called recursively, e.g. if dSpaceCollide calls the callback which in turn calls dSpaceCollide with the same callback function. In this case the user must make sure that the callback function is properly reentrant.
Here is a sample callback function that traverses through all spaces and sub-spaces, generating all possible contact points for all intersecting geoms:
void nearCallback (void *data, dGeomID o1, dGeomID o2)
{
if (dGeomIsSpace (o1) || dGeomIsSpace (o2)) {
// colliding a space with something
dSpaceCollide2 (o1,o2,data,&nearCallback);
// collide all geoms internal to the space(s)
if (dGeomIsSpace (o1)) dSpaceCollide (o1,data,&nearCallback);
if (dGeomIsSpace (o2)) dSpaceCollide (o2,data,&nearCallback);
}
else {
// colliding two non-space geoms, so generate contact
// points between o1 and o2
int num_contact = dCollide (o1,o2,max_contacts,contact_array,skip);
// add these contact points to the simulation
...
}
}
...
// collide all objects together
dSpaceCollide (top_level_space,0,&nearCallback);
A space callback function is not allowed to modify a space while that space is being processed with dSpaceCollide or dSpaceCollide2. For example, you can not add or remove geoms from a space, and you can not reposition the geoms within a space. Doing so will trigger a runtime error in the debug build of ODE.
Each geom has a ``category'' and ``collide'' bitfield that can be used to assist the space algorithms in determining which geoms should interact and which should not. Use of this feature is optional - by default geoms are considered to be capable of colliding with any other geom.
Each bit position in the bitfield represents a different category of object. The actual meaning of these categories (if any) is user defined. The category bitfield indicates which categories a geom is a member of. The collide bitfield indicates which categories the geom will collide with during collision detection.
A pair of geoms will be considered by dSpaceCollide and dSpaceCollide2 for passing to the callback only if one of them has a collide bit set that corresponds to a category bit in the other. The exact test is as follows:
// test if geom o1 and geom o2 can collide
cat1 = dGeomGetCategoryBits (o1);
cat2 = dGeomGetCategoryBits (o2);
col1 = dGeomGetCollideBits (o1);
col2 = dGeomGetCollideBits (o2);
if ((cat1 & col2) || (cat2 & col1)) {
// call the callback with o1 and o2
}
else {
// do nothing, o1 and o2 do not collide
}
Note that only dSpaceCollide and dSpaceCollide2 use these bitfields, they are ignored by dCollide.
Typically a geom will belong only to a single category, so only one bit will be set in the category bitfield. The bitfields are guaranteed to be at least 32 bits wide, so the user is able to specify an arbitrary pattern of interactions for up to 32 objects. If there are more than 32 objects then some of them will obviously have to have the same category.
Sometimes the category field will contain multiple bits set, e.g. if the geom is a space them you may want to set the category to the union of all the geom categories that are contained.
Design note: Why don't we just have a single category bitfield and use the test (cat1 & cat2) ? This is simpler, but a single field requires more bits to represent some patterns of interaction. For example, if 32 geoms have an interaction pattern that is a 5 dimensional hypercube, 80 bit are required in the simpler scheme. The simpler scheme also makes it harder to determine what the categories should be for some situations.