Skip to content
Open
Show file tree
Hide file tree
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
95 changes: 54 additions & 41 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ relative to the ray so we can early exit as soon as we have a hit.
//=============================================================================
/** Return intersection of a ray with the heightmap mesh.
This is a quick version that just checks every polygon inside
a 2D bounding rectangle of the ray projected onto the heightfield plane.
a 2D bounding rectangle of the ray projected onto the height map plane.
For most of our view-picking cases the ray is almost perpendicular to the
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
Expand All @@ -678,19 +678,22 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
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
//HeightSampleType *pData = m_map->getDataPtr();
//Clip ray to extents of heightfield

//Clip ray to extents of height map
AABoxClass hbox;
LineSegClass lineseg,lineseg2;
CastResultStruct result;
Int StartCellX = 0;
Int EndCellX = 0;
Int StartCellY = 0;
Int EndCellY = 0;
const Int overhang = 2*VERTEX_BUFFER_TILE_LENGTH + m_map->getBorderSizeInline(); // Allow picking past the edge for scrolling & objects.
Int startCellX = 0;
Int startCellY = 0;
Int endCellX = 0;
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);
Expand All @@ -699,84 +702,94 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)

lineseg=raytest.Ray;

//Set initial ray endpoints
P0 = raytest.Ray.Get_P0();
P1 = raytest.Ray.Get_P1();
result.ComputeContactPoint=true;

Int p;
for (p=0; p<3; p++) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no clue why there was this loop here. It does not do anything. I removed it and it works.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now see why this loop is there and I put it back and fixed the looping logic as far as I could tell.

//find intersection point of ray and terrain bounding box
result.Reset();
result.ComputeContactPoint=true;
Bool newP0 = false;
Bool newP1 = false;

if (CollisionMath::Collide(lineseg,hbox,&result))
{ //ray intersects terrain or starts inside the terrain.
{
//ray intersects terrain or starts inside the terrain.
if (!result.StartBad) //check if start point inside terrain
P0 = result.ContactPoint; //make intersection point the new start of the ray.
{
newP0 = P0 != result.ContactPoint;
hasP0 = true;
P0 = result.ContactPoint; //make intersection point the new start of the ray.
}

//reverse direction of original ray and clip again to extent of
//heightmap
//reverse direction of original ray and clip again to extent of heightmap
result.Fraction=1.0f; //reset the result
result.StartBad=false;
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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the ray length was very large, then this collision here failed. I am not sure why but I suspect because of floating point precision loss at very large numbers.

When it failed, then P1 was a very large number, and the cells loop further below would then iterate over very large integer numbers which practically meant the application hung up.

With this change the ray cast now aborts early if this collision test failed, which avoids all this trouble.

P1 = result.ContactPoint; //make intersection point the new end pont of ray
{
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
}
}
} else {
if (p==0) return(false);
break;
}

if (!newP0 || !newP1)
break;

// Take the 2D bounding box of ray and check heights
// inside this box for intersection.
if (P0.X > P1.X) { //flip start/end points
StartCellX = REAL_TO_INT_FLOOR(P1.X/MAP_XY_FACTOR);
EndCellX = REAL_TO_INT_CEIL(P0.X/MAP_XY_FACTOR);
startCellX = REAL_TO_INT_FLOOR(P1.X/MAP_XY_FACTOR);
endCellX = REAL_TO_INT_CEIL(P0.X/MAP_XY_FACTOR);
} else {
StartCellX = REAL_TO_INT_FLOOR(P0.X/MAP_XY_FACTOR);
EndCellX = REAL_TO_INT_CEIL(P1.X/MAP_XY_FACTOR);
startCellX = REAL_TO_INT_FLOOR(P0.X/MAP_XY_FACTOR);
endCellX = REAL_TO_INT_CEIL(P1.X/MAP_XY_FACTOR);
}
if (P0.Y > P1.Y) { //flip start/end points
StartCellY = REAL_TO_INT_FLOOR(P1.Y/MAP_XY_FACTOR);
EndCellY = REAL_TO_INT_CEIL(P0.Y/MAP_XY_FACTOR);
startCellY = REAL_TO_INT_FLOOR(P1.Y/MAP_XY_FACTOR);
endCellY = REAL_TO_INT_CEIL(P0.Y/MAP_XY_FACTOR);
} else {
StartCellY = REAL_TO_INT_FLOOR(P0.Y/MAP_XY_FACTOR);
EndCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
startCellY = REAL_TO_INT_FLOOR(P0.Y/MAP_XY_FACTOR);
endCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
}

Int i, j, minHt, maxHt;

minHt = m_map->getMaxHeightValue();
maxHt = 0;

for (j=StartCellY; j<=EndCellY; j++) {
for (i=StartCellX; i<=EndCellX; i++) {
Short cur = getClipHeight(i+m_map->getBorderSizeInline(),j+m_map->getBorderSizeInline());
for (j=startCellY; j<=endCellY; j++) {
for (i=startCellX; i<=endCellX; i++) {
Short cur = getClipHeight(i+borderSize,j+borderSize);
if (cur<minHt) minHt = cur;
if (maxHt<cur) maxHt = cur;
}
}
Vector3 minPt(MAP_XY_FACTOR*(StartCellX-1), MAP_XY_FACTOR*(StartCellY-1), MAP_HEIGHT_SCALE*(minHt-1));
Vector3 maxPt(MAP_XY_FACTOR*(EndCellX+1), MAP_XY_FACTOR*(EndCellY+1), MAP_HEIGHT_SCALE*(maxHt+1));
Vector3 minPt(MAP_XY_FACTOR*(startCellX-1), MAP_XY_FACTOR*(startCellY-1), MAP_HEIGHT_SCALE*(minHt-1));
Vector3 maxPt(MAP_XY_FACTOR*(endCellX+1), MAP_XY_FACTOR*(endCellY+1), MAP_HEIGHT_SCALE*(maxHt+1));
MinMaxAABoxClass mmbox(minPt, maxPt);
hbox.Init(mmbox);
Comment thread
xezon marked this conversation as resolved.
}

if (!hasP0 || !hasP1)
return false;

raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.

// Adjust indexes into the bordered height map.

StartCellX += m_map->getBorderSizeInline();
EndCellX += m_map->getBorderSizeInline();
StartCellY += m_map->getBorderSizeInline();
EndCellY += m_map->getBorderSizeInline();
startCellX += borderSize;
endCellX += borderSize;
startCellY += borderSize;
endCellY += borderSize;

Int offset;
for (offset = 1; offset < 5; offset *= 3) {
for (Y=StartCellY-offset; Y<=EndCellY+offset; Y++) {
for (Y=startCellY-offset; Y<=endCellY+offset; Y++) {

for (X=StartCellX-offset; X<=EndCellX+offset; X++) {
for (X=startCellX-offset; X<=endCellX+offset; X++) {
//test the 2 triangles in this cell
// 3-----2
// | /|
Expand Down
4 changes: 2 additions & 2 deletions Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *ray
m_3DCamera->Un_Project(*rayEnd,Vector2(logX,logY)); //get world space point
*rayEnd -= *rayStart; //vector camera to world space point
rayEnd->Normalize(); //make unit vector
*rayEnd *= sqr(m_3DCamera->Get_Depth()); //adjust length to reach far clip plane and beyond
*rayEnd *= m_3DCamera->Get_Depth() * 2; //adjust length to reach far clip plane and beyond

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot recall why I picked sqr(farZ) before. farZ*2 appears to also be sufficient.

*rayEnd += *rayStart; //get point on far clip plane along ray from camera.
}

Expand Down Expand Up @@ -2622,7 +2622,7 @@ void W3DView::lookAt( const Coord3D *o )
m_3DCamera->Un_Project(rayEnd,Vector2(0.0f,0.0f)); //get world space point
rayEnd -= rayStart; //vector camera to world space point
rayEnd.Normalize(); //make unit vector
rayEnd *= sqr(m_3DCamera->Get_Depth()); //adjust length to reach far clip plane and beyond
rayEnd *= m_3DCamera->Get_Depth() * 2; //adjust length to reach far clip plane and beyond
rayStart.Set(pos.x, pos.y, pos.z);
rayEnd += rayStart; //get point on far clip plane along ray from camera.
lineseg.Set(rayStart,rayEnd);
Expand Down
Loading