[ODE] - dHeightfield 0.1b -

gl gl at ntlworld.com
Wed Mar 12 11:24:02 2003


A bout of illness later, I've finally got something for you to play with.

http://r-i-l.net/_files/ODE/dHeightfield0.1b.zip

Right now it only implements sphere collision, but I should have my ray
collider in soon.  I've also included skeleton code for other geoms, so if
anybody can implement those, please do!  I know Fabian has been working on
boxes - cylinders would be really handy too.

Details of how it works are in the dHeighfield header - here's a quick
overview:

1) Build the dHeightfield static lib (VC6 workspace included), then include
dHeightfield.h in your project and link with the lib.

2) You supply a terrain_tile struct (blech, bad name, will change for next
release) for each of your heightfields/tiles (or you can break a large
heightfield up into collider tiles for performance if you like).

When a geom's AABB overlaps the heightfield's, it requests the triangles for
all the quads this AABB potentially intersects (in 2D) from the app via a
callback function.  For effeciency I decided to use a single callback, where
you fill in each 'patch' vertex only once, and then fill out a face index
array (just like indexed rendering).  You can also supply face normals if
you don't want them auto-generated.  I've pasted my own tri callback code to
get you started (at the end).

Internally I'm using a vertex/face cache per tile, to avoid having to
allocate memory all the time, and to reuse cached stuff wherever possible.

I ended up using Pierre's suggested method (closest point in tri) for the
sphere collider - it gives you a guaranteed max 1 contact per triangle,
which also makes estimating how many contacts to allow easier.  This is
important because if you don't allow enough contacts in a dense heightfield,
spheres can actually fall through it!  I've written it so that deep
penetrations are kicked out, as they are with planes, so you can get away
with relatively few contacts, but some odd things can happen as a result
(balance away).

Fabian, I haven't tested how this compares performance wise with your
method, as I never got the edges to work - perhaps you can give it a shot?
(I'll send you the code I got working, so maybe you can fix the few
outstanding problems).

Let me know suggestions etc.
--
gl

// -------------------------------------------------------------------------
-------------------------------
void ODETriCallback (terrain_tile *tile,
      unsigned minx_vertex, unsigned maxx_vertex,
      unsigned miny_vertex, unsigned maxy_vertex,
      dVector3 *vertices,
      hf_face  *faces)
 {
 world_tile &wtile = *(world_tile*)tile;

 unsigned min_x = wtile.WorldHeightmapIndexX + minx_vertex;
 unsigned max_x = wtile.WorldHeightmapIndexX + maxx_vertex;
 unsigned min_y = wtile.WorldHeightmapIndexY + miny_vertex;
 unsigned max_y = wtile.WorldHeightmapIndexY + maxy_vertex;

 wtile.WorldHeightmap->Open(true);
 dVector3 *vertex = vertices;

 // fill the collider-allocated vertex array from sample heights:
 //  find the position of the first bottom/left sample
 vec3 tile_pos = vec3(wtile.WorldPosition().x - tile->x_halfsize,
       wtile.WorldPosition().y,
       wtile.WorldPosition().z - tile->y_halfsize);
 tile_pos.x += (minx_vertex * tile->sample_spacing);
 tile_pos.z += (miny_vertex * tile->sample_spacing);

 float vert_z = tile_pos.z;
 for(unsigned y=min_y; y<=max_y; y++)
  {
  float vert_x = tile_pos.x;
  for(unsigned x=min_x; x<=max_x; x++)
   {
   float height = (wtile.WorldHeightmap->GetHeight(x,y) / 255.f) - 0.5f;
   height     *= wtile.WorldScale();

   (*vertex)[0] = vert_x;
   (*vertex)[1] = height;
   (*vertex)[2] = vert_z;
   vertex++;

   vert_x += tile->sample_spacing;
   }
  vert_z += tile->sample_spacing;
  }

 wtile.WorldHeightmap->Close();

 // and fill the face index array  (using |\| quad layout in my case)
 const unsigned x_quads  = (max_x - min_x);
 const unsigned y_quads  = (max_y - min_y);
 const unsigned row   = x_quads + 1;
 unsigned    row_start = 0;
 for(y=0; y<y_quads; y++)
  {
  unsigned quad = row_start;
  for(unsigned x=0; x<x_quads; x++)
   {
   faces->index[0] = quad;
   faces->index[1] = quad + row;
   faces->index[2] = quad + 1;
   faces++;

   faces->index[0] = quad + row;
   faces->index[1] = quad + row + 1;
   faces->index[2] = quad + 1;
   faces++;

   quad++;
   }
  row_start += row;
  }
 }