Cas*_*yle 5 javascript collision-detection
我正在尝试在 3D 空间中实现 EPA 算法,但我似乎发现了一种凸单纯形可以变成凹单纯形的情况。
考虑这个单纯形:
因为很难看出这里发生了什么,所以它是动画的:
原点是红、绿、蓝轴助手。没有边缘连接的白色球体代表我需要将多面体扩展到下一个的点。5 个黄色箭头是应删除的面的法线,因为它们与新点的原点方向相同。有些面看起来并不在同一方向,但我已经验证它们是作为面法线和新点的点积是:
所以 .gif 右侧的那两个面只是同一方向的大麦。
好的,EPA 算法要求删除这些面孔:
然后使用我们删除的面中的剩余边缘构造到新点的新面。但现在您可以看到凸单纯形已变成凹单纯形:
这显然是不对的,但我不确定我错在哪里。对我来说,就像我删除了不应该有的面,但这些面与新点的方向相同。
这是我的代码:
var EPA = function(aWorldVerts, bWorldVerts, simplex) {
var simplexFaces = [{a: 0, b: 1, c: 2},
{a: 0, b: 1, c: 3},
{a: 0, b: 2, c: 3},
{a: 1, b: 2, c: 3}];
var ret = null;
while(true) {
var face = findClosestFace(simplex, simplexFaces);
var point = support(aWorldVerts, bWorldVerts, face.norm);
var dist = point.clone().dot(face.norm);
if(dist - face.dist < 0.00001) {
ret = {axis: face.norm, dist: dist};
break;
}
simplex.push(point);
reconstruct(simplex, simplexFaces, point);
}
return ret;
}
var reconstruct = function(simplex, simplexFaces, extendPoint) {
//I do realize that this function can be done more efficietly
var removalFaces = [];
for(var i = 0; i < simplexFaces.length; i++) {
var face = simplexFaces[i];
var ab = simplex[face.b].clone().sub(simplex[face.a]);
var ac = simplex[face.c].clone().sub(simplex[face.a]);
var norm = ab.cross(ac).normalize();
var a0 = new THREE.Vector3().sub(simplex[face.a]);
if(a0.dot(norm) > 0)
norm.negate();
if(extendPoint.clone().dot(norm) > 0) {
removalFaces.push(i);
}
}
//get the edges that are not shared between the faces that should be removed
var edges = [];
for(var i = 0; i < removalFaces.length; i++) {
var face = simplexFaces[removalFaces[i]];
var edgeAB = {a: face.a, b: face.b};
var edgeAC = {a: face.a, b: face.c};
var edgeBC = {a: face.b, b: face.c};
var k = edgeInEdges(edges, edgeAB);
if(k != -1)
edges.splice(k, 1);
else
edges.push(edgeAB);
k = edgeInEdges(edges, edgeAC);
if(k != -1)
edges.splice(k, 1);
else
edges.push(edgeAC);
k = edgeInEdges(edges, edgeBC);
if(k != -1)
edges.splice(k, 1);
else
edges.push(edgeBC);
}
//remove the faces from the polytope
for(var i = removalFaces.length - 1; i >= 0; i--) {
simplexFaces.splice(removalFaces[i], 1);
}
//form new faces with the edges and new point
for(var i = 0; i < edges.length; i++) {
simplexFaces.push({a: edges[i].a, b: edges[i].b, c: simplex.length - 1});
}
}
var edgeInEdges = function(edges, edge) {
for(var i = 0; i < edges.length; i++) {
if(edges[i].a == edge.a && edges[i].b == edge.b)
return i;
}
return -1;
}
var findClosestFace = function(simplex, simplexFaces) {
var closest = {dist: Infinity};
for(var i = 0; i < simplexFaces.length; i++) {
var face = simplexFaces[i];
var ab = simplex[face.b].clone().sub(simplex[face.a]);
var ac = simplex[face.c].clone().sub(simplex[face.a]);
var norm = ab.cross(ac).normalize();
var a0 = new THREE.Vector3().sub(simplex[face.a]);
if(a0.dot(norm) > 0)
norm.negate();
var dist = simplex[face.a].clone().dot(norm);
if(dist < closest.dist) {
closest = {index: i, dist: dist, norm: norm, a: face.a, b: face.b, c: face.c};
}
}
return closest;
}
var support = function(aVerts, bVerts, dir) {
a = getFurthestPointInDirection(aVerts, dir);
b = getFurthestPointInDirection(bVerts, dir.clone().negate());
return a.clone().sub(b);
}
var getFurthestPointInDirection = function(verts, dir) {
var index = 0;
var maxDot = verts[index].clone().dot(dir.clone().normalize());
for(var i = 1; i < verts.length; i++) {
var dot = verts[i].clone().dot(dir.clone().normalize());
if(dot > maxDot) {
maxDot = dot;
index = i;
}
}
return verts[index];
}
Run Code Online (Sandbox Code Playgroud)
我知道支持功能以及 和findClosestFace()都能正常工作edgeInEdges()。另外,这应该不重要,但这是使用 Three.js 和 Javascript 实现的。也许我只是从根本上误解了算法的工作原理?
我做错了什么以及如何解决?
经过几个小时的调试后我发现了我的问题。通过检查面的法线是否与新点的原点方向相同,找不到在将多面体延伸到新点之前要删除的面。关于这个主题的许多文章都说你想要删除新点可以“看到”的面,我认为这意味着法线处于同一方向。情况并非如此,因为您很可能在与新点的原点相同的方向上拥有一个法线面,但是该点无法“看到”该面,因此删除它会出现问题,这就是我正在做的事情。您基本上需要想象您的相机正好位于新点的位置,环顾四周,您可以看到的任何面孔都应该被删除。
要检查新点是否可以“看到”给定的面,您需要形成从所述面的顶点到新点的向量,并检查该面与面法线的点积。所以我在函数中替换if(extendPoint.clone().dot(norm) > 0)为,现在它可以工作了。if(norm.clone().dot(extendPoint.clone().sub(simplex[face.a])) > 0)reconstruct()