diff --git a/src/Marble.hx b/src/Marble.hx index 5001071e..1f2dd7e6 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -916,7 +916,7 @@ class Marble extends GameObject { rollVolume = 0; var slipVolume = 0.0; - if (slipAmount > 0) { + if (slipAmount > 1e-4) { slipVolume = slipAmount / 5; if (slipVolume > 1) slipVolume = 1; @@ -1337,7 +1337,7 @@ class Marble extends GameObject { sph.position = finalPos; sph.radius = _radius; - var pt = GJK.gjk(sph, chull); + var pt = GJK.gjk(sph, chull).epa; while (pt != null) { if (pt.lengthSq() < 0.0001) { @@ -1346,7 +1346,7 @@ class Marble extends GameObject { trace('Separating Vector Len: ${pt.length()}'); finalPos = finalPos.sub(pt); sph.position = finalPos; - pt = GJK.gjk(sph, chull); + pt = GJK.gjk(sph, chull).epa; } // if (pt != null) { diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 9402e3b1..ded877f9 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1518,7 +1518,7 @@ class MarbleWorld extends Scheduler { if (this.finishTime == null) { 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) { touchFinish(); endPad.inFinish = true; @@ -1527,6 +1527,9 @@ class MarbleWorld extends Scheduler { if (endPad.inFinish) endPad.inFinish = false; } + } else { + if (endPad.inFinish) + endPad.inFinish = false; } } } diff --git a/src/collision/CollisionHull.hx b/src/collision/CollisionHull.hx index 4aec9244..d5fe0924 100644 --- a/src/collision/CollisionHull.hx +++ b/src/collision/CollisionHull.hx @@ -38,7 +38,7 @@ class CollisionHull extends CollisionEntity { newTform.setPosition(newpos); hull.setTransform(newTform); - var pt = GJK.gjk(sph, this.hull); + var pt = GJK.gjk(sph, this.hull).epa; if (pt != null) { var cinfo = new CollisionInfo(); cinfo.normal = pt.normalized(); diff --git a/src/collision/gjk/Cylinder.hx b/src/collision/gjk/Cylinder.hx new file mode 100644 index 00000000..e4a18111 --- /dev/null +++ b/src/collision/gjk/Cylinder.hx @@ -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); + } +} diff --git a/src/collision/gjk/GJK.hx b/src/collision/gjk/GJK.hx index e2f848ad..490f79c2 100644 --- a/src/collision/gjk/GJK.hx +++ b/src/collision/gjk/GJK.hx @@ -10,7 +10,10 @@ class GJK { public static var maxEpaLooseEdges = 64; public static var maxEpaIterations = 64; - public static function gjk(s1:GJKShape, s2:GJKShape) { + static var epaFaces:Array>; + static var loose_edges:Array>; + + public static function gjk(s1:GJKShape, s2:GJKShape, doEpa:Bool = true) { var searchDir = s1.getCenter().sub(s2.getCenter()); var a = new Vector(); var b = new Vector(); @@ -21,7 +24,10 @@ class GJK { searchDir = c.multiply(-1); b = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1))); if (b.dot(searchDir) < 0) - return null; + return { + result: false, + epa: null + }; searchDir = c.sub(b).cross(b.multiply(-1)).cross(c.sub(b)); if (searchDir.length() == 0) { @@ -35,7 +41,10 @@ class GJK { for (i in 0...maxIterations) { a = s2.support(searchDir).sub(s1.support(searchDir.multiply(-1))); if (a.dot(searchDir) < 0) { - return null; + return { + result: false, + epa: null + }; } simpDim++; @@ -83,44 +92,57 @@ class GJK { b = a; searchDir = adb; } 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) { - var faces = []; - for (i in 0...maxEpaFaces) - faces.push([new Vector(), new Vector(), new Vector(), new Vector()]); + if (epaFaces == null) { + epaFaces = []; + for (i in 0...maxEpaFaces) + epaFaces.push([new Vector(), new Vector(), new Vector(), new Vector()]); + } - faces[0][0] = a; - faces[0][1] = b; - faces[0][2] = c; - faces[0][3] = b.sub(a).cross(c.sub(a)).normalized(); // ABC - faces[1][0] = a; - faces[1][1] = c; - faces[1][2] = d; - faces[1][3] = c.sub(a).cross(d.sub(a)).normalized(); - faces[2][0] = a; - faces[2][1] = d; - faces[2][2] = b; - faces[2][3] = d.sub(a).cross(b.sub(a)).normalized(); - faces[3][0] = b; - faces[3][1] = d; - faces[3][2] = c; - faces[3][3] = d.sub(b).cross(c.sub(b)).normalized(); + epaFaces[0][0] = a; + epaFaces[0][1] = b; + epaFaces[0][2] = c; + epaFaces[0][3] = b.sub(a).cross(c.sub(a)).normalized(); // ABC + epaFaces[1][0] = a; + epaFaces[1][1] = c; + epaFaces[1][2] = d; + epaFaces[1][3] = c.sub(a).cross(d.sub(a)).normalized(); + epaFaces[2][0] = a; + epaFaces[2][1] = d; + epaFaces[2][2] = b; + epaFaces[2][3] = d.sub(a).cross(b.sub(a)).normalized(); + epaFaces[3][0] = b; + epaFaces[3][1] = d; + epaFaces[3][2] = c; + epaFaces[3][3] = d.sub(b).cross(c.sub(b)).normalized(); var numFaces = 4; var closestFace = 0; for (iteration in 0...maxEpaIterations) { // 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; 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) { min_dist = dist; closestFace = i; @@ -128,28 +150,30 @@ class GJK { } // 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))); if (p.dot(search_dir) - min_dist < epaTolerance) { // 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! + } + if (loose_edges == null) { + loose_edges = []; + for (i in 0...maxEpaLooseEdges) + loose_edges.push([new Vector(), new Vector()]); } - var loose_edges = []; - for (i in 0...maxEpaLooseEdges) - loose_edges.push([new Vector(), new Vector()]); var num_loose_edges = 0; // Find all triangles that are facing p var i = 0; 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. // If it's already there, remove it (both triangles it belonged to are gone) 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; 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 - faces[i][0] = faces[numFaces - 1][0]; - faces[i][1] = faces[numFaces - 1][1]; - faces[i][2] = faces[numFaces - 1][2]; - faces[i][3] = faces[numFaces - 1][3]; + epaFaces[i][0] = epaFaces[numFaces - 1][0]; + epaFaces[i][1] = epaFaces[numFaces - 1][1]; + epaFaces[i][2] = epaFaces[numFaces - 1][2]; + epaFaces[i][3] = epaFaces[numFaces - 1][3]; numFaces--; i--; } // endif p can see triangle i @@ -194,22 +218,22 @@ class GJK { // assert(num_faces= maxEpaFaces) break; - faces[numFaces][0] = loose_edges[i][0]; - faces[numFaces][1] = loose_edges[i][1]; - faces[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][0] = loose_edges[i][0]; + epaFaces[numFaces][1] = loose_edges[i][1]; + epaFaces[numFaces][2] = p; + 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 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) { - var temp = faces[numFaces][0]; - faces[numFaces][0] = faces[numFaces][1]; - faces[numFaces][1] = temp; - faces[numFaces][3] = faces[numFaces][3].multiply(-1); + if (epaFaces[numFaces][0].dot(epaFaces[numFaces][3]) + bias < 0) { + var temp = epaFaces[numFaces][0]; + epaFaces[numFaces][0] = epaFaces[numFaces][1]; + epaFaces[numFaces][1] = temp; + epaFaces[numFaces][3] = epaFaces[numFaces][3].multiply(-1); } 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])); } } diff --git a/src/gui/EnterNameDlg.hx b/src/gui/EnterNameDlg.hx index 6c57f895..0461b39e 100644 --- a/src/gui/EnterNameDlg.hx +++ b/src/gui/EnterNameDlg.hx @@ -82,7 +82,7 @@ class EnterNameDlg extends GuiControl { enterNameText.position = new Vector(37, 23); enterNameText.extent = new Vector(345, 85); // enterNameText.justify = Center; - enterNameText.text.text = '

Well Done!
You have the${["", " second", " third", " fourth", "fifth"][place]} top time!

'; + enterNameText.text.text = '

Well Done!
You have the${["", " second", " third", " fourth", " fifth"][place]} top time!

'; dlg.addChild(enterNameText); dlg.addChild(enterNameEdit); diff --git a/src/shapes/EndPad.hx b/src/shapes/EndPad.hx index 226c22e2..12cd1732 100644 --- a/src/shapes/EndPad.hx +++ b/src/shapes/EndPad.hx @@ -1,5 +1,7 @@ package shapes; +import src.MarbleGame; +import collision.gjk.Cylinder; import src.AudioManager; import h3d.Quat; import h3d.mat.Material; @@ -25,7 +27,7 @@ import h3d.mat.Texture; class EndPad extends DtsObject { var fireworks:Array = []; - var finishCollider:ConvexHull; + var finishCollider:Cylinder; var finishBounds:Bounds; 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)); } } - 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(); for (vert in vertices) @@ -85,7 +92,7 @@ class EndPad extends DtsObject { var tform = this.getAbsPos().clone(); tform.prependRotation(Math.PI / 2, 0, 0); - finishCollider.transform = tform; + // finishCollider.transform = tform; finishBounds.transform(tform);