/*
|
* 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 implements java.io.Serializable {
|
|
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 implements java.io.Serializable {
|
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 implements java.io.Serializable {
|
public final Vector3f v = new Vector3f();
|
public He n;
|
}
|
|
protected class GJK implements java.io.Serializable {
|
//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 implements java.io.Serializable {
|
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 implements java.io.Serializable {
|
//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();
|
}
|
}
|
|
}
|