Skip to content

Commit 8c6dca9

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

File tree

1 file changed

+88
-107
lines changed

1 file changed

+88
-107
lines changed

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

Lines changed: 88 additions & 107 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,33 @@ 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+
}
586600
//Real maxTolerance = 0.1f * dmax;
587601
//Real distTolerance = max(20.0f, 0.5f * maxTolerance);
588602

@@ -599,102 +613,73 @@ static Real fast_hypot(Real x1, Real x2, Real y1, Real y2)
599613

600614
//-----------------------------------------------------------------------------
601615
static void testSphereAgainstRect(
602-
const Coord2D *pts, // an array of 4
616+
const Coord2D pts[], // an array of 4
603617
const CollideInfo *a,
604618
//Real angle,
605-
Real &distance
619+
Real &distSqr
606620
)
607621
{
608622
// Get two points that are closest to the facing direction
609623
//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;
624+
Real dx1, dx2, dy1, dy2;
625+
//x1 = x2 = y1 = y2 = 0.0f;
626+
Real minDist = 1e10;
627+
Real secondMinDist = 0.0f;
628+
Int minIdx = -1;
629+
Int secondMinIdx = -1;
612630

613-
Real dist[4];
614-
Coord2D points[4];
615-
for (Int i = 0; i < 4; ++i, ++pts)
631+
Real derivative[4][2];
632+
for (Int i = 0; i < 4; ++i)
616633
{
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-
}
634+
derivative[i][0] = pts[i].x - a->position.x;
635+
derivative[i][1] = pts[i].y - a->position.y;
662636

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++)
637+
Real curDistSqr = sqr(derivative[i][0]) + sqr(derivative[i][1]);
638+
if(minDist > curDistSqr)
671639
{
672-
if(minDist > dist[idx] && idx != lastMinIdx)
673-
{
674-
minDist = dist[idx];
675-
minIdx = idx;
676-
}
640+
minDist = curDistSqr;
641+
minIdx = i;
677642
}
678-
if(x1 == 0.0f && y1 == 0.0f)
643+
if( minDist >= secondMinDist ||
644+
(secondMinDist > curDistSqr && curDistSqr > minDist) )
679645
{
680-
x1 = points[minIdx].x;
681-
y1 = points[minIdx].y;
682-
lastMinIdx = minIdx;
646+
secondMinDist = curDistSqr;
647+
secondMinIdx = i;
683648
}
684-
else
649+
// Get the second last min idx if the last min idx is not yet registered.
650+
651+
652+
if(i == 3)
685653
{
686-
x2 = points[minIdx].x;
687-
y2 = points[minIdx].y;
688-
break;
654+
dx1 = derivative[minIdx][0];
655+
dy1 = derivative[minIdx][1];
656+
dx2 = derivative[secondMinIdx][0];
657+
dy2 = derivative[secondMinIdx][1];
658+
659+
Bool polarity_x1 = dx1 >= 0;
660+
Bool polarity_x2 = dx2 >= 0;
661+
Bool polarity_y1 = dy1 >= 0;
662+
Bool polarity_y2 = dy2 >= 0;
663+
664+
if( polarity_x1 == polarity_x2 && polarity_y1 == polarity_y2 )
665+
{
666+
distSqr = sqr(derivative[minIdx][0]) + sqr(derivative[minIdx][1]);
667+
return;
668+
}
689669
}
690670
}
691671

692672
DEBUG_ASSERTCRASH(minIdx <= 3, ("Hmm, this should not be possible."));
693-
673+
694674
// 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);
675+
Real boundary_h = fast_hypot(pts[minIdx].x - pts[secondMinIdx].x, pts[minIdx].y - pts[secondMinIdx].y);
676+
//Real boundary_1 = fast_hypot(dx1, dy1);
677+
//Real boundary_2 = fast_hypot(dx2, dy2);
678+
//Real sqr_boundary_h = sqr(boundary_h);
679+
//Real sqr_boundary_1 = sqr(boundary_1);
680+
//Real sqr_boundary_h = sqr((*pts)[minIdx].x - (*pts)[lastMinIdx].x) + sqr((*pts)[minIdx].y - (*pts)[lastMinIdx].y);
681+
Real sqr_boundary_1 = sqr(dx1) + sqr(dy1);
682+
Real sqr_boundary_2 = sqr(dx2) + sqr(dy2);
698683

699684
//Real boundary_h = sqrtf(sqr(x1 - x2) + sqr(y1 - y2));
700685
//Real boundary_1 = sqrtf(sqr(x1 - a->position.x) + sqr(y1 - a->position.y));
@@ -704,23 +689,21 @@ static void testSphereAgainstRect(
704689
//Real boundary_1 = Hypot(fabs(x1 - a->position.x), fabs(y1 - a->position.y));
705690
//Real boundary_2 = Hypot(fabs(x2 - a->position.x), fabs(y2 - a->position.y));
706691

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;
692+
// Heron's formula (Not reliable for accounting the edges)
693+
//Real semiPeri = (boundary_h + boundary_1 + boundary_2) * 0.5;
694+
//Real Area = sqrtf(semiPeri * (semiPeri - boundary_h) * (semiPeri - boundary_1) * (semiPeri - boundary_2));
695+
//distSqr = sqr(Area * 2 / boundary_h);
711696

712-
// Fast Hypothenus formula h = ((sqrt(2) - 1) * a) + b 1.41421356237
697+
// Law of Cosines (Demonstration)
713698
//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)
715699

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);
700+
// Converting formula to count for Radius
701+
//Real cosAngle_1 = (sqr(boundary_2) + sqr(boundary_h) - sqr(boundary_1)) * 0.5 / (boundary_2 * boundary_h));
702+
//Real boundary_h1 = cosAngle_1 * boundary_2;
720703

721-
// After we got an angle we can calculate the radius required.
722-
use boundary_2
723-
Real angle_h = PI/2 - angle_1;*/
704+
// Formula Summarization
705+
Real boundary_h1 = (sqr_boundary_2 + sqr(boundary_h) - sqr_boundary_1) * 0.5 / boundary_h;
706+
distSqr = sqr_boundary_2 - sqr(boundary_h1);
724707
}
725708

726709
//-----------------------------------------------------------------------------
@@ -799,11 +782,9 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
799782

800783
Coord2D pts[4];
801784
rectToFourPoints(a, pts);
785+
testSphereAgainstRect(pts, b, distSqr);
802786

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

808789
/*Real circ_l = b->position.x - b->geom.getMajorRadius();
809790
Real circ_r = b->position.x + b->geom.getMajorRadius();
@@ -817,7 +798,7 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
817798
if (circ_r >= rect_l && circ_l <= rect_r &&
818799
circ_b >= rect_t && circ_t <= rect_b)
819800
*/
820-
if(distance <= b->geom.getMajorRadius())
801+
if(distSqr <= sqr(b->geom.getMajorRadius()))
821802
{
822803
if (cinfo)
823804
{
@@ -846,7 +827,7 @@ static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *
846827
//cinfo->distSqr = minDistSqr;
847828
//}
848829
if(cinfo->getDistance)
849-
cinfo->distSqr = sqr(distance);
830+
cinfo->distSqr = distSqr;
850831
}
851832
return true;
852833
}

0 commit comments

Comments
 (0)