|
43 | 43 | #ifdef _MSC_VER |
44 | 44 | #pragma warning(disable:4291) // for VC++, no complaints about "no matching operator delete found" |
45 | 45 | #endif |
| 46 | +#include <valarray> |
46 | 47 |
|
47 | 48 | //**************************************************************************** |
48 | 49 | // box public API |
@@ -740,137 +741,238 @@ int dCollideBoxBox (dxGeom *o1, dxGeom *o2, int flags, |
740 | 741 | return num; |
741 | 742 | } |
742 | 743 |
|
743 | | -int dCollideBoxPlane (dxGeom *o1, dxGeom *o2, |
744 | | - int flags, dContactGeom *contact, int skip) |
745 | | -{ |
746 | | - dIASSERT (skip >= (int)sizeof(dContactGeom)); |
747 | | - dIASSERT (o1->type == dBoxClass); |
748 | | - dIASSERT (o2->type == dPlaneClass); |
749 | | - dIASSERT ((flags & NUMC_MASK) >= 1); |
750 | | - |
751 | | - dxBox *box = (dxBox*) o1; |
752 | | - dxPlane *plane = (dxPlane*) o2; |
753 | | - |
754 | | - contact->g1 = o1; |
755 | | - contact->g2 = o2; |
756 | | - contact->side1 = -1; |
757 | | - contact->side2 = -1; |
758 | | - |
759 | | - int ret = 0; |
760 | | - |
761 | | - //@@@ problem: using 4-vector (plane->p) as 3-vector (normal). |
762 | | - const dReal *R = o1->final_posr->R; // rotation of box |
763 | | - const dReal *n = plane->p; // normal vector |
764 | | - |
765 | | - // project sides lengths along normal vector, get absolute values |
766 | | - dReal Q1 = dCalcVectorDot3_14(n,R+0); |
767 | | - dReal Q2 = dCalcVectorDot3_14(n,R+1); |
768 | | - dReal Q3 = dCalcVectorDot3_14(n,R+2); |
769 | | - dReal A1 = box->side[0] * Q1; |
770 | | - dReal A2 = box->side[1] * Q2; |
771 | | - dReal A3 = box->side[2] * Q3; |
772 | | - dReal B1 = dFabs(A1); |
773 | | - dReal B2 = dFabs(A2); |
774 | | - dReal B3 = dFabs(A3); |
775 | | - |
776 | | - // early exit test |
777 | | - dReal depth = plane->p[3] + REAL(0.5)*(B1+B2+B3) - dCalcVectorDot3(n,o1->final_posr->pos); |
778 | | - if (depth < 0) return 0; |
779 | | - |
780 | | - // find number of contacts requested |
781 | | - int maxc = flags & NUMC_MASK; |
782 | | - // if (maxc < 1) maxc = 1; // an assertion is made on entry |
783 | | - if (maxc > 4) maxc = 4; // not more than 4 contacts per box allowed |
784 | | - |
785 | | - // find deepest point |
786 | | - dVector3 p; |
787 | | - p[0] = o1->final_posr->pos[0]; |
788 | | - p[1] = o1->final_posr->pos[1]; |
789 | | - p[2] = o1->final_posr->pos[2]; |
790 | | -#define FOO(i,op) \ |
791 | | - p[0] op REAL(0.5)*box->side[i] * R[0+i]; \ |
792 | | - p[1] op REAL(0.5)*box->side[i] * R[4+i]; \ |
793 | | - p[2] op REAL(0.5)*box->side[i] * R[8+i]; |
794 | | -#define BAR(i,iinc) if (A ## iinc > 0) { FOO(i,-=) } else { FOO(i,+=) } |
795 | | - BAR(0,1); |
796 | | - BAR(1,2); |
797 | | - BAR(2,3); |
798 | | -#undef FOO |
799 | | -#undef BAR |
800 | | - |
801 | | - // the deepest point is the first contact point |
802 | | - contact->pos[0] = p[0]; |
803 | | - contact->pos[1] = p[1]; |
804 | | - contact->pos[2] = p[2]; |
805 | | - contact->depth = depth; |
806 | | - ret = 1; // ret is number of contact points found so far |
807 | | - if (maxc == 1) goto done; |
808 | | - |
809 | | - // get the second and third contact points by starting from `p' and going |
810 | | - // along the two sides with the smallest projected length. |
811 | | - |
812 | | -#define FOO(i,j,op) \ |
813 | | - CONTACT(contact,i*skip)->pos[0] = p[0] op box->side[j] * R[0+j]; \ |
814 | | - CONTACT(contact,i*skip)->pos[1] = p[1] op box->side[j] * R[4+j]; \ |
815 | | - CONTACT(contact,i*skip)->pos[2] = p[2] op box->side[j] * R[8+j]; |
816 | | -#define BAR(ctact,side,sideinc) \ |
817 | | - if (depth - B ## sideinc < 0) goto done; \ |
818 | | - if (A ## sideinc > 0) { FOO(ctact,side,+); } else { FOO(ctact,side,-); } \ |
819 | | - CONTACT(contact,ctact*skip)->depth = depth - B ## sideinc; \ |
820 | | - ret++; |
821 | | - |
822 | | - if (B1 < B2) { |
823 | | - if (B3 < B1) goto use_side_3; else { |
824 | | - BAR(1,0,1); // use side 1 |
825 | | - if (maxc == 2) goto done; |
826 | | - if (B2 < B3) goto contact2_2; else goto contact2_3; |
827 | | - } |
828 | | - } |
829 | | - else { |
830 | | - if (B3 < B2) { |
831 | | -use_side_3: // use side 3 |
832 | | - BAR(1,2,3); |
833 | | - if (maxc == 2) goto done; |
834 | | - if (B1 < B2) goto contact2_1; else goto contact2_2; |
835 | | - } |
836 | | - else { |
837 | | - BAR(1,1,2); // use side 2 |
838 | | - if (maxc == 2) goto done; |
839 | | - if (B1 < B3) goto contact2_1; else goto contact2_3; |
840 | | - } |
841 | | - } |
842 | | - |
843 | | -contact2_1: BAR(2,0,1); goto done; |
844 | | -contact2_2: BAR(2,1,2); goto done; |
845 | | -contact2_3: BAR(2,2,3); goto done; |
846 | | -#undef FOO |
847 | | -#undef BAR |
848 | | - |
849 | | -done: |
850 | | - |
851 | | - if (maxc == 4 && ret == 3) { // If user requested 4 contacts, and the first 3 were created... |
852 | | - // Combine contacts 2 and 3 (vectorial sum) and get the fourth one |
853 | | - // Result: if a box face is completely inside a plane, contacts are created for all the 4 vertices |
854 | | - dReal d4 = CONTACT(contact,1*skip)->depth + CONTACT(contact,2*skip)->depth - depth; // depth is the depth for first contact |
855 | | - if (d4 > 0) { |
856 | | - CONTACT(contact,3*skip)->pos[0] = CONTACT(contact,1*skip)->pos[0] + CONTACT(contact,2*skip)->pos[0] - p[0]; // p is the position of first contact |
857 | | - CONTACT(contact,3*skip)->pos[1] = CONTACT(contact,1*skip)->pos[1] + CONTACT(contact,2*skip)->pos[1] - p[1]; |
858 | | - CONTACT(contact,3*skip)->pos[2] = CONTACT(contact,1*skip)->pos[2] + CONTACT(contact,2*skip)->pos[2] - p[2]; |
859 | | - CONTACT(contact,3*skip)->depth = d4; |
860 | | - ret++; |
861 | | - } |
862 | | - } |
863 | | - |
864 | | - for (int i=0; i<ret; i++) { |
865 | | - dContactGeom *currContact = CONTACT(contact,i*skip); |
866 | | - currContact->g1 = o1; |
867 | | - currContact->g2 = o2; |
868 | | - currContact->side1 = -1; |
869 | | - currContact->side2 = -1; |
| 744 | +// Fill in the the non-varying contact info for the first n contacts. |
| 745 | +static inline void finishContacts(int n, dContactGeom *&contact, int skip, dxGeom *o1, dxGeom *o2, const dReal *normal) { |
| 746 | + for (int i = 0; i < n; i++) { |
| 747 | + dContactGeom *currContact = CONTACT(contact, i * skip); |
| 748 | + currContact->g1 = o1; |
| 749 | + currContact->g2 = o2; |
| 750 | + currContact->side1 = -1; |
| 751 | + currContact->side2 = -1; |
| 752 | + |
| 753 | + currContact->normal[0] = normal[0]; |
| 754 | + currContact->normal[1] = normal[1]; |
| 755 | + currContact->normal[2] = normal[2]; |
| 756 | + } |
| 757 | +} |
870 | 758 |
|
871 | | - currContact->normal[0] = n[0]; |
872 | | - currContact->normal[1] = n[1]; |
873 | | - currContact->normal[2] = n[2]; |
874 | | - } |
875 | | - return ret; |
| 759 | +int dCollideBoxPlane(dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip) { |
| 760 | + dIASSERT(skip >= (int)sizeof(dContactGeom)); |
| 761 | + dIASSERT(o1->type == dBoxClass); |
| 762 | + dIASSERT(o2->type == dPlaneClass); |
| 763 | + dIASSERT((flags & NUMC_MASK) >= 1); |
| 764 | + |
| 765 | + dxBox *box = (dxBox *)o1; |
| 766 | + dxPlane *plane = (dxPlane *)o2; |
| 767 | + |
| 768 | + //@@@ problem: using 4-vector (plane->p) as 3-vector (normal). |
| 769 | + const dReal *R = o1->final_posr->R; // rotation of box |
| 770 | + const dReal *n = plane->p; // normal vector |
| 771 | + |
| 772 | + // project sides lengths along normal vector, get absolute values |
| 773 | + // A[i] will be the reduction in depth resulting from moving in direction i by an amount of side[i]. |
| 774 | + const dReal Q0 = dCalcVectorDot3_14(n, R + 0); |
| 775 | + const dReal Q1 = dCalcVectorDot3_14(n, R + 1); |
| 776 | + const dReal Q2 = dCalcVectorDot3_14(n, R + 2); |
| 777 | + const dReal *const side = box->side; |
| 778 | + const dVector3 A = {side[0] * Q0, side[1] * Q1, side[2] * Q2}; |
| 779 | + |
| 780 | + // Find centerDepth |
| 781 | + const dReal centerDepth = plane->p[3] - dCalcVectorDot3(n, o1->final_posr->pos); |
| 782 | + |
| 783 | + // The depths (positive = deeper) of the 8 vertices will be: |
| 784 | + // |
| 785 | + // centerDepth - 0.5*(+/- A[0] +/- A[1] +/- A[2]) |
| 786 | + // |
| 787 | + // for the 8 combinations of +/-. |
| 788 | + |
| 789 | + // What we'd *really* like is to get the vertices in depth order (deepest first), so that we can |
| 790 | + // easily return the maxc deepest vertices and also return early once we get to a vertex with a |
| 791 | + // negative depth. To achieve that efficiently, it will turn out to be useful to use |A[s]| |
| 792 | + // instead of A[s] when computing the depths. We store that in B. |
| 793 | + dVector3 B = {dFabs(A[0]), dFabs(A[1]), dFabs(A[2])}; |
| 794 | + |
| 795 | + // Note that A[s]=sgn(A[s])*B[s]. That means that the depths are |
| 796 | + // given by: |
| 797 | + // |
| 798 | + // centerDepth - 0.5*(+/- sgn(A[0])*B[0] +/- sgn(A[1])*B[1] +/- sgn(A[2])*B[2]) |
| 799 | + // |
| 800 | + // The depth of the deepest vertex is achieved when the choice of + or - is opposite the |
| 801 | + // corresponding sgn() function. That will lead to the depth of the deepest vertex being: |
| 802 | + dReal depth = centerDepth + REAL(0.5) * (B[0] + B[1] + B[2]); |
| 803 | + |
| 804 | + // The number of contacts found. |
| 805 | + int nContactsFound = 0; |
| 806 | + |
| 807 | + do { |
| 808 | + // If that isn't below the plane, there is no collision and we can return immediately. |
| 809 | + if (depth < 0) |
| 810 | + break; |
| 811 | + |
| 812 | + // Otherwise, we need to figure out the position of the vertex, and potentially other vertices. |
| 813 | + |
| 814 | + // Let p be the center point of the box. |
| 815 | + const dVector3 p = {o1->final_posr->pos[0], o1->final_posr->pos[1], o1->final_posr->pos[2]}; |
| 816 | + |
| 817 | + // To compute the positions of each of the 8 vertices, note that side[s]*R[4*c+s] is the change |
| 818 | + // in coordinate c associated with moving along the full length of side s. As a result, the cth |
| 819 | + // coord of the vertices are given by: |
| 820 | + // |
| 821 | + // p[c] +/- 0.5*side[0]*R[4*c+0] +/- 0.5*side[1]*R[4*c+1] +/- 0.5*side[2]*R[4*c+2] |
| 822 | + // |
| 823 | + // for c in 0, 1, 2, and all 8 combinations of +/- where the choices for +/- for a vertex match |
| 824 | + // the choices when computing the depth of the vertex: |
| 825 | + // |
| 826 | + // centerDepth - 0.5*(+/- sgn(A[0])*B[0] +/- sgn(A[1])*B[1] +/- sgn(A[2])*B[2]) |
| 827 | + // |
| 828 | + // Since the sgn() function will just flip or not flip the +/- to a -/+, and we just need to |
| 829 | + // keep the choices consistent between the computations of the vertices' depths and the |
| 830 | + // positions, we can move the sgn() to the computation of the coords to get: |
| 831 | + // |
| 832 | + // centerDepth - 0.5*(+/- B[0] +/- B[1] +/- B[2]) |
| 833 | + // |
| 834 | + // and: |
| 835 | + // |
| 836 | + // p[c] +/- 0.5*sgn(A[0])*side[0]*R[4*c+0] +/- 0.5*sgn(A[1])*side[1]*R[4*c+1] +/- |
| 837 | + // 0.5*sgn(A[2])*side[2]*R[4*c+2] |
| 838 | + // |
| 839 | + // We can precompute signedHalfSides[s]=0.5*sgn(A[s])*side[s] and |
| 840 | + // signedHalfSideVectors[s][c]=signedHalfSides[s]*R[4*s+c] |
| 841 | + const dVector3 signedHalfSides = { |
| 842 | + (std::signbit(A[0]) ? REAL(-0.5) : REAL(0.5)) * side[0], |
| 843 | + (std::signbit(A[1]) ? REAL(-0.5) : REAL(0.5)) * side[1], |
| 844 | + (std::signbit(A[2]) ? REAL(-0.5) : REAL(0.5)) * side[2], |
| 845 | + }; |
| 846 | + |
| 847 | + const dVector3 signedHalfSideVectors[3] = { |
| 848 | + {signedHalfSides[0] * R[0 + 0], signedHalfSides[0] * R[4 + 0], signedHalfSides[0] * R[8 + 0]}, |
| 849 | + {signedHalfSides[1] * R[0 + 1], signedHalfSides[1] * R[4 + 1], signedHalfSides[1] * R[8 + 1]}, |
| 850 | + {signedHalfSides[2] * R[0 + 2], signedHalfSides[2] * R[4 + 2], signedHalfSides[2] * R[8 + 2]}, |
| 851 | + }; |
| 852 | + |
| 853 | + // Now the vertices are given by: |
| 854 | + // |
| 855 | + // p[c] +/- signedHalfSides[0]*R[4*c+0] +/- signedHalfSides[1]*R[4*c+1] +/- |
| 856 | + // signedHalfSides[2]*R[4*c+2] |
| 857 | + // |
| 858 | + // The deepest vertex corresponds to always choosing "-". So we can fill it in right away. |
| 859 | + dContactGeom *currContact; |
| 860 | + currContact = contact; |
| 861 | + currContact->pos[0] = p[0] - signedHalfSideVectors[2][0] - signedHalfSideVectors[1][0] - signedHalfSideVectors[0][0]; |
| 862 | + currContact->pos[1] = p[1] - signedHalfSideVectors[2][1] - signedHalfSideVectors[1][1] - signedHalfSideVectors[0][1]; |
| 863 | + currContact->pos[2] = p[2] - signedHalfSideVectors[2][2] - signedHalfSideVectors[1][2] - signedHalfSideVectors[0][2]; |
| 864 | + currContact->depth = depth; |
| 865 | + |
| 866 | + nContactsFound++; |
| 867 | + |
| 868 | + // The number of contacts requested. We can't return more than this many. |
| 869 | + const int maxc = flags & NUMC_MASK; |
| 870 | + |
| 871 | + // If only 1 contact was requested, finish filling in the non-varying contact info and return. |
| 872 | + if (maxc == 1) |
| 873 | + break; |
| 874 | + |
| 875 | + // The second deepest will correspond to choosing the - option for all but the smallest B[s]. |
| 876 | + // That corresponds to moving along the edge that causes the least change in depth. The third |
| 877 | + // deepest will correspond to choosing the - option for all but the second smallest B[s]. The |
| 878 | + // least, second least, and third *least* deep can be found similarly. So the ranks of 6 of the |
| 879 | + // 8 vertices can be determined in this way. The only remaining ambiguity is which of the 2 |
| 880 | + // other vertices is deeper, which we will handle later. |
| 881 | + // |
| 882 | + // To compute the vertices and depths efficiently in the desired order, we will presort the |
| 883 | + // sides and their corresponding Bs in ascending order of the Bs and we will precompute each of |
| 884 | + // the signedHalfSide[s]*R[4*c+s] terms as follows: |
| 885 | + // |
| 886 | + // Let minSide, midSide, maxSide be the indices of the sides such that |
| 887 | + // B[minSide]<=B[midSide]<=B[maxSide] |
| 888 | + // |
| 889 | + // Let orderedB[0]=B[minSide], orderedB[1]=B[midSide], and orderedB[2]=B[maxSide] |
| 890 | + // |
| 891 | + // and: |
| 892 | + // |
| 893 | + // orderedSignedHalfSideVectors[0][c] = signedHalfSideVectors[minSide] |
| 894 | + // orderedSignedHalfSideVectors[1][c] = signedHalfSideVectors[midSide] |
| 895 | + // orderedSignedHalfSideVectors[2][c] = signedHalfSideVectors[maxSide] |
| 896 | + // |
| 897 | + // The positions of the vertices will then be: |
| 898 | + // |
| 899 | + // p[c] +/- orderedHalfSideVector[0][c] +/- orderedHalfSideVector[1][c] +/- |
| 900 | + // orderedHalfSideVector[2][c] |
| 901 | + // |
| 902 | + // and the corresponding depths: |
| 903 | + // |
| 904 | + // centerDepth - 0.5*(+/- orderedB[0] +/- orderedB[1] +/- orderedB[2]) |
| 905 | + // |
| 906 | + // We will call the following macro with the various +/- combos to compute the depth of a |
| 907 | + // vertex, add it to the contacts if it is inside and stop early if it isn't or we've found |
| 908 | + // the requested number of contacts. |
| 909 | +#define ADD_VERTEX_IF_INSIDE(op1, op2, op3) \ |
| 910 | + depth = (centerDepth - REAL(0.5) * (op3 orderedB[2] op2 orderedB[1] op1 orderedB[0])); \ |
| 911 | + if (depth >= 0.0) { \ |
| 912 | + currContact = CONTACT(contact, nContactsFound * skip); \ |
| 913 | + currContact->pos[0] = p[0] op3 orderedSignedHalfSideVectors[2][0] op2 orderedSignedHalfSideVectors[1][0] op1 \ |
| 914 | + orderedSignedHalfSideVectors[0][0]; \ |
| 915 | + currContact->pos[1] = p[1] op3 orderedSignedHalfSideVectors[2][1] op2 orderedSignedHalfSideVectors[1][1] op1 \ |
| 916 | + orderedSignedHalfSideVectors[0][1]; \ |
| 917 | + currContact->pos[2] = p[2] op3 orderedSignedHalfSideVectors[2][2] op2 orderedSignedHalfSideVectors[1][2] op1 \ |
| 918 | + orderedSignedHalfSideVectors[0][2]; \ |
| 919 | + currContact->depth = depth; \ |
| 920 | + nContactsFound++; \ |
| 921 | + } else \ |
| 922 | + break; \ |
| 923 | + if (nContactsFound >= maxc) \ |
| 924 | + break; |
| 925 | + // END OF MACRO DEFINITION |
| 926 | + |
| 927 | + // But first we need to sort the sides and Bs. We'll start with them in their original order... |
| 928 | + const dReal *orderedSignedHalfSideVectors[3] = { |
| 929 | + &(signedHalfSideVectors[0][0]), |
| 930 | + &(signedHalfSideVectors[1][0]), |
| 931 | + &(signedHalfSideVectors[2][0]), |
| 932 | + }; |
| 933 | + const dReal *orderedB = B; |
| 934 | + |
| 935 | + // Swap the one that corresponds to the lowest B into the 0th position. |
| 936 | + if (B[1] < B[0]) { |
| 937 | + std::swap(B[1], B[0]); |
| 938 | + std::swap(orderedSignedHalfSideVectors[1], orderedSignedHalfSideVectors[0]); |
| 939 | + } |
| 940 | + if (B[2] < B[0]) { |
| 941 | + std::swap(B[2], B[0]); |
| 942 | + std::swap(orderedSignedHalfSideVectors[2], orderedSignedHalfSideVectors[0]); |
| 943 | + } |
| 944 | + |
| 945 | + // For the second deepest vertex, we don't actually care about the order of other 2 sides, so we |
| 946 | + // can go ahead and process that one. Maybe we'll get lucky and exit early. |
| 947 | + ADD_VERTEX_IF_INSIDE(+, -, -); |
| 948 | + |
| 949 | + // If we didn't get lucky, we need to sort the remaining 2 sides. |
| 950 | + if (B[2] < B[1]) { |
| 951 | + std::swap(B[2], B[1]); |
| 952 | + std::swap(orderedSignedHalfSideVectors[2], orderedSignedHalfSideVectors[1]); |
| 953 | + } |
| 954 | + |
| 955 | + // Now we can process the third deepest vertex. |
| 956 | + ADD_VERTEX_IF_INSIDE(-, +, -); |
| 957 | + |
| 958 | + // Determine which of the 2 middle vertices is deeper and process them in the correct order. |
| 959 | + if (B[0] + B[1] < B[2]) { |
| 960 | + ADD_VERTEX_IF_INSIDE(+, +, -); |
| 961 | + ADD_VERTEX_IF_INSIDE(-, -, +); |
| 962 | + } else { |
| 963 | + ADD_VERTEX_IF_INSIDE(-, -, +); |
| 964 | + ADD_VERTEX_IF_INSIDE(+, +, -); |
| 965 | + } |
| 966 | + |
| 967 | + // Process the 3 remaining vertices in order. |
| 968 | + ADD_VERTEX_IF_INSIDE(+, -, +); |
| 969 | + ADD_VERTEX_IF_INSIDE(-, +, +); |
| 970 | + ADD_VERTEX_IF_INSIDE(+, +, +); |
| 971 | + } while (false); |
| 972 | + |
| 973 | + if (nContactsFound) |
| 974 | + finishContacts(nContactsFound, contact, skip, o1, o2, n); |
| 975 | + |
| 976 | + int retVal = nContactsFound; |
| 977 | + return retVal; |
876 | 978 | } |
0 commit comments