Skip to content

Commit c52765d

Browse files
committed
Optimized "UseAccurateSphereToRectCollision" to be much less expensive than current formula
1 parent fa500cc commit c52765d

File tree

1 file changed

+93
-109
lines changed

1 file changed

+93
-109
lines changed

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/PartitionManager.cpp

Lines changed: 93 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -370,16 +370,14 @@ static void testRotatedPointsAgainstRect(
370370
Real *minDistSqr
371371
);*/
372372
static Real fast_hypot(
373-
Real x1,
374-
Real x2,
375-
Real y1,
376-
Real y2
373+
Real x,
374+
Real y
377375
);
378376
static void testSphereAgainstRect(
379-
const Coord2D *pts, // an array of 4
377+
const Coord2D pts[], // an array of 4
380378
const CollideInfo *a,
381379
//Real angle,
382-
Real &distance
380+
Real &distSqr
383381
);
384382

385383
static Bool xy_collideTest_Rect_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
@@ -572,17 +570,36 @@ static void testRotatedPointsAgainstRect(
572570
}*/
573571

574572
//-----------------------------------------------------------------------------
575-
static Real fast_hypot(Real x1, Real x2, Real y1, Real y2)
573+
static Real fast_hypot(Real x, Real y)
576574
{
577575
// Fast approximation of Boundary length, generally if one line has the length of only 10% or less of the other line, we take the longest line as the boundary.
578576
// Has an error rate of approx 0.5%.
579577
// Example: A line of dx = 5, and dy = 0.5, would give h = 5.025, we take dx directly
580-
Real dx = fabs(x1 - x2);
581-
Real dy = fabs(y1 - y2);
578+
/*Real dSqr = x*x + y*y;
579+
Real curGuess = 1.0f;
580+
Real tolerance = 0.1f;
581+
while(fabs( (curGuess * curGuess) / dSqr - 1.0) > tolerance)
582+
curGuess = ((dSqr/curGuess) + curGuess) * 0.5;
583+
return curGuess;*/
584+
585+
Real dx = fabs(x);
586+
Real dy = fabs(y);
582587

583588
// Longest line and shortest between x and y
584-
Real a = max(dx, dy);
585-
Real b = min(dx, dy);
589+
Real a, b;
590+
if(dx > dy)
591+
{
592+
a = dx;
593+
b = dy;
594+
}
595+
else
596+
{
597+
a = dy;
598+
b = dx;
599+
}
600+
601+
Real ratio = b / a;
602+
ratio *= ratio;
586603
//Real maxTolerance = 0.1f * dmax;
587604
//Real distTolerance = max(20.0f, 0.5f * maxTolerance);
588605

@@ -594,107 +611,78 @@ static Real fast_hypot(Real x1, Real x2, Real y1, Real y2)
594611
//else
595612

596613
// max error ≈ 1.04 %
597-
return a * ( 1 + 0.428 * pow(b/a, 2) );
614+
return a * ( 1 + 0.428 * ratio );
598615
}
599616

600617
//-----------------------------------------------------------------------------
601618
static void testSphereAgainstRect(
602-
const Coord2D *pts, // an array of 4
619+
const Coord2D pts[], // an array of 4
603620
const CollideInfo *a,
604621
//Real angle,
605-
Real &distance
622+
Real &distSqr
606623
)
607624
{
608625
// Get two points that are closest to the facing direction
609626
//DEBUG_LOG(("Source Points: x: %f y: %f", a->position.x, a->position.y));
610-
Real x1, x2, y1, y2;
611-
x1 = x2 = y1 = y2 = 0.0f;
627+
Real dx1, dx2, dy1, dy2;
628+
//x1 = x2 = y1 = y2 = 0.0f;
629+
Real minDist = HUGE_DIST_SQR;
630+
Real secondMinDist = 0.0f;
631+
Int minIdx = -1;
632+
Int secondMinIdx = -1;
612633

613-
Real dist[4];
614-
Coord2D points[4];
615-
for (Int i = 0; i < 4; ++i, ++pts)
634+
Real derivative[4][2];
635+
for (Int i = 0; i < 4; ++i)
616636
{
617-
/*if(x1 != 0.0f && x2 != 0.0f)
618-
break;
619-
Real dir = atan2(pts->y - a->position.y, pts->x - a->position.x);
620-
Real relAngle = stdAngleDiff(dir, angle);
621-
DEBUG_LOG(( "Angle: %f", angle ));
622-
DEBUG_LOG(( "Dir: %f", dir ));
623-
DEBUG_LOG(( "Rel Angle: %f", relAngle ));
624-
if(fabs(relAngle) <= PI/2)
625-
{
626-
switch(i)
627-
{
628-
// tl
629-
case 0:
630-
DEBUG_LOG(( "Top Left" ));
631-
break;
632-
// tr
633-
case 1:
634-
DEBUG_LOG(( "Top Right" ));
635-
break;
636-
// bl
637-
case 2:
638-
DEBUG_LOG(( "Bottom Left" ));
639-
break;
640-
// br
641-
case 3:
642-
DEBUG_LOG(( "Bottom Right" ));
643-
break;
644-
}
645-
if(x1 == 0.0f && y1 == 0.0f)
646-
{
647-
x1 = pts->x;
648-
y1 = pts->y;
649-
DEBUG_LOG(("Point 1: x: %f y: %f", x1, y1));
650-
}
651-
else
652-
{
653-
x2 = pts->x;
654-
y2 = pts->y;
655-
DEBUG_LOG(("Point 2: x: %f y: %f", x2, y2));
656-
}
657-
}*/
658-
points[i].x = pts->x;
659-
points[i].y = pts->y;
660-
dist[i] = sqr(pts->x - a->position.x) + sqr(pts->y - a->position.y);
661-
}
637+
derivative[i][0] = pts[i].x - a->position.x;
638+
derivative[i][1] = pts[i].y - a->position.y;
662639

663-
Real minDist;
664-
Int minIdx;
665-
Int lastMinIdx = -1;
666-
while( TRUE )
667-
{
668-
minDist = HUGE_DIST_SQR;
669-
minIdx = 4;
670-
for (Int idx = 0; idx < 4; idx++)
640+
Real curDistSqr = sqr(derivative[i][0]) + sqr(derivative[i][1]);
641+
if(minDist > curDistSqr)
671642
{
672-
if(minDist > dist[idx] && idx != lastMinIdx)
673-
{
674-
minDist = dist[idx];
675-
minIdx = idx;
676-
}
643+
minDist = curDistSqr;
644+
minIdx = i;
677645
}
678-
if(x1 == 0.0f && y1 == 0.0f)
646+
if( minDist >= secondMinDist ||
647+
(secondMinDist > curDistSqr && curDistSqr > minDist) )
679648
{
680-
x1 = points[minIdx].x;
681-
y1 = points[minIdx].y;
682-
lastMinIdx = minIdx;
649+
secondMinDist = curDistSqr;
650+
secondMinIdx = i;
683651
}
684-
else
652+
// Get the second last min idx if the last min idx is not yet registered.
653+
654+
655+
if(i == 3)
685656
{
686-
x2 = points[minIdx].x;
687-
y2 = points[minIdx].y;
688-
break;
657+
dx1 = derivative[minIdx][0];
658+
dy1 = derivative[minIdx][1];
659+
dx2 = derivative[secondMinIdx][0];
660+
dy2 = derivative[secondMinIdx][1];
661+
662+
Bool polarity_x1 = dx1 >= 0;
663+
Bool polarity_x2 = dx2 >= 0;
664+
Bool polarity_y1 = dy1 >= 0;
665+
Bool polarity_y2 = dy2 >= 0;
666+
667+
if( polarity_x1 == polarity_x2 && polarity_y1 == polarity_y2 )
668+
{
669+
distSqr = sqr(derivative[minIdx][0]) + sqr(derivative[minIdx][1]);
670+
return;
671+
}
689672
}
690673
}
691674

692-
DEBUG_ASSERTCRASH(minIdx <= 3, ("Hmm, this should not be possible."));
693-
675+
DEBUG_ASSERTCRASH(secondMinIdx >= 0 && secondMinIdx <= 3, ("Hmm, this should not be possible."));
676+
694677
// Get the Triangle length of all 3 points
695-
Real boundary_h = fast_hypot(x1, x2, y1, y2);
696-
Real boundary_1 = fast_hypot(x1, a->position.x, y1, a->position.y);
697-
Real boundary_2 = fast_hypot(x2, a->position.x, y2, a->position.y);
678+
Real boundary_h = fast_hypot(pts[minIdx].x - pts[secondMinIdx].x, pts[minIdx].y - pts[secondMinIdx].y);
679+
//Real boundary_1 = fast_hypot(dx1, dy1);
680+
//Real boundary_2 = fast_hypot(dx2, dy2);
681+
//Real sqr_boundary_h = sqr(boundary_h);
682+
//Real sqr_boundary_1 = sqr(boundary_1);
683+
//Real sqr_boundary_h = sqr((*pts)[minIdx].x - (*pts)[lastMinIdx].x) + sqr((*pts)[minIdx].y - (*pts)[lastMinIdx].y);
684+
Real sqr_boundary_1 = sqr(dx1) + sqr(dy1);
685+
Real sqr_boundary_2 = sqr(dx2) + sqr(dy2);
698686

699687
//Real boundary_h = sqrtf(sqr(x1 - x2) + sqr(y1 - y2));
700688
//Real boundary_1 = sqrtf(sqr(x1 - a->position.x) + sqr(y1 - a->position.y));
@@ -704,23 +692,21 @@ static void testSphereAgainstRect(
704692
//Real boundary_1 = Hypot(fabs(x1 - a->position.x), fabs(y1 - a->position.y));
705693
//Real boundary_2 = Hypot(fabs(x2 - a->position.x), fabs(y2 - a->position.y));
706694

707-
// Heron's formula
708-
Real semiPeri = (boundary_h + boundary_1 + boundary_2) * 0.5;
709-
Real Area = sqrtf(semiPeri * (semiPeri - boundary_h) * (semiPeri - boundary_1) * (semiPeri - boundary_2));
710-
distance = Area * 2 / boundary_h;
695+
// Heron's formula (Not reliable for accounting the edges)
696+
//Real semiPeri = (boundary_h + boundary_1 + boundary_2) * 0.5;
697+
//Real Area = sqrtf(semiPeri * (semiPeri - boundary_h) * (semiPeri - boundary_1) * (semiPeri - boundary_2));
698+
//distSqr = sqr(Area * 2 / boundary_h);
711699

712-
// Fast Hypothenus formula h = ((sqrt(2) - 1) * a) + b 1.41421356237
700+
// Law of Cosines (Demonstration)
713701
//sqr(boundary_1) = sqr(boundary_2) + sqr(boundary_h) - (2 * boundary_2 * boundary_h * cos(angle_1)); // b^2 = a^2 + c^2 - 2ac cos B
714-
//boundary_1 * sin(angle_2) = boundary_2 * sin(angle_1)
715702

716-
/*Real cosAngle_1 = (boundary_2_Sqr + boundary_h_Sqr - boundary_1_Sqr) * 0.5 / (boundary_2 * boundary_h);
717-
Real angle_1 = (Real)Acos(cosAngle_1);
718-
//Real sinAngle_2 = boundary_2 / boundary_1 * (Real)Sin(angle_1);
719-
//Real angle_2 = (Real)Asin(sinAngle_2);
703+
// Converting formula to count for Radius
704+
//Real cosAngle_1 = (sqr(boundary_2) + sqr(boundary_h) - sqr(boundary_1)) * 0.5 / (boundary_2 * boundary_h));
705+
//Real boundary_h1 = cosAngle_1 * boundary_2;
720706

721-
// After we got an angle we can calculate the radius required.
722-
use boundary_2
723-
Real angle_h = PI/2 - angle_1;*/
707+
// Formula Summarization
708+
Real boundary_h1 = (sqr_boundary_2 + sqr(boundary_h) - sqr_boundary_1) * 0.5 / boundary_h;
709+
distSqr = sqr_boundary_2 - sqr(boundary_h1);
724710
}
725711

726712
//-----------------------------------------------------------------------------
@@ -799,11 +785,9 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
799785

800786
Coord2D pts[4];
801787
rectToFourPoints(a, pts);
788+
testSphereAgainstRect(pts, b, distSqr);
802789

803-
Real distance = 0.0f;
804-
testSphereAgainstRect(pts, b, distance);
805-
806-
//DEBUG_LOG(("Radius: %f Distance: %f", b->geom.getMajorRadius(), distance));
790+
//DEBUG_LOG(("Radius: %f Distance: %f", b->geom.getMajorRadius(), distSqr));
807791

808792
/*Real circ_l = b->position.x - b->geom.getMajorRadius();
809793
Real circ_r = b->position.x + b->geom.getMajorRadius();
@@ -817,7 +801,7 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
817801
if (circ_r >= rect_l && circ_l <= rect_r &&
818802
circ_b >= rect_t && circ_t <= rect_b)
819803
*/
820-
if(distance <= b->geom.getMajorRadius())
804+
if(distSqr <= sqr(b->geom.getMajorRadius()))
821805
{
822806
if (cinfo)
823807
{
@@ -846,7 +830,7 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
846830
//cinfo->distSqr = minDistSqr;
847831
//}
848832
if(cinfo->getDistance)
849-
cinfo->distSqr = sqr(distance);
833+
cinfo->distSqr = distSqr;
850834
}
851835
return true;
852836
}

0 commit comments

Comments
 (0)