your tile should be made of 2 triangles. What you have is an approximation of a rough plane, and that plane could not possibly go through the 4 points (unless they are on the same plane obviously).
What you have to do, is do it the hard way, and test the intersection with the two triangles on the tile. It is not as complicated as it seems, but be careful if you use triangle strips for rendering, as the tile diagonal segment will 'criss-cross' from one tile to the next tile.
For here, I'll assume all the tiles have the same configuration, with a diagonal going from top-left to bottom-right, and NEVER from bottom-left to top-right.
V0 V1+-------+|\ || \ .(x, z) | \ || \ T1|| \ || T0 \ || \|+-------+V2 V3
when getting the vectors, you also have to be careful, as there are 1 more vertex per line than there are tiles (two vectors for one tile).
how to extract the tile vectors...
void GetTileVertices(Vector* V, float x, float z){ // size of the map in world coordinates float Imin = 0.0f; float Imin = 0.0f; float Jmax = (TILESIZE * (NUM_TILES_ON_X + 1)); float Jmax = (TILESIZE * (NUM_TILES_ON_Z + 1)); // how far are we inside the map (as a 'percentage'). float t = (x - Imin) / (Imax - Imin); float u = (z - Jmin) / (Jmax - Jmin); // bottom left pixel on heightmap corresponding to the bottom left corner of the tile in 3D coordinates int i = (int) (t * TILESIZE); int j = (int) (u * TILESIZE); // the 4 corners of the tile in the 3D float xmin = t * (float) i; float zmin = u * (float) j; float xmax = xmin + TILESIZE; float zmax = zmin + TILESIZE; V[0] = Vector(xmin, GetHeightmapHeight(i , j ), zmin); V[1] = Vector(xmax, GetHeightmapHeight(i+1, j ), zmin); V[2] = Vector(xmin, GetHeightmapHeight(i , j+1), zmax); V[3] = Vector(xmax, GetHeightmapHeight(i+1, j+1), zmax);}
now you have the two triangles making the tile, you need to find the two triangle planes
void CalculateNormal(Vector V0, Vector V1, Vector V2, Vector& N, float &d){ Vector E = V1 - V0; Vector F = V2 - V1; N = E.Cross(F); N.Normalise(); d = V0 * N;}
from there, you need to cast a ray to a plane, from the player position and going down, to find it's position on the surface of the triangle.
// plane normal, plane d-param, ray start pos, ray direction, resultbool RayPlaneIntersect(Vector N, float d, Vector P, Vector D, Vector &Q){ float denom = D * N; // ray parallel to the plane if (fabs(D * N) < 0.000001f) { Q = P; return false; } float t = (d - (P * N)) / denom; Vector Q = P + D * t; return true;}
you need to decide which of the two triangles to test. It depends on wich side of the diagonal line the player is
// tri index, vertices of the tile, pos of player int SelectTileTriangle(Vector* V, Vector Pos){ Vector E = V3 - V0; Vector D = Pos - V0; if (fabs(D.z) < 0.000001f) return 0; float s = fabs(E.x) / fabs(E.z); float t = fabs(D.x) / fabs(D.z); if (t > s) { return 1; } return 0;}
now, you put everything together
Vector V[4];Vector N;float d;Vector Q;GetTileVertices(V, PlayerPos.x, PlayerPos.z);int i = SelectTileTriangle(V, PlayerPos);if (i == 0) CalculateNormal(V0, V3, V1, N, d);else CalculateNormal(V0, V2, V3, N, d);RayPlaneIntersect(N, d, PlayerPos, Vector(0, -1, 0), Q);PlayerPos.y = Q.y;
now, I don't know how this works for you, but it seems that you are using heighmaps vertices as tile heights, when they should be tile CORNERS heights. Hence the '+1' when extracting the tile corners, and diagonal malarkey.
also, the only thing you have to change, for when the diagonal is of the other kind (bottom-left to top right), is to swap around vertices 2 and 3 at the end of the first function. Be careful if you use the triangles for other things, as the triangles will have their diagonals inverted (going down instead of up).
If you use strips, in that function, you can detect if a tile is using a top-left/bottom-right diagonal or the other by looking at if the variable 'i' is odd or even. Every even columns should be the same kind, and every odd columns should be of the other kind. Which one? Well, you have to test both and see what works.
[edited by - oliii on June 27, 2003 10:05:58 PM]