rbdlsim/3rdparty/libccd/src/ccd.c

997 lines
31 KiB
C

/***
* libccd
* ---------------------------------
* Copyright (c)2012 Daniel Fiser <danfis@danfis.cz>
*
*
* This file is part of libccd.
*
* Distributed under the OSI-approved BSD License (the "License");
* see accompanying file BDS-LICENSE for details or see
* <http://www.opensource.org/licenses/bsd-license.php>.
*
* This software is distributed WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the License for more information.
*/
#include <stdio.h>
#include <float.h>
#include <ccd/ccd.h>
#include <ccd/vec3.h>
#include "simplex.h"
#include "polytope.h"
#include "alloc.h"
#include "dbg.h"
/** Performs GJK algorithm. Returns 0 if intersection was found and simplex
* is filled with resulting polytope. */
static int __ccdGJK(const void *obj1, const void *obj2,
const ccd_t *ccd, ccd_simplex_t *simplex);
/** Performs GJK+EPA algorithm. Returns 0 if intersection was found and
* pt is filled with resulting polytope and nearest with pointer to
* nearest element (vertex, edge, face) of polytope to origin. */
static int __ccdGJKEPA(const void *obj1, const void *obj2,
const ccd_t *ccd,
ccd_pt_t *pt, ccd_pt_el_t **nearest);
/** Returns true if simplex contains origin.
* This function also alteres simplex and dir according to further
* processing of GJK algorithm. */
static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir);
static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir);
static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir);
static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir);
/** d = a x b x c */
_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
const ccd_vec3_t *c, ccd_vec3_t *d);
/** Transforms simplex to polytope. It is assumed that simplex has 4
* vertices. */
static int simplexToPolytope4(const void *obj1, const void *obj2,
const ccd_t *ccd,
ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest);
/** Transforms simplex to polytope, three vertices required */
static int simplexToPolytope3(const void *obj1, const void *obj2,
const ccd_t *ccd,
const ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest);
/** Transforms simplex to polytope, two vertices required */
static int simplexToPolytope2(const void *obj1, const void *obj2,
const ccd_t *ccd,
const ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest);
/** Expands polytope using new vertex v.
* Return 0 on success, -2 on memory allocation failure.*/
static int expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
const ccd_support_t *newv);
/** Finds next support point (at stores it in out argument).
* Returns 0 on success, -1 otherwise */
static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
const ccd_pt_el_t *el,
ccd_support_t *out);
void ccdFirstDirDefault(const void *o1, const void *o2, ccd_vec3_t *dir)
{
ccdVec3Set(dir, CCD_ONE, CCD_ZERO, CCD_ZERO);
}
int ccdGJKIntersect(const void *obj1, const void *obj2, const ccd_t *ccd)
{
ccd_simplex_t simplex;
return __ccdGJK(obj1, obj2, ccd, &simplex) == 0;
}
int ccdGJKSeparate(const void *obj1, const void *obj2, const ccd_t *ccd,
ccd_vec3_t *sep)
{
ccd_pt_t polytope;
ccd_pt_el_t *nearest;
int ret;
ccdPtInit(&polytope);
ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
// set separation vector
if (nearest)
ccdVec3Copy(sep, &nearest->witness);
ccdPtDestroy(&polytope);
return ret;
}
static int penEPAPosCmp(const void *a, const void *b)
{
ccd_pt_vertex_t *v1, *v2;
v1 = *(ccd_pt_vertex_t **)a;
v2 = *(ccd_pt_vertex_t **)b;
if (ccdEq(v1->dist, v2->dist)){
return 0;
}else if (v1->dist < v2->dist){
return -1;
}else{
return 1;
}
}
static int penEPAPos(const ccd_pt_t *pt, const ccd_pt_el_t *nearest,
ccd_vec3_t *pos)
{
ccd_pt_vertex_t *v;
ccd_pt_vertex_t **vs;
size_t i, len;
ccd_real_t scale;
// compute median
len = 0;
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
len++;
}
vs = CCD_ALLOC_ARR(ccd_pt_vertex_t *, len);
if (vs == NULL)
return -1;
i = 0;
ccdListForEachEntry(&pt->vertices, v, ccd_pt_vertex_t, list){
vs[i++] = v;
}
qsort(vs, len, sizeof(ccd_pt_vertex_t *), penEPAPosCmp);
ccdVec3Set(pos, CCD_ZERO, CCD_ZERO, CCD_ZERO);
scale = CCD_ZERO;
if (len % 2 == 1)
len++;
for (i = 0; i < len / 2; i++){
ccdVec3Add(pos, &vs[i]->v.v1);
ccdVec3Add(pos, &vs[i]->v.v2);
scale += CCD_REAL(2.);
}
ccdVec3Scale(pos, CCD_ONE / scale);
free(vs);
return 0;
}
int ccdGJKPenetration(const void *obj1, const void *obj2, const ccd_t *ccd,
ccd_real_t *depth, ccd_vec3_t *dir, ccd_vec3_t *pos)
{
ccd_pt_t polytope;
ccd_pt_el_t *nearest;
int ret;
ccdPtInit(&polytope);
ret = __ccdGJKEPA(obj1, obj2, ccd, &polytope, &nearest);
// set separation vector
if (ret == 0 && nearest){
// compute depth of penetration
*depth = CCD_SQRT(nearest->dist);
// store normalized direction vector
ccdVec3Copy(dir, &nearest->witness);
ccdVec3Normalize(dir);
// compute position
if (penEPAPos(&polytope, nearest, pos) != 0){
ccdPtDestroy(&polytope);
return -2;
}
}
ccdPtDestroy(&polytope);
return ret;
}
static int __ccdGJK(const void *obj1, const void *obj2,
const ccd_t *ccd, ccd_simplex_t *simplex)
{
unsigned long iterations;
ccd_vec3_t dir; // direction vector
ccd_support_t last; // last support point
int do_simplex_res;
// initialize simplex struct
ccdSimplexInit(simplex);
// get first direction
ccd->first_dir(obj1, obj2, &dir);
// get first support point
__ccdSupport(obj1, obj2, &dir, ccd, &last);
// and add this point to simplex as last one
ccdSimplexAdd(simplex, &last);
// set up direction vector to as (O - last) which is exactly -last
ccdVec3Copy(&dir, &last.v);
ccdVec3Scale(&dir, -CCD_ONE);
// start iterations
for (iterations = 0UL; iterations < ccd->max_iterations; ++iterations) {
// obtain support point
__ccdSupport(obj1, obj2, &dir, ccd, &last);
// check if farthest point in Minkowski difference in direction dir
// isn't somewhere before origin (the test on negative dot product)
// - because if it is, objects are not intersecting at all.
if (ccdVec3Dot(&last.v, &dir) < CCD_ZERO){
return -1; // intersection not found
}
// add last support vector to simplex
ccdSimplexAdd(simplex, &last);
// if doSimplex returns 1 if objects intersect, -1 if objects don't
// intersect and 0 if algorithm should continue
do_simplex_res = doSimplex(simplex, &dir);
if (do_simplex_res == 1){
return 0; // intersection found
}else if (do_simplex_res == -1){
return -1; // intersection not found
}
if (ccdIsZero(ccdVec3Len2(&dir))){
return -1; // intersection not found
}
}
// intersection wasn't found
return -1;
}
static int __ccdGJKEPA(const void *obj1, const void *obj2,
const ccd_t *ccd,
ccd_pt_t *polytope, ccd_pt_el_t **nearest)
{
ccd_simplex_t simplex;
ccd_support_t supp; // support point
int ret, size;
*nearest = NULL;
// run GJK and obtain terminal simplex
ret = __ccdGJK(obj1, obj2, ccd, &simplex);
if (ret != 0)
return -1;
// transform simplex to polytope - simplex won't be used anymore
size = ccdSimplexSize(&simplex);
if (size == 4){
ret = simplexToPolytope4(obj1, obj2, ccd, &simplex, polytope, nearest);
}else if (size == 3){
ret = simplexToPolytope3(obj1, obj2, ccd, &simplex, polytope, nearest);
}else{ // size == 2
ret = simplexToPolytope2(obj1, obj2, ccd, &simplex, polytope, nearest);
}
if (ret == -1){
// touching contact
return 0;
}else if (ret == -2){
// failed memory allocation
return -2;
}
while (1){
// get triangle nearest to origin
*nearest = ccdPtNearest(polytope);
// get next support point
if (nextSupport(obj1, obj2, ccd, *nearest, &supp) != 0)
break;
// expand nearest triangle using new point - supp
if (expandPolytope(polytope, *nearest, &supp) != 0)
return -2;
}
return 0;
}
static int doSimplex2(ccd_simplex_t *simplex, ccd_vec3_t *dir)
{
const ccd_support_t *A, *B;
ccd_vec3_t AB, AO, tmp;
ccd_real_t dot;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other point
B = ccdSimplexPoint(simplex, 0);
// compute AB oriented segment
ccdVec3Sub2(&AB, &B->v, &A->v);
// compute AO vector
ccdVec3Copy(&AO, &A->v);
ccdVec3Scale(&AO, -CCD_ONE);
// dot product AB . AO
dot = ccdVec3Dot(&AB, &AO);
// check if origin doesn't lie on AB segment
ccdVec3Cross(&tmp, &AB, &AO);
if (ccdIsZero(ccdVec3Len2(&tmp)) && dot > CCD_ZERO){
return 1;
}
// check if origin is in area where AB segment is
if (ccdIsZero(dot) || dot < CCD_ZERO){
// origin is in outside are of A
ccdSimplexSet(simplex, 0, A);
ccdSimplexSetSize(simplex, 1);
ccdVec3Copy(dir, &AO);
}else{
// origin is in area where AB segment is
// keep simplex untouched and set direction to
// AB x AO x AB
tripleCross(&AB, &AO, &AB, dir);
}
return 0;
}
static int doSimplex3(ccd_simplex_t *simplex, ccd_vec3_t *dir)
{
const ccd_support_t *A, *B, *C;
ccd_vec3_t AO, AB, AC, ABC, tmp;
ccd_real_t dot, dist;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other points
B = ccdSimplexPoint(simplex, 1);
C = ccdSimplexPoint(simplex, 0);
// check touching contact
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
if (ccdIsZero(dist)){
return 1;
}
// check if triangle is really triangle (has area > 0)
// if not simplex can't be expanded and thus no itersection is found
if (ccdVec3Eq(&A->v, &B->v) || ccdVec3Eq(&A->v, &C->v)){
return -1;
}
// compute AO vector
ccdVec3Copy(&AO, &A->v);
ccdVec3Scale(&AO, -CCD_ONE);
// compute AB and AC segments and ABC vector (perpendircular to triangle)
ccdVec3Sub2(&AB, &B->v, &A->v);
ccdVec3Sub2(&AC, &C->v, &A->v);
ccdVec3Cross(&ABC, &AB, &AC);
ccdVec3Cross(&tmp, &ABC, &AC);
dot = ccdVec3Dot(&tmp, &AO);
if (ccdIsZero(dot) || dot > CCD_ZERO){
dot = ccdVec3Dot(&AC, &AO);
if (ccdIsZero(dot) || dot > CCD_ZERO){
// C is already in place
ccdSimplexSet(simplex, 1, A);
ccdSimplexSetSize(simplex, 2);
tripleCross(&AC, &AO, &AC, dir);
}else{
ccd_do_simplex3_45:
dot = ccdVec3Dot(&AB, &AO);
if (ccdIsZero(dot) || dot > CCD_ZERO){
ccdSimplexSet(simplex, 0, B);
ccdSimplexSet(simplex, 1, A);
ccdSimplexSetSize(simplex, 2);
tripleCross(&AB, &AO, &AB, dir);
}else{
ccdSimplexSet(simplex, 0, A);
ccdSimplexSetSize(simplex, 1);
ccdVec3Copy(dir, &AO);
}
}
}else{
ccdVec3Cross(&tmp, &AB, &ABC);
dot = ccdVec3Dot(&tmp, &AO);
if (ccdIsZero(dot) || dot > CCD_ZERO){
goto ccd_do_simplex3_45;
}else{
dot = ccdVec3Dot(&ABC, &AO);
if (ccdIsZero(dot) || dot > CCD_ZERO){
ccdVec3Copy(dir, &ABC);
}else{
ccd_support_t Ctmp;
ccdSupportCopy(&Ctmp, C);
ccdSimplexSet(simplex, 0, B);
ccdSimplexSet(simplex, 1, &Ctmp);
ccdVec3Copy(dir, &ABC);
ccdVec3Scale(dir, -CCD_ONE);
}
}
}
return 0;
}
static int doSimplex4(ccd_simplex_t *simplex, ccd_vec3_t *dir)
{
const ccd_support_t *A, *B, *C, *D;
ccd_vec3_t AO, AB, AC, AD, ABC, ACD, ADB;
int B_on_ACD, C_on_ADB, D_on_ABC;
int AB_O, AC_O, AD_O;
ccd_real_t dist;
// get last added as A
A = ccdSimplexLast(simplex);
// get the other points
B = ccdSimplexPoint(simplex, 2);
C = ccdSimplexPoint(simplex, 1);
D = ccdSimplexPoint(simplex, 0);
// check if tetrahedron is really tetrahedron (has volume > 0)
// if it is not simplex can't be expanded and thus no intersection is
// found
dist = ccdVec3PointTriDist2(&A->v, &B->v, &C->v, &D->v, NULL);
if (ccdIsZero(dist)){
return -1;
}
// check if origin lies on some of tetrahedron's face - if so objects
// intersect
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &C->v, NULL);
if (ccdIsZero(dist))
return 1;
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &C->v, &D->v, NULL);
if (ccdIsZero(dist))
return 1;
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &A->v, &B->v, &D->v, NULL);
if (ccdIsZero(dist))
return 1;
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &B->v, &C->v, &D->v, NULL);
if (ccdIsZero(dist))
return 1;
// compute AO, AB, AC, AD segments and ABC, ACD, ADB normal vectors
ccdVec3Copy(&AO, &A->v);
ccdVec3Scale(&AO, -CCD_ONE);
ccdVec3Sub2(&AB, &B->v, &A->v);
ccdVec3Sub2(&AC, &C->v, &A->v);
ccdVec3Sub2(&AD, &D->v, &A->v);
ccdVec3Cross(&ABC, &AB, &AC);
ccdVec3Cross(&ACD, &AC, &AD);
ccdVec3Cross(&ADB, &AD, &AB);
// side (positive or negative) of B, C, D relative to planes ACD, ADB
// and ABC respectively
B_on_ACD = ccdSign(ccdVec3Dot(&ACD, &AB));
C_on_ADB = ccdSign(ccdVec3Dot(&ADB, &AC));
D_on_ABC = ccdSign(ccdVec3Dot(&ABC, &AD));
// whether origin is on same side of ACD, ADB, ABC as B, C, D
// respectively
AB_O = ccdSign(ccdVec3Dot(&ACD, &AO)) == B_on_ACD;
AC_O = ccdSign(ccdVec3Dot(&ADB, &AO)) == C_on_ADB;
AD_O = ccdSign(ccdVec3Dot(&ABC, &AO)) == D_on_ABC;
if (AB_O && AC_O && AD_O){
// origin is in tetrahedron
return 1;
// rearrange simplex to triangle and call doSimplex3()
}else if (!AB_O){
// B is farthest from the origin among all of the tetrahedron's
// points, so remove it from the list and go on with the triangle
// case
// D and C are in place
ccdSimplexSet(simplex, 2, A);
ccdSimplexSetSize(simplex, 3);
}else if (!AC_O){
// C is farthest
ccdSimplexSet(simplex, 1, D);
ccdSimplexSet(simplex, 0, B);
ccdSimplexSet(simplex, 2, A);
ccdSimplexSetSize(simplex, 3);
}else{ // (!AD_O)
ccdSimplexSet(simplex, 0, C);
ccdSimplexSet(simplex, 1, B);
ccdSimplexSet(simplex, 2, A);
ccdSimplexSetSize(simplex, 3);
}
return doSimplex3(simplex, dir);
}
static int doSimplex(ccd_simplex_t *simplex, ccd_vec3_t *dir)
{
if (ccdSimplexSize(simplex) == 2){
// simplex contains segment only one segment
return doSimplex2(simplex, dir);
}else if (ccdSimplexSize(simplex) == 3){
// simplex contains triangle
return doSimplex3(simplex, dir);
}else{ // ccdSimplexSize(simplex) == 4
// tetrahedron - this is the only shape which can encapsule origin
// so doSimplex4() also contains test on it
return doSimplex4(simplex, dir);
}
}
_ccd_inline void tripleCross(const ccd_vec3_t *a, const ccd_vec3_t *b,
const ccd_vec3_t *c, ccd_vec3_t *d)
{
ccd_vec3_t e;
ccdVec3Cross(&e, a, b);
ccdVec3Cross(d, &e, c);
}
/** Transforms simplex to polytope. It is assumed that simplex has 4
* vertices! */
static int simplexToPolytope4(const void *obj1, const void *obj2,
const ccd_t *ccd,
ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest)
{
const ccd_support_t *a, *b, *c, *d;
int use_polytope3;
ccd_real_t dist;
ccd_pt_vertex_t *v[4];
ccd_pt_edge_t *e[6];
size_t i;
a = ccdSimplexPoint(simplex, 0);
b = ccdSimplexPoint(simplex, 1);
c = ccdSimplexPoint(simplex, 2);
d = ccdSimplexPoint(simplex, 3);
// check if origin lies on some of tetrahedron's face - if so use
// simplexToPolytope3()
use_polytope3 = 0;
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &c->v, NULL);
if (ccdIsZero(dist)){
use_polytope3 = 1;
}
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &c->v, &d->v, NULL);
if (ccdIsZero(dist)){
use_polytope3 = 1;
ccdSimplexSet(simplex, 1, c);
ccdSimplexSet(simplex, 2, d);
}
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &a->v, &b->v, &d->v, NULL);
if (ccdIsZero(dist)){
use_polytope3 = 1;
ccdSimplexSet(simplex, 2, d);
}
dist = ccdVec3PointTriDist2(ccd_vec3_origin, &b->v, &c->v, &d->v, NULL);
if (ccdIsZero(dist)){
use_polytope3 = 1;
ccdSimplexSet(simplex, 0, b);
ccdSimplexSet(simplex, 1, c);
ccdSimplexSet(simplex, 2, d);
}
if (use_polytope3){
ccdSimplexSetSize(simplex, 3);
return simplexToPolytope3(obj1, obj2, ccd, simplex, pt, nearest);
}
// no touching contact - simply create tetrahedron
for (i = 0; i < 4; i++){
v[i] = ccdPtAddVertex(pt, ccdSimplexPoint(simplex, i));
}
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
// ccdPtAdd*() functions return NULL either if the memory allocation
// failed of if any of the input pointers are NULL, so the bad
// allocation can be checked by the last calls of ccdPtAddFace()
// because the rest of the bad allocations eventually "bubble up" here
if (ccdPtAddFace(pt, e[0], e[1], e[2]) == NULL
|| ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL){
return -2;
}
return 0;
}
/** Transforms simplex to polytope, three vertices required */
static int simplexToPolytope3(const void *obj1, const void *obj2,
const ccd_t *ccd,
const ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest)
{
const ccd_support_t *a, *b, *c;
ccd_support_t d, d2;
ccd_vec3_t ab, ac, dir;
ccd_pt_vertex_t *v[5];
ccd_pt_edge_t *e[9];
ccd_real_t dist, dist2;
*nearest = NULL;
a = ccdSimplexPoint(simplex, 0);
b = ccdSimplexPoint(simplex, 1);
c = ccdSimplexPoint(simplex, 2);
// If only one triangle left from previous GJK run origin lies on this
// triangle. So it is necessary to expand triangle into two
// tetrahedrons connected with base (which is exactly abc triangle).
// get next support point in direction of normal of triangle
ccdVec3Sub2(&ab, &b->v, &a->v);
ccdVec3Sub2(&ac, &c->v, &a->v);
ccdVec3Cross(&dir, &ab, &ac);
__ccdSupport(obj1, obj2, &dir, ccd, &d);
dist = ccdVec3PointTriDist2(&d.v, &a->v, &b->v, &c->v, NULL);
// and second one take in opposite direction
ccdVec3Scale(&dir, -CCD_ONE);
__ccdSupport(obj1, obj2, &dir, ccd, &d2);
dist2 = ccdVec3PointTriDist2(&d2.v, &a->v, &b->v, &c->v, NULL);
// check if face isn't already on edge of minkowski sum and thus we
// have touching contact
if (ccdIsZero(dist) || ccdIsZero(dist2)){
v[0] = ccdPtAddVertex(pt, a);
v[1] = ccdPtAddVertex(pt, b);
v[2] = ccdPtAddVertex(pt, c);
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
*nearest = (ccd_pt_el_t *)ccdPtAddFace(pt, e[0], e[1], e[2]);
if (*nearest == NULL)
return -2;
return -1;
}
// form polyhedron
v[0] = ccdPtAddVertex(pt, a);
v[1] = ccdPtAddVertex(pt, b);
v[2] = ccdPtAddVertex(pt, c);
v[3] = ccdPtAddVertex(pt, &d);
v[4] = ccdPtAddVertex(pt, &d2);
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
e[2] = ccdPtAddEdge(pt, v[2], v[0]);
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
e[6] = ccdPtAddEdge(pt, v[4], v[0]);
e[7] = ccdPtAddEdge(pt, v[4], v[1]);
e[8] = ccdPtAddEdge(pt, v[4], v[2]);
if (ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL
|| ccdPtAddFace(pt, e[6], e[7], e[0]) == NULL
|| ccdPtAddFace(pt, e[7], e[8], e[1]) == NULL
|| ccdPtAddFace(pt, e[8], e[6], e[2]) == NULL){
return -2;
}
return 0;
}
/** Transforms simplex to polytope, two vertices required */
static int simplexToPolytope2(const void *obj1, const void *obj2,
const ccd_t *ccd,
const ccd_simplex_t *simplex,
ccd_pt_t *pt, ccd_pt_el_t **nearest)
{
const ccd_support_t *a, *b;
ccd_vec3_t ab, ac, dir;
ccd_support_t supp[4];
ccd_pt_vertex_t *v[6];
ccd_pt_edge_t *e[12];
size_t i;
int found;
a = ccdSimplexPoint(simplex, 0);
b = ccdSimplexPoint(simplex, 1);
// This situation is a bit tricky. If only one segment comes from
// previous run of GJK - it means that either this segment is on
// minkowski edge (and thus we have touch contact) or it it isn't and
// therefore segment is somewhere *inside* minkowski sum and it *must*
// be possible to fully enclose this segment with polyhedron formed by
// at least 8 triangle faces.
// get first support point (any)
found = 0;
for (i = 0; i < ccd_points_on_sphere_len; i++){
__ccdSupport(obj1, obj2, &ccd_points_on_sphere[i], ccd, &supp[0]);
if (!ccdVec3Eq(&a->v, &supp[0].v) && !ccdVec3Eq(&b->v, &supp[0].v)){
found = 1;
break;
}
}
if (!found)
goto simplexToPolytope2_touching_contact;
// get second support point in opposite direction than supp[0]
ccdVec3Copy(&dir, &supp[0].v);
ccdVec3Scale(&dir, -CCD_ONE);
__ccdSupport(obj1, obj2, &dir, ccd, &supp[1]);
if (ccdVec3Eq(&a->v, &supp[1].v) || ccdVec3Eq(&b->v, &supp[1].v))
goto simplexToPolytope2_touching_contact;
// next will be in direction of normal of triangle a,supp[0],supp[1]
ccdVec3Sub2(&ab, &supp[0].v, &a->v);
ccdVec3Sub2(&ac, &supp[1].v, &a->v);
ccdVec3Cross(&dir, &ab, &ac);
__ccdSupport(obj1, obj2, &dir, ccd, &supp[2]);
if (ccdVec3Eq(&a->v, &supp[2].v) || ccdVec3Eq(&b->v, &supp[2].v))
goto simplexToPolytope2_touching_contact;
// and last one will be in opposite direction
ccdVec3Scale(&dir, -CCD_ONE);
__ccdSupport(obj1, obj2, &dir, ccd, &supp[3]);
if (ccdVec3Eq(&a->v, &supp[3].v) || ccdVec3Eq(&b->v, &supp[3].v))
goto simplexToPolytope2_touching_contact;
goto simplexToPolytope2_not_touching_contact;
simplexToPolytope2_touching_contact:
v[0] = ccdPtAddVertex(pt, a);
v[1] = ccdPtAddVertex(pt, b);
*nearest = (ccd_pt_el_t *)ccdPtAddEdge(pt, v[0], v[1]);
if (*nearest == NULL)
return -2;
return -1;
simplexToPolytope2_not_touching_contact:
// form polyhedron
v[0] = ccdPtAddVertex(pt, a);
v[1] = ccdPtAddVertex(pt, &supp[0]);
v[2] = ccdPtAddVertex(pt, b);
v[3] = ccdPtAddVertex(pt, &supp[1]);
v[4] = ccdPtAddVertex(pt, &supp[2]);
v[5] = ccdPtAddVertex(pt, &supp[3]);
e[0] = ccdPtAddEdge(pt, v[0], v[1]);
e[1] = ccdPtAddEdge(pt, v[1], v[2]);
e[2] = ccdPtAddEdge(pt, v[2], v[3]);
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
e[4] = ccdPtAddEdge(pt, v[4], v[0]);
e[5] = ccdPtAddEdge(pt, v[4], v[1]);
e[6] = ccdPtAddEdge(pt, v[4], v[2]);
e[7] = ccdPtAddEdge(pt, v[4], v[3]);
e[8] = ccdPtAddEdge(pt, v[5], v[0]);
e[9] = ccdPtAddEdge(pt, v[5], v[1]);
e[10] = ccdPtAddEdge(pt, v[5], v[2]);
e[11] = ccdPtAddEdge(pt, v[5], v[3]);
if (ccdPtAddFace(pt, e[4], e[5], e[0]) == NULL
|| ccdPtAddFace(pt, e[5], e[6], e[1]) == NULL
|| ccdPtAddFace(pt, e[6], e[7], e[2]) == NULL
|| ccdPtAddFace(pt, e[7], e[4], e[3]) == NULL
|| ccdPtAddFace(pt, e[8], e[9], e[0]) == NULL
|| ccdPtAddFace(pt, e[9], e[10], e[1]) == NULL
|| ccdPtAddFace(pt, e[10], e[11], e[2]) == NULL
|| ccdPtAddFace(pt, e[11], e[8], e[3]) == NULL){
return -2;
}
return 0;
}
/** Expands polytope's tri by new vertex v. Triangle tri is replaced by
* three triangles each with one vertex in v. */
static int expandPolytope(ccd_pt_t *pt, ccd_pt_el_t *el,
const ccd_support_t *newv)
{
ccd_pt_vertex_t *v[5];
ccd_pt_edge_t *e[8];
ccd_pt_face_t *f[2];
// element can be either segment or triangle
if (el->type == CCD_PT_EDGE){
// In this case, segment should be replaced by new point.
// Simpliest case is when segment stands alone and in this case
// this segment is replaced by two other segments both connected to
// newv.
// Segment can be also connected to max two faces and in that case
// each face must be replaced by two other faces. To do this
// correctly it is necessary to have correctly ordered edges and
// vertices which is exactly what is done in following code.
//
ccdPtEdgeVertices((const ccd_pt_edge_t *)el, &v[0], &v[2]);
ccdPtEdgeFaces((ccd_pt_edge_t *)el, &f[0], &f[1]);
if (f[0]){
ccdPtFaceEdges(f[0], &e[0], &e[1], &e[2]);
if (e[0] == (ccd_pt_edge_t *)el){
e[0] = e[2];
}else if (e[1] == (ccd_pt_edge_t *)el){
e[1] = e[2];
}
ccdPtEdgeVertices(e[0], &v[1], &v[3]);
if (v[1] != v[0] && v[3] != v[0]){
e[2] = e[0];
e[0] = e[1];
e[1] = e[2];
if (v[1] == v[2])
v[1] = v[3];
}else{
if (v[1] == v[0])
v[1] = v[3];
}
if (f[1]){
ccdPtFaceEdges(f[1], &e[2], &e[3], &e[4]);
if (e[2] == (ccd_pt_edge_t *)el){
e[2] = e[4];
}else if (e[3] == (ccd_pt_edge_t *)el){
e[3] = e[4];
}
ccdPtEdgeVertices(e[2], &v[3], &v[4]);
if (v[3] != v[2] && v[4] != v[2]){
e[4] = e[2];
e[2] = e[3];
e[3] = e[4];
if (v[3] == v[0])
v[3] = v[4];
}else{
if (v[3] == v[2])
v[3] = v[4];
}
}
v[4] = ccdPtAddVertex(pt, newv);
ccdPtDelFace(pt, f[0]);
if (f[1]){
ccdPtDelFace(pt, f[1]);
ccdPtDelEdge(pt, (ccd_pt_edge_t *)el);
}
e[4] = ccdPtAddEdge(pt, v[4], v[2]);
e[5] = ccdPtAddEdge(pt, v[4], v[0]);
e[6] = ccdPtAddEdge(pt, v[4], v[1]);
if (f[1])
e[7] = ccdPtAddEdge(pt, v[4], v[3]);
if (ccdPtAddFace(pt, e[1], e[4], e[6]) == NULL
|| ccdPtAddFace(pt, e[0], e[6], e[5]) == NULL){
return -2;
}
if (f[1]){
if (ccdPtAddFace(pt, e[3], e[5], e[7]) == NULL
|| ccdPtAddFace(pt, e[4], e[7], e[2]) == NULL){
return -2;
}
}else{
if (ccdPtAddFace(pt, e[4], e[5], (ccd_pt_edge_t *)el) == NULL)
return -2;
}
}
}else{ // el->type == CCD_PT_FACE
// replace triangle by tetrahedron without base (base would be the
// triangle that will be removed)
// get triplet of surrounding edges and vertices of triangle face
ccdPtFaceEdges((const ccd_pt_face_t *)el, &e[0], &e[1], &e[2]);
ccdPtEdgeVertices(e[0], &v[0], &v[1]);
ccdPtEdgeVertices(e[1], &v[2], &v[3]);
// following code sorts edges to have e[0] between vertices 0-1,
// e[1] between 1-2 and e[2] between 2-0
if (v[2] != v[1] && v[3] != v[1]){
// swap e[1] and e[2]
e[3] = e[1];
e[1] = e[2];
e[2] = e[3];
}
if (v[3] != v[0] && v[3] != v[1])
v[2] = v[3];
// remove triangle face
ccdPtDelFace(pt, (ccd_pt_face_t *)el);
// expand triangle to tetrahedron
v[3] = ccdPtAddVertex(pt, newv);
e[3] = ccdPtAddEdge(pt, v[3], v[0]);
e[4] = ccdPtAddEdge(pt, v[3], v[1]);
e[5] = ccdPtAddEdge(pt, v[3], v[2]);
if (ccdPtAddFace(pt, e[3], e[4], e[0]) == NULL
|| ccdPtAddFace(pt, e[4], e[5], e[1]) == NULL
|| ccdPtAddFace(pt, e[5], e[3], e[2]) == NULL){
return -2;
}
}
return 0;
}
/** Finds next support point (and stores it in out argument).
* Returns 0 on success, -1 otherwise */
static int nextSupport(const void *obj1, const void *obj2, const ccd_t *ccd,
const ccd_pt_el_t *el,
ccd_support_t *out)
{
ccd_vec3_t *a, *b, *c;
ccd_real_t dist;
if (el->type == CCD_PT_VERTEX)
return -1;
// touch contact
if (ccdIsZero(el->dist))
return -1;
__ccdSupport(obj1, obj2, &el->witness, ccd, out);
// Compute dist of support point along element witness point direction
// so we can determine whether we expanded a polytope surrounding the
// origin a bit.
dist = ccdVec3Dot(&out->v, &el->witness);
if (dist - el->dist < ccd->epa_tolerance)
return -1;
if (el->type == CCD_PT_EDGE){
// fetch end points of edge
ccdPtEdgeVec3((ccd_pt_edge_t *)el, &a, &b);
// get distance from segment
dist = ccdVec3PointSegmentDist2(&out->v, a, b, NULL);
}else{ // el->type == CCD_PT_FACE
// fetch vertices of triangle face
ccdPtFaceVec3((ccd_pt_face_t *)el, &a, &b, &c);
// check if new point can significantly expand polytope
dist = ccdVec3PointTriDist2(&out->v, a, b, c, NULL);
}
if (dist < ccd->epa_tolerance)
return -1;
return 0;
}