[ODE] Re: - dHeightfield 0.1b -

gl gl at ntlworld.com
Wed Mar 12 11:56:01 2003


Oh yeah, another thing - the collider currently only supports unrotated
heightfields in the left-handed coord system (where X,Z is the 2D plane and
+Y is up).

I don't currently need rotations, but they can be added easily enough, by
transforming geoms into the field's space first.
--
gl

----- Original Message -----
From: "gl" <gl@ntlworld.com>
To: <ode@q12.org>
Sent: Wednesday, March 12, 2003 6:23 PM
Subject: - dHeightfield 0.1b -


>
> 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;
>   }
>  }
>