Skip to content

Commit 6a19b8b

Browse files
authored
bugfix(heightmap): Reduce ray cast lengths and fix ray casting in BaseHeightMapRenderObjClass::Cast_Ray (TheSuperHackers#2836)
1 parent c7ee26a commit 6a19b8b

2 files changed

Lines changed: 56 additions & 43 deletions

File tree

Core/GameEngineDevice/Source/W3DDevice/GameClient/BaseHeightMap.cpp

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ relative to the ray so we can early exit as soon as we have a hit.
665665
//=============================================================================
666666
/** Return intersection of a ray with the heightmap mesh.
667667
This is a quick version that just checks every polygon inside
668-
a 2D bounding rectangle of the ray projected onto the heightfield plane.
668+
a 2D bounding rectangle of the ray projected onto the height map plane.
669669
For most of our view-picking cases the ray is almost perpendicular to the
670670
map plane so this is very quick (small bounding box). But it can become slow
671671
for arbitrary rays such as those used in AI visibility checks(2 units on
@@ -678,19 +678,22 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
678678
Bool hit = false;
679679
Int X,Y;
680680
Vector3 normal,P0,P1,P2,P3;
681+
Bool hasP0 = false;
682+
Bool hasP1 = false;
681683

682684
if (!m_map)
683685
return false; //need valid pointer to heightmap samples
684-
//HeightSampleType *pData = m_map->getDataPtr();
685-
//Clip ray to extents of heightfield
686+
687+
//Clip ray to extents of height map
686688
AABoxClass hbox;
687689
LineSegClass lineseg,lineseg2;
688690
CastResultStruct result;
689-
Int StartCellX = 0;
690-
Int EndCellX = 0;
691-
Int StartCellY = 0;
692-
Int EndCellY = 0;
693-
const Int overhang = 2*VERTEX_BUFFER_TILE_LENGTH + m_map->getBorderSizeInline(); // Allow picking past the edge for scrolling & objects.
691+
Int startCellX = 0;
692+
Int startCellY = 0;
693+
Int endCellX = 0;
694+
Int endCellY = 0;
695+
const Int borderSize = m_map->getBorderSizeInline();
696+
const Int overhang = 2*VERTEX_BUFFER_TILE_LENGTH + borderSize; // Allow picking past the edge for scrolling & objects.
694697
Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), -MAP_XY_FACTOR);
695698
Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang),
696699
MAP_XY_FACTOR*(m_map->getYExtent()+overhang), MAP_HEIGHT_SCALE*m_map->getMaxHeightValue()+MAP_XY_FACTOR);
@@ -699,84 +702,94 @@ bool BaseHeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
699702

700703
lineseg=raytest.Ray;
701704

702-
//Set initial ray endpoints
703-
P0 = raytest.Ray.Get_P0();
704-
P1 = raytest.Ray.Get_P1();
705-
result.ComputeContactPoint=true;
706-
707705
Int p;
708706
for (p=0; p<3; p++) {
709707
//find intersection point of ray and terrain bounding box
710708
result.Reset();
711709
result.ComputeContactPoint=true;
710+
Bool newP0 = false;
711+
Bool newP1 = false;
712+
712713
if (CollisionMath::Collide(lineseg,hbox,&result))
713-
{ //ray intersects terrain or starts inside the terrain.
714+
{
715+
//ray intersects terrain or starts inside the terrain.
714716
if (!result.StartBad) //check if start point inside terrain
715-
P0 = result.ContactPoint; //make intersection point the new start of the ray.
717+
{
718+
newP0 = P0 != result.ContactPoint;
719+
hasP0 = true;
720+
P0 = result.ContactPoint; //make intersection point the new start of the ray.
721+
}
716722

717-
//reverse direction of original ray and clip again to extent of
718-
//heightmap
723+
//reverse direction of original ray and clip again to extent of heightmap
719724
result.Fraction=1.0f; //reset the result
720725
result.StartBad=false;
721726
lineseg2.Set(lineseg.Get_P1(),lineseg.Get_P0()); //reverse line segment
722727
if (CollisionMath::Collide(lineseg2,hbox,&result))
723-
{ if (!result.StartBad) //check if end point inside terrain
724-
P1 = result.ContactPoint; //make intersection point the new end pont of ray
728+
{
729+
if (!result.StartBad) //check if end point inside terrain
730+
{
731+
newP1 = P1 != result.ContactPoint;
732+
hasP1 = true;
733+
P1 = result.ContactPoint; //make intersection point the new end point of ray
734+
}
725735
}
726-
} else {
727-
if (p==0) return(false);
728-
break;
729736
}
730737

738+
if (!newP0 || !newP1)
739+
break;
740+
731741
// Take the 2D bounding box of ray and check heights
732742
// inside this box for intersection.
733743
if (P0.X > P1.X) { //flip start/end points
734-
StartCellX = REAL_TO_INT_FLOOR(P1.X/MAP_XY_FACTOR);
735-
EndCellX = REAL_TO_INT_CEIL(P0.X/MAP_XY_FACTOR);
744+
startCellX = REAL_TO_INT_FLOOR(P1.X/MAP_XY_FACTOR);
745+
endCellX = REAL_TO_INT_CEIL(P0.X/MAP_XY_FACTOR);
736746
} else {
737-
StartCellX = REAL_TO_INT_FLOOR(P0.X/MAP_XY_FACTOR);
738-
EndCellX = REAL_TO_INT_CEIL(P1.X/MAP_XY_FACTOR);
747+
startCellX = REAL_TO_INT_FLOOR(P0.X/MAP_XY_FACTOR);
748+
endCellX = REAL_TO_INT_CEIL(P1.X/MAP_XY_FACTOR);
739749
}
740750
if (P0.Y > P1.Y) { //flip start/end points
741-
StartCellY = REAL_TO_INT_FLOOR(P1.Y/MAP_XY_FACTOR);
742-
EndCellY = REAL_TO_INT_CEIL(P0.Y/MAP_XY_FACTOR);
751+
startCellY = REAL_TO_INT_FLOOR(P1.Y/MAP_XY_FACTOR);
752+
endCellY = REAL_TO_INT_CEIL(P0.Y/MAP_XY_FACTOR);
743753
} else {
744-
StartCellY = REAL_TO_INT_FLOOR(P0.Y/MAP_XY_FACTOR);
745-
EndCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
754+
startCellY = REAL_TO_INT_FLOOR(P0.Y/MAP_XY_FACTOR);
755+
endCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
746756
}
747757

748758
Int i, j, minHt, maxHt;
749759

750760
minHt = m_map->getMaxHeightValue();
751761
maxHt = 0;
752762

753-
for (j=StartCellY; j<=EndCellY; j++) {
754-
for (i=StartCellX; i<=EndCellX; i++) {
755-
Short cur = getClipHeight(i+m_map->getBorderSizeInline(),j+m_map->getBorderSizeInline());
763+
for (j=startCellY; j<=endCellY; j++) {
764+
for (i=startCellX; i<=endCellX; i++) {
765+
Short cur = getClipHeight(i+borderSize,j+borderSize);
756766
if (cur<minHt) minHt = cur;
757767
if (maxHt<cur) maxHt = cur;
758768
}
759769
}
760-
Vector3 minPt(MAP_XY_FACTOR*(StartCellX-1), MAP_XY_FACTOR*(StartCellY-1), MAP_HEIGHT_SCALE*(minHt-1));
761-
Vector3 maxPt(MAP_XY_FACTOR*(EndCellX+1), MAP_XY_FACTOR*(EndCellY+1), MAP_HEIGHT_SCALE*(maxHt+1));
770+
Vector3 minPt(MAP_XY_FACTOR*(startCellX-1), MAP_XY_FACTOR*(startCellY-1), MAP_HEIGHT_SCALE*(minHt-1));
771+
Vector3 maxPt(MAP_XY_FACTOR*(endCellX+1), MAP_XY_FACTOR*(endCellY+1), MAP_HEIGHT_SCALE*(maxHt+1));
762772
MinMaxAABoxClass mmbox(minPt, maxPt);
763773
hbox.Init(mmbox);
764774
}
765775

776+
if (!hasP0 || !hasP1)
777+
return false;
778+
766779
raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.
767780

768781
// Adjust indexes into the bordered height map.
769782

770-
StartCellX += m_map->getBorderSizeInline();
771-
EndCellX += m_map->getBorderSizeInline();
772-
StartCellY += m_map->getBorderSizeInline();
773-
EndCellY += m_map->getBorderSizeInline();
783+
startCellX += borderSize;
784+
endCellX += borderSize;
785+
startCellY += borderSize;
786+
endCellY += borderSize;
774787

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

779-
for (X=StartCellX-offset; X<=EndCellX+offset; X++) {
792+
for (X=startCellX-offset; X<=endCellX+offset; X++) {
780793
//test the 2 triangles in this cell
781794
// 3-----2
782795
// | /|

Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DView.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *ray
657657
m_3DCamera->Un_Project(*rayEnd,Vector2(logX,logY)); //get world space point
658658
*rayEnd -= *rayStart; //vector camera to world space point
659659
rayEnd->Normalize(); //make unit vector
660-
*rayEnd *= sqr(m_3DCamera->Get_Depth()); //adjust length to reach far clip plane and beyond
660+
*rayEnd *= m_3DCamera->Get_Depth() * 2; //adjust length to reach far clip plane and beyond
661661
*rayEnd += *rayStart; //get point on far clip plane along ray from camera.
662662
}
663663

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

0 commit comments

Comments
 (0)