Skip to content

Commit 0246163

Browse files
kbayescopybara-github
authored andcommitted
Support edge contacts with multiccd in nativeccd.
PiperOrigin-RevId: 730489114 Change-Id: I02847d84bbc15a1526391d5e1b7109ab83ea4a59
1 parent a6f1dc5 commit 0246163

File tree

7 files changed

+861
-49
lines changed

7 files changed

+861
-49
lines changed

src/engine/engine_collision_gjk.c

Lines changed: 227 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,11 @@ static mjtNum planeIntersect(mjtNum res[3], const mjtNum pn[3], mjtNum pd,
15721572
static 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
17721839
static 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
18291935
static 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
19412064
static 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

src/engine/engine_collision_gjk.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
extern "C" {
2626
#endif
2727

28-
// tolerance for considering two normals to be aligned
29-
#define mjCOSINE_TOL 0.99999872
28+
// tolerance for normal alignment of two faces (cosine of 1.6e-3)
29+
#define mjFACE_TOL 0.99999872
30+
31+
// tolerance for edge-face alignment (sine of 1.6e-3)
32+
#define mjEDGE_TOL 0.00159999931
3033

3134
// max number of supported vertices in a polygon face of a mesh
3235
#define mjMAX_POLYVERT 150

0 commit comments

Comments
 (0)