Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 43 additions & 19 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -656,10 +656,6 @@ void BaseHeightMapRenderObjClass::reset()
}
}

/**@todo: Ray intersection needs to be optimized with some sort of grid-tracing
(ala line drawing). We should also try making the search in a front->back order
relative to the ray so we can early exit as soon as we have a hit.
*
//=============================================================================
// BaseHeightMapRenderObjClass::Cast_Ray
//=============================================================================
Expand All @@ -671,18 +667,27 @@ map plane so this is very quick (small bounding box). But it can become slow
for arbitrary rays such as those used in AI visibility checks(2 units on
opposite corners of the map would check every polygon in the map).
*/
/** @todo: Ray intersection needs to be optimized with some sort of grid-tracing
(ala line drawing). We should also try making the search in a front->back order
relative to the ray so we can early exit as soon as we have a hit.
*/
// TheSuperHackers @fix This function now creates its initial hit box
// with dimensions that fit into the ray cast and no longer falls back to an
// infinitely large search region if the initial box cannot be collided with.
//=============================================================================
bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
{
if (!m_map)
return false; //need valid pointer to heightmap samples

TriClass tri;
Bool hit = false;
Int X,Y;
Vector3 normal,P0,P1,P2,P3;
Bool hasP0 = false;
Bool hasP1 = false;

if (!m_map)
return false; //need valid pointer to heightmap samples
P0.Set(FLT_MAX, FLT_MAX, FLT_MAX); // Set initial bogus value
P1.Set(FLT_MAX, FLT_MAX, FLT_MAX); // Set initial bogus value
Int P0HitCount = 0;
Int P1HitCount = 0;

//Clip ray to extents of height map
AABoxClass hbox;
Expand All @@ -694,16 +699,26 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
Int endCellY = 0;
const Int borderSize = m_map->getBorderSizeInline();
const Int overhang = 2*VERTEX_BUFFER_TILE_LENGTH + borderSize; // Allow picking past the edge for scrolling & objects.
Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), -MAP_XY_FACTOR);
Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang),
MAP_XY_FACTOR*(m_map->getYExtent()+overhang), MAP_HEIGHT_SCALE*m_map->getMaxHeightValue()+MAP_XY_FACTOR);

const Real rayMinHeight = std::min(raytest.Ray.Get_P0().Z, raytest.Ray.Get_P1().Z);
const Real rayMaxHeight = std::max(raytest.Ray.Get_P0().Z, raytest.Ray.Get_P1().Z);
const Real rayHeightDelta = rayMaxHeight - rayMinHeight;
const Real rayHeightMargin = std::min(1.0f, rayHeightDelta * 0.49f);
Real mapMinHeight = MAP_HEIGHT_SCALE * m_map->getMinHeightValue(); // Begin with min map height.
Real mapMaxHeight = MAP_HEIGHT_SCALE * m_map->getMaxHeightValue(); // Begin with max map height.
mapMinHeight = std::max(mapMinHeight, rayMinHeight + rayHeightMargin); // But not lower than the ray end plus a margin.
mapMaxHeight = std::min(mapMaxHeight, rayMaxHeight - rayHeightMargin); // But not higher than the ray start minus a margin.

// The first hit box is very rough and is only meant to narrow the search.
Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), mapMinHeight);
Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang), MAP_XY_FACTOR*(m_map->getYExtent()+overhang), mapMaxHeight);
MinMaxAABoxClass mmbox(minPt, maxPt);
hbox.Init(mmbox);

lineseg=raytest.Ray;

Int p;
for (p=0; p<3; p++) {
Int terrainIntersectionIteration = 0;
for (; ; ++terrainIntersectionIteration) {
//find intersection point of ray and terrain bounding box
result.Reset();
result.ComputeContactPoint=true;
Expand All @@ -716,8 +731,8 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
if (!result.StartBad) //check if start point inside terrain
{
newP0 = P0 != result.ContactPoint;
hasP0 = true;
P0 = result.ContactPoint; //make intersection point the new start of the ray.
++P0HitCount;
}

//reverse direction of original ray and clip again to extent of heightmap
Expand All @@ -729,13 +744,18 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
if (!result.StartBad) //check if end point inside terrain
{
newP1 = P1 != result.ContactPoint;
hasP1 = true;
P1 = result.ContactPoint; //make intersection point the new end point of ray
++P1HitCount;
}
}
}

if (!newP0 || !newP1)
// Has not even hit the first hit box?
if (P0HitCount == 0 || P1HitCount == 0)
return false;

// Has no new hit points?
if (!newP0 && !newP1)
break;

// Take the 2D bounding box of ray and check heights
Expand All @@ -755,6 +775,10 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
endCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
}

// Stop narrowing after the third iteration
if (terrainIntersectionIteration == 2)
break;

Int i, j, minHt, maxHt;

minHt = m_map->getMaxHeightValue();
Expand All @@ -773,7 +797,8 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
hbox.Init(mmbox);
}

if (!hasP0 || !hasP1)
// Needs at least 2 hit counts for a proper terrain collision.
if (P0HitCount < 2 || P1HitCount < 2)
return false;

raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.
Expand All @@ -788,7 +813,6 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
Int offset;
for (offset = 1; offset < 5; offset *= 3) {
for (Y=startCellY-offset; Y<=endCellY+offset; Y++) {

for (X=startCellX-offset; X<=endCellX+offset; X++) {
//test the 2 triangles in this cell
// 3-----2
Expand Down
Loading