Determines vertical surfaces and gets nearest ledge angle. Allows to eventually use unconstrained vaults and shimmying.
| 750 | // Determines vertical surfaces and gets nearest ledge angle. |
| 751 | // Allows to eventually use unconstrained vaults and shimmying. |
| 752 | short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance) |
| 753 | { |
| 754 | // Calculation ledge angle for non-Lara objects is unnecessary. |
| 755 | if (!item->IsLara()) |
| 756 | return 0; |
| 757 | |
| 758 | // Get item bounds and current rotation. |
| 759 | auto bounds = GameBoundingBox(item); |
| 760 | float cosForwardAngle = phd_cos(coll->Setup.ForwardAngle); |
| 761 | float sinForwardAngle = phd_sin(coll->Setup.ForwardAngle); |
| 762 | |
| 763 | // Origin test position should be slightly in front of origin, because otherwise misfire may occur near block corners for split angles. |
| 764 | auto frontalOffset = coll->Setup.Radius * 0.3f; |
| 765 | auto x = item->Pose.Position.x + frontalOffset * sinForwardAngle; |
| 766 | auto z = item->Pose.Position.z + frontalOffset * cosForwardAngle; |
| 767 | |
| 768 | // Determine two Y points to test (lower and higher). |
| 769 | // 1/10 headroom crop is needed to avoid possible issues with tight diagonal headrooms. |
| 770 | int headroom = bounds.GetHeight() / 20.0f; |
| 771 | int yPoints[2] = { item->Pose.Position.y + bounds.Y1 + headroom, |
| 772 | item->Pose.Position.y + bounds.Y2 - headroom }; |
| 773 | |
| 774 | // Prepare test data. |
| 775 | float finalDistance[2] = { FLT_MAX, FLT_MAX }; |
| 776 | short finalResult[2] = { 0 }; |
| 777 | bool hitBridge = false; |
| 778 | |
| 779 | // Do a two-pass surface test for all possible planes in a block. |
| 780 | // Two-pass test is needed to resolve different scissor cases with diagonal geometry. |
| 781 | for (int h = 0; h < 2; h++) |
| 782 | { |
| 783 | // Use either bottom or top Y point to test. |
| 784 | auto y = yPoints[h]; |
| 785 | |
| 786 | // Prepare test data. |
| 787 | Ray originRay; |
| 788 | Plane closestPlane[3] = { }; |
| 789 | float closestDistance[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; |
| 790 | short result[3] = { }; |
| 791 | |
| 792 | // If bridge was hit on the first pass, stop checking. |
| 793 | if (h == 1 && hitBridge) |
| 794 | break; |
| 795 | |
| 796 | for (int p = 0; p < 3; p++) |
| 797 | { |
| 798 | // Prepare test data. |
| 799 | float dist = 0.0f; |
| 800 | |
| 801 | // Determine horizontal probe coordinates. |
| 802 | auto eX = x; |
| 803 | auto eZ = z; |
| 804 | |
| 805 | // Determine if probe must be shifted (if left or right probe). |
| 806 | if (p > 0) |
| 807 | { |
| 808 | auto s2 = phd_sin(coll->Setup.ForwardAngle + (p == 1 ? ANGLE(90.0f) : -ANGLE(90.0f))); |
| 809 | auto c2 = phd_cos(coll->Setup.ForwardAngle + (p == 1 ? ANGLE(90.0f) : -ANGLE(90.0f))); |
no test coverage detected