Remove libccd compatibility layer from native ccd.

PiperOrigin-RevId: 671740643
Change-Id: If3c1097df5c505f70c2dc0c090a7f5a93a43c683
This commit is contained in:
Kyle Bayes
2024-09-06 06:55:01 -07:00
committed by Copybara-Service
parent fa156ed22f
commit 4fc6fb179b
5 changed files with 218 additions and 242 deletions
+44 -23
View File
@@ -32,13 +32,40 @@
// call LibCCD or GJK to recover penetration info
static int mjc_penetration(const mjModel* m, const void *obj1, const void *obj2, const ccd_t *ccd,
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) {
static int mjc_penetration(const mjModel* m, mjCCDObj* obj1, mjCCDObj* obj2,
const ccd_t* ccd, ccd_real_t* depth, ccd_vec3_t* dir, ccd_vec3_t* pos) {
if (mjENABLED(mjENBL_NATIVECCD)) {
return mj_gjkPenetration(obj1, obj2, ccd, depth, dir, pos);
} else {
return ccdMPRPenetration(obj1, obj2, ccd, depth, dir, pos);
mjCCDConfig config;
mjCCDStatus status;
// set config
config.max_iterations = ccd->max_iterations,
config.tolerance = ccd->mpr_tolerance,
config.contacts = 1;
config.distances = 0; // no geom distances needed
mjtNum dist = mjc_ccd(&config, &status, obj1, obj2);
if (dist < 0) {
if (depth) *depth = -dist;
if (dir) {
mju_sub3(dir->v, status.x1, status.x2);
mju_normalize3(dir->v);
}
if (pos) {
pos->v[0] = 0.5 * (status.x1[0] + status.x2[0]);
pos->v[1] = 0.5 * (status.x1[1] + status.x2[1]);
pos->v[2] = 0.5 * (status.x1[2] + status.x2[2]);
}
return 0;
}
if (depth) *depth = 0;
if (dir) mju_zero3(dir->v);
if (pos) mju_zero3(dir->v);
return 1;
}
// fallback to MPR
return ccdMPRPenetration(obj1, obj2, ccd, depth, dir, pos);
}
@@ -298,8 +325,8 @@ static void mjc_initCCD(ccd_t* ccd, const mjModel* m) {
// find single convex-convex collision, using libccd
static int mjc_MPRIteration(mjCCDObj* obj1, mjCCDObj* obj2, const ccd_t* ccd,
// find single convex-convex collision
static int mjc_CCDIteration(mjCCDObj* obj1, mjCCDObj* obj2, const ccd_t* ccd,
const mjModel* m, const mjData* d,
mjContact* con, mjtNum margin) {
ccd_vec3_t dir, pos;
@@ -370,10 +397,8 @@ static void mju_rotateFrame(const mjtNum origin[3], const mjtNum rot[9],
int mjc_Convex(const mjModel* m, const mjData* d,
mjContact* con, int g1, int g2, mjtNum margin) {
ccd_t ccd;
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, margin, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, margin, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, margin, {1, 0, 0, 0}, mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, margin, {1, 0, 0, 0}, mjc_center, mjc_support};
// init ccd structure
mjc_initCCD(&ccd, m);
@@ -384,7 +409,7 @@ int mjc_Convex(const mjModel* m, const mjData* d,
ccd.support2 = mjccd_support;
// find initial contact
int ncon = mjc_MPRIteration(&obj1, &obj2, &ccd, m, d, con, margin);
int ncon = mjc_CCDIteration(&obj1, &obj2, &ccd, m, d, con, margin);
// look for additional contacts
if (ncon && mjENABLED(mjENBL_MULTICCD) // TODO(tassa) leave as bitflag or make geom attribute (?)
@@ -433,7 +458,7 @@ int mjc_Convex(const mjModel* m, const mjData* d,
mju_rotateFrame(con[0].pos, invrot, d->geom_xmat+9*g2, d->geom_xpos+3*g2);
// search for new contact
int new_contact = mjc_MPRIteration(&obj1, &obj2, &ccd, m, d, con+ncon, margin);
int new_contact = mjc_CCDIteration(&obj1, &obj2, &ccd, m, d, con+ncon, margin);
// check new contact
if (new_contact && mjc_isDistinctContact(con, ncon + 1, tolerance)) {
@@ -501,7 +526,7 @@ int mjc_PlaneConvex(const mjModel* m, const mjData* d,
mjGETINFO
mjtNum dist, dif[3], normal[3] = {mat1[2], mat1[5], mat1[8]};
ccd_vec3_t dir, vec;
mjCCDObj obj = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0}};
mjCCDObj obj = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}};
// get support point in -normal direction
ccdVec3Set(&dir, -mat1[2], -mat1[5], -mat1[8]);
@@ -683,8 +708,7 @@ int mjc_ConvexHField(const mjModel* m, const mjData* d,
// ccd-related
ccd_vec3_t dirccd, vecccd;
ccd_real_t depth;
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, mjc_center, mjc_support};
ccd_t ccd;
// point size1 to hfield size instead of geom1 size
@@ -1118,10 +1142,8 @@ void mjc_fixNormal(const mjModel* m, const mjData* d, mjContact* con, int g1, in
int mjc_ConvexElem(const mjModel* m, const mjData* d, mjContact* con,
int g1, int f1, int e1, int v1, int f2, int e2, mjtNum margin) {
ccd_t ccd;
mjCCDObj obj1 = {m, d, g1, -1, f1, e1, v1, margin, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, -1, -1, f2, e2, -1, margin, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj1 = {m, d, g1, -1, f1, e1, v1, margin, {1, 0, 0, 0}, mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, -1, -1, f2, e2, -1, margin, {1, 0, 0, 0}, mjc_center, mjc_support};
// init ccd structure
mjc_initCCD(&ccd, m);
@@ -1132,7 +1154,7 @@ int mjc_ConvexElem(const mjModel* m, const mjData* d, mjContact* con,
ccd.support2 = mjccd_support;
// find contacts
int ncon = mjc_MPRIteration(&obj1, &obj2, &ccd, m, d, con, margin);
int ncon = mjc_CCDIteration(&obj1, &obj2, &ccd, m, d, con, margin);
return ncon;
}
@@ -1170,8 +1192,7 @@ int mjc_HFieldElem(const mjModel* m, const mjData* d, mjContact* con,
// ccd-related
ccd_vec3_t dirccd, vecccd;
ccd_real_t depth;
mjCCDObj obj2 = {m, d, -1, -1, f, e, -1, margin, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, -1, -1, f, e, -1, margin, {1, 0, 0, 0}, mjc_center, mjc_support};
ccd_t ccd;
//------------------------------------- AABB computation, box-box test
-1
View File
@@ -51,7 +51,6 @@ struct _mjCCDObj {
int vert;
mjtNum margin;
mjtNum rotate[4];
mjtNum x0[3]; // initial guess of the witness point
void (*center)(mjtNum res[3], const struct _mjCCDObj* obj);
void (*support)(mjtNum res[3], struct _mjCCDObj* obj, const mjtNum dir[3]);
mjtNum prism[6][3]; // for hfield
+73 -130
View File
@@ -25,9 +25,6 @@
#include "engine/engine_util_errmem.h"
#include "engine/engine_util_spatial.h"
#include <ccd/ccd.h>
#include <ccd/vec3.h>
// Computes the shortest distance between the origin and an n-simplex (n <= 3) and returns the
// barycentric coordinates of the closest point in the simplex. This is the so called distance
// sub-algorithm of the original 1988 GJK algorithm.
@@ -48,8 +45,8 @@ static void support(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
// support function tweaked for GJK by taking kth iteration point as input and setting both
// support points to recover witness points
static void gjk_support(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
const mjtNum x_k[3]);
static void gjkSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
const mjtNum x_k[3]);
// linear algebra utility functions
static mjtNum det3(const mjtNum v1[3], const mjtNum v2[3], const mjtNum v3[3]);
@@ -81,11 +78,9 @@ typedef struct {
// generates a polytope from a 1-simplex, 2-simplex, or 3-simplex respectively
// returns true if the polytope can be generated, false otherwise
static int polytope2(Polytope* pt, const mjtNum simplex1[6], const mjtNum simplex2[6],
mjCCDObj* obj1, mjCCDObj* obj2);
static int polytope3(Polytope* pt, const mjtNum simplex1[9], const mjtNum simplex2[9],
mjCCDObj* obj1, mjCCDObj* obj2);
static int polytope4(Polytope* pt, const mjtNum simplex1[12], const mjtNum simplex2[12]);
static int polytope2(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2);
static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2);
static int polytope4(Polytope* pt, const mjCCDStatus* status);
// initializes the polytope (faces and vertices must be freed by caller)
static void initPolytope(Polytope* pt);
@@ -98,37 +93,31 @@ static void attachFace(Polytope* pt, int v1, int v2, int v3);
// returns the penetration depth (negative distance) of the convex objects
// witness points are stored in x1 and x2
static mjtNum epa(const mjCCDConfig* config, Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2,
mjtNum dir[3]);
static mjtNum epa(mjCCDStatus* status, Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2);
// internal data structure for the returning simplex from GJK
typedef struct {
mjtNum verts[12];
int nverts;
} Simplex;
// internal GJK with returned data for EPA
static mjtNum _gjk(const mjCCDConfig* config, mjCCDObj* obj1, mjCCDObj* obj2,
Simplex* ret1, Simplex* ret2) {
// GJK algorithm
static mjtNum gjk(mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
mjtNum simplex[12]; // our current simplex with max 4 vertices due to only 3 dimensions
int n = 0; // number of vertices in the simplex
mjtNum x_k[3]; // the kth approximation point with initial value x_0
// segregated simplices and points for the two objects to recover witness points
mjtNum simplex1[12], simplex2[12];
mjtNum* x1_k = obj1->x0;
mjtNum* x2_k = obj2->x0;
mjtNum *simplex1 = status->simplex1, *simplex2 = status->simplex2;
mjtNum* x1_k = status->x1;
mjtNum* x2_k = status->x2;
mju_sub3(x_k, x1_k, x2_k);
mjtNum epsilon = config->tolerance * config->tolerance;
mjtNum epsilon = status->tolerance * status->tolerance;
int N = config->max_iterations;
for (size_t k = 0; k < N; k++) {
int k = 0, N = status->max_iterations;
for (; k < N; k++) {
mjtNum s1[3], s2[3]; // the support points in obj1 and obj2
mjtNum s_k[3]; // the kth support point of Minkowski difference
mjtNum lambda[4]; // barycentric coordinates for x_k
// compute the kth support point
gjk_support(s1, s2, obj1, obj2, x_k);
gjkSupport(s1, s2, obj1, obj2, x_k);
mju_sub3(s_k, s1, s2);
// the stopping criteria relies on the Frank-Wolfe duality gap given by
@@ -181,30 +170,17 @@ static mjtNum _gjk(const mjCCDConfig* config, mjCCDObj* obj1, mjCCDObj* obj2,
break;
}
}
if (ret1 && ret2) {
ret1->nverts = n;
ret2->nverts = n;
for (int i = 0; i < n; i++) {
mju_copy3(ret1->verts + 3*i, simplex1 + 3*i);
mju_copy3(ret2->verts + 3*i, simplex2 + 3*i);
}
}
status->gjk_iterations = k;
status->nsimplex = n;
return mju_norm3(x_k);
}
// returns the distance between the two objects. The witness points are
// recoverable from the x_0 field in obj1 and obj2.
mjtNum mj_gjk(const mjCCDConfig* config, mjCCDObj* obj1, mjCCDObj* obj2) {
return _gjk(config, obj1, obj2, NULL, NULL);
}
// computes the support points in obj1 and obj2 for the kth approximation point
static void gjk_support(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
const mjtNum x_k[3]) {
static void gjkSupport(mjtNum s1[3], mjtNum s2[3], mjCCDObj* obj1, mjCCDObj* obj2,
const mjtNum x_k[3]) {
mjtNum dir[3], dir_neg[3];
mju_copy3(dir_neg, x_k);
mju_normalize3(dir_neg); // mjc_support assumes a normalized direction
@@ -648,11 +624,10 @@ static void rotmat(mjtNum R[9], const mjtNum axis[3]) {
// creates a polytope from a 1-simplex (2 points i.e. line segment)
static int polytope2(Polytope* pt, const mjtNum simplex1[6], const mjtNum simplex2[6],
mjCCDObj* obj1, mjCCDObj* obj2) {
static int polytope2(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
mjtNum v1[3], v2[3];
mju_sub3(v1, simplex1 + 0, simplex2 + 0);
mju_sub3(v2, simplex1 + 3, simplex2 + 3);
mju_sub3(v1, status->simplex1 + 0, status->simplex2 + 0);
mju_sub3(v2, status->simplex1 + 3, status->simplex2 + 3);
mjtNum diff[3];
mju_sub3(diff, v2, v1);
@@ -701,8 +676,8 @@ static int polytope2(Polytope* pt, const mjtNum simplex1[6], const mjtNum simple
}
// save vertices and get indices for each one
int v1i = newVertex(pt, simplex1 + 0, simplex2 + 0);
int v2i = newVertex(pt, simplex1 + 3, simplex2 + 3);
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);
@@ -795,13 +770,12 @@ static int triPointIntersect(const mjtNum v1[3], const mjtNum v2[3], const mjtNu
// creates a polytope from a 2-simplex (3 points i.e. triangle)
static int polytope3(Polytope* pt, const mjtNum simplex1[9], const mjtNum simplex2[9],
mjCCDObj* obj1, mjCCDObj* obj2) {
static int polytope3(Polytope* pt, const mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
// get vertices of simplex from GJK
mjtNum v1[3], v2[3], v3[3];
mju_sub3(v1, simplex1 + 0, simplex2 + 0);
mju_sub3(v2, simplex1 + 3, simplex2 + 3);
mju_sub3(v3, simplex1 + 6, simplex2 + 6);
mju_sub3(v1, status->simplex1 + 0, status->simplex2 + 0);
mju_sub3(v2, status->simplex1 + 3, status->simplex2 + 3);
mju_sub3(v3, status->simplex1 + 6, status->simplex2 + 6);
// get normals in both directions
mjtNum diff1[3], diff2[3], n[3], nn[3];
@@ -842,15 +816,15 @@ static int polytope3(Polytope* pt, const mjtNum simplex1[9], const mjtNum simple
// it but within tolerance from it. In that case the hexahedron could possibly be constructed
// that doesn't contain the origin, but nonetheless there is penetration depth.
mjtNum dir[3];
mju_sub3(dir, obj1->x0, obj2->x0);
mju_sub3(dir, status->x1, status->x2);
if (mju_norm3(dir) > mjMINVAL && !testTetra(v1, v2, v3, v4) && !testTetra(v1, v2, v3, v5)) {
return 0;
}
// save vertices and get indices for each one
int v1i = newVertex(pt, simplex1 + 0, simplex2 + 0);
int v2i = newVertex(pt, simplex1 + 3, simplex2 + 3);
int v3i = newVertex(pt, simplex1 + 6, simplex2 + 6);
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);
@@ -875,11 +849,11 @@ static int polytope3(Polytope* pt, const mjtNum simplex1[9], const mjtNum simple
// creates a polytope from a 3-simplex (4 points i.e. tetrahedron)
static int polytope4(Polytope* pt, const mjtNum simplex1[12], const mjtNum simplex2[12]) {
int v1 = newVertex(pt, simplex1 + 0, simplex2 + 0);
int v2 = newVertex(pt, simplex1 + 3, simplex2 + 3);
int v3 = newVertex(pt, simplex1 + 6, simplex2 + 6);
int v4 = newVertex(pt, simplex1 + 9, simplex2 + 9);
static int polytope4(Polytope* pt, const mjCCDStatus* status) {
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);
attachFace(pt, v1, v2, v3);
attachFace(pt, v1, v2, v4);
@@ -1035,16 +1009,15 @@ static void epaWitness(const Polytope* pt, int index, mjtNum x1[3], mjtNum x2[3]
// returns the penetration depth (negative distance) of the convex objects
static mjtNum epa(const mjCCDConfig* config, Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2,
mjtNum dir[3]) {
static mjtNum epa(mjCCDStatus* status, Polytope* pt, mjCCDObj* obj1, mjCCDObj* obj2) {
mjtNum dist = mjMAXVAL;
int index;
Horizon h;
initHorizon(&h);
int N = config->max_iterations;
mjtNum tolerance = config->tolerance;
mjtNum tolerance = status->tolerance;
for (int j = 0; j < N; j++) {
int k = 0, N = status->max_iterations;
for (; k < N; k++) {
// find the closest face to the origin
dist = mjMAXVAL;
index = -1;
@@ -1100,79 +1073,49 @@ static mjtNum epa(const mjCCDConfig* config, Polytope* pt, mjCCDObj* obj1, mjCCD
h.n = 0; // clear horizon
}
free(h.edges);
Face face = pt->faces[index];
mju_copy3(dir, face.v);
epaWitness(pt, index, obj1->x0, obj2->x0);
epaWitness(pt, index, status->x1, status->x2);
status->epa_iterations = k;
return dist;
}
// runs both GJK and EPA (if needed)
static mjtNum _gjk_epa(const mjCCDConfig* config, mjCCDObj* obj1, mjCCDObj* obj2, Polytope* pt,
mjtNum dir[3]) {
Simplex simplex1, simplex2;
mjtNum dist = _gjk(config, obj1, obj2, &simplex1, &simplex2);
// run general convex collision detection
mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2) {
// set up
obj1->center(status->x1, obj1);
obj2->center(status->x2, obj2);
status->epa_iterations = -1;
status->tolerance = config->tolerance;
status->max_iterations = config->max_iterations;
if (dist <= config->tolerance && simplex1.nverts > 1) {
mjtNum dist = gjk(status, obj1, obj2);
// penetration recovery for contacts not needed
if (!config->contacts) {
return dist;
}
if (dist <= config->tolerance && status->nsimplex > 1) {
Polytope pt;
initPolytope(&pt);
int ret;
if (simplex1.nverts == 2) {
ret = polytope2(pt, simplex1.verts, simplex2.verts, obj1, obj2);
} else if (simplex1.nverts == 3) {
ret = polytope3(pt, simplex1.verts, simplex2.verts, obj1, obj2);
if (status->nsimplex == 2) {
ret = polytope2(&pt, status, obj1, obj2);
} else if (status->nsimplex == 3) {
ret = polytope3(&pt, status, obj1, obj2);
} else {
ret = polytope4(pt, simplex1.verts, simplex2.verts);
ret = polytope4(&pt, status);
}
// simplex not on boundary (objects are penetrating)
if (ret) {
dist = epa(config, pt, obj1, obj2, dir);
return -dist;
dist = -epa(status, &pt, obj1, obj2);
} else {
dist = 0;
}
return 0;
mju_free(pt.faces);
mju_free(pt.verts);
}
return dist;
}
// --------------------------- LibCCD Compatibility Layer -----------------------------------------
// Penetration function with same signature as LibCCD's ccdMPRPenetration and ccdGJKPenetration
int mj_gjkPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos) {
Polytope pt;
initPolytope(&pt);
mjCCDConfig config;
mjCCDObj* o1 = (mjCCDObj*) obj1;
mjtNum* x1 = o1->x0;
mjCCDObj* o2 = (mjCCDObj*) obj2;
mjtNum* x2 = o2->x0;
o1->center(x1, o1);
o2->center(x2, o2);
config.max_iterations = ccd->max_iterations;
config.tolerance = ccd->mpr_tolerance;
mjtNum d[3];
mjtNum dist = _gjk_epa(&config, o1, o2, &pt, d);
if (dist < 0) {
if (depth) *depth = -dist;
if (dir) {
mju_normalize3(d);
mju_copy3(dir->v, d);
}
if (pos) {
mju_scl3(x1, x1, 0.5);
mju_scl3(x2, x2, 0.5);
mju_add3(pos->v, x1, x2);
}
} else {
if (depth) *depth = 0;
if (dir) mju_zero3(dir->v);
if (pos) mju_zero3(dir->v);
}
free(pt.faces);
free(pt.verts);
return dist >= 0;
}
+24 -12
View File
@@ -19,27 +19,39 @@
#include <mujoco/mjtnum.h>
#include "engine/engine_collision_convex.h"
#include <ccd/ccd.h>
#include <ccd/vec3.h>
#ifdef __cplusplus
extern "C" {
#endif
// internal configuration for convex collision detection
// configuration for convex collision detection
struct _mjCCDConfig {
int max_iterations;
mjtNum tolerance;
int max_iterations; // the maximum number of iterations for GJK and EPA
mjtNum tolerance; // tolerance used by GJK and EPA
int contacts; // set to true to recover contact (pendetration) info
int distances; // set to true to recover distance info
};
typedef struct _mjCCDConfig mjCCDConfig;
// Returns the distance between the two objects. The witness points are
// recoverable from x_0 in obj1 and obj2.
MJAPI mjtNum mj_gjk(const mjCCDConfig* config, mjCCDObj* obj1, mjCCDObj* obj2);
// data produced from running GJK and EPA
struct _mjCCDStatus {
mjtNum x1[3]; // witness point for geom 1
mjtNum x2[3]; // witness point for geom 2
// Penetration function with same signature as LibCCD's ccdMPRPenetration and ccdGJKPenetration
MJAPI int mj_gjkPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos);
// configurations used
int max_iterations; // the maximum number of iterations for GJK and EPA
mjtNum tolerance; // tolerance used by GJK and EPA
// statistics for debugging purposes
int gjk_iterations; // number of iterations that GJK ran
int epa_iterations; // number of iterations that EPA ran (negative if EPA did not run)
mjtNum simplex1[12]; // the simplex that GJK returned for obj1
mjtNum simplex2[12]; // the simplex that GJK returned for obj2
int nsimplex; // size of simplex 1 & 2
};
typedef struct _mjCCDStatus mjCCDStatus;
// run general convex collision detection, returns positive for distance, negative for penetration
MJAPI mjtNum mjc_ccd(const mjCCDConfig* config, mjCCDStatus* status, mjCCDObj* obj1, mjCCDObj* obj2);
#ifdef __cplusplus
}
#endif
+77 -76
View File
@@ -47,28 +47,58 @@ void mjccd_support(const void *obj, const ccd_vec3_t *_dir, ccd_vec3_t *vec) {
mjc_support(vec->v, (mjCCDObj*) obj, _dir->v);
}
mjtNum run_gjk(mjModel* m, mjData* d, int g1, int g2, mjtNum x1[3],
mjtNum x2[3]) {
mjtNum GeomDist(mjModel* m, mjData* d, int g1, int g2, mjtNum x1[3],
mjtNum x2[3]) {
mjCCDConfig config = {kMaxIterations, kTolerance};
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjc_center(obj1.x0, &obj1);
mjc_center(obj2.x0, &obj2);
mjtNum dist = mj_gjk(&config, &obj1, &obj2);
if (x1 != nullptr) mju_copy3(x1, obj1.x0);
if (x2 != nullptr) mju_copy3(x2, obj2.x0);
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, 0, {1, 0, 0, 0}, mjc_center,
mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, mjc_center,
mjc_support};
mjCCDStatus status;
mjtNum dist = mjc_ccd(&config, &status, &obj1, &obj2);
if (x1 != nullptr) mju_copy3(x1, status.x1);
if (x2 != nullptr) mju_copy3(x2, status.x2);
return dist;
}
// drop in replacement for ccdMPRPenetration taken from mjc_penetration
int PenetrationWrapper(mjCCDObj* obj1, mjCCDObj* obj2, const ccd_t* ccd,
ccd_real_t* depth, ccd_vec3_t* dir, ccd_vec3_t* pos) {
mjCCDConfig config;
mjCCDStatus status;
mjtNum run_gjkPenetration(mjModel* m, mjData* d, int g1, int g2,
mjtNum dir[3] = nullptr, mjtNum pos[3] = nullptr) {
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, {0, 0, 0},
mjc_center, mjc_support};
// set config
config.max_iterations = ccd->max_iterations,
config.tolerance = ccd->mpr_tolerance,
config.contacts = 1;
config.distances = 0; // no geom distances needed
mjtNum dist = mjc_ccd(&config, &status, obj1, obj2);
if (dist < 0) {
if (depth) *depth = -dist;
if (dir) {
mju_sub3(dir->v, status.x1, status.x2);
mju_normalize3(dir->v);
}
if (pos) {
pos->v[0] = 0.5 * (status.x1[0] + status.x2[0]);
pos->v[1] = 0.5 * (status.x1[1] + status.x2[1]);
pos->v[2] = 0.5 * (status.x1[2] + status.x2[2]);
}
return 0;
}
if (depth) *depth = 0;
if (dir) mju_zero3(dir->v);
if (pos) mju_zero3(dir->v);
return 1;
}
mjtNum Penetration(mjModel* m, mjData* d, int g1, int g2,
mjtNum dir[3] = nullptr, mjtNum pos[3] = nullptr) {
mjCCDObj obj1 = {m, d, g1, -1, -1, -1, -1, 0, {1, 0, 0, 0}, mjc_center,
mjc_support};
mjCCDObj obj2 = {m, d, g2, -1, -1, -1, -1, 0, {1, 0, 0, 0}, mjc_center,
mjc_support};
ccd_t ccd;
// CCD_INIT(&ccd); // uncomment to run ccdMPRPenetration
ccd.mpr_tolerance = kTolerance;
@@ -82,7 +112,7 @@ mjtNum run_gjkPenetration(mjModel* m, mjData* d, int g1, int g2,
ccd_real_t depth;
ccd_vec3_t ccd_dir, ccd_pos;
int ret = mj_gjkPenetration(&obj1, &obj2, &ccd, &depth, &ccd_dir, &ccd_pos);
int ret = PenetrationWrapper(&obj1, &obj2, &ccd, &depth, &ccd_dir, &ccd_pos);
if (ret) return 0; // objects not colliding
if (dir) mju_copy3(dir, ccd_dir.v);
if (pos) mju_copy3(pos, ccd_pos.v);
@@ -116,7 +146,7 @@ TEST_F(MjGjkTest, SphereSphere) {
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum x1[3], x2[3];
mjtNum dist = run_gjk(model, data, geom1, geom2, x1, x2);
mjtNum dist = GeomDist(model, data, geom1, geom2, x1, x2);
EXPECT_EQ(dist, 1);
EXPECT_THAT(x1, ElementsAre(-.5, 0, 0));
@@ -125,60 +155,6 @@ TEST_F(MjGjkTest, SphereSphere) {
mj_deleteModel(model);
}
TEST_F(MjGjkTest, BoxBox) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<geom name="geom1" type="box" pos="-1.5 .5 0" size="1 1 1"/>
<geom name="geom2" type="box" pos="1.5 0 0" size="1 1 1"/>
</worldbody>
</mujoco>)";
std::array<char, 1000> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
mjData* data = mj_makeData(model);
mj_forward(model, data);
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dist = run_gjk(model, data, geom1, geom2, nullptr, nullptr);
EXPECT_EQ(dist, 1);
mj_deleteData(data);
mj_deleteModel(model);
}
TEST_F(MjGjkTest, BoxBoxIntersect) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<geom name="geom1" type="box" pos="-1 0 0" size="2.5 2.5 2.5"/>
<geom name="geom2" type="box" pos="1.5 0 0" size="1 1 1"/>
</worldbody>
</mujoco>)";
std::array<char, 1000> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
mjData* data = mj_makeData(model);
mj_forward(model, data);
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dir[3], pos[3];
mjtNum dist = run_gjkPenetration(model, data, geom1, geom2, dir, pos);
EXPECT_NEAR(dist, -1, kTolerance);
EXPECT_NEAR(dir[0], 1, kTolerance);
EXPECT_NEAR(dir[1], 0, kTolerance);
EXPECT_NEAR(dir[2], 0, kTolerance);
mj_deleteData(data);
mj_deleteModel(model);
}
TEST_F(MjGjkTest, EllipsoidEllipsoid) {
static constexpr char xml[] = R"(
<mujoco>
@@ -197,13 +173,38 @@ TEST_F(MjGjkTest, EllipsoidEllipsoid) {
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dist = run_gjk(model, data, geom1, geom2, nullptr, nullptr);
mjtNum dist = GeomDist(model, data, geom1, geom2, nullptr, nullptr);
EXPECT_NEAR(dist, 0.7542, .0001);
mj_deleteData(data);
mj_deleteModel(model);
}
TEST_F(MjGjkTest, BoxBox) {
static constexpr char xml[] = R"(
<mujoco>
<worldbody>
<geom name="geom1" type="box" pos="-1.5 .5 0" size="1 1 1"/>
<geom name="geom2" type="box" pos="1.5 0 0" size="1 1 1"/>
</worldbody>
</mujoco>)";
std::array<char, 1000> error;
mjModel* model = LoadModelFromString(xml, error.data(), error.size());
ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data();
mjData* data = mj_makeData(model);
mj_forward(model, data);
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dist = GeomDist(model, data, geom1, geom2, nullptr, nullptr);
EXPECT_EQ(dist, 1);
mj_deleteData(data);
mj_deleteModel(model);
}
TEST_F(MjGjkTest, EllipsoidEllipsoidIntersect) {
static constexpr char xml[] = R"(
<mujoco>
@@ -222,7 +223,7 @@ TEST_F(MjGjkTest, EllipsoidEllipsoidIntersect) {
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dist = run_gjk(model, data, geom1, geom2, nullptr, nullptr);
mjtNum dist = GeomDist(model, data, geom1, geom2, nullptr, nullptr);
EXPECT_NEAR(dist, 0, kTolerance);
mj_deleteData(data);
@@ -247,7 +248,7 @@ TEST_F(MjGjkTest, CapsuleCapsule) {
int geom1 = mj_name2id(model, mjOBJ_GEOM, "geom1");
int geom2 = mj_name2id(model, mjOBJ_GEOM, "geom2");
mjtNum dist = run_gjk(model, data, geom1, geom2, nullptr, nullptr);
mjtNum dist = GeomDist(model, data, geom1, geom2, nullptr, nullptr);
EXPECT_NEAR(dist, 0.4711, .0001);
mj_deleteData(data);