Add internal mj_narrowphase for batch processing of potential geom pair collisions.
PiperOrigin-RevId: 918988042 Change-Id: I871dcdf42f8105e2c8a57e275a3775ea46c69e65
This commit is contained in:
committed by
Copybara-Service
parent
7c47a89cd0
commit
ad96acfb16
@@ -24,7 +24,7 @@
|
|||||||
#include <mujoco/mjmacro.h>
|
#include <mujoco/mjmacro.h>
|
||||||
#include <mujoco/mjmodel.h>
|
#include <mujoco/mjmodel.h>
|
||||||
#include "engine/engine_collision_gjk.h"
|
#include "engine/engine_collision_gjk.h"
|
||||||
#include "engine/engine_collision_primitive.h"
|
#include "engine/engine_macro.h"
|
||||||
#include "engine/engine_memory.h"
|
#include "engine/engine_memory.h"
|
||||||
#include "engine/engine_inline.h"
|
#include "engine/engine_inline.h"
|
||||||
#include "engine/engine_util_blas.h"
|
#include "engine/engine_util_blas.h"
|
||||||
@@ -34,6 +34,15 @@
|
|||||||
|
|
||||||
#define mjMINVAL2 (mjMINVAL * mjMINVAL)
|
#define mjMINVAL2 (mjMINVAL * mjMINVAL)
|
||||||
|
|
||||||
|
// CCD internal buffer used for batched processing; if NULL, stack memory allocated on each
|
||||||
|
// mjc_penetration call
|
||||||
|
static mjTHREADLOCAL void* ccd_buffer = NULL;
|
||||||
|
|
||||||
|
// set CCD internal buffer
|
||||||
|
void mjc_setCCDBuffer(void* buffer) {
|
||||||
|
ccd_buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
// ccd prism first dir
|
// ccd prism first dir
|
||||||
static void prism_firstdir(const void* o1, const void* o2, ccd_vec3_t *vec) {
|
static void prism_firstdir(const void* o1, const void* o2, ccd_vec3_t *vec) {
|
||||||
ccdVec3Set(vec, 0, 0, 1);
|
ccdVec3Set(vec, 0, 0, 1);
|
||||||
@@ -85,18 +94,23 @@ static int mjc_penetration(const mjModel* m, mjData* d, mjCCDObj* obj1, mjCCDObj
|
|||||||
mjCCDConfig config;
|
mjCCDConfig config;
|
||||||
mjCCDStatus status;
|
mjCCDStatus status;
|
||||||
mjtNum dist;
|
mjtNum dist;
|
||||||
|
int nwitness = 0;
|
||||||
|
void* buffer = ccd_buffer;
|
||||||
|
|
||||||
// set config
|
// set config
|
||||||
mj_markStack(d);
|
|
||||||
config.max_iterations = m->opt.ccd_iterations;
|
config.max_iterations = m->opt.ccd_iterations;
|
||||||
config.tolerance = m->opt.ccd_tolerance;
|
config.tolerance = m->opt.ccd_tolerance;
|
||||||
config.max_contacts = ncon;
|
config.max_contacts = ncon;
|
||||||
config.dist_cutoff = 0; // no geom distances needed
|
config.dist_cutoff = 0; // no geom distances needed
|
||||||
config.buffer = mj_stackAllocByte(d, mjc_ccdSize(config.max_iterations), sizeof(mjtNum));
|
if (buffer) {
|
||||||
|
config.buffer = buffer;
|
||||||
|
} else {
|
||||||
|
mj_markStack(d);
|
||||||
|
config.buffer = mj_stackAllocByte(d, mjc_ccdSize(config.max_iterations), sizeof(mjtNum));
|
||||||
|
}
|
||||||
|
|
||||||
if ((dist = mjc_ccd(&config, &status, obj1, obj2)) < 0) {
|
if ((dist = mjc_ccd(&config, &status, obj1, obj2)) < 0) {
|
||||||
mj_freeStack(d);
|
nwitness = status.nx;
|
||||||
int nwitness = status.nx;
|
|
||||||
for (int i = 0; i < nwitness; i++) {
|
for (int i = 0; i < nwitness; i++) {
|
||||||
con[i].dist = margin + dist;
|
con[i].dist = margin + dist;
|
||||||
con[i].pos[0] = 0.5*(status.x1[3*i + 0] + status.x2[3*i + 0]);
|
con[i].pos[0] = 0.5*(status.x1[3*i + 0] + status.x2[3*i + 0]);
|
||||||
@@ -106,10 +120,11 @@ static int mjc_penetration(const mjModel* m, mjData* d, mjCCDObj* obj1, mjCCDObj
|
|||||||
mju_normalize3(con[i].normal);
|
mju_normalize3(con[i].normal);
|
||||||
mji_zero3(con[i].tangent);
|
mji_zero3(con[i].tangent);
|
||||||
}
|
}
|
||||||
return nwitness;
|
|
||||||
}
|
}
|
||||||
mj_freeStack(d);
|
if (!buffer) {
|
||||||
return 0;
|
mj_freeStack(d);
|
||||||
|
}
|
||||||
|
return nwitness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ int mjc_HFieldElem(const mjModel* m, mjData* d, mjContact* con, int g, int f, in
|
|||||||
// fix contact frame normal
|
// fix contact frame normal
|
||||||
void mjc_fixNormal(const mjModel* m, const mjData* d, mjPreContact* con, int g1, int g2);
|
void mjc_fixNormal(const mjModel* m, const mjData* d, mjPreContact* con, int g1, int g2);
|
||||||
|
|
||||||
|
// set CCD internal buffer
|
||||||
|
void mjc_setCCDBuffer(void* buffer);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <mujoco/mjsan.h> // IWYU pragma: keep
|
#include <mujoco/mjsan.h> // IWYU pragma: keep
|
||||||
#include "engine/engine_callback.h"
|
#include "engine/engine_callback.h"
|
||||||
#include "engine/engine_collision_convex.h"
|
#include "engine/engine_collision_convex.h"
|
||||||
|
#include "engine/engine_collision_gjk.h"
|
||||||
#include "engine/engine_collision_primitive.h"
|
#include "engine/engine_collision_primitive.h"
|
||||||
#include "engine/engine_collision_sdf.h"
|
#include "engine/engine_collision_sdf.h"
|
||||||
#include "engine/engine_core_constraint.h"
|
#include "engine/engine_core_constraint.h"
|
||||||
@@ -155,6 +156,24 @@ int mj_maxContact(const mjModel* m, int g1, int g2, int has_margin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return the margin for a given geom pair
|
||||||
|
static inline mjtNum getMargin(const mjModel* m, int g1, int g2, int ipair) {
|
||||||
|
if (ipair >= 0) {
|
||||||
|
return mj_assignMargin(m, m->pair_margin[ipair]);
|
||||||
|
}
|
||||||
|
return mj_assignMargin(m, m->geom_margin[g1] + m->geom_margin[g2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return the gap for a given geom pair
|
||||||
|
static inline mjtNum getGap(const mjModel* m, int g1, int g2, int ipair) {
|
||||||
|
if (ipair >= 0) {
|
||||||
|
return m->pair_gap[ipair];
|
||||||
|
}
|
||||||
|
return m->geom_gap[g1] + m->geom_gap[g2];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// move arena pointer back to the end of the contact array
|
// move arena pointer back to the end of the contact array
|
||||||
static inline void resetArena(mjData* d) {
|
static inline void resetArena(mjData* d) {
|
||||||
d->parena = d->ncon * sizeof(mjContact);
|
d->parena = d->ncon * sizeof(mjContact);
|
||||||
@@ -167,6 +186,16 @@ static inline void resetArena(mjData* d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// realign the arena pointer to the specified alignment
|
||||||
|
static inline size_t alignArena(mjData* d, size_t alignment) {
|
||||||
|
size_t misalignment = d->parena % alignment;
|
||||||
|
if (misalignment) {
|
||||||
|
d->parena += alignment - misalignment;
|
||||||
|
}
|
||||||
|
return d->parena;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// plane to geom_center squared distance, g1 is a plane
|
// plane to geom_center squared distance, g1 is a plane
|
||||||
static mjtNum planeGeomDist(const mjModel* m, mjData* d, int g1, int g2) {
|
static mjtNum planeGeomDist(const mjModel* m, mjData* d, int g1, int g2) {
|
||||||
mjtNum* mat1 = d->geom_xmat + 9*g1;
|
mjtNum* mat1 = d->geom_xmat + 9*g1;
|
||||||
@@ -322,6 +351,16 @@ int mj_isElemActive(const mjModel* m, int f, int e) {
|
|||||||
|
|
||||||
//----------------------------- collision detection entry point ------------------------------------
|
//----------------------------- collision detection entry point ------------------------------------
|
||||||
|
|
||||||
|
// binary search between two bodyflex trees
|
||||||
|
void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2,
|
||||||
|
int merged, int startadr, int pairadr);
|
||||||
|
|
||||||
|
// compute contacts for a batch of collision pairs contained in a buffer of
|
||||||
|
// stride 3 ints (g1, g2, ipair)
|
||||||
|
// if buffer is NULL, results are read from arena starting at parena
|
||||||
|
void mj_narrowphase(const mjModel* m, mjData* d, const int* buffer, int npair, size_t parena);
|
||||||
|
|
||||||
|
|
||||||
// compare contact pairs by their geom/elem/vert IDs
|
// compare contact pairs by their geom/elem/vert IDs
|
||||||
static inline int contactcompare(const mjContact* c1, const mjContact* c2, void* context) {
|
static inline int contactcompare(const mjContact* c1, const mjContact* c2, void* context) {
|
||||||
const mjModel* m = (const mjModel*) context;
|
const mjModel* m = (const mjModel*) context;
|
||||||
@@ -333,7 +372,7 @@ static inline int contactcompare(const mjContact* c1, const mjContact* c2, void*
|
|||||||
int con2_obj2 = c2->geom[1] >= 0 ? c2->geom[1] : (c2->elem[1] >= 0 ? c2->elem[1] : c2->vert[1]);
|
int con2_obj2 = c2->geom[1] >= 0 ? c2->geom[1] : (c2->elem[1] >= 0 ? c2->elem[1] : c2->vert[1]);
|
||||||
|
|
||||||
// for geom:geom, reproduce the order of contacts without mj_collideTree
|
// for geom:geom, reproduce the order of contacts without mj_collideTree
|
||||||
// normally sorted by (g1, g2), but in mj_collideGeoms, g1 and g2 are swapped based on geom_type
|
// normally sorted by (g1, g2), but g1 and g2 are swapped based on geom_type
|
||||||
// here we undo this swapping for the purpose of sorting - needs to be done for each mjContact
|
// here we undo this swapping for the purpose of sorting - needs to be done for each mjContact
|
||||||
if (c1->geom[0] >= 0 && c1->geom[1] >= 0 &&
|
if (c1->geom[0] >= 0 && c1->geom[1] >= 0 &&
|
||||||
c2->geom[0] >= 0 && c2->geom[1] >= 0) {
|
c2->geom[0] >= 0 && c2->geom[1] >= 0) {
|
||||||
@@ -431,6 +470,72 @@ static void filterFlexContacts(mjData* d, int ncon_before) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// push a candidate collision pair onto the arena
|
||||||
|
static void pushPairArena(const mjModel* m, mjData* d, int g1, int g2, int ipair) {
|
||||||
|
// allocate geom pair on the arena
|
||||||
|
int* pair = (int*) mj_arenaAllocByte(d, 3 * sizeof(int), _Alignof(int));
|
||||||
|
if (!pair) {
|
||||||
|
mjERROR("arena too small to allocate geom pair");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m->geom_type[g1] > m->geom_type[g2]) {
|
||||||
|
pair[0] = g2;
|
||||||
|
pair[1] = g1;
|
||||||
|
} else {
|
||||||
|
pair[0] = g1;
|
||||||
|
pair[1] = g2;
|
||||||
|
}
|
||||||
|
pair[2] = ipair;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// filter candidate collision pair; return 0 if pair should be discarded
|
||||||
|
static int filterCollisionPair(const mjModel* m, mjData* d, int g1, int g2, int ipair,
|
||||||
|
int merged, int startadr, int pairadr) {
|
||||||
|
// merged, find matching pair
|
||||||
|
if (merged) {
|
||||||
|
for (int k=startadr; k < pairadr; k++) {
|
||||||
|
if ((m->pair_geom1[k] == g1 && m->pair_geom2[k] == g2) ||
|
||||||
|
(m->pair_geom1[k] == g2 && m->pair_geom2[k] == g1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipair >= 0) {
|
||||||
|
if (mjENABLED(mjENBL_SLEEP)) {
|
||||||
|
int b1 = m->geom_bodyid[g1];
|
||||||
|
int b2 = m->geom_bodyid[g2];
|
||||||
|
if (d->body_awake[b1] != mjS_AWAKE && d->body_awake[b2] != mjS_AWAKE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipair < 0) {
|
||||||
|
if (mjcb_contactfilter) {
|
||||||
|
if (mjcb_contactfilter(m, d, g1, g2)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} else if (filterBitmask(m->geom_contype[g1], m->geom_conaffinity[g1],
|
||||||
|
m->geom_contype[g2], m->geom_conaffinity[g2])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bounding sphere filter
|
||||||
|
mjtNum margin = getMargin(m, g1, g2, ipair);
|
||||||
|
mjtNum gap = getGap(m, g1, g2, ipair);
|
||||||
|
if (mj_filterSphere(m, d, g1, g2, margin + gap)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// highly unlikely, but check collision function is well-defined
|
||||||
|
int type1 = mjMIN(m->geom_type[g1], m->geom_type[g2]);
|
||||||
|
int type2 = mjMAX(m->geom_type[g1], m->geom_type[g2]);
|
||||||
|
return mjCOLLISIONFUNC[type1][type2] != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// main collision function
|
// main collision function
|
||||||
void mj_collision(const mjModel* m, mjData* d) {
|
void mj_collision(const mjModel* m, mjData* d) {
|
||||||
@@ -469,7 +574,11 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
TM_RESTART;
|
TM_RESTART;
|
||||||
|
|
||||||
// process bodyflex pairs returned by broadphase, merge with predefined geom pairs
|
// process bodyflex pairs returned by broadphase, merge with predefined geom pairs
|
||||||
int pairadr = 0;
|
int pairadr = 0, ngeompair = 0, g1, g2;
|
||||||
|
|
||||||
|
// align the arena for candidate collision pairs for narrowphase
|
||||||
|
size_t parena = alignArena(d, _Alignof(int));
|
||||||
|
|
||||||
for (int i=0; i < nbfpair; i++) {
|
for (int i=0; i < nbfpair; i++) {
|
||||||
// reconstruct bodyflex pair ids
|
// reconstruct bodyflex pair ids
|
||||||
int bf1 = (broadphasepair[i]>>16) & 0xFFFF;
|
int bf1 = (broadphasepair[i]>>16) & 0xFFFF;
|
||||||
@@ -490,9 +599,11 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
// test all predefined pairs for which pair_signature <= signature
|
// test all predefined pairs for which pair_signature <= signature
|
||||||
for (; pairadr < npair && m->pair_signature[pairadr] <= signature; pairadr++) {
|
for (; pairadr < npair && m->pair_signature[pairadr] <= signature; pairadr++) {
|
||||||
merged = (m->pair_signature[pairadr] == signature);
|
merged = (m->pair_signature[pairadr] == signature);
|
||||||
int g1 = m->pair_geom1[pairadr];
|
g1 = m->pair_geom1[pairadr], g2 = m->pair_geom2[pairadr];
|
||||||
int g2 = m->pair_geom2[pairadr];
|
if (filterCollisionPair(m, d, g1, g2, pairadr, 0, 0, 0)) {
|
||||||
mj_collideGeoms(m, d, pairadr, g1, g2);
|
pushPairArena(m, d, g1, g2, pairadr);
|
||||||
|
ngeompair++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply bitmask filtering at the bodyflex level
|
// apply bitmask filtering at the bodyflex level
|
||||||
@@ -524,11 +635,20 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
|
|
||||||
// process bodyflex pair: two single-geom bodies
|
// process bodyflex pair: two single-geom bodies
|
||||||
if (isbody1 && isbody2 && m->body_geomnum[bf1] == 1 && m->body_geomnum[bf2] == 1) {
|
if (isbody1 && isbody2 && m->body_geomnum[bf1] == 1 && m->body_geomnum[bf2] == 1) {
|
||||||
mj_collideGeomPair(m, d, geomadr1, geomadr2, merged, startadr, pairadr);
|
if (filterCollisionPair(m, d, geomadr1, geomadr2, -1, merged, startadr, pairadr)) {
|
||||||
|
pushPairArena(m, d, geomadr1, geomadr2, -1);
|
||||||
|
ngeompair++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process bodyflex pair: midphase
|
// process bodyflex pair: midphase
|
||||||
else if (!mjDISABLED(mjDSBL_MIDPHASE) && bvh1 >= 0 && bvh2 >= 0) {
|
else if (!mjDISABLED(mjDSBL_MIDPHASE) && bvh1 >= 0 && bvh2 >= 0) {
|
||||||
|
// flush geom pairs before calling mj_collideTree as post sorting needs to happen
|
||||||
|
if (ngeompair > 0) {
|
||||||
|
mj_narrowphase(m, d, NULL, ngeompair, parena);
|
||||||
|
ngeompair = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int ncon_before = d->ncon;
|
int ncon_before = d->ncon;
|
||||||
mj_collideTree(m, d, bf1, bf2, merged, startadr, pairadr);
|
mj_collideTree(m, d, bf1, bf2, merged, startadr, pairadr);
|
||||||
int ncon_after = d->ncon;
|
int ncon_after = d->ncon;
|
||||||
@@ -547,6 +667,9 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
contactSort(d->contact + ncon_before, buf, n, (void*)m);
|
contactSort(d->contact + ncon_before, buf, n, (void*)m);
|
||||||
mj_freeStack(d);
|
mj_freeStack(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// realign the arena after adding contacts
|
||||||
|
parena = alignArena(d, _Alignof(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
// process bodyflex pair: all-to-all
|
// process bodyflex pair: all-to-all
|
||||||
@@ -556,15 +679,23 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
|
|
||||||
// body : body
|
// body : body
|
||||||
if (isbody1 && isbody2) {
|
if (isbody1 && isbody2) {
|
||||||
for (int g1=geomadr1; g1 < geomadr_end1; g1++) {
|
for (g1=geomadr1; g1 < geomadr_end1; g1++) {
|
||||||
for (int g2=geomadr2; g2 < geomadr_end2; g2++) {
|
for (g2=geomadr2; g2 < geomadr_end2; g2++) {
|
||||||
mj_collideGeomPair(m, d, g1, g2, merged, startadr, pairadr);
|
if (filterCollisionPair(m, d, g1, g2, -1, merged, startadr, pairadr)) {
|
||||||
|
pushPairArena(m, d, g1, g2, -1);
|
||||||
|
ngeompair++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// body : flex
|
// body : flex
|
||||||
else if (isbody1) {
|
else if (isbody1) {
|
||||||
|
if (ngeompair > 0) {
|
||||||
|
mj_narrowphase(m, d, NULL, ngeompair, parena);
|
||||||
|
ngeompair = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int f = bf2 - nbody;
|
int f = bf2 - nbody;
|
||||||
|
|
||||||
// process body geoms
|
// process body geoms
|
||||||
@@ -599,10 +730,18 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
}
|
}
|
||||||
filterFlexContacts(d, ncon_before);
|
filterFlexContacts(d, ncon_before);
|
||||||
}
|
}
|
||||||
|
// realign the arena after adding contacts
|
||||||
|
parena = alignArena(d, _Alignof(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
// flex : flex
|
// flex : flex
|
||||||
else {
|
else {
|
||||||
|
// flush accumulated geompairs before flex:flex processing
|
||||||
|
if (ngeompair > 0) {
|
||||||
|
mj_narrowphase(m, d, NULL, ngeompair, parena);
|
||||||
|
ngeompair = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int f1 = bf1 - nbody;
|
int f1 = bf1 - nbody;
|
||||||
int f2 = bf2 - nbody;
|
int f2 = bf2 - nbody;
|
||||||
|
|
||||||
@@ -614,6 +753,9 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
filterFlexContacts(d, ncon_before);
|
filterFlexContacts(d, ncon_before);
|
||||||
|
|
||||||
|
// realign the arena after adding contacts
|
||||||
|
parena = alignArena(d, _Alignof(int));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -621,9 +763,17 @@ void mj_collision(const mjModel* m, mjData* d) {
|
|||||||
|
|
||||||
// finish merging predefined geom pairs
|
// finish merging predefined geom pairs
|
||||||
for (; pairadr < npair; pairadr++) {
|
for (; pairadr < npair; pairadr++) {
|
||||||
int g1 = m->pair_geom1[pairadr];
|
g1 = m->pair_geom1[pairadr], g2 = m->pair_geom2[pairadr];
|
||||||
int g2 = m->pair_geom2[pairadr];
|
if (filterCollisionPair(m, d, g1, g2, pairadr, 0, 0, 0)) {
|
||||||
mj_collideGeoms(m, d, pairadr, g1, g2);
|
pushPairArena(m, d, g1, g2, pairadr);
|
||||||
|
ngeompair++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush remaining collision pairs
|
||||||
|
if (ngeompair > 0) {
|
||||||
|
mj_narrowphase(m, d, NULL, ngeompair, parena);
|
||||||
|
ngeompair = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flex self-collisions
|
// flex self-collisions
|
||||||
@@ -689,25 +839,6 @@ typedef struct {
|
|||||||
int node2;
|
int node2;
|
||||||
} mjCollisionTree;
|
} mjCollisionTree;
|
||||||
|
|
||||||
|
|
||||||
// checks if the proposed collision pair is already present in pair_geom and calls narrow phase
|
|
||||||
void mj_collideGeomPair(const mjModel* m, mjData* d, int g1, int g2, int merged,
|
|
||||||
int startadr, int pairadr) {
|
|
||||||
// merged, find matching pair
|
|
||||||
if (merged) {
|
|
||||||
for (int k=startadr; k < pairadr; k++) {
|
|
||||||
if ((m->pair_geom1[k] == g1 && m->pair_geom2[k] == g2) ||
|
|
||||||
(m->pair_geom1[k] == g2 && m->pair_geom2[k] == g1)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not merged, always test
|
|
||||||
mj_collideGeoms(m, d, -1, g1, g2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// oriented bounding boxes collision (see Gottschalk et al.)
|
// oriented bounding boxes collision (see Gottschalk et al.)
|
||||||
int mj_collideOBB(const mjtNum aabb1[6], const mjtNum aabb2[6],
|
int mj_collideOBB(const mjtNum aabb1[6], const mjtNum aabb2[6],
|
||||||
const mjtNum xpos1[3], const mjtNum xmat1[9],
|
const mjtNum xpos1[3], const mjtNum xmat1[9],
|
||||||
@@ -806,7 +937,6 @@ int mj_collideOBB(const mjtNum aabb1[6], const mjtNum aabb2[6],
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// binary search between two bodyflex trees
|
|
||||||
void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2,
|
void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2,
|
||||||
int merged, int startadr, int pairadr) {
|
int merged, int startadr, int pairadr) {
|
||||||
int nbody = m->nbody, nbvhstatic = m->nbvhstatic;
|
int nbody = m->nbody, nbvhstatic = m->nbvhstatic;
|
||||||
@@ -894,7 +1024,16 @@ void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2,
|
|||||||
d->geom_xpos + 3*nodeid1, d->geom_xmat + 9*nodeid1,
|
d->geom_xpos + 3*nodeid1, d->geom_xmat + 9*nodeid1,
|
||||||
d->geom_xpos + 3*nodeid2, d->geom_xmat + 9*nodeid2,
|
d->geom_xpos + 3*nodeid2, d->geom_xmat + 9*nodeid2,
|
||||||
margin + gap, NULL, NULL, &initialize)) {
|
margin + gap, NULL, NULL, &initialize)) {
|
||||||
mj_collideGeomPair(m, d, nodeid1, nodeid2, merged, startadr, pairadr);
|
|
||||||
|
if (filterCollisionPair(m, d, nodeid1, nodeid2, -1, merged, startadr, pairadr)) {
|
||||||
|
int n1 = nodeid1, n2 = nodeid2;
|
||||||
|
if (m->geom_type[n1] > m->geom_type[n2]) {
|
||||||
|
n1 = nodeid2;
|
||||||
|
n2 = nodeid1;
|
||||||
|
}
|
||||||
|
int pair[3] = {n1, n2, -1};
|
||||||
|
mj_narrowphase(m, d, pair, 1, 0);
|
||||||
|
}
|
||||||
if (mark_active) {
|
if (mark_active) {
|
||||||
d->bvh_active[node1 + bvhadr1] = 1;
|
d->bvh_active[node1 + bvhadr1] = 1;
|
||||||
d->bvh_active[node2 + bvhadr2] = 1;
|
d->bvh_active[node2 + bvhadr2] = 1;
|
||||||
@@ -1675,139 +1814,141 @@ static void mj_makeCapsule(const mjModel* m, mjData* d, int f, const int vid[2],
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// test two geoms for collision, apply filters, add to contact list
|
// compute contacts for a batch of collision pairs contained in a buffer of
|
||||||
void mj_collideGeoms(const mjModel* m, mjData* d, int ipair, int g1, int g2) {
|
// stride 3 ints (g1, g2, ipair)
|
||||||
int num, type1, type2, condim;
|
// if buffer is NULL, results are read from arena starting at parena
|
||||||
mjtNum margin, gap, friction[5], solref[mjNREF], solimp[mjNIMP];
|
void mj_narrowphase(const mjModel* m, mjData* d, const int* buffer, int npair, size_t parena) {
|
||||||
mjtNum solreffriction[mjNREF] = {0};
|
int ccd_size = mjc_ccdSize(m->opt.ccd_iterations);
|
||||||
|
mjtNum margin, gap;
|
||||||
|
|
||||||
// sleep filtering for explicit pairs
|
// set buffer and arena pointer
|
||||||
if (ipair >= 0) {
|
if (!buffer) {
|
||||||
if (mjENABLED(mjENBL_SLEEP)) {
|
buffer = (const int*) ((char*) d->arena + parena);
|
||||||
int b1 = m->geom_bodyid[g1];
|
|
||||||
int b2 = m->geom_bodyid[g2];
|
|
||||||
if (d->body_awake[b1] != mjS_AWAKE && d->body_awake[b2] != mjS_AWAKE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// order geoms by type
|
|
||||||
if (m->geom_type[g1] > m->geom_type[g2]) {
|
|
||||||
int i = g1;
|
|
||||||
g1 = g2;
|
|
||||||
g2 = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// copy types and bodies
|
|
||||||
type1 = m->geom_type[g1];
|
|
||||||
type2 = m->geom_type[g2];
|
|
||||||
|
|
||||||
mjfCollision collisionFunc = mjCOLLISIONFUNC[type1][type2];
|
|
||||||
|
|
||||||
// return if no collision function
|
|
||||||
if (!collisionFunc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply filters if not predefined pair
|
|
||||||
if (ipair < 0) {
|
|
||||||
// user filter if defined
|
|
||||||
if (mjcb_contactfilter) {
|
|
||||||
if (mjcb_contactfilter(m, d, g1, g2)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise built-in filter
|
|
||||||
else if (filterBitmask(m->geom_contype[g1], m->geom_conaffinity[g1],
|
|
||||||
m->geom_contype[g2], m->geom_conaffinity[g2])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set margin: dynamic or pair
|
|
||||||
if (ipair < 0) {
|
|
||||||
margin = mj_assignMargin(m, m->geom_margin[g1] + m->geom_margin[g2]);
|
|
||||||
} else {
|
} else {
|
||||||
margin = mj_assignMargin(m, m->pair_margin[ipair]);
|
parena = d->parena;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set gap: dynamic or pair
|
mj_markStack(d);
|
||||||
if (ipair < 0) {
|
|
||||||
gap = m->geom_gap[g1] + m->geom_gap[g2];
|
// buffer store how many contacts are generated for each pair
|
||||||
} else {
|
int* nconbuffer = mj_stackAllocInt(d, npair);
|
||||||
gap = m->pair_gap[ipair];
|
|
||||||
|
// buffer for pair data (g1, g2, ipair, index into conbuffer)
|
||||||
|
int* pairbuffer = mj_stackAllocInt(d, 4 * npair);
|
||||||
|
int maxcon = 0;
|
||||||
|
for (int i = 0; i < npair; i++) {
|
||||||
|
int g1 = buffer[3*i + 0];
|
||||||
|
int g2 = buffer[3*i + 1];
|
||||||
|
int ipair = buffer[3*i + 2];
|
||||||
|
|
||||||
|
pairbuffer[4*i + 0] = g1;
|
||||||
|
pairbuffer[4*i + 1] = g2;
|
||||||
|
pairbuffer[4*i + 2] = ipair;
|
||||||
|
pairbuffer[4*i + 3] = maxcon;
|
||||||
|
margin = getMargin(m, g1, g2, ipair);
|
||||||
|
gap = getGap(m, g1, g2, ipair);
|
||||||
|
maxcon += mj_maxContact(m, g1, g2, margin + gap > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bounding sphere filter
|
// buffer for precontact data
|
||||||
if (mj_filterSphere(m, d, g1, g2, margin + gap)) {
|
mjPreContact* conbuffer = mjSTACKALLOC(d, maxcon, mjPreContact);
|
||||||
|
|
||||||
|
// buffer data has been copied to metadata on the stack;
|
||||||
|
// reclaim arena space so contacts can overwrite the buffer region
|
||||||
|
d->parena = parena;
|
||||||
|
|
||||||
|
// set buffer for nativeccd
|
||||||
|
mj_markStack(d);
|
||||||
|
mjc_setCCDBuffer(mj_stackAllocByte(d, ccd_size, sizeof(mjtNum)));
|
||||||
|
|
||||||
|
for (int i = 0; i < npair; i++) {
|
||||||
|
int g1 = pairbuffer[4*i + 0];
|
||||||
|
int g2 = pairbuffer[4*i + 1];
|
||||||
|
int ipair = pairbuffer[4*i + 2];
|
||||||
|
int idx = pairbuffer[4*i + 3];
|
||||||
|
mjfCollision collision_func = mjCOLLISIONFUNC[m->geom_type[g1]][m->geom_type[g2]];
|
||||||
|
margin = getMargin(m, g1, g2, ipair);
|
||||||
|
gap = getGap(m, g1, g2, ipair);
|
||||||
|
nconbuffer[i] = collision_func(m, d, conbuffer + idx, g1, g2, margin + gap);
|
||||||
|
|
||||||
|
// SHOULD NOT OCCUR
|
||||||
|
int expected_max = (i + 1 < npair ? pairbuffer[4*(i+1) + 3] : maxcon) - idx;
|
||||||
|
if (nconbuffer[i] > expected_max) {
|
||||||
|
mjERROR("collision function returned %d contacts for geom pair (%d, %d), "
|
||||||
|
"expected at most %d from mj_maxContact", nconbuffer[i], g1, g2, expected_max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set nativeccd buffer back to NULL
|
||||||
|
mjc_setCCDBuffer(NULL);
|
||||||
|
mj_freeStack(d);
|
||||||
|
|
||||||
|
int ncon = 0;
|
||||||
|
for (int i = 0; i < npair; i++) {
|
||||||
|
ncon += nconbuffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ncon == 0) {
|
||||||
|
mj_freeStack(d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try allocate contact buffer in arena
|
||||||
|
mjContact* con =
|
||||||
// call collision detector to generate contacts
|
(mjContact*) mj_arenaAllocByte(d, sizeof(mjContact) * ncon, _Alignof(mjContact));
|
||||||
mjPreContact precon[mjMAXCONPAIR];
|
|
||||||
if (!(num = collisionFunc(m, d, precon, g1, g2, margin + gap))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check number of contacts, SHOULD NOT OCCUR
|
|
||||||
if (num > mjMAXCONPAIR) {
|
|
||||||
mjERROR("too many contacts returned by collision function");
|
|
||||||
}
|
|
||||||
|
|
||||||
mjContact* con = (mjContact*) mj_arenaAllocByte(d, sizeof(mjContact) * num, _Alignof(mjContact));
|
|
||||||
if (!con) {
|
if (!con) {
|
||||||
mj_warning(d, mjWARN_CONTACTFULL, d->ncon);
|
mj_warning(d, mjWARN_CONTACTFULL, d->ncon);
|
||||||
|
mj_freeStack(d);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
d->ncon += ncon;
|
||||||
|
|
||||||
// set condim, solref, solimp, friction: dynamic
|
// fill in contact data
|
||||||
if (ipair < 0) {
|
int conpos = 0;
|
||||||
mj_contactParam(m, &condim, solref, solimp, friction, g1, g2, -1, -1);
|
for (int i = 0; i < npair; i++) {
|
||||||
}
|
if (!(ncon = nconbuffer[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
// set condim, solref, solimp, friction: pair
|
int condim;
|
||||||
else {
|
mjtNum friction[5], solref[mjNREF], solimp[mjNIMP];
|
||||||
condim = m->pair_dim[ipair];
|
mjtNum solreffriction[mjNREF] = {0};
|
||||||
mju_copy(solref, m->pair_solref+mjNREF*ipair, mjNREF);
|
int g1 = pairbuffer[4*i + 0];
|
||||||
mju_copy(solimp, m->pair_solimp+mjNIMP*ipair, mjNIMP);
|
int g2 = pairbuffer[4*i + 1];
|
||||||
mju_copy(friction, m->pair_friction+5*ipair, 5);
|
int ipair = pairbuffer[4*i + 2];
|
||||||
|
|
||||||
// reference, friction directions
|
if (ipair >= 0) {
|
||||||
if (m->pair_solreffriction[mjNREF*ipair] || m->pair_solreffriction[mjNREF*ipair + 1]) {
|
condim = m->pair_dim[ipair];
|
||||||
mju_copy(solreffriction, m->pair_solreffriction+mjNREF*ipair, mjNREF);
|
mju_copy(solref, m->pair_solref+mjNREF*ipair, mjNREF);
|
||||||
|
mju_copy(solimp, m->pair_solimp+mjNIMP*ipair, mjNIMP);
|
||||||
|
mju_copy(friction, m->pair_friction+5*ipair, 5);
|
||||||
|
if (m->pair_solreffriction[mjNREF*ipair] || m->pair_solreffriction[mjNREF*ipair + 1]) {
|
||||||
|
mju_copy(solreffriction, m->pair_solreffriction+mjNREF*ipair, mjNREF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mj_contactParam(m, &condim, solref, solimp, friction, g1, g2, -1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mjPreContact* bc = conbuffer + pairbuffer[4*i + 3];
|
||||||
|
margin = getMargin(m, g1, g2, ipair);
|
||||||
|
for (int j=0; j < ncon; j++) {
|
||||||
|
mjContact* c = con + conpos + j;
|
||||||
|
c->dist = bc[j].dist;
|
||||||
|
mji_copy3(c->pos, bc[j].pos);
|
||||||
|
mji_copy3(c->frame, bc[j].normal);
|
||||||
|
mji_copy3(c->frame + 3, bc[j].tangent);
|
||||||
|
c->geom[0] = g1;
|
||||||
|
c->geom[1] = g2;
|
||||||
|
c->flex[0] = -1;
|
||||||
|
c->flex[1] = -1;
|
||||||
|
c->elem[0] = -1;
|
||||||
|
c->elem[1] = -1;
|
||||||
|
c->vert[0] = -1;
|
||||||
|
c->vert[1] = -1;
|
||||||
|
mj_setContact(m, c, condim, margin, solref, solreffriction, solimp, friction);
|
||||||
|
}
|
||||||
|
conpos += ncon;
|
||||||
}
|
}
|
||||||
|
mj_freeStack(d);
|
||||||
// add contacts returned by collision detector
|
|
||||||
for (int i=0; i < num; i++) {
|
|
||||||
// set contact parameters
|
|
||||||
con[i].dist = precon[i].dist;
|
|
||||||
mji_copy3(con[i].pos, precon[i].pos);
|
|
||||||
mji_copy3(con[i].frame + 0, precon[i].normal);
|
|
||||||
mji_copy3(con[i].frame + 3, precon[i].tangent);
|
|
||||||
|
|
||||||
con[i].geom[0] = g1;
|
|
||||||
con[i].geom[1] = g2;
|
|
||||||
con[i].flex[0] = -1;
|
|
||||||
con[i].flex[1] = -1;
|
|
||||||
con[i].elem[0] = -1;
|
|
||||||
con[i].elem[1] = -1;
|
|
||||||
con[i].vert[0] = -1;
|
|
||||||
con[i].vert[1] = -1;
|
|
||||||
|
|
||||||
// set remaining contact parameters
|
|
||||||
mj_setContact(m, con + i, condim, margin, solref, solreffriction, solimp, friction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to ncon
|
|
||||||
d->ncon += num;
|
|
||||||
|
|
||||||
// move arena pointer back to the end of the contact array
|
|
||||||
resetArena(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,20 +44,9 @@ MJAPI int mj_collideOBB(const mjtNum aabb1[6], const mjtNum aabb2[6],
|
|||||||
// is element active (for collisions)
|
// is element active (for collisions)
|
||||||
MJAPI int mj_isElemActive(const mjModel* m, int f, int e);
|
MJAPI int mj_isElemActive(const mjModel* m, int f, int e);
|
||||||
|
|
||||||
// checks if pair is already present in pair_geom and calls narrow phase
|
|
||||||
void mj_collideGeomPair(const mjModel* m, mjData* d, int g1, int g2, int merged,
|
|
||||||
int startadr, int pairadr);
|
|
||||||
|
|
||||||
// binary search between two bodyflex trees
|
|
||||||
void mj_collideTree(const mjModel* m, mjData* d, int bf1, int bf2,
|
|
||||||
int merged, int startadr, int pairadr);
|
|
||||||
|
|
||||||
// broad phase collision detection; return list of bodyflex pairs
|
// broad phase collision detection; return list of bodyflex pairs
|
||||||
int mj_broadphase(const mjModel* m, mjData* d, int* bfpair, int maxpair);
|
int mj_broadphase(const mjModel* m, mjData* d, int* bfpair, int maxpair);
|
||||||
|
|
||||||
// test two geoms for collision, apply filters, add to contact list
|
|
||||||
void mj_collideGeoms(const mjModel* m, mjData* d, int ipair, int g1, int g2);
|
|
||||||
|
|
||||||
// test a plane geom and a flex for collision, add to contact list
|
// test a plane geom and a flex for collision, add to contact list
|
||||||
void mj_collidePlaneFlex(const mjModel* m, mjData* d, int g, int f);
|
void mj_collidePlaneFlex(const mjModel* m, mjData* d, int g, int f);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user