Add saving the indices of mesh vertices with the support points in nativeccd.
PiperOrigin-RevId: 725660975 Change-Id: I036882a5bde5d009bfa6e66b6a10f6cfb1a78292
This commit is contained in:
committed by
Copybara-Service
parent
ac2a324fbd
commit
e3b9538d92
@@ -347,8 +347,8 @@ static void mjc_meshSupport(mjtNum res[3], mjCCDObj* obj, const mjtNum dir[3]) {
|
||||
int imax = 0;
|
||||
|
||||
// used cached results from previous search
|
||||
if (obj->meshindex >= 0) {
|
||||
imax = obj->meshindex;
|
||||
if (obj->vertindex >= 0) {
|
||||
imax = obj->vertindex;
|
||||
max = dot3f(local_dir, verts + 3*imax);
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ static void mjc_meshSupport(mjtNum res[3], mjCCDObj* obj, const mjtNum dir[3]) {
|
||||
}
|
||||
|
||||
// record vertex index of maximum
|
||||
obj->meshindex = imax;
|
||||
obj->vertindex = imax;
|
||||
|
||||
local_dir[0] = (mjtNum)verts[3*imax + 0];
|
||||
local_dir[1] = (mjtNum)verts[3*imax + 1];
|
||||
@@ -416,6 +416,7 @@ static void mjc_hillclimbSupport(mjtNum res[3], mjCCDObj* obj, const mjtNum dir[
|
||||
|
||||
// get resulting support vertex
|
||||
imax = 3*vert_globalid[imax];
|
||||
obj->vertindex = imax / 3;
|
||||
local_dir[0] = (mjtNum)verts[imax + 0];
|
||||
local_dir[1] = (mjtNum)verts[imax + 1];
|
||||
local_dir[2] = (mjtNum)verts[imax + 2];
|
||||
@@ -711,6 +712,7 @@ void mjc_initCCDObj(mjCCDObj* obj, const mjModel* m, const mjData* d, int g, mjt
|
||||
obj->geom = g;
|
||||
obj->margin = margin;
|
||||
obj->center = mjc_center;
|
||||
obj->vertindex = -1;
|
||||
obj->meshindex = -1;
|
||||
obj->flex = -1;
|
||||
obj->elem = -1;
|
||||
|
||||
@@ -49,6 +49,7 @@ struct _mjCCDObj {
|
||||
const mjData* data;
|
||||
int geom;
|
||||
int geom_type;
|
||||
int vertindex;
|
||||
int meshindex;
|
||||
int flex;
|
||||
int elem;
|
||||
|
||||
+171
-181
@@ -17,7 +17,6 @@
|
||||
#include <float.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mujoco/mjtnum.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
@@ -29,7 +28,8 @@
|
||||
// subdistance algorithm for GJK that computes the barycentric coordinates of the point in a
|
||||
// simplex closest to the origin
|
||||
// implementation adapted from Montanari et al, ToG 2017
|
||||
static void subdistance(mjtNum lambda[4], const mjtNum simplex[12], int n);
|
||||
static void subdistance(mjtNum lambda[4], int n, const mjtNum s1[3], const mjtNum s2[3],
|
||||
const mjtNum s3[3], const mjtNum s4[3]);
|
||||
|
||||
// compute the barycentric coordinates of the closest point to the origin in the n-simplex,
|
||||
// where n = 3, 2, 1 respectively
|
||||
@@ -39,30 +39,25 @@ static void S2D(mjtNum lambda[3], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
static void S1D(mjtNum lambda[2], const mjtNum s1[3], const mjtNum s2[3]);
|
||||
|
||||
// compute the support point for GJK
|
||||
static void gjkSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
static void gjkSupport(Vertex* v, mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum x_k[3]);
|
||||
|
||||
// compute the support point for EPA
|
||||
static void epaSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum d[3], mjtNum dnorm);
|
||||
|
||||
// compute the linear combination of n 3D vectors
|
||||
static void lincomb(mjtNum res[3], const mjtNum* coef, const mjtNum* v, int n);
|
||||
// compute the linear combination of 1 - 4 3D vectors
|
||||
static inline void lincomb(mjtNum res[3], const mjtNum* coef, int n, const mjtNum v1[3],
|
||||
const mjtNum v2[3], const mjtNum v3[3], const mjtNum v4[3]);
|
||||
|
||||
// one face in a polytope
|
||||
typedef struct {
|
||||
int verts[3]; // indices of the three vertices of the face in the polytope
|
||||
int adj[3]; // adjacent faces, one for each edge: [v1,v2], [v2,v3], [v3,v1]
|
||||
mjtNum v[3]; // projection of the origin on face, can be used as face normal
|
||||
mjtNum dist; // norm of v; negative if deleted
|
||||
int index; // index in map; -1: not in map, -2: deleted from polytope
|
||||
int verts[3]; // indices of the three vertices of the face in the polytope
|
||||
int adj[3]; // adjacent faces, one for each edge: [v1,v2], [v2,v3], [v3,v1]
|
||||
mjtNum v[3]; // projection of the origin on face, can be used as face normal
|
||||
mjtNum dist; // norm of v; negative if deleted
|
||||
int index; // index in map; -1: not in map, -2: deleted from polytope
|
||||
} Face;
|
||||
|
||||
// polytope used in the Expanding Polytope Algorithm (EPA)
|
||||
typedef struct {
|
||||
mjtNum* verts1; // vertices of polytope in obj1
|
||||
mjtNum* verts2; // vertices of polytope in obj2
|
||||
mjtNum* verts; // v1 - v2; vertices in Minkowski sum making up polytope
|
||||
Vertex* verts; // list of vertices that make up the polytope
|
||||
int nverts; // number of vertices
|
||||
Face* faces; // list of faces that make up the polytope
|
||||
int nfaces; // number of faces
|
||||
@@ -71,8 +66,12 @@ typedef struct {
|
||||
int nmap; // number of faces in map
|
||||
} Polytope;
|
||||
|
||||
// compute the support point for EPA
|
||||
static int epaSupport(Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum d[3], mjtNum dnorm);
|
||||
|
||||
// make copy of vertex in polytope and return its index
|
||||
static int newVertex(Polytope* pt, const mjtNum v1[3], const mjtNum v2[3]);
|
||||
static int insertVertex(Polytope* pt, const Vertex* v);
|
||||
|
||||
// attach a face to the polytope with the given vertex indices; return distance to origin
|
||||
static mjtNum attachFace(Polytope* pt, int v1, int v2, int v3, int adj1, int adj2, int adj3);
|
||||
@@ -164,9 +163,7 @@ static int discreteGeoms(mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
static void gjk(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
int get_dist = status->dist_cutoff > 0; // need to recover geom distances if not in contact
|
||||
int backup_gjk = !get_dist; // use gjkIntersect if no geom distances needed
|
||||
mjtNum *simplex1 = status->simplex1; // simplex for obj1
|
||||
mjtNum *simplex2 = status->simplex2; // simplex for obj2
|
||||
mjtNum *simplex = status->simplex; // simplex in Minkowski difference
|
||||
Vertex* simplex = status->simplex;
|
||||
int n = 0; // number of vertices in the simplex
|
||||
int k = 0; // current iteration
|
||||
int kmax = status->max_iterations; // max number of iterations
|
||||
@@ -183,13 +180,9 @@ static void gjk(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
sub3(x_k, x1_k, x2_k);
|
||||
|
||||
for (; k < kmax; k++) {
|
||||
mjtNum *s1_k = simplex1 + 3*n; // the kth support point in obj1
|
||||
mjtNum *s2_k = simplex2 + 3*n; // the kth support point in obj2
|
||||
mjtNum *s_k = simplex + 3*n; // the kth support point of Minkowski difference
|
||||
|
||||
// compute the kth support point
|
||||
gjkSupport(s1_k, s2_k, obj1, obj2, x_k);
|
||||
sub3(s_k, s1_k, s2_k);
|
||||
gjkSupport(simplex + n, obj1, obj2, x_k);
|
||||
mjtNum *s_k = simplex[n].vert;
|
||||
|
||||
// stopping criteria using the Frank-Wolfe duality gap given by
|
||||
// |f(x_k) - f(x_min)|^2 <= < grad f(x_k), (x_k - s_k) >
|
||||
@@ -237,21 +230,20 @@ static void gjk(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
|
||||
// run the distance subalgorithm to compute the barycentric coordinates
|
||||
// of the closest point to the origin in the simplex
|
||||
subdistance(lambda, simplex, n + 1);
|
||||
subdistance(lambda, n + 1, simplex[0].vert, simplex[1].vert, simplex[2].vert, simplex[3].vert);
|
||||
|
||||
// remove vertices from the simplex no longer needed
|
||||
n = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (lambda[i] == 0) continue;
|
||||
copy3(simplex1 + 3*n, simplex1 + 3*i);
|
||||
copy3(simplex2 + 3*n, simplex2 + 3*i);
|
||||
copy3(simplex + 3*n, simplex + 3*i);
|
||||
simplex[n] = simplex[i];
|
||||
lambda[n++] = lambda[i];
|
||||
}
|
||||
|
||||
// get the next iteration of x_k
|
||||
mjtNum x_next[3];
|
||||
lincomb(x_next, lambda, simplex, n);
|
||||
lincomb(x_next, lambda, n, simplex[0].vert, simplex[1].vert,
|
||||
simplex[2].vert, simplex[3].vert);
|
||||
|
||||
// x_k has converged to minimum
|
||||
if (equal3(x_next, x_k)) {
|
||||
@@ -268,8 +260,10 @@ static void gjk(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
}
|
||||
|
||||
// compute the approximate witness points
|
||||
lincomb(x1_k, lambda, simplex1, n);
|
||||
lincomb(x2_k, lambda, simplex2, n);
|
||||
lincomb(x1_k, lambda, n, simplex[0].vert1, simplex[1].vert1, simplex[2].vert1,
|
||||
simplex[3].vert1);
|
||||
lincomb(x2_k, lambda, n, simplex[0].vert2, simplex[1].vert2, simplex[2].vert2,
|
||||
simplex[3].vert2);
|
||||
|
||||
status->nx = 1;
|
||||
status->gjk_iterations = k;
|
||||
@@ -304,7 +298,7 @@ static inline void support(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj*
|
||||
|
||||
|
||||
// compute the support points in obj1 and obj2 for the kth approximation point
|
||||
static void gjkSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
static void gjkSupport(Vertex* v, mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum x_k[3]) {
|
||||
mjtNum dir[3] = {-1, 0, 0}, dir_neg[3] = {1, 0, 0};
|
||||
|
||||
@@ -317,14 +311,22 @@ static void gjkSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj
|
||||
}
|
||||
|
||||
// compute S_{A-B}(dir) = S_A(dir) - S_B(-dir)
|
||||
support(s1, s2, obj1, obj2, dir, dir_neg);
|
||||
support(v->vert1, v->vert2, obj1, obj2, dir, dir_neg);
|
||||
sub3(v->vert, v->vert1, v->vert2);
|
||||
// copy mesh indices
|
||||
if (obj1->vertindex >= 0) {
|
||||
v->index1 = obj1->vertindex;
|
||||
}
|
||||
if (obj2->vertindex >= 0) {
|
||||
v->index2 = obj2->vertindex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compute the support point in the Minkowski difference for EPA
|
||||
static void epaSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum d[3], mjtNum dnorm) {
|
||||
// compute support points in Minkowski difference, return index of new vertex in polytope
|
||||
static int epaSupport(Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum d[3], mjtNum dnorm) {
|
||||
mjtNum dir[3] = {1, 0, 0}, dir_neg[3] = {-1, 0, 0};
|
||||
|
||||
// mjc_support assumes a normalized direction
|
||||
@@ -335,34 +337,52 @@ static void epaSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj
|
||||
scl3(dir_neg, dir, -1);
|
||||
}
|
||||
|
||||
int n = 3*pt->nverts++;
|
||||
Vertex* v = pt->verts + n;
|
||||
|
||||
// compute S_{A-B}(dir) = S_A(dir) - S_B(-dir)
|
||||
support(s1, s2, obj1, obj2, dir, dir_neg);
|
||||
support(v->vert1, v->vert2, obj1, obj2, dir, dir_neg);
|
||||
sub3(v->vert, v->vert1, v->vert2);
|
||||
if (obj1->vertindex >= 0) {
|
||||
v->index1 = obj1->vertindex;
|
||||
}
|
||||
if (obj2->vertindex >= 0) {
|
||||
v->index2 = obj2->vertindex;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compute the support point in the Minkowski difference for gjkIntersect (without normalization)
|
||||
static void gjkIntersectSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
static void gjkIntersectSupport(Vertex* v, mjCCDObj* obj1, mjCCDObj* obj2,
|
||||
const mjtNum dir[3]) {
|
||||
mjtNum dir_neg[3] = {-dir[0], -dir[1], -dir[2]};
|
||||
// compute S_{A-B}(dir) = S_A(dir) - S_B(-dir)
|
||||
support(s1, s2, obj1, obj2, dir, dir_neg);
|
||||
support(v->vert1, v->vert2, obj1, obj2, dir, dir_neg);
|
||||
sub3(v->vert, v->vert1, v->vert2);
|
||||
if (obj1->vertindex >= 0) {
|
||||
v->index1 = obj1->vertindex;
|
||||
}
|
||||
if (obj2->vertindex >= 0) {
|
||||
v->index2 = obj2->vertindex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compute the signed distance of a face along with the normal
|
||||
static inline mjtNum signedDistance(mjtNum normal[3], const mjtNum v1[3], const mjtNum v2[3],
|
||||
const mjtNum v3[3]) {
|
||||
static inline mjtNum signedDistance(mjtNum normal[3], const Vertex* v1, const Vertex* v2,
|
||||
const Vertex* v3) {
|
||||
mjtNum diff1[3], diff2[3];
|
||||
sub3(diff1, v3, v1);
|
||||
sub3(diff2, v2, v1);
|
||||
sub3(diff1, v3->vert, v1->vert);
|
||||
sub3(diff2, v2->vert, v1->vert);
|
||||
cross3(normal, diff1, diff2);
|
||||
mjtNum norm = dot3(normal, normal);
|
||||
if (norm > mjMINVAL*mjMINVAL && norm < mjMAXVAL*mjMAXVAL) {
|
||||
norm = 1/mju_sqrt(norm);
|
||||
scl3(normal, normal, norm);
|
||||
return dot3(normal, v1);
|
||||
return dot3(normal, v1->vert);
|
||||
}
|
||||
return mjMAXVAL; // cannot recover normal (ignore face)
|
||||
}
|
||||
@@ -371,11 +391,9 @@ static inline mjtNum signedDistance(mjtNum normal[3], const mjtNum v1[3], const
|
||||
|
||||
// return 1 if objects are in contact; 0 if not; -1 if inconclusive
|
||||
static int gjkIntersect(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
mjtNum simplex1[12], simplex2[12], simplex[12];
|
||||
memcpy(simplex1, status->simplex1, sizeof(mjtNum) * 12);
|
||||
memcpy(simplex2, status->simplex2, sizeof(mjtNum) * 12);
|
||||
memcpy(simplex, status->simplex, sizeof(mjtNum) * 12);
|
||||
int s[4] = {0, 3, 6, 9};
|
||||
Vertex simplex[4] = {status->simplex[0], status->simplex[1],
|
||||
status->simplex[2], status->simplex[3]};
|
||||
int s[4] = {0, 1, 2, 3};
|
||||
|
||||
int k = status->gjk_iterations, kmax = status->max_iterations;
|
||||
for (; k < kmax; k++) {
|
||||
@@ -400,21 +418,19 @@ static int gjkIntersect(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
// origin inside of simplex (run EPA for contact information)
|
||||
if (dist[index] > 0) {
|
||||
status->nsimplex = 4;
|
||||
for (int n = 0; n < 4; n++) {
|
||||
copy3(status->simplex + 3*n, simplex + s[n]);
|
||||
copy3(status->simplex1 + 3*n, simplex1 + s[n]);
|
||||
copy3(status->simplex2 + 3*n, simplex2 + s[n]);
|
||||
}
|
||||
status->simplex[0] = simplex[s[0]];
|
||||
status->simplex[1] = simplex[s[1]];
|
||||
status->simplex[2] = simplex[s[2]];
|
||||
status->simplex[3] = simplex[s[3]];
|
||||
status->gjk_iterations = k;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// replace worst vertex (farthest from origin) with new candidate
|
||||
gjkIntersectSupport(simplex1 + s[index], simplex2 + s[index], obj1, obj2, normals + 3*index);
|
||||
sub3(simplex + s[index], simplex1 + s[index], simplex2 + s[index]);
|
||||
gjkIntersectSupport(simplex + s[index], obj1, obj2, normals + 3*index);
|
||||
|
||||
// found origin outside the Minkowski difference (return no collision)
|
||||
if (dot3(&normals[3*index], simplex + s[index]) < 0) {
|
||||
if (dot3(&normals[3*index], simplex[s[index]].vert) < 0) {
|
||||
status->nsimplex = 0;
|
||||
status->gjk_iterations = k;
|
||||
return 0;
|
||||
@@ -434,37 +450,34 @@ static int gjkIntersect(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
|
||||
|
||||
// linear combination of n 3D vectors
|
||||
static inline void lincomb(mjtNum res[3], const mjtNum* coef, const mjtNum* v, int n) {
|
||||
res[0] = res[1] = res[2] = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
res[0] += coef[i] * v[3*i + 0];
|
||||
res[1] += coef[i] * v[3*i + 1];
|
||||
res[2] += coef[i] * v[3*i + 2];
|
||||
static inline void lincomb(mjtNum res[3], const mjtNum* coef, int n, const mjtNum v1[3],
|
||||
const mjtNum v2[3], const mjtNum v3[3], const mjtNum v4[3]) {
|
||||
switch (n) {
|
||||
case 1:
|
||||
res[0] = coef[0]*v1[0];
|
||||
res[1] = coef[0]*v1[1];
|
||||
res[2] = coef[0]*v1[2];
|
||||
break;
|
||||
case 2:
|
||||
res[0] = coef[0]*v1[0] + coef[1]*v2[0];
|
||||
res[1] = coef[0]*v1[1] + coef[1]*v2[1];
|
||||
res[2] = coef[0]*v1[2] + coef[1]*v2[2];
|
||||
break;
|
||||
case 3:
|
||||
res[0] = coef[0]*v1[0] + coef[1]*v2[0] + coef[2]*v3[0];
|
||||
res[1] = coef[0]*v1[1] + coef[1]*v2[1] + coef[2]*v3[1];
|
||||
res[2] = coef[0]*v1[2] + coef[1]*v2[2] + coef[2]*v3[2];
|
||||
break;
|
||||
case 4:
|
||||
res[0] = coef[0]*v1[0] + coef[1]*v2[0] + coef[2]*v3[0] + coef[3]*v4[0];
|
||||
res[1] = coef[0]*v1[1] + coef[1]*v2[1] + coef[2]*v3[1] + coef[3]*v4[1];
|
||||
res[2] = coef[0]*v1[2] + coef[1]*v2[2] + coef[2]*v3[2] + coef[3]*v4[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// linear combination of 2 3D vectors
|
||||
static inline void lincomb2(mjtNum res[3], const mjtNum coef[2], const mjtNum v1[3],
|
||||
const mjtNum v2[3]) {
|
||||
res[0] = coef[0]*v1[0] + coef[1]*v2[0];
|
||||
res[1] = coef[0]*v1[1] + coef[1]*v2[1];
|
||||
res[2] = coef[0]*v1[2] + coef[1]*v2[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// linear combination of 3 3D vectors
|
||||
static inline void lincomb3(mjtNum res[3], const mjtNum coef[3], const mjtNum v1[3],
|
||||
const mjtNum v2[3], const mjtNum v3[3]) {
|
||||
res[0] = coef[0]*v1[0] + coef[1]*v2[0] + coef[2]*v3[0];
|
||||
res[1] = coef[0]*v1[1] + coef[1]*v2[1] + coef[2]*v3[1];
|
||||
res[2] = coef[0]*v1[2] + coef[1]*v2[2] + coef[2]*v3[2];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// res = origin projected onto plane defined by v1, v2, v3
|
||||
static int projectOriginPlane(mjtNum res[3], const mjtNum v1[3], const mjtNum v2[3],
|
||||
const mjtNum v3[3]) {
|
||||
@@ -537,13 +550,9 @@ static inline int sameSign3(mjtNum a, mjtNum b, mjtNum c) {
|
||||
// subdistance algorithm for GJK that computes the barycentric coordinates of the point in a
|
||||
// simplex closest to the origin
|
||||
// implementation adapted from Montanari et al, ToG 2017
|
||||
static inline void subdistance(mjtNum lambda[4], const mjtNum simplex[12], int n) {
|
||||
static inline void subdistance(mjtNum lambda[4], int n, const mjtNum s1[3],
|
||||
const mjtNum s2[3], const mjtNum s3[3], const mjtNum s4[3]) {
|
||||
lambda[0] = lambda[1] = lambda[2] = lambda[3] = 0;
|
||||
const mjtNum* s1 = simplex;
|
||||
const mjtNum* s2 = simplex + 3;
|
||||
const mjtNum* s3 = simplex + 6;
|
||||
const mjtNum* s4 = simplex + 9;
|
||||
|
||||
if (n == 4) {
|
||||
S3D(lambda, s1, s2, s3, s4);
|
||||
} else if (n == 3) {
|
||||
@@ -597,7 +606,7 @@ static void S3D(mjtNum lambda[4], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp1) {
|
||||
mjtNum lambda_2d[3], x[3];
|
||||
S2D(lambda_2d, s2, s3, s4);
|
||||
lincomb3(x, lambda_2d, s2, s3, s4);
|
||||
lincomb(x, lambda_2d, 3, s2, s3, s4, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
lambda[0] = 0;
|
||||
lambda[1] = lambda_2d[0];
|
||||
@@ -609,7 +618,7 @@ static void S3D(mjtNum lambda[4], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp2) {
|
||||
mjtNum lambda_2d[3], x[3];
|
||||
S2D(lambda_2d, s1, s3, s4);
|
||||
lincomb3(x, lambda_2d, s1, s3, s4);
|
||||
lincomb(x, lambda_2d, 3, s1, s3, s4, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
if (d < dmin) {
|
||||
lambda[0] = lambda_2d[0];
|
||||
@@ -623,7 +632,7 @@ static void S3D(mjtNum lambda[4], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp3) {
|
||||
mjtNum lambda_2d[3], x[3];
|
||||
S2D(lambda_2d, s1, s2, s4);
|
||||
lincomb3(x, lambda_2d, s1, s2, s4);
|
||||
lincomb(x, lambda_2d, 3, s1, s2, s4, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
if (d < dmin) {
|
||||
lambda[0] = lambda_2d[0];
|
||||
@@ -637,7 +646,7 @@ static void S3D(mjtNum lambda[4], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp4) {
|
||||
mjtNum lambda_2d[3], x[3];
|
||||
S2D(lambda_2d, s1, s2, s3);
|
||||
lincomb3(x, lambda_2d, s1, s2, s3);
|
||||
lincomb(x, lambda_2d, 3, s1, s2, s3, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
if (d < dmin) {
|
||||
lambda[0] = lambda_2d[0];
|
||||
@@ -748,7 +757,7 @@ static void S2D(mjtNum lambda[3], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp1) {
|
||||
mjtNum lambda_1d[2], x[3];
|
||||
S1D(lambda_1d, s2, s3);
|
||||
lincomb2(x, lambda_1d, s2, s3);
|
||||
lincomb(x, lambda_1d, 2, s2, s3, NULL, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
lambda[0] = 0;
|
||||
lambda[1] = lambda_1d[0];
|
||||
@@ -759,7 +768,7 @@ static void S2D(mjtNum lambda[3], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp2) {
|
||||
mjtNum lambda_1d[2], x[3];
|
||||
S1D(lambda_1d, s1, s3);
|
||||
lincomb2(x, lambda_1d, s1, s3);
|
||||
lincomb(x, lambda_1d, 2, s1, s3, NULL, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
if (d < dmin) {
|
||||
lambda[0] = lambda_1d[0];
|
||||
@@ -772,7 +781,7 @@ static void S2D(mjtNum lambda[3], const mjtNum s1[3], const mjtNum s2[3], const
|
||||
if (!comp3) {
|
||||
mjtNum lambda_1d[2], x[3];
|
||||
S1D(lambda_1d, s1, s2);
|
||||
lincomb2(x, lambda_1d, s1, s2);
|
||||
lincomb(x, lambda_1d, 2, s1, s2, NULL, NULL);
|
||||
mjtNum d = dot3(x, x);
|
||||
if (d < dmin) {
|
||||
lambda[0] = lambda_1d[0];
|
||||
@@ -819,17 +828,18 @@ static void S1D(mjtNum lambda[2], const mjtNum s1[3], const mjtNum s2[3]) {
|
||||
// replace a 3-simplex with one of its faces
|
||||
static inline void replaceSimplex3(Polytope* pt, mjCCDStatus* status, int v1, int v2, int v3) {
|
||||
status->nsimplex = 3;
|
||||
copy3(status->simplex1 + 0, pt->verts1 + v1);
|
||||
copy3(status->simplex1 + 3, pt->verts1 + v2);
|
||||
copy3(status->simplex1 + 6, pt->verts1 + v3);
|
||||
Vertex* v = pt->verts;
|
||||
copy3(status->simplex[0].vert1, v[v1].vert1);
|
||||
copy3(status->simplex[1].vert1, v[v2].vert1);
|
||||
copy3(status->simplex[2].vert1, v[v3].vert1);
|
||||
|
||||
copy3(status->simplex2 + 0, pt->verts2 + v1);
|
||||
copy3(status->simplex2 + 3, pt->verts2 + v2);
|
||||
copy3(status->simplex2 + 6, pt->verts2 + v3);
|
||||
copy3(status->simplex[0].vert2, v[v1].vert2);
|
||||
copy3(status->simplex[1].vert2, v[v2].vert2);
|
||||
copy3(status->simplex[2].vert2, v[v3].vert2);
|
||||
|
||||
copy3(status->simplex + 0, pt->verts + v1);
|
||||
copy3(status->simplex + 3, pt->verts + v2);
|
||||
copy3(status->simplex + 6, pt->verts + v3);
|
||||
copy3(status->simplex[0].vert, v[v1].vert);
|
||||
copy3(status->simplex[1].vert, v[v2].vert);
|
||||
copy3(status->simplex[2].vert, v[v3].vert);
|
||||
|
||||
pt->nfaces = 0;
|
||||
pt->nverts = 0;
|
||||
@@ -889,9 +899,7 @@ static void rotmat(mjtNum R[9], const mjtNum axis[3]) {
|
||||
|
||||
// create a polytope from a 1-simplex (returns 0 on success)
|
||||
static int polytope2(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
mjtNum v1[3], v2[3];
|
||||
sub3(v1, status->simplex1 + 0, status->simplex2 + 0);
|
||||
sub3(v2, status->simplex1 + 3, status->simplex2 + 3);
|
||||
mjtNum *v1 = status->simplex[0].vert, *v2 = status->simplex[1].vert;
|
||||
|
||||
mjtNum diff[3];
|
||||
sub3(diff, v2, v1);
|
||||
@@ -919,25 +927,16 @@ static int polytope2(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
mju_mulMatVec3(d2, R, d1);
|
||||
mju_mulMatVec3(d3, R, d2);
|
||||
|
||||
|
||||
mjtNum v3a[3], v3b[3], v3[3];
|
||||
epaSupport(v3a, v3b, obj1, obj2, d1, mju_norm3(d1));
|
||||
sub3(v3, v3a, v3b);
|
||||
|
||||
mjtNum v4a[3], v4b[3], v4[3];
|
||||
epaSupport(v4a, v4b, obj1, obj2, d2, mju_norm3(d2));
|
||||
sub3(v4, v4a, v4b);
|
||||
|
||||
mjtNum v5a[3], v5b[3], v5[3];
|
||||
epaSupport(v5a, v5b, obj1, obj2, d3, mju_norm3(d3));
|
||||
sub3(v5, v5a, v5b);
|
||||
|
||||
// save vertices and get indices for each one
|
||||
int v1i = newVertex(pt, status->simplex1 + 0, status->simplex2 + 0);
|
||||
int v2i = newVertex(pt, status->simplex1 + 3, status->simplex2 + 3);
|
||||
int v3i = newVertex(pt, v3a, v3b);
|
||||
int v4i = newVertex(pt, v4a, v4b);
|
||||
int v5i = newVertex(pt, v5a, v5b);
|
||||
int v1i = insertVertex(pt, status->simplex + 0);
|
||||
int v2i = insertVertex(pt, status->simplex + 1);
|
||||
int v3i = epaSupport(pt, obj1, obj2, d1, mju_norm3(d1));
|
||||
int v4i = epaSupport(pt, obj1, obj2, d2, mju_norm3(d2));
|
||||
int v5i = epaSupport(pt, obj1, obj2, d3, mju_norm3(d3));
|
||||
|
||||
mjtNum* v3 = pt->verts[v3i].vert;
|
||||
mjtNum* v4 = pt->verts[v4i].vert;
|
||||
mjtNum* v5 = pt->verts[v5i].vert;
|
||||
|
||||
// build hexahedron
|
||||
if (attachFace(pt, v1i, v3i, v4i, 1, 3, 2) < mjMINVAL) {
|
||||
@@ -1049,9 +1048,9 @@ static int triPointIntersect(const mjtNum v1[3], const mjtNum v2[3], const mjtNu
|
||||
// create a polytope from a 2-simplex (returns 0 on success)
|
||||
static int polytope3(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
// get vertices of simplex from GJK
|
||||
const mjtNum *v1 = status->simplex,
|
||||
*v2 = status->simplex + 3,
|
||||
*v3 = status->simplex + 6;
|
||||
const mjtNum *v1 = status->simplex[0].vert,
|
||||
*v2 = status->simplex[1].vert,
|
||||
*v3 = status->simplex[2].vert;
|
||||
|
||||
// get normals in both directions
|
||||
mjtNum diff1[3], diff2[3], n[3], n_neg[3];
|
||||
@@ -1066,21 +1065,20 @@ static int polytope3(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
// negative of triangle normal n
|
||||
scl3(n_neg, n, -1);
|
||||
|
||||
// get 4th vertex in n direction
|
||||
mjtNum v4a[3], v4b[3], v4[3];
|
||||
epaSupport(v4a, v4b, obj1, obj2, n, n_norm);
|
||||
sub3(v4, v4a, v4b);
|
||||
// save vertices and get indices for each one
|
||||
int v1i = insertVertex(pt, status->simplex + 0);
|
||||
int v2i = insertVertex(pt, status->simplex + 1);
|
||||
int v3i = insertVertex(pt, status->simplex + 2);
|
||||
int v5i = epaSupport(pt, obj1, obj2, n_neg, n_norm);
|
||||
int v4i = epaSupport(pt, obj1, obj2, n, n_norm);
|
||||
mjtNum* v4 = pt->verts[v4i].vert;
|
||||
mjtNum* v5 = pt->verts[v5i].vert;
|
||||
|
||||
// check that v4 is not contained in the 2-simplex
|
||||
if (triPointIntersect(v1, v2, v3, v4)) {
|
||||
return mjEPA_P3_INVALID_V4;
|
||||
}
|
||||
|
||||
// get 5th vertex in -n direction
|
||||
mjtNum v5a[3], v5b[3], v5[3];
|
||||
epaSupport(v5a, v5b, obj1, obj2, n_neg, n_norm);
|
||||
sub3(v5, v5a, v5b);
|
||||
|
||||
// check that v5 is not contained in the 2-simplex
|
||||
if (triPointIntersect(v1, v2, v3, v5)) {
|
||||
return mjEPA_P3_INVALID_V5;
|
||||
@@ -1096,13 +1094,6 @@ static int polytope3(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
return mjEPA_P3_MISSING_ORIGIN;
|
||||
}
|
||||
|
||||
// save vertices and get indices for each one
|
||||
int v1i = newVertex(pt, status->simplex1 + 0, status->simplex2 + 0);
|
||||
int v2i = newVertex(pt, status->simplex1 + 3, status->simplex2 + 3);
|
||||
int v3i = newVertex(pt, status->simplex1 + 6, status->simplex2 + 6);
|
||||
int v5i = newVertex(pt, v5a, v5b);
|
||||
int v4i = newVertex(pt, v4a, v4b);
|
||||
|
||||
// create hexahedron for EPA
|
||||
attachFace(pt, v4i, v1i, v2i, 1, 3, 2);
|
||||
attachFace(pt, v4i, v3i, v1i, 2, 4, 0);
|
||||
@@ -1129,10 +1120,10 @@ static int polytope3(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
|
||||
// create a polytope from a 3-simplex (returns 0 on success)
|
||||
static int polytope4(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
|
||||
int v1 = newVertex(pt, status->simplex1 + 0, status->simplex2 + 0);
|
||||
int v2 = newVertex(pt, status->simplex1 + 3, status->simplex2 + 3);
|
||||
int v3 = newVertex(pt, status->simplex1 + 6, status->simplex2 + 6);
|
||||
int v4 = newVertex(pt, status->simplex1 + 9, status->simplex2 + 9);
|
||||
int v1 = insertVertex(pt, status->simplex + 0);
|
||||
int v2 = insertVertex(pt, status->simplex + 1);
|
||||
int v3 = insertVertex(pt, status->simplex + 2);
|
||||
int v4 = insertVertex(pt, status->simplex + 3);
|
||||
|
||||
// if the origin is on a face, replace the 3-simplex with a 2-simplex
|
||||
if (attachFace(pt, v1, v2, v3, 1, 3, 2) < mjMINVAL) {
|
||||
@@ -1152,7 +1143,7 @@ static int polytope4(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
return polytope3(pt, status, obj1, obj2);
|
||||
}
|
||||
|
||||
if (!testTetra(pt->verts + v1, pt->verts + v2, pt->verts + v3, pt->verts + v4)) {
|
||||
if (!testTetra(pt->verts[v1].vert, pt->verts[v2].vert, pt->verts[v3].vert, pt->verts[v4].vert)) {
|
||||
return mjEPA_P4_MISSING_ORIGIN;
|
||||
}
|
||||
|
||||
@@ -1167,16 +1158,18 @@ static int polytope4(Polytope* pt, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj
|
||||
|
||||
|
||||
// make a copy of vertex in polytope and return its index
|
||||
static int newVertex(Polytope* pt, const mjtNum v1[3], const mjtNum v2[3]) {
|
||||
static inline int insertVertex(Polytope* pt, const Vertex* v) {
|
||||
int n = 3*pt->nverts++;
|
||||
copy3(pt->verts1 + n, v1);
|
||||
copy3(pt->verts2 + n, v2);
|
||||
sub3(pt->verts + n, v1, v2);
|
||||
Vertex* new_v = pt->verts + n;
|
||||
copy3(new_v->vert1, v->vert1);
|
||||
copy3(new_v->vert2, v->vert2);
|
||||
new_v->index1 = v->index1;
|
||||
new_v->index2 = v->index2;
|
||||
sub3(new_v->vert, v->vert1, v->vert2);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// delete face from map (return non-zero on error)
|
||||
static void deleteFace(Polytope* pt, Face* face) {
|
||||
if (face->index >= 0) {
|
||||
@@ -1209,7 +1202,7 @@ static inline mjtNum attachFace(Polytope* pt, int v1, int v2, int v3,
|
||||
face->adj[2] = adj3;
|
||||
|
||||
// compute witness point v
|
||||
int ret = projectOriginPlane(face->v, pt->verts + v3, pt->verts + v2, pt->verts + v1);
|
||||
int ret = projectOriginPlane(face->v, pt->verts[v3].vert, pt->verts[v2].vert, pt->verts[v1].vert);
|
||||
if (ret) return 0;
|
||||
face->dist = mju_sqrt(dot3(face->v, face->v));
|
||||
face->index = -1;
|
||||
@@ -1305,23 +1298,23 @@ static void horizon(Horizon* h, Face* face) {
|
||||
static void epaWitness(const Polytope* pt, const Face* face, mjtNum x1[3], mjtNum x2[3]) {
|
||||
// compute affine coordinates for witness points on plane defined by face
|
||||
mjtNum lambda[3];
|
||||
mjtNum* v1 = pt->verts + face->verts[0];
|
||||
mjtNum* v2 = pt->verts + face->verts[1];
|
||||
mjtNum* v3 = pt->verts + face->verts[2];
|
||||
mjtNum* v1 = pt->verts[face->verts[0]].vert;
|
||||
mjtNum* v2 = pt->verts[face->verts[1]].vert;
|
||||
mjtNum* v3 = pt->verts[face->verts[2]].vert;
|
||||
triAffineCoord(lambda, v1, v2, v3, face->v);
|
||||
|
||||
// face on geom 1
|
||||
v1 = pt->verts1 + face->verts[0];
|
||||
v2 = pt->verts1 + face->verts[1];
|
||||
v3 = pt->verts1 + face->verts[2];
|
||||
v1 = pt->verts[face->verts[0]].vert1;
|
||||
v2 = pt->verts[face->verts[1]].vert1;
|
||||
v3 = pt->verts[face->verts[2]].vert1;
|
||||
x1[0] = v1[0]*lambda[0] + v2[0]*lambda[1] + v3[0]*lambda[2];
|
||||
x1[1] = v1[1]*lambda[0] + v2[1]*lambda[1] + v3[1]*lambda[2];
|
||||
x1[2] = v1[2]*lambda[0] + v2[2]*lambda[1] + v3[2]*lambda[2];
|
||||
|
||||
// face on geom 2
|
||||
v1 = pt->verts2 + face->verts[0];
|
||||
v2 = pt->verts2 + face->verts[1];
|
||||
v3 = pt->verts2 + face->verts[2];
|
||||
v1 = pt->verts[face->verts[0]].vert2;
|
||||
v2 = pt->verts[face->verts[1]].vert2;
|
||||
v3 = pt->verts[face->verts[2]].vert2;
|
||||
x2[0] = v1[0]*lambda[0] + v2[0]*lambda[1] + v3[0]*lambda[2];
|
||||
x2[1] = v1[1]*lambda[0] + v2[1]*lambda[1] + v3[1]*lambda[2];
|
||||
x2[2] = v1[2]*lambda[0] + v2[2]*lambda[1] + v3[2]*lambda[2];
|
||||
@@ -1370,9 +1363,8 @@ static Face* epa(mjCCDStatus* status, Polytope* pt, mjCCDObj* obj1, mjCCDObj* ob
|
||||
}
|
||||
|
||||
// compute support point w from the closest face's normal
|
||||
mjtNum w1[3], w2[3], w[3];
|
||||
epaSupport(w1, w2, obj1, obj2, face->v, lower);
|
||||
sub3(w, w1, w2);
|
||||
int wi = epaSupport(pt, obj1, obj2, face->v, lower);
|
||||
mjtNum* w = pt->verts[wi].vert;
|
||||
mjtNum upper_k = dot3(face->v, w) / lower; // upper bound for kth iteration
|
||||
if (upper_k < upper) upper = upper_k;
|
||||
if (upper - lower < tolerance) {
|
||||
@@ -1392,7 +1384,7 @@ static Face* epa(mjCCDStatus* status, Polytope* pt, mjCCDObj* obj1, mjCCDObj* ob
|
||||
}
|
||||
|
||||
// insert w as new vertex and attach faces along the horizon
|
||||
int wi = newVertex(pt, w1, w2), nfaces = pt->nfaces, nedges = h.nedges;
|
||||
int nfaces = pt->nfaces, nedges = h.nedges;
|
||||
|
||||
// check if there's enough memory to store new faces
|
||||
if (nedges > maxFaces(pt)) {
|
||||
@@ -1769,12 +1761,12 @@ static void multicontact(Polytope* pt, Face* face, mjCCDStatus* status,
|
||||
mjtNum face1[mjMAX_SIDES * 3], face2[mjMAX_SIDES * 3];
|
||||
|
||||
// get vertices of faces from EPA
|
||||
const mjtNum* v11 = pt->verts1 + face->verts[0];
|
||||
const mjtNum* v12 = pt->verts1 + face->verts[1];
|
||||
const mjtNum* v13 = pt->verts1 + face->verts[2];
|
||||
const mjtNum* v21 = pt->verts2 + face->verts[0];
|
||||
const mjtNum* v22 = pt->verts2 + face->verts[1];
|
||||
const mjtNum* v23 = pt->verts2 + face->verts[2];
|
||||
const mjtNum* v11 = pt->verts[face->verts[0]].vert1;
|
||||
const mjtNum* v12 = pt->verts[face->verts[1]].vert1;
|
||||
const mjtNum* v13 = pt->verts[face->verts[2]].vert1;
|
||||
const mjtNum* v21 = pt->verts[face->verts[0]].vert2;
|
||||
const mjtNum* v22 = pt->verts[face->verts[1]].vert2;
|
||||
const mjtNum* v23 = pt->verts[face->verts[2]].vert2;
|
||||
|
||||
// get dimensions of features of geoms 1 and 2
|
||||
int nface1 = simplexDim(v11, v12, v13);
|
||||
@@ -1931,9 +1923,7 @@ mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, m
|
||||
pt.nfaces = pt.nmap = pt.nverts = 0;
|
||||
|
||||
// allocate memory for vertices
|
||||
pt.verts = mjSTACKALLOC(d, 3*(5 + N), mjtNum);
|
||||
pt.verts1 = mjSTACKALLOC(d, 3*(5 + N), mjtNum);
|
||||
pt.verts2 = mjSTACKALLOC(d, 3*(5 + N), mjtNum);
|
||||
pt.verts = mjSTACKALLOC(d, 3*(5 + N), Vertex);
|
||||
|
||||
// allocate memory for faces
|
||||
pt.maxfaces = (6*N > 1000) ? 6*N : 1000; // use 1000 faces as lower bound
|
||||
|
||||
@@ -43,6 +43,15 @@ typedef enum {
|
||||
mjEPA_P4_MISSING_ORIGIN,
|
||||
} mjEPAStatus;
|
||||
|
||||
// vertex in a polytope
|
||||
typedef struct {
|
||||
mjtNum vert[3]; // v1 - v2; vertex in Minkowski sum making up polytope
|
||||
mjtNum vert1[3]; // vertex of polytope in obj1
|
||||
mjtNum vert2[3]; // vertex of polytope in obj2
|
||||
int index1; // vertex index in mesh 1
|
||||
int index2; // vertex index in mesh 2
|
||||
} Vertex;
|
||||
|
||||
// configuration for convex collision detection
|
||||
typedef struct {
|
||||
int max_iterations; // the maximum number of iterations for GJK and EPA
|
||||
@@ -69,10 +78,8 @@ typedef struct {
|
||||
int gjk_iterations; // number of iterations that GJK ran
|
||||
int epa_iterations; // number of iterations that EPA ran (zero if EPA did not run)
|
||||
mjEPAStatus epa_status; // status of the EPA run
|
||||
mjtNum simplex1[12]; // the simplex that GJK returned for obj1
|
||||
mjtNum simplex2[12]; // the simplex that GJK returned for obj2
|
||||
mjtNum simplex[12]; // the simplex that GJK returned for the Minkowski difference
|
||||
int nsimplex; // size of simplex 1 & 2
|
||||
Vertex simplex[4];
|
||||
int nsimplex;
|
||||
} mjCCDStatus;
|
||||
|
||||
// run general convex collision detection, returns positive for distance, negative for penetration
|
||||
|
||||
Reference in New Issue
Block a user