improve gjk a bit to reduce some lag

This commit is contained in:
RandomityGuy 2022-12-17 23:23:32 +05:30
parent 81bf76bfb2
commit 97d9db8295
7 changed files with 113 additions and 56 deletions

View file

@ -916,7 +916,7 @@ class Marble extends GameObject {
rollVolume = 0; rollVolume = 0;
var slipVolume = 0.0; var slipVolume = 0.0;
if (slipAmount > 0) { if (slipAmount > 1e-4) {
slipVolume = slipAmount / 5; slipVolume = slipAmount / 5;
if (slipVolume > 1) if (slipVolume > 1)
slipVolume = 1; slipVolume = 1;
@ -1337,7 +1337,7 @@ class Marble extends GameObject {
sph.position = finalPos; sph.position = finalPos;
sph.radius = _radius; sph.radius = _radius;
var pt = GJK.gjk(sph, chull); var pt = GJK.gjk(sph, chull).epa;
while (pt != null) { while (pt != null) {
if (pt.lengthSq() < 0.0001) { if (pt.lengthSq() < 0.0001) {
@ -1346,7 +1346,7 @@ class Marble extends GameObject {
trace('Separating Vector Len: ${pt.length()}'); trace('Separating Vector Len: ${pt.length()}');
finalPos = finalPos.sub(pt); finalPos = finalPos.sub(pt);
sph.position = finalPos; sph.position = finalPos;
pt = GJK.gjk(sph, chull); pt = GJK.gjk(sph, chull).epa;
} }
// if (pt != null) { // if (pt != null) {

View file

@ -1518,7 +1518,7 @@ class MarbleWorld extends Scheduler {
if (this.finishTime == null) { if (this.finishTime == null) {
if (spherebounds.collide(this.endPad.finishBounds)) { if (spherebounds.collide(this.endPad.finishBounds)) {
if (collision.gjk.GJK.gjk(gjkSphere, this.endPad.finishCollider) != null) { if (collision.gjk.GJK.gjk(gjkSphere, this.endPad.finishCollider, false) != null) {
if (!endPad.inFinish) { if (!endPad.inFinish) {
touchFinish(); touchFinish();
endPad.inFinish = true; endPad.inFinish = true;
@ -1527,6 +1527,9 @@ class MarbleWorld extends Scheduler {
if (endPad.inFinish) if (endPad.inFinish)
endPad.inFinish = false; endPad.inFinish = false;
} }
} else {
if (endPad.inFinish)
endPad.inFinish = false;
} }
} }
} }

View file

@ -38,7 +38,7 @@ class CollisionHull extends CollisionEntity {
newTform.setPosition(newpos); newTform.setPosition(newpos);
hull.setTransform(newTform); hull.setTransform(newTform);
var pt = GJK.gjk(sph, this.hull); var pt = GJK.gjk(sph, this.hull).epa;
if (pt != null) { if (pt != null) {
var cinfo = new CollisionInfo(); var cinfo = new CollisionInfo();
cinfo.normal = pt.normalized(); cinfo.normal = pt.normalized();

View file

@ -0,0 +1,23 @@
package collision.gjk;
import h3d.Vector;
@:publicFields
class Cylinder implements GJKShape {
var p1:Vector;
var p2:Vector;
var radius:Float;
public function new() {}
public function getCenter():Vector {
return p1.add(p2).multiply(0.5);
}
public function support(dir:Vector) {
var axis = p2.sub(p1);
var v = axis.dot(dir) > 0 ? p2 : p1;
var rejection = dir.sub(axis.multiply(dir.dot(axis) / (axis.dot(axis) * dir.dot(dir)))).normalized().multiply(radius);
return v.add(rejection);
}
}

View file

@ -10,7 +10,10 @@ class GJK {
public static var maxEpaLooseEdges = 64; public static var maxEpaLooseEdges = 64;
public static var maxEpaIterations = 64; public static var maxEpaIterations = 64;
public static function gjk(s1:GJKShape, s2:GJKShape) { static var epaFaces:Array<Array<Vector>>;
static var loose_edges:Array<Array<Vector>>;
public static function gjk(s1:GJKShape, s2:GJKShape, doEpa:Bool = true) {
var searchDir = s1.getCenter().sub(s2.getCenter()); var searchDir = s1.getCenter().sub(s2.getCenter());
var a = new Vector(); var a = new Vector();
var b = new Vector(); var b = new Vector();
@ -21,7 +24,10 @@ class GJK {
searchDir = c.multiply(-1); searchDir = c.multiply(-1);
b = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1))); b = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1)));
if (b.dot(searchDir) < 0) if (b.dot(searchDir) < 0)
return null; return {
result: false,
epa: null
};
searchDir = c.sub(b).cross(b.multiply(-1)).cross(c.sub(b)); searchDir = c.sub(b).cross(b.multiply(-1)).cross(c.sub(b));
if (searchDir.length() == 0) { if (searchDir.length() == 0) {
@ -35,7 +41,10 @@ class GJK {
for (i in 0...maxIterations) { for (i in 0...maxIterations) {
a = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1))); a = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1)));
if (a.dot(searchDir) < 0) { if (a.dot(searchDir) < 0) {
return null; return {
result: false,
epa: null
};
} }
simpDim++; simpDim++;
@ -83,44 +92,57 @@ class GJK {
b = a; b = a;
searchDir = adb; searchDir = adb;
} else { } else {
return epa(a, b, c, d, s1, s2); if (doEpa)
return {
result: true,
epa: epa(a, b, c, d, s1, s2)
};
return {
result: true,
epa: null
};
} }
} }
} }
return null; return {
result: false,
epa: null
};
} }
public static function epa(a:Vector, b:Vector, c:Vector, d:Vector, s1:GJKShape, s2:GJKShape) { public static function epa(a:Vector, b:Vector, c:Vector, d:Vector, s1:GJKShape, s2:GJKShape) {
var faces = []; if (epaFaces == null) {
epaFaces = [];
for (i in 0...maxEpaFaces) for (i in 0...maxEpaFaces)
faces.push([new Vector(), new Vector(), new Vector(), new Vector()]); epaFaces.push([new Vector(), new Vector(), new Vector(), new Vector()]);
}
faces[0][0] = a; epaFaces[0][0] = a;
faces[0][1] = b; epaFaces[0][1] = b;
faces[0][2] = c; epaFaces[0][2] = c;
faces[0][3] = b.sub(a).cross(c.sub(a)).normalized(); // ABC epaFaces[0][3] = b.sub(a).cross(c.sub(a)).normalized(); // ABC
faces[1][0] = a; epaFaces[1][0] = a;
faces[1][1] = c; epaFaces[1][1] = c;
faces[1][2] = d; epaFaces[1][2] = d;
faces[1][3] = c.sub(a).cross(d.sub(a)).normalized(); epaFaces[1][3] = c.sub(a).cross(d.sub(a)).normalized();
faces[2][0] = a; epaFaces[2][0] = a;
faces[2][1] = d; epaFaces[2][1] = d;
faces[2][2] = b; epaFaces[2][2] = b;
faces[2][3] = d.sub(a).cross(b.sub(a)).normalized(); epaFaces[2][3] = d.sub(a).cross(b.sub(a)).normalized();
faces[3][0] = b; epaFaces[3][0] = b;
faces[3][1] = d; epaFaces[3][1] = d;
faces[3][2] = c; epaFaces[3][2] = c;
faces[3][3] = d.sub(b).cross(c.sub(b)).normalized(); epaFaces[3][3] = d.sub(b).cross(c.sub(b)).normalized();
var numFaces = 4; var numFaces = 4;
var closestFace = 0; var closestFace = 0;
for (iteration in 0...maxEpaIterations) { for (iteration in 0...maxEpaIterations) {
// Find face that's closest to origin // Find face that's closest to origin
var min_dist = faces[0][0].dot(faces[0][3]); var min_dist = epaFaces[0][0].dot(epaFaces[0][3]);
closestFace = 0; closestFace = 0;
for (i in 1...numFaces) { for (i in 1...numFaces) {
var dist = faces[i][0].dot(faces[i][3]); var dist = epaFaces[i][0].dot(epaFaces[i][3]);
if (dist < min_dist) { if (dist < min_dist) {
min_dist = dist; min_dist = dist;
closestFace = i; closestFace = i;
@ -128,28 +150,30 @@ class GJK {
} }
// search normal to face that's closest to origin // search normal to face that's closest to origin
var search_dir = faces[closestFace][3]; var search_dir = epaFaces[closestFace][3];
var p = s2.support(search_dir).sub(s1.support(search_dir.multiply(-1))); var p = s2.support(search_dir).sub(s1.support(search_dir.multiply(-1)));
if (p.dot(search_dir) - min_dist < epaTolerance) { if (p.dot(search_dir) - min_dist < epaTolerance) {
// Convergence (new point is not significantly further from origin) // Convergence (new point is not significantly further from origin)
return faces[closestFace][3].multiply(p.dot(search_dir)); // dot vertex with normal to resolve collision along normal! return epaFaces[closestFace][3].multiply(p.dot(search_dir)); // dot vertex with normal to resolve collision along normal!
} }
var loose_edges = []; if (loose_edges == null) {
loose_edges = [];
for (i in 0...maxEpaLooseEdges) for (i in 0...maxEpaLooseEdges)
loose_edges.push([new Vector(), new Vector()]); loose_edges.push([new Vector(), new Vector()]);
}
var num_loose_edges = 0; var num_loose_edges = 0;
// Find all triangles that are facing p // Find all triangles that are facing p
var i = 0; var i = 0;
while (i < numFaces) { while (i < numFaces) {
if (faces[i][3].dot(p.sub(faces[i][0])) > 0) // triangle i faces p, remove it if (epaFaces[i][3].dot(p.sub(epaFaces[i][0])) > 0) // triangle i faces p, remove it
{ {
// Add removed triangle's edges to loose edge list. // Add removed triangle's edges to loose edge list.
// If it's already there, remove it (both triangles it belonged to are gone) // If it's already there, remove it (both triangles it belonged to are gone)
for (j in 0...3) // Three edges per face for (j in 0...3) // Three edges per face
{ {
var current_edge = [faces[i][j], faces[i][(j + 1) % 3]]; var current_edge = [epaFaces[i][j], epaFaces[i][(j + 1) % 3]];
var found_edge = false; var found_edge = false;
for (k in 0...num_loose_edges) // Check if current edge is already in list for (k in 0...num_loose_edges) // Check if current edge is already in list
{ {
@ -178,10 +202,10 @@ class GJK {
} }
// Remove triangle i from list // Remove triangle i from list
faces[i][0] = faces[numFaces - 1][0]; epaFaces[i][0] = epaFaces[numFaces - 1][0];
faces[i][1] = faces[numFaces - 1][1]; epaFaces[i][1] = epaFaces[numFaces - 1][1];
faces[i][2] = faces[numFaces - 1][2]; epaFaces[i][2] = epaFaces[numFaces - 1][2];
faces[i][3] = faces[numFaces - 1][3]; epaFaces[i][3] = epaFaces[numFaces - 1][3];
numFaces--; numFaces--;
i--; i--;
} // endif p can see triangle i } // endif p can see triangle i
@ -194,22 +218,22 @@ class GJK {
// assert(num_faces<EPA_MAX_NUM_FACES); // assert(num_faces<EPA_MAX_NUM_FACES);
if (numFaces >= maxEpaFaces) if (numFaces >= maxEpaFaces)
break; break;
faces[numFaces][0] = loose_edges[i][0]; epaFaces[numFaces][0] = loose_edges[i][0];
faces[numFaces][1] = loose_edges[i][1]; epaFaces[numFaces][1] = loose_edges[i][1];
faces[numFaces][2] = p; epaFaces[numFaces][2] = p;
faces[numFaces][3] = loose_edges[i][0].sub(loose_edges[i][1]).cross(loose_edges[i][0].sub(p)).normalized(); epaFaces[numFaces][3] = loose_edges[i][0].sub(loose_edges[i][1]).cross(loose_edges[i][0].sub(p)).normalized();
// Check for wrong normal to maintain CCW winding // Check for wrong normal to maintain CCW winding
var bias = 0.000001; // in case dot result is only slightly < 0 (because origin is on face) var bias = 0.000001; // in case dot result is only slightly < 0 (because origin is on face)
if (faces[numFaces][0].dot(faces[numFaces][3]) + bias < 0) { if (epaFaces[numFaces][0].dot(epaFaces[numFaces][3]) + bias < 0) {
var temp = faces[numFaces][0]; var temp = epaFaces[numFaces][0];
faces[numFaces][0] = faces[numFaces][1]; epaFaces[numFaces][0] = epaFaces[numFaces][1];
faces[numFaces][1] = temp; epaFaces[numFaces][1] = temp;
faces[numFaces][3] = faces[numFaces][3].multiply(-1); epaFaces[numFaces][3] = epaFaces[numFaces][3].multiply(-1);
} }
numFaces++; numFaces++;
} }
} }
return faces[closestFace][3].multiply(faces[closestFace][0].dot(faces[closestFace][3])); return epaFaces[closestFace][3].multiply(epaFaces[closestFace][0].dot(epaFaces[closestFace][3]));
} }
} }

View file

@ -82,7 +82,7 @@ class EnterNameDlg extends GuiControl {
enterNameText.position = new Vector(37, 23); enterNameText.position = new Vector(37, 23);
enterNameText.extent = new Vector(345, 85); enterNameText.extent = new Vector(345, 85);
// enterNameText.justify = Center; // enterNameText.justify = Center;
enterNameText.text.text = '<font face="Arial14"><br/></font><p align="center"><font face="DomCasual48">Well Done!<br/></font><font face="DomCasual32">You have the${["", " second", " third", " fourth", "fifth"][place]} top time!</font></p>'; enterNameText.text.text = '<font face="Arial14"><br/></font><p align="center"><font face="DomCasual48">Well Done!<br/></font><font face="DomCasual32">You have the${["", " second", " third", " fourth", " fifth"][place]} top time!</font></p>';
dlg.addChild(enterNameText); dlg.addChild(enterNameText);
dlg.addChild(enterNameEdit); dlg.addChild(enterNameEdit);

View file

@ -1,5 +1,7 @@
package shapes; package shapes;
import src.MarbleGame;
import collision.gjk.Cylinder;
import src.AudioManager; import src.AudioManager;
import h3d.Quat; import h3d.Quat;
import h3d.mat.Material; import h3d.mat.Material;
@ -25,7 +27,7 @@ import h3d.mat.Texture;
class EndPad extends DtsObject { class EndPad extends DtsObject {
var fireworks:Array<Firework> = []; var fireworks:Array<Firework> = [];
var finishCollider:ConvexHull; var finishCollider:Cylinder;
var finishBounds:Bounds; var finishBounds:Bounds;
var inFinish:Bool = false; var inFinish:Bool = false;
@ -72,7 +74,12 @@ class EndPad extends DtsObject {
vertices.push(new Vector(x * radius * this.scaleX, (i != 0 ? 4.8 : 0) * 1, z * radius * this.scaleY)); vertices.push(new Vector(x * radius * this.scaleX, (i != 0 ? 4.8 : 0) * 1, z * radius * this.scaleY));
} }
} }
finishCollider = new ConvexHull(vertices); finishCollider = new Cylinder();
finishCollider.p1 = this.getAbsPos().getPosition();
finishCollider.p2 = finishCollider.p1.clone();
var vertDir = this.getAbsPos().up();
finishCollider.p2 = finishCollider.p2.add(vertDir.multiply(height));
finishCollider.radius = radius * this.scaleY;
finishBounds = new Bounds(); finishBounds = new Bounds();
for (vert in vertices) for (vert in vertices)
@ -85,7 +92,7 @@ class EndPad extends DtsObject {
var tform = this.getAbsPos().clone(); var tform = this.getAbsPos().clone();
tform.prependRotation(Math.PI / 2, 0, 0); tform.prependRotation(Math.PI / 2, 0, 0);
finishCollider.transform = tform; // finishCollider.transform = tform;
finishBounds.transform(tform); finishBounds.transform(tform);