Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
64 changes: 41 additions & 23 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 The ray cast can now correctly collide with the initial
// hit box even if the ray starts inside of it 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,19 @@ 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);
MinMaxAABoxClass mmbox(minPt, maxPt);

// The first hit box is very rough and is only meant to narrow the search.
const Real mapMinHeight = MAP_HEIGHT_SCALE * m_map->getMinHeightValue();
const Real mapMaxHeight = MAP_HEIGHT_SCALE * m_map->getMaxHeightValue();
const Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), mapMinHeight);
const Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang), MAP_XY_FACTOR*(m_map->getYExtent()+overhang), mapMaxHeight);
const 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 @@ -712,12 +720,12 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)

if (CollisionMath::Collide(lineseg,hbox,&result))
{
//ray intersects terrain or starts inside the terrain.
if (!result.StartBad) //check if start point inside terrain
// Go ahead if point is not starting inside terrain or it is the first rough box collision.
if (!result.StartBad || terrainIntersectionIteration == 0)
{
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 @@ -726,16 +734,22 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
lineseg2.Set(lineseg.Get_P1(),lineseg.Get_P0()); //reverse line segment
if (CollisionMath::Collide(lineseg2,hbox,&result))
{
if (!result.StartBad) //check if end point inside terrain
// Go ahead if point is not starting inside terrain or it is the first rough box collision.
if (!result.StartBad || terrainIntersectionIteration == 0)
{
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 +769,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 +791,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;
Comment thread
xezon marked this conversation as resolved.
Outdated

raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.
Expand All @@ -788,7 +807,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
2 changes: 1 addition & 1 deletion Core/Libraries/Source/WWVegas/WWMath/castres.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct CastResultStruct
CastResultStruct() { Reset(); }
void Reset() { StartBad = false; Fraction = 1.0f; Normal.Set(0,0,0); SurfaceType = 0; ComputeContactPoint = false; ContactPoint.Set(0,0,0); }

bool StartBad; // was the initial configuration interpenetrating something?
bool StartBad; // was the initial configuration interpenetrating something? true if ray starts inside the collided geometry
float Fraction; // fraction of the move up until collision
Vector3 Normal; // surface normal at the collision point
uint32 SurfaceType; // surface type of polygon at collision point (see W3D_SURFACE_TYPES in w3d_file.h)
Expand Down
8 changes: 8 additions & 0 deletions Core/Libraries/Source/WWVegas/WWMath/colmathline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@ bool CollisionMath::Collide(const LineSegClass & line,const AABoxClass & box,Cas
// if ray starts inside the box, note that fact and bail.
if (test.Inside) {
res->StartBad = true;
// TheSuperHackers @tweak The contact point will be where the line has started.
if (res->ComputeContactPoint) {
res->ContactPoint = line.Get_P0();
}
return true;
}

Expand Down Expand Up @@ -328,6 +332,10 @@ bool CollisionMath::Collide(const LineSegClass & line,const OBBoxClass & box,Cas
// if ray starts inside the box, don't collide
if (test.Inside) {
result->StartBad = true;
// TheSuperHackers @tweak The contact point will be where the line has started.
if (result->ComputeContactPoint) {
result->ContactPoint = line.Get_P0();
}
return true;
}

Expand Down
Loading