/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* Bullet Continuous Collision Detection and Physics Library
* Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package com.bulletphysics.collision.narrowphase;
import java.util.Arrays;
import com.bulletphysics.BulletGlobals;
import com.bulletphysics.util.ObjectStackList;
import com.bulletphysics.collision.shapes.ConvexShape;
import com.bulletphysics.linearmath.MatrixUtil;
import com.bulletphysics.linearmath.QuaternionUtil;
import com.bulletphysics.linearmath.Transform;
import com.bulletphysics.linearmath.VectorUtil;
import com.bulletphysics.util.ArrayPool;
import cz.advel.stack.Stack;
import javax.vecmath.Matrix3f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;
/*
GJK-EPA collision solver by Nathanael Presson
Nov.2006
*/
/**
* GjkEpaSolver contributed under zlib by Nathanael Presson.
*
* @author jezek2
*/
public class GjkEpaSolver {
protected final ArrayPool<float[]> floatArrays = ArrayPool.get(float.class);
protected final ObjectStackList<Mkv> stackMkv = new ObjectStackList<Mkv>(Mkv.class);
protected final ObjectStackList<He> stackHe = new ObjectStackList<He>(He.class);
protected final ObjectStackList<Face> stackFace = new ObjectStackList<Face>(Face.class);
protected void pushStack() {
stackMkv.push();
stackHe.push();
stackFace.push();
}
protected void popStack() {
stackMkv.pop();
stackHe.pop();
stackFace.pop();
}
public enum ResultsStatus {
Separated, /* Shapes doesnt penetrate */
Penetrating, /* Shapes are penetrating */
GJK_Failed, /* GJK phase fail, no big issue, shapes are probably just 'touching' */
EPA_Failed, /* EPA phase fail, bigger problem, need to save parameters, and debug */
}
public static class Results {
public ResultsStatus status;
public final Vector3f[] witnesses/*[2]*/ = new Vector3f[] { new Vector3f(), new Vector3f() };
public final Vector3f normal = new Vector3f();
public float depth;
public int epa_iterations;
public int gjk_iterations;
}
////////////////////////////////////////////////////////////////////////////
private static final float cstInf = BulletGlobals.SIMD_INFINITY;
private static final float cstPi = BulletGlobals.SIMD_PI;
private static final float cst2Pi = BulletGlobals.SIMD_2_PI;
private static final int GJK_maxiterations = 128;
private static final int GJK_hashsize = 1 << 6;
private static final int GJK_hashmask = GJK_hashsize - 1;
private static final float GJK_insimplex_eps = 0.0001f;
private static final float GJK_sqinsimplex_eps = GJK_insimplex_eps * GJK_insimplex_eps;
private static final int EPA_maxiterations = 256;
private static final float EPA_inface_eps = 0.01f;
private static final float EPA_accuracy = 0.001f;
////////////////////////////////////////////////////////////////////////////
public static class Mkv {
public final Vector3f w = new Vector3f(); // Minkowski vertice
public final Vector3f r = new Vector3f(); // Ray
public void set(Mkv m) {
w.set(m.w);
r.set(m.r);
}
}
public static class He {
public final Vector3f v = new Vector3f();
public He n;
}
protected class GJK {
//protected final BulletStack stack = BulletStack.get();
//public btStackAlloc sa;
//public Block sablock;
public final He[] table = new He[GJK_hashsize];
public final Matrix3f[] wrotations/*[2]*/ = new Matrix3f[] { new Matrix3f(), new Matrix3f() };
public final Vector3f[] positions/*[2]*/ = new Vector3f[] { new Vector3f(), new Vector3f() };
public final ConvexShape[] shapes = new ConvexShape[2];
public final Mkv[] simplex = new Mkv[5];
public final Vector3f ray = new Vector3f();
public /*unsigned*/ int order;
public /*unsigned*/ int iterations;
public float margin;
public boolean failed;
{
for (int i=0; i<simplex.length; i++) simplex[i] = new Mkv();
}
public GJK() {
}
public GJK(/*StackAlloc psa,*/
Matrix3f wrot0, Vector3f pos0, ConvexShape shape0,
Matrix3f wrot1, Vector3f pos1, ConvexShape shape1) {
this(wrot0, pos0, shape0, wrot1, pos1, shape1, 0f);
}
public GJK(/*StackAlloc psa,*/
Matrix3f wrot0, Vector3f pos0, ConvexShape shape0,
Matrix3f wrot1, Vector3f pos1, ConvexShape shape1,
float pmargin) {
init(wrot0, pos0, shape0, wrot1, pos1, shape1, pmargin);
}
public void init(/*StackAlloc psa,*/
Matrix3f wrot0, Vector3f pos0, ConvexShape shape0,
Matrix3f wrot1, Vector3f pos1, ConvexShape shape1,
float pmargin) {
pushStack();
wrotations[0].set(wrot0);
positions[0].set(pos0);
shapes[0] = shape0;
wrotations[1].set(wrot1);
positions[1].set(pos1);
shapes[1] = shape1;
//sa =psa;
//sablock =sa->beginBlock();
margin = pmargin;
failed = false;
}
public void destroy() {
popStack();
}
// vdh: very dummy hash
public /*unsigned*/ int Hash(Vector3f v) {
int h = (int)(v.x * 15461) ^ (int)(v.y * 83003) ^ (int)(v.z * 15473);
return (h * 169639) & GJK_hashmask;
}
public Vector3f LocalSupport(Vector3f d, /*unsigned*/ int i, Vector3f out) {
Vector3f tmp = Stack.alloc(Vector3f.class);
MatrixUtil.transposeTransform(tmp, d, wrotations[i]);
shapes[i].localGetSupportingVertex(tmp, out);
wrotations[i].transform(out);
out.add(positions[i]);
return out;
}
public void Support(Vector3f d, Mkv v) {
v.r.set(d);
Vector3f tmp1 = LocalSupport(d, 0, Stack.alloc(Vector3f.class));
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.set(d);
tmp.negate();
Vector3f tmp2 = LocalSupport(tmp, 1, Stack.alloc(Vector3f.class));
v.w.sub(tmp1, tmp2);
v.w.scaleAdd(margin, d, v.w);
}
public boolean FetchSupport() {
int h = Hash(ray);
He e = table[h];
while (e != null) {
if (e.v.equals(ray)) {
--order;
return false;
}
else {
e = e.n;
}
}
//e = (He*)sa->allocate(sizeof(He));
//e = new He();
e = stackHe.get();
e.v.set(ray);
e.n = table[h];
table[h] = e;
Support(ray, simplex[++order]);
return (ray.dot(simplex[order].w) > 0);
}
public boolean SolveSimplex2(Vector3f ao, Vector3f ab) {
if (ab.dot(ao) >= 0) {
Vector3f cabo = Stack.alloc(Vector3f.class);
cabo.cross(ab, ao);
if (cabo.lengthSquared() > GJK_sqinsimplex_eps) {
ray.cross(cabo, ab);
}
else {
return true;
}
}
else {
order = 0;
simplex[0].set(simplex[1]);
ray.set(ao);
}
return (false);
}
public boolean SolveSimplex3(Vector3f ao, Vector3f ab, Vector3f ac)
{
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.cross(ab, ac);
return (SolveSimplex3a(ao,ab,ac,tmp));
}
public boolean SolveSimplex3a(Vector3f ao, Vector3f ab, Vector3f ac, Vector3f cabc) {
// TODO: optimize
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.cross(cabc, ab);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
tmp2.cross(cabc, ac);
if (tmp.dot(ao) < -GJK_insimplex_eps) {
order = 1;
simplex[0].set(simplex[1]);
simplex[1].set(simplex[2]);
return SolveSimplex2(ao, ab);
}
else if (tmp2.dot(ao) > +GJK_insimplex_eps) {
order = 1;
simplex[1].set(simplex[2]);
return SolveSimplex2(ao, ac);
}
else {
float d = cabc.dot(ao);
if (Math.abs(d) > GJK_insimplex_eps) {
if (d > 0) {
ray.set(cabc);
}
else {
ray.negate(cabc);
Mkv swapTmp = new Mkv();
swapTmp.set(simplex[0]);
simplex[0].set(simplex[1]);
simplex[1].set(swapTmp);
}
return false;
}
else {
return true;
}
}
}
public boolean SolveSimplex4(Vector3f ao, Vector3f ab, Vector3f ac, Vector3f ad) {
// TODO: optimize
Vector3f crs = Stack.alloc(Vector3f.class);
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.cross(ab, ac);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
tmp2.cross(ac, ad);
Vector3f tmp3 = Stack.alloc(Vector3f.class);
tmp3.cross(ad, ab);
if (tmp.dot(ao) > GJK_insimplex_eps) {
crs.set(tmp);
order = 2;
simplex[0].set(simplex[1]);
simplex[1].set(simplex[2]);
simplex[2].set(simplex[3]);
return SolveSimplex3a(ao, ab, ac, crs);
}
else if (tmp2.dot(ao) > GJK_insimplex_eps) {
crs.set(tmp2);
order = 2;
simplex[2].set(simplex[3]);
return SolveSimplex3a(ao, ac, ad, crs);
}
else if (tmp3.dot(ao) > GJK_insimplex_eps) {
crs.set(tmp3);
order = 2;
simplex[1].set(simplex[0]);
simplex[0].set(simplex[2]);
simplex[2].set(simplex[3]);
return SolveSimplex3a(ao, ad, ab, crs);
}
else {
return (true);
}
}
public boolean SearchOrigin() {
Vector3f tmp = Stack.alloc(Vector3f.class);
tmp.set(1f, 0f, 0f);
return SearchOrigin(tmp);
}
public boolean SearchOrigin(Vector3f initray) {
Vector3f tmp1 = Stack.alloc(Vector3f.class);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
Vector3f tmp3 = Stack.alloc(Vector3f.class);
Vector3f tmp4 = Stack.alloc(Vector3f.class);
iterations = 0;
order = -1;
failed = false;
ray.set(initray);
ray.normalize();
Arrays.fill(table, null);
FetchSupport();
ray.negate(simplex[0].w);
for (; iterations < GJK_maxiterations; ++iterations) {
float rl = ray.length();
ray.scale(1f / (rl > 0f ? rl : 1f));
if (FetchSupport()) {
boolean found = false;
switch (order) {
case 1: {
tmp1.negate(simplex[1].w);
tmp2.sub(simplex[0].w, simplex[1].w);
found = SolveSimplex2(tmp1, tmp2);
break;
}
case 2: {
tmp1.negate(simplex[2].w);
tmp2.sub(simplex[1].w, simplex[2].w);
tmp3.sub(simplex[0].w, simplex[2].w);
found = SolveSimplex3(tmp1, tmp2, tmp3);
break;
}
case 3: {
tmp1.negate(simplex[3].w);
tmp2.sub(simplex[2].w, simplex[3].w);
tmp3.sub(simplex[1].w, simplex[3].w);
tmp4.sub(simplex[0].w, simplex[3].w);
found = SolveSimplex4(tmp1, tmp2, tmp3, tmp4);
break;
}
}
if (found) {
return true;
}
}
else {
return false;
}
}
failed = true;
return false;
}
public boolean EncloseOrigin() {
Vector3f tmp = Stack.alloc(Vector3f.class);
Vector3f tmp1 = Stack.alloc(Vector3f.class);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
switch (order) {
// Point
case 0:
break;
// Line
case 1: {
Vector3f ab = Stack.alloc(Vector3f.class);
ab.sub(simplex[1].w, simplex[0].w);
Vector3f[] b = new Vector3f[] { Stack.alloc(Vector3f.class), Stack.alloc(Vector3f.class), Stack.alloc(Vector3f.class) };
b[0].set(1f, 0f, 0f);
b[1].set(0f, 1f, 0f);
b[2].set(0f, 0f, 1f);
b[0].cross(ab, b[0]);
b[1].cross(ab, b[1]);
b[2].cross(ab, b[2]);
float m[] = new float[] { b[0].lengthSquared(), b[1].lengthSquared(), b[2].lengthSquared() };
Quat4f tmpQuat = Stack.alloc(Quat4f.class);
tmp.normalize(ab);
QuaternionUtil.setRotation(tmpQuat, tmp, cst2Pi / 3f);
Matrix3f r = Stack.alloc(Matrix3f.class);
MatrixUtil.setRotation(r, tmpQuat);
Vector3f w = Stack.alloc(Vector3f.class);
w.set(b[m[0] > m[1] ? m[0] > m[2] ? 0 : 2 : m[1] > m[2] ? 1 : 2]);
tmp.normalize(w);
Support(tmp, simplex[4]); r.transform(w);
tmp.normalize(w);
Support(tmp, simplex[2]); r.transform(w);
tmp.normalize(w);
Support(tmp, simplex[3]); r.transform(w);
order = 4;
return (true);
}
// Triangle
case 2: {
tmp1.sub(simplex[1].w, simplex[0].w);
tmp2.sub(simplex[2].w, simplex[0].w);
Vector3f n = Stack.alloc(Vector3f.class);
n.cross(tmp1, tmp2);
n.normalize();
Support(n, simplex[3]);
tmp.negate(n);
Support(tmp, simplex[4]);
order = 4;
return (true);
}
// Tetrahedron
case 3:
return (true);
// Hexahedron
case 4:
return (true);
}
return (false);
}
}
////////////////////////////////////////////////////////////////////////////
private static int[] mod3 = new int[] { 0, 1, 2, 0, 1 };
private static final int[][] tetrahedron_fidx/*[4][3]*/ = new int[][] {{2,1,0},{3,0,1},{3,1,2},{3,2,0}};
private static final int[][] tetrahedron_eidx/*[6][4]*/ = new int[][] {{0,0,2,1},{0,1,1,1},{0,2,3,1},{1,0,3,2},{2,0,1,2},{3,0,2,2}};
private static final int[][] hexahedron_fidx/*[6][3]*/ = new int[][] {{2,0,4},{4,1,2},{1,4,0},{0,3,1},{0,2,3},{1,3,2}};
private static final int[][] hexahedron_eidx/*[9][4]*/ = new int[][] {{0,0,4,0},{0,1,2,1},{0,2,1,2},{1,1,5,2},{1,0,2,0},{2,2,3,2},{3,1,5,0},{3,0,4,2},{5,1,4,1}};
public static class Face {
public final Mkv[] v = new Mkv[3];
public final Face[] f = new Face[3];
public final int[] e = new int[3];
public final Vector3f n = new Vector3f();
public float d;
public int mark;
public Face prev;
public Face next;
}
protected class EPA {
//protected final BulletStack stack = BulletStack.get();
public GJK gjk;
//public btStackAlloc* sa;
public Face root;
public int nfaces;
public int iterations;
public final Vector3f[][] features = new Vector3f[2][3];
public final Vector3f[] nearest/*[2]*/ = new Vector3f[] { new Vector3f(), new Vector3f() };
public final Vector3f normal = new Vector3f();
public float depth;
public boolean failed;
{
for (int i=0; i<features.length; i++) {
for (int j=0; j<features[i].length; j++) {
features[i][j] = new Vector3f();
}
}
}
public EPA(GJK pgjk) {
gjk = pgjk;
//sa = pgjk->sa;
}
public Vector3f GetCoordinates(Face face, Vector3f out) {
Vector3f tmp = Stack.alloc(Vector3f.class);
Vector3f tmp1 = Stack.alloc(Vector3f.class);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
Vector3f o = Stack.alloc(Vector3f.class);
o.scale(-face.d, face.n);
float[] a = floatArrays.getFixed(3);
tmp1.sub(face.v[0].w, o);
tmp2.sub(face.v[1].w, o);
tmp.cross(tmp1, tmp2);
a[0] = tmp.length();
tmp1.sub(face.v[1].w, o);
tmp2.sub(face.v[2].w, o);
tmp.cross(tmp1, tmp2);
a[1] = tmp.length();
tmp1.sub(face.v[2].w, o);
tmp2.sub(face.v[0].w, o);
tmp.cross(tmp1, tmp2);
a[2] = tmp.length();
float sm = a[0] + a[1] + a[2];
out.set(a[1], a[2], a[0]);
out.scale(1f / (sm > 0f ? sm : 1f));
floatArrays.release(a);
return out;
}
public Face FindBest() {
Face bf = null;
if (root != null) {
Face cf = root;
float bd = cstInf;
do {
if (cf.d < bd) {
bd = cf.d;
bf = cf;
}
}
while (null != (cf = cf.next));
}
return bf;
}
public boolean Set(Face f, Mkv a, Mkv b, Mkv c) {
Vector3f tmp1 = Stack.alloc(Vector3f.class);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
Vector3f tmp3 = Stack.alloc(Vector3f.class);
Vector3f nrm = Stack.alloc(Vector3f.class);
tmp1.sub(b.w, a.w);
tmp2.sub(c.w, a.w);
nrm.cross(tmp1, tmp2);
float len = nrm.length();
tmp1.cross(a.w, b.w);
tmp2.cross(b.w, c.w);
tmp3.cross(c.w, a.w);
boolean valid = (tmp1.dot(nrm) >= -EPA_inface_eps) &&
(tmp2.dot(nrm) >= -EPA_inface_eps) &&
(tmp3.dot(nrm) >= -EPA_inface_eps);
f.v[0] = a;
f.v[1] = b;
f.v[2] = c;
f.mark = 0;
f.n.scale(1f / (len > 0f ? len : cstInf), nrm);
f.d = Math.max(0, -f.n.dot(a.w));
return valid;
}
public Face NewFace(Mkv a, Mkv b, Mkv c) {
//Face pf = new Face();
Face pf = stackFace.get();
if (Set(pf, a, b, c)) {
if (root != null) {
root.prev = pf;
}
pf.prev = null;
pf.next = root;
root = pf;
++nfaces;
}
else {
pf.prev = pf.next = null;
}
return (pf);
}
public void Detach(Face face) {
if (face.prev != null || face.next != null) {
--nfaces;
if (face == root) {
root = face.next;
root.prev = null;
}
else {
if (face.next == null) {
face.prev.next = null;
}
else {
face.prev.next = face.next;
face.next.prev = face.prev;
}
}
face.prev = face.next = null;
}
}
public void Link(Face f0, int e0, Face f1, int e1) {
f0.f[e0] = f1; f1.e[e1] = e0;
f1.f[e1] = f0; f0.e[e0] = e1;
}
public Mkv Support(Vector3f w) {
//Mkv v = new Mkv();
Mkv v = stackMkv.get();
gjk.Support(w, v);
return v;
}
public int BuildHorizon(int markid, Mkv w, Face f, int e, Face[] cf, Face[] ff) {
int ne = 0;
if (f.mark != markid) {
int e1 = mod3[e + 1];
if ((f.n.dot(w.w) + f.d) > 0) {
Face nf = NewFace(f.v[e1], f.v[e], w);
Link(nf, 0, f, e);
if (cf[0] != null) {
Link(cf[0], 1, nf, 2);
}
else {
ff[0] = nf;
}
cf[0] = nf;
ne = 1;
}
else {
int e2 = mod3[e + 2];
Detach(f);
f.mark = markid;
ne += BuildHorizon(markid, w, f.f[e1], f.e[e1], cf, ff);
ne += BuildHorizon(markid, w, f.f[e2], f.e[e2], cf, ff);
}
}
return (ne);
}
public float EvaluatePD() {
return EvaluatePD(EPA_accuracy);
}
public float EvaluatePD(float accuracy) {
pushStack();
try {
Vector3f tmp = Stack.alloc(Vector3f.class);
//btBlock* sablock = sa->beginBlock();
Face bestface = null;
int markid = 1;
depth = -cstInf;
normal.set(0f, 0f, 0f);
root = null;
nfaces = 0;
iterations = 0;
failed = false;
/* Prepare hull */
if (gjk.EncloseOrigin()) {
//const U* pfidx = 0;
int[][] pfidx_ptr = null;
int pfidx_index = 0;
int nfidx = 0;
//const U* peidx = 0;
int[][] peidx_ptr = null;
int peidx_index = 0;
int neidx = 0;
Mkv[] basemkv = new Mkv[5];
Face[] basefaces = new Face[6];
switch (gjk.order) {
// Tetrahedron
case 3:
{
//pfidx=(const U*)fidx;
pfidx_ptr = tetrahedron_fidx;
pfidx_index = 0;
nfidx = 4;
//peidx=(const U*)eidx;
peidx_ptr = tetrahedron_eidx;
peidx_index = 0;
neidx = 6;
}
break;
// Hexahedron
case 4:
{
//pfidx=(const U*)fidx;
pfidx_ptr = hexahedron_fidx;
pfidx_index = 0;
nfidx = 6;
//peidx=(const U*)eidx;
peidx_ptr = hexahedron_eidx;
peidx_index = 0;
neidx = 9;
}
break;
}
int i;
for (i = 0; i <= gjk.order; ++i) {
basemkv[i] = new Mkv();
basemkv[i].set(gjk.simplex[i]);
}
for (i = 0; i < nfidx; ++i, pfidx_index++) {
basefaces[i] = NewFace(basemkv[pfidx_ptr[pfidx_index][0]], basemkv[pfidx_ptr[pfidx_index][1]], basemkv[pfidx_ptr[pfidx_index][2]]);
}
for (i = 0; i < neidx; ++i, peidx_index++) {
Link(basefaces[peidx_ptr[peidx_index][0]], peidx_ptr[peidx_index][1], basefaces[peidx_ptr[peidx_index][2]], peidx_ptr[peidx_index][3]);
}
}
if (0 == nfaces) {
//sa->endBlock(sablock);
return (depth);
}
/* Expand hull */
for (; iterations < EPA_maxiterations; ++iterations) {
Face bf = FindBest();
if (bf != null) {
tmp.negate(bf.n);
Mkv w = Support(tmp);
float d = bf.n.dot(w.w) + bf.d;
bestface = bf;
if (d < -accuracy) {
Face[] cf = new Face[]{null};
Face[] ff = new Face[]{null};
int nf = 0;
Detach(bf);
bf.mark = ++markid;
for (int i = 0; i < 3; ++i) {
nf += BuildHorizon(markid, w, bf.f[i], bf.e[i], cf, ff);
}
if (nf <= 2) {
break;
}
Link(cf[0], 1, ff[0], 2);
}
else {
break;
}
}
else {
break;
}
}
/* Extract contact */
if (bestface != null) {
Vector3f b = GetCoordinates(bestface, Stack.alloc(Vector3f.class));
normal.set(bestface.n);
depth = Math.max(0, bestface.d);
for (int i = 0; i < 2; ++i) {
float s = i != 0 ? -1f : 1f;
for (int j = 0; j < 3; ++j) {
tmp.scale(s, bestface.v[j].r);
gjk.LocalSupport(tmp, i, features[i][j]);
}
}
Vector3f tmp1 = Stack.alloc(Vector3f.class);
Vector3f tmp2 = Stack.alloc(Vector3f.class);
Vector3f tmp3 = Stack.alloc(Vector3f.class);
tmp1.scale(b.x, features[0][0]);
tmp2.scale(b.y, features[0][1]);
tmp3.scale(b.z, features[0][2]);
VectorUtil.add(nearest[0], tmp1, tmp2, tmp3);
tmp1.scale(b.x, features[1][0]);
tmp2.scale(b.y, features[1][1]);
tmp3.scale(b.z, features[1][2]);
VectorUtil.add(nearest[1], tmp1, tmp2, tmp3);
}
else {
failed = true;
}
//sa->endBlock(sablock);
return (depth);
}
finally {
popStack();
}
}
}
////////////////////////////////////////////////////////////////////////////
private GJK gjk = new GJK();
public boolean collide(ConvexShape shape0, Transform wtrs0,
ConvexShape shape1, Transform wtrs1,
float radialmargin/*,
btStackAlloc* stackAlloc*/,
Results results) {
// Initialize
results.witnesses[0].set(0f, 0f, 0f);
results.witnesses[1].set(0f, 0f, 0f);
results.normal.set(0f, 0f, 0f);
results.depth = 0;
results.status = ResultsStatus.Separated;
results.epa_iterations = 0;
results.gjk_iterations = 0;
/* Use GJK to locate origin */
gjk.init(/*stackAlloc,*/
wtrs0.basis, wtrs0.origin, shape0,
wtrs1.basis, wtrs1.origin, shape1,
radialmargin + EPA_accuracy);
try {
boolean collide = gjk.SearchOrigin();
results.gjk_iterations = gjk.iterations + 1;
if (collide) {
/* Then EPA for penetration depth */
EPA epa = new EPA(gjk);
float pd = epa.EvaluatePD();
results.epa_iterations = epa.iterations + 1;
if (pd > 0) {
results.status = ResultsStatus.Penetrating;
results.normal.set(epa.normal);
results.depth = pd;
results.witnesses[0].set(epa.nearest[0]);
results.witnesses[1].set(epa.nearest[1]);
return (true);
}
else {
if (epa.failed) {
results.status = ResultsStatus.EPA_Failed;
}
}
}
else {
if (gjk.failed) {
results.status = ResultsStatus.GJK_Failed;
}
}
return (false);
}
finally {
gjk.destroy();
}
}
}