@@ -1572,6 +1572,11 @@ static mjtNum planeIntersect(mjtNum res[3], const mjtNum pn[3], mjtNum pd,
15721572static void polygonClip (mjCCDStatus * status , const mjtNum * face1 , int nface1 ,
15731573 const mjtNum * face2 , int nface2 , const mjtNum n [3 ],
15741574 const mjtNum dir [3 ]) {
1575+ // clipping face needs to be at least a triangle
1576+ if (nface1 < 3 ) {
1577+ return ;
1578+ }
1579+
15751580 // compute plane normal and distance to plane for each vertex
15761581 mjtNum pn [3 * mjMAX_POLYVERT ], pd [mjMAX_POLYVERT ];
15771582 for (int i = 0 ; i < nface1 - 1 ; i ++ ) {
@@ -1658,11 +1663,23 @@ static void polygonClip(mjCCDStatus* status, const mjtNum* face1, int nface1,
16581663 }
16591664
16601665 // no pruning needed
1661- status -> nx = npolygon ;
1666+ int k = 0 ;
16621667 for (int i = 0 ; i < 3 * npolygon ; i += 3 ) {
1663- copy3 (status -> x2 + i , polygon + i );
1664- sub3 (status -> x1 + i , status -> x2 + i , dir );
1668+ int skip = 0 ;
1669+
1670+ // find possible duplicate vertices
1671+ for (int j = 0 ; j < k ; j += 3 ) {
1672+ if (equal3 (status -> x2 + j , polygon + i )) {
1673+ skip = 1 ;
1674+ break ;
1675+ }
1676+ }
1677+ if (skip ) continue ;
1678+ copy3 (status -> x2 + k , polygon + i );
1679+ sub3 (status -> x1 + k , status -> x2 + k , dir );
1680+ k += 3 ;
16651681 }
1682+ status -> nx = k /3 ;
16661683}
16671684
16681685
@@ -1768,6 +1785,56 @@ static int meshNormals(mjtNum* res, int resind[3], int dim, mjCCDObj* obj,
17681785
17691786
17701787
1788+ // compute normal directional vectors along possible edges given by up to two vertices
1789+ static int meshEdgeNormals (mjtNum * res , mjtNum * endverts , int dim , mjCCDObj * obj ,
1790+ const mjtNum v1 [3 ], const mjtNum v2 [3 ], int v1i , int v2i ) {
1791+ // mesh data
1792+ int g = obj -> geom ;
1793+ const mjtNum * mat = obj -> data -> geom_xmat + 9 * g ;
1794+ const mjtNum * pos = obj -> data -> geom_xpos + 3 * g ;
1795+
1796+ // only one edge
1797+ if (dim == 2 ) {
1798+ copy3 (endverts , v2 );
1799+ sub3 (res , v2 , v1 );
1800+ mju_normalize3 (res );
1801+ return 1 ;
1802+ }
1803+
1804+ if (dim == 1 ) {
1805+ const mjModel * m = obj -> model ;
1806+ int polyadr = m -> mesh_polyadr [m -> geom_dataid [g ]];
1807+ int vertadr = m -> mesh_vertadr [m -> geom_dataid [g ]];
1808+
1809+ int v1_adr = m -> mesh_polymapadr [vertadr + v1i ];
1810+ int v1_num = m -> mesh_polymapnum [vertadr + v1i ];
1811+ if (v1_num > mjMAX_POLYVERT ) v1_num = mjMAX_POLYVERT ;
1812+
1813+ // loop through all faces with vertex v1
1814+ for (int i = 0 ; i < v1_num ; i ++ ) {
1815+ int idx = m -> mesh_polymap [v1_adr + i ];
1816+ int adr = m -> mesh_polyvertadr [polyadr + idx ];
1817+ int nvert = m -> mesh_polyvertnum [polyadr + idx ];
1818+ // find previous vertex in polygon to form edge
1819+ for (int j = 0 ; j < nvert ; j ++ ) {
1820+ int v = m -> mesh_polyvert [adr + j ];
1821+ if (v == v1i ) {
1822+ float * verts = m -> mesh_vert + 3 * vertadr ;
1823+ int k = (j == 0 ) ? nvert - 1 : j - 1 ;
1824+ float * vert = verts + 3 * k ;
1825+ globalcoord (endverts + 3 * i , mat , pos , vert [0 ], vert [1 ], vert [2 ]);
1826+ sub3 (res + 3 * i , endverts + 3 * i , v1 );
1827+ mju_normalize3 (res + 3 * i );
1828+ }
1829+ }
1830+ }
1831+ return 3 ;
1832+ }
1833+ return 0 ;
1834+ }
1835+
1836+
1837+
17711838// compute possible face normals of a box given up to 3 vertices
17721839static int boxNormals (mjtNum res [9 ], int resind [3 ], int dim , mjCCDObj * obj ,
17731840 int v1 , int v2 , int v3 ) {
@@ -1825,6 +1892,45 @@ static int boxNormals(mjtNum res[9], int resind[3], int dim, mjCCDObj* obj,
18251892
18261893
18271894
1895+ // compute possible edge normals for box for edge collisions
1896+ static int boxEdgeNormals (mjtNum res [9 ], mjtNum endverts [9 ], int dim , mjCCDObj * obj ,
1897+ const mjtNum v1 [3 ], const mjtNum v2 [3 ], int v1i , int v2i ) {
1898+ // box data
1899+ int g = 3 * obj -> geom ;
1900+ const mjtNum * mat = obj -> data -> geom_xmat + 3 * g ;
1901+ const mjtNum * pos = obj -> data -> geom_xpos + g ;
1902+ const mjtNum * size = obj -> model -> geom_size + g ;
1903+
1904+ if (dim == 2 ) {
1905+ copy3 (endverts , v2 );
1906+ sub3 (res , v2 , v1 );
1907+ mju_normalize3 (res );
1908+ return 1 ;
1909+ }
1910+
1911+ // return 3 adjacent vertices
1912+ if (dim == 1 ) {
1913+ mjtNum x = (v1i & 1 ) ? size [0 ] : - size [0 ];
1914+ mjtNum y = (v1i & 2 ) ? size [1 ] : - size [1 ];
1915+ mjtNum z = (v1i & 4 ) ? size [2 ] : - size [2 ];
1916+
1917+ globalcoord (endverts , mat , pos , - x , y , z );
1918+ sub3 (res , endverts , v1 );
1919+ mju_normalize3 (res );
1920+
1921+ globalcoord (endverts + 3 , mat , pos , x , - y , z );
1922+ sub3 (res + 3 , endverts + 3 , v1 );
1923+ mju_normalize3 (res + 3 );
1924+
1925+ globalcoord (endverts + 6 , mat , pos , x , y , - z );
1926+ sub3 (res + 6 , endverts + 6 , v1 );
1927+ mju_normalize3 (res + 6 );
1928+ return 3 ;
1929+ }
1930+ return 0 ;
1931+ }
1932+
1933+
18281934// recover face of a box from its index
18291935static int boxFace (mjtNum res [12 ], mjCCDObj * obj , int idx ) {
18301936 // box data
@@ -1903,11 +2009,11 @@ static int meshFace(mjtNum* res, mjCCDObj* obj, int idx) {
19032009
19042010
19052011// find two normals that are facing each other within a tolerance, return 1 if found
1906- static inline int alignedNormals (int res [2 ], const mjtNum * v , int nv ,
2012+ static inline int alignedFaces (int res [2 ], const mjtNum * v , int nv ,
19072013 const mjtNum * w , int nw ) {
19082014 for (int i = 0 ; i < nv ; i ++ ) {
19092015 for (int j = 0 ; j < nw ; j ++ ) {
1910- if (dot3 (v + 3 * i , w + 3 * j ) < - mjCOSINE_TOL ) {
2016+ if (dot3 (v + 3 * i , w + 3 * j ) < - mjFACE_TOL ) {
19112017 res [0 ] = i ;
19122018 res [1 ] = j ;
19132019 return 1 ;
@@ -1919,16 +2025,33 @@ static inline int alignedNormals(int res[2], const mjtNum* v, int nv,
19192025
19202026
19212027
1922- // return number of dimensions of a feature (1, 2 or 3)
1923- static inline int simplexDim (int * v1 , int * v2 , int * v3 ) {
1924- int val1 = * v1 ;
1925- int val2 = * v2 ;
1926- int val3 = * v3 ;
2028+ // find two normals that are perpendicular to each other within a tolerance, return 1 if found
2029+ static inline int alignedFaceEdge (int res [2 ], const mjtNum * edge , int nedge ,
2030+ const mjtNum * face , int nface ) {
2031+ for (int i = 0 ; i < nface ; i ++ ) {
2032+ for (int j = 0 ; j < nedge ; j ++ ) {
2033+ if (mju_abs (dot3 (edge + 3 * j , face + 3 * i )) < mjEDGE_TOL ) {
2034+ res [0 ] = j ;
2035+ res [1 ] = i ;
2036+ return 1 ;
2037+ }
2038+ }
2039+ }
2040+ return 0 ;
2041+ }
2042+
2043+
2044+ // return number (1, 2 or 3) of dimensions of a simplex; reorder vertices if necessary
2045+ static inline int simplexDim (int * v1i , int * v2i , int * v3i , mjtNum * * v1 , mjtNum * * v2 , mjtNum * * v3 ) {
2046+ int val1 = * v1i ;
2047+ int val2 = * v2i ;
2048+ int val3 = * v3i ;
19272049
19282050 if (val1 != val2 ) {
19292051 return (val3 == val1 || val3 == val2 ) ? 2 : 3 ;
19302052 }
19312053 if (val1 != val3 ) {
2054+ * v2i = * v3i ;
19322055 * v2 = * v3 ;
19332056 return 2 ;
19342057 }
@@ -1940,65 +2063,122 @@ static inline int simplexDim(int* v1, int* v2, int* v3) {
19402063// recover multiple contacts from EPA polytope
19412064static void multicontact (Polytope * pt , Face * face , mjCCDStatus * status ,
19422065 mjCCDObj * obj1 , mjCCDObj * obj2 ) {
1943- mjtNum face1 [mjMAX_POLYVERT * 3 ], face2 [mjMAX_POLYVERT * 3 ];
1944-
2066+ mjtNum face1 [mjMAX_POLYVERT * 3 ], face2 [mjMAX_POLYVERT * 3 ], endverts [mjMAX_POLYVERT * 3 ];
19452067 // get vertices of faces from EPA
1946- int v11 = pt -> verts [face -> verts [0 ]].index1 ;
1947- int v12 = pt -> verts [face -> verts [1 ]].index1 ;
1948- int v13 = pt -> verts [face -> verts [2 ]].index1 ;
1949- int v21 = pt -> verts [face -> verts [0 ]].index2 ;
1950- int v22 = pt -> verts [face -> verts [1 ]].index2 ;
1951- int v23 = pt -> verts [face -> verts [2 ]].index2 ;
2068+ int v11i = pt -> verts [face -> verts [0 ]].index1 ;
2069+ int v12i = pt -> verts [face -> verts [1 ]].index1 ;
2070+ int v13i = pt -> verts [face -> verts [2 ]].index1 ;
2071+ int v21i = pt -> verts [face -> verts [0 ]].index2 ;
2072+ int v22i = pt -> verts [face -> verts [1 ]].index2 ;
2073+ int v23i = pt -> verts [face -> verts [2 ]].index2 ;
2074+ mjtNum * v11 = pt -> verts [face -> verts [0 ]].vert1 ;
2075+ mjtNum * v12 = pt -> verts [face -> verts [1 ]].vert1 ;
2076+ mjtNum * v13 = pt -> verts [face -> verts [2 ]].vert1 ;
2077+ mjtNum * v21 = pt -> verts [face -> verts [0 ]].vert2 ;
2078+ mjtNum * v22 = pt -> verts [face -> verts [1 ]].vert2 ;
2079+ mjtNum * v23 = pt -> verts [face -> verts [2 ]].vert2 ;
19522080
19532081 // get dimensions of features of geoms 1 and 2
1954- int nface1 = simplexDim (& v11 , & v12 , & v13 );
1955- int nface2 = simplexDim (& v21 , & v22 , & v23 );
2082+ int nface1 = simplexDim (& v11i , & v12i , & v13i , & v11 , & v12 , & v13 );
2083+ int nface2 = simplexDim (& v21i , & v22i , & v23i , & v21 , & v22 , & v23 );
19562084 int nnorms1 = 0 , nnorms2 = 0 ;
19572085 mjtNum n1 [3 * mjMAX_POLYVERT ], n2 [3 * mjMAX_POLYVERT ]; // normals of possible face collisions
19582086 int idx1 [mjMAX_POLYVERT ], idx2 [mjMAX_POLYVERT ]; // indices of faces
19592087
19602088 // get all possible face normals for each geom
19612089 if (obj1 -> geom_type == mjGEOM_BOX ) {
1962- nnorms1 = boxNormals (n1 , idx1 , nface1 , obj1 , v11 , v12 , v13 );
2090+ nnorms1 = boxNormals (n1 , idx1 , nface1 , obj1 , v11i , v12i , v13i );
19632091 } else if (obj1 -> geom_type == mjGEOM_MESH ) {
1964- nnorms1 = meshNormals (n1 , idx1 , nface1 , obj1 , v11 , v12 , v13 );
2092+ nnorms1 = meshNormals (n1 , idx1 , nface1 , obj1 , v11i , v12i , v13i );
19652093 }
19662094 if (obj2 -> geom_type == mjGEOM_BOX ) {
1967- nnorms2 = boxNormals (n2 , idx2 , nface2 , obj2 , v21 , v22 , v23 );
2095+ nnorms2 = boxNormals (n2 , idx2 , nface2 , obj2 , v21i , v22i , v23i );
19682096 } else if (obj2 -> geom_type == mjGEOM_MESH ) {
1969- nnorms2 = meshNormals (n2 , idx2 , nface2 , obj2 , v21 , v22 , v23 );
1970- }
1971-
1972- // determine if any two normals match
1973- int res [2 ];
1974- if (!alignedNormals (res , n1 , nnorms1 , n2 , nnorms2 )) {
1975- return ;
2097+ nnorms2 = meshNormals (n2 , idx2 , nface2 , obj2 , v21i , v22i , v23i );
2098+ }
2099+
2100+ // determine if any two face normals match
2101+ int res [2 ], edgecon1 = 0 , edgecon2 = 0 ;
2102+ if (!alignedFaces (res , n1 , nnorms1 , n2 , nnorms2 )) {
2103+ // check if edge-face collision
2104+ if (nface1 < 3 && nface1 <= nface2 ) {
2105+ nnorms1 = 0 ;
2106+ if (obj1 -> geom_type == mjGEOM_BOX ) {
2107+ nnorms1 = boxEdgeNormals (n1 , endverts , nface1 , obj1 , v11 , v12 , v11i , v12i );
2108+ } else if (obj1 -> geom_type == mjGEOM_MESH ) {
2109+ nnorms1 = meshEdgeNormals (n1 , endverts , nface1 , obj1 , v11 , v12 , v11i , v12i );
2110+ }
2111+ if (!alignedFaceEdge (res , n1 , nnorms1 , n2 , nnorms2 )) return ;
2112+ edgecon1 = 1 ;
2113+
2114+ // check if face-edge collision
2115+ } else if (nface2 < 3 ) {
2116+ nnorms2 = 0 ;
2117+ if (obj2 -> geom_type == mjGEOM_BOX ) {
2118+ nnorms2 = boxEdgeNormals (n2 , endverts , nface2 , obj2 , v21 , v22 , v21i , v22i );
2119+ } else if (obj2 -> geom_type == mjGEOM_MESH ) {
2120+ nnorms2 = meshEdgeNormals (n2 , endverts , nface2 , obj2 , v21 , v22 , v21i , v22i );
2121+ }
2122+ if (!alignedFaceEdge (res , n2 , nnorms2 , n1 , nnorms1 )) return ;
2123+ edgecon2 = 1 ;
2124+ } else {
2125+ // no multi-contact
2126+ return ;
2127+ }
19762128 }
19772129 int i = res [0 ], j = res [1 ];
19782130
1979- // recover matching faces
1980- if (obj1 -> geom_type == mjGEOM_BOX ) {
1981- nface1 = boxFace (face1 , obj1 , idx1 [i ]);
1982- } else if (obj1 -> geom_type == mjGEOM_MESH ) {
1983- nface1 = meshFace (face1 , obj1 , idx1 [i ]);
2131+ // recover geom1 matching edge or face
2132+ if (edgecon1 ) {
2133+ copy3 (face1 , pt -> verts [face -> verts [0 ]].vert1 );
2134+ copy3 (face1 + 3 , endverts + 3 * i );
2135+ nface1 = 2 ;
2136+ } else {
2137+ if (obj1 -> geom_type == mjGEOM_BOX ) {
2138+ int ind = (edgecon2 ? idx1 [j ] : idx1 [i ]);
2139+ nface1 = boxFace (face1 , obj1 , ind );
2140+ } else if (obj1 -> geom_type == mjGEOM_MESH ) {
2141+ int ind = (edgecon2 ? idx1 [j ] : idx1 [i ]);
2142+ nface1 = meshFace (face1 , obj1 , ind );
2143+ }
19842144 }
1985- if (obj2 -> geom_type == mjGEOM_BOX ) {
1986- nface2 = boxFace (face2 , obj2 , idx2 [j ]);
1987- } else if (obj2 -> geom_type == mjGEOM_MESH ) {
1988- nface2 = meshFace (face2 , obj2 , idx2 [j ]);
2145+
2146+ // recover geom2 matching edge or face
2147+ if (edgecon2 ) {
2148+ copy3 (face2 , pt -> verts [face -> verts [0 ]].vert2 );
2149+ copy3 (face2 + 3 , endverts + 3 * i );
2150+ nface2 = 2 ;
2151+ } else {
2152+ if (obj2 -> geom_type == mjGEOM_BOX ) {
2153+ nface2 = boxFace (face2 , obj2 , idx2 [j ]);
2154+ } else if (obj2 -> geom_type == mjGEOM_MESH ) {
2155+ nface2 = meshFace (face2 , obj2 , idx2 [j ]);
2156+ }
19892157 }
19902158
1991- if (nface1 >= 3 && nface2 >= 3 ) {
1992- // TODO(kylebayes): this approximates the contact direction, by scaling the face normal by the
1993- // single contact direction's magnitude. This is effective, but polygonClip should compute
1994- // this for each contact point.
1995- mjtNum diff [3 ], approx_dir [3 ];
1996- sub3 (diff , status -> x2 , status -> x1 );
2159+ // TODO(kylebayes): this approximates the contact direction, by scaling the face normal by the
2160+ // single contact direction's magnitude. This is effective, but polygonClip should compute
2161+ // this for each contact point.
2162+ mjtNum diff [3 ], approx_dir [3 ];
2163+ sub3 (diff , status -> x2 , status -> x1 );
2164+
2165+ // face1 is an edge; clip face1 against face2
2166+ if (edgecon1 ) {
19972167 scl3 (approx_dir , n2 + 3 * j , norm3 (diff ));
2168+ polygonClip (status , face2 , nface2 , face1 , nface1 , n2 + 3 * j , approx_dir );
2169+ return ;
2170+ }
19982171
1999- // clip the faces and store the results in status
2000- polygonClip (status , face1 , nface1 , face2 , nface2 , n1 + 3 * i , approx_dir );
2172+ // face2 is an edge; clip face2 against face1
2173+ if (edgecon2 ) {
2174+ scl3 (approx_dir , n1 + 3 * j , - norm3 (diff ));
2175+ polygonClip (status , face1 , nface1 , face2 , nface2 , n1 + 3 * j , approx_dir );
2176+ return ;
20012177 }
2178+
2179+ // face-face collision
2180+ scl3 (approx_dir , n2 + 3 * j , norm3 (diff ));
2181+ polygonClip (status , face1 , nface1 , face2 , nface2 , n1 + 3 * i , approx_dir );
20022182}
20032183
20042184
0 commit comments