From e5fa86524fa94a228aea78e814f5dd746f053fa0 Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Tue, 13 Dec 2022 23:40:18 +0530 Subject: [PATCH] fix CCD and internal edges again this time by using mb methods, and other small lag fixes --- src/DtsObject.hx | 8 +- src/GameObject.hx | 3 +- src/Marble.hx | 412 ++++++++++++++----------------- src/MarbleWorld.hx | 15 -- src/collision/Collision.hx | 160 ++++++++---- src/collision/CollisionEntity.hx | 2 +- src/shapes/AbstractBumper.hx | 5 + src/shapes/Gem.hx | 1 + src/shapes/PowerUp.hx | 1 + src/shapes/Trapdoor.hx | 1 + 10 files changed, 319 insertions(+), 289 deletions(-) diff --git a/src/DtsObject.hx b/src/DtsObject.hx index b1a88a9b..6e1f48b0 100644 --- a/src/DtsObject.hx +++ b/src/DtsObject.hx @@ -330,7 +330,7 @@ class DtsObject extends GameObject { rootObject.scaleX = -1; - if (this.level != null) { + if (this.level != null && this.isBoundingBoxCollideable) { var boundthing = new Bounds(); boundthing.addPoint(new h3d.col.Point(this.dts.bounds.minX, this.dts.bounds.minY, this.dts.bounds.minZ)); boundthing.addPoint(new h3d.col.Point(this.dts.bounds.maxX, this.dts.bounds.maxY, this.dts.bounds.maxZ)); @@ -632,8 +632,10 @@ class DtsObject extends GameObject { public override function setTransform(mat:Matrix) { super.setTransform(mat); - this.boundingCollider.setTransform(mat); - this.level.collisionWorld.updateTransform(this.boundingCollider); + if (this.isBoundingBoxCollideable) { + this.boundingCollider.setTransform(mat); + this.level.collisionWorld.updateTransform(this.boundingCollider); + } for (i in 0...this.dirtyTransforms.length) { this.dirtyTransforms[i] = true; } diff --git a/src/GameObject.hx b/src/GameObject.hx index 1013ac89..c3deaff6 100644 --- a/src/GameObject.hx +++ b/src/GameObject.hx @@ -11,7 +11,8 @@ class GameObject extends Object { public var identifier:String; public var currentOpacity:Float = 1; public var isCollideable:Bool = false; - public var isBoundingBoxCollideable:Bool = true; + public var isBoundingBoxCollideable:Bool = false; + public var enableCollideCallbacks:Bool = false; var textureResources:Array> = []; var soundResources:Array> = []; diff --git a/src/Marble.hx b/src/Marble.hx index 18b91cc6..590a6279 100644 --- a/src/Marble.hx +++ b/src/Marble.hx @@ -1,5 +1,7 @@ package src; +import h3d.shader.AlphaMult; +import shaders.DtsTexture; import collision.gjk.GJK; import collision.gjk.ConvexHull; import hxd.snd.effect.Pitch; @@ -733,16 +735,7 @@ class Marble extends GameObject { rPitch.value = pitch; } - function testMove(velocity:Vector, position:Vector, deltaT:Float, radius:Float, testPIs:Bool):{position:Vector, t:Float} { - var velLen = velocity.length(); - if (velLen < 0.001) - return {position: position, t: deltaT}; - - var velocityDir = velocity.normalized(); - - var deltaPosition = velocity.multiply(deltaT); - var finalPosition = position.add(deltaPosition); - + function testMove(velocity:Vector, position:Vector, deltaT:Float, radius:Float, testPIs:Bool):{position:Vector, t:Float, found:Bool} { var searchbox = new Bounds(); searchbox.addSpherePos(this.x, this.y, this.z, _radius); searchbox.addSpherePos(this.x + velocity.x * deltaT * 2, this.y + velocity.y * deltaT * 2, this.z + velocity.z * deltaT * 2, _radius); @@ -750,8 +743,7 @@ class Marble extends GameObject { var foundObjs = this.level.collisionWorld.boundingSearch(searchbox); var finalT = deltaT; - var marbleCollisionTime = finalT; - var marbleCollisionNormal = new Vector(0, 0, 1); + var found = false; var lastContactPos = new Vector(); @@ -762,8 +754,6 @@ class Marble extends GameObject { return new Vector(vec.x, vec.y, vec.z); } - var contactPoly:{v0:Vector, v:Vector, v2:Vector}; - for (obj in foundObjs) { // Its an MP so bruh @@ -789,93 +779,141 @@ class Marble extends GameObject { for (surf in surfaces) { var surface:CollisionSurface = cast surf; + var currentFinalPos = localpos.add(relLocalVel.multiply(finalT)); + var i = 0; while (i < surface.indices.length) { - var v0 = surface.points[surface.indices[i]].transformed(obj.transform); - var v = surface.points[surface.indices[i + 1]].transformed(obj.transform); - var v2 = surface.points[surface.indices[i + 2]].transformed(obj.transform); + var v0 = surface.points[surface.indices[i]]; + var v = surface.points[surface.indices[i + 1]]; + var v2 = surface.points[surface.indices[i + 2]]; - var polyPlane = PlaneF.ThreePoints(toDifPoint(v0), toDifPoint(v), toDifPoint(v2)); + var surfaceNormal = surface.normals[surface.indices[i]]; + var surfaceD = -surfaceNormal.dot(v0); // If we're going the wrong direction or not going to touch the plane, ignore... - if (!(polyPlane.getNormal().dot(toDifPoint(velocityDir)) > -0.001 - || polyPlane.getNormal().dot(toDifPoint(finalPosition)) + polyPlane.d > radius)) { - // Time until collision with the plane - var collisionTime = (radius - - (polyPlane.getNormal().dot(toDifPoint(position)) + polyPlane.d)) / polyPlane.getNormal().dot(toDifPoint(velocity)); + if (surfaceNormal.dot(relLocalVel) > -0.001 || surfaceNormal.dot(currentFinalPos) + surfaceD > radius) { + i += 3; + continue; + } - // Are we going to touch the plane during this time step? - if (collisionTime >= 0.0 && finalT >= collisionTime) { - var lastVertIndex = surface.indices[surface.indices.length - 1]; - var lastVert = surface.points[lastVertIndex]; + // Time until collision with the plane + var collisionTime = (-localpos.dot(surfaceNormal) - surfaceD + radius) / surfaceNormal.dot(relLocalVel); - var collisionPos = velocity.multiply(collisionTime).add(position); - - var isOnEdge:Bool = false; - - for (j in 0...surface.indices.length) { - var thisVert = surface.points[surface.indices[j]]; - if (thisVert != lastVert) { - var edgePlane = PlaneF.ThreePoints(toDifPoint(thisVert).add(polyPlane.getNormal()), toDifPoint(thisVert), - toDifPoint(lastVert)); - lastVert = thisVert; - - // if we are on the far side of the edge - if (edgePlane.getNormal().dot(toDifPoint(collisionPos)) + edgePlane.d < 0.0) - break; - } - - isOnEdge = j != surface.indices.length; - } - - // If we're inside the poly, just get the position - if (!isOnEdge) { - finalT = collisionTime; - finalPosition = collisionPos; - lastContactPos = fromDifPoint(polyPlane.project(toDifPoint(collisionPos))); - contactPoly = {v0: v0, v: v, v2: v2}; - i += 3; - continue; + // Are we going to touch the plane during this time step? + if (collisionTime > 0 && collisionTime < finalT) { + var collisionPoint = localpos.add(relLocalVel.multiply(collisionTime)); + var lastPoint = v2; + var testPlane:h3d.col.Plane = null; + var j = 0; + while (j < 3) { + var testPoint = surface.points[surface.indices[i + j]]; + if (testPoint != lastPoint) { + testPlane = h3d.col.Plane.fromPoints(testPoint.add(surfaceNormal).toPoint(), testPoint.toPoint(), lastPoint.toPoint()); + lastPoint = testPoint; + // if we are on the far side of the edge + if (testPlane.distance(collisionPoint.toPoint()) < 0) + break; } + j++; } - - // We *might* be colliding with an edge - - var lastVert = surface.points[surface.indices[surface.indices.length - 1]]; - - if (surface.indices.length == 0) { - i += 3; + // If we're inside the poly, just get the position + if (j == 3) { + finalT = collisionTime; + currentFinalPos = localpos.add(relLocalVel.multiply(finalT)); + found = true; continue; } - var radSq = radius * radius; - for (iter in 0...surface.indices.length) { - var thisVert = surface.points[surface.indices[iter]]; + } - var vertDiff = lastVert.sub(thisVert); - var posDiff = position.sub(thisVert); + // We *might* be colliding with an edge - var velRejection = vertDiff.cross(velocity); - var posRejection = vertDiff.cross(posDiff); + var lastVert = v2; - // Build a quadratic equation to solve for the collision time - var a = velRejection.lengthSq(); - var halfB = posRejection.dot(velRejection); - var b = halfB + halfB; + var radSq = radius * radius; + for (iter in 0...surface.indices.length) { + var thisVert = surface.points[surface.indices[iter]]; - var discriminant = b * b - (posRejection.lengthSq() - vertDiff.lengthSq() * radSq) * (a * 4.0); + var vertDiff = lastVert.sub(thisVert); + var posDiff = localpos.sub(thisVert); - // If it's not quadratic or has no solution, ignore this edge. - if (a == 0.0 || discriminant < 0.0) { + var velRejection = vertDiff.cross(relLocalVel); + var posRejection = vertDiff.cross(posDiff); + + // Build a quadratic equation to solve for the collision time + var a = velRejection.lengthSq(); + var b = 2 * posRejection.dot(velRejection); + var c = (posRejection.lengthSq() - vertDiff.lengthSq() * radSq); + + var discriminant = b * b - (4 * a * c); + + // If it's not quadratic or has no solution, ignore this edge. + if (a == 0.0 || discriminant < 0.0) { + lastVert = thisVert; + continue; + } + + var oneOverTwoA = 0.5 / a; + var discriminantSqrt = Math.sqrt(discriminant); + + // Solve using the quadratic formula + var edgeCollisionTime = (discriminantSqrt - b) * oneOverTwoA; + var edgeCollisionTime2 = (-b - discriminantSqrt) * oneOverTwoA; + + // Make sure the 2 times are in ascending order + if (edgeCollisionTime2 < edgeCollisionTime) { + var temp = edgeCollisionTime2; + edgeCollisionTime2 = edgeCollisionTime; + edgeCollisionTime = temp; + } + + // If the collision doesn't happen on this time step, ignore this edge. + if (edgeCollisionTime2 <= 0.0001 || finalT <= edgeCollisionTime) { + lastVert = thisVert; + continue; + } + + // Check if the collision hasn't already happened + if (edgeCollisionTime >= 0.0) { + var edgeLen = vertDiff.length(); + + var relativeCollisionPos = localpos.add(relLocalVel.multiply(edgeCollisionTime)).sub(thisVert); + + var distanceAlongEdge = relativeCollisionPos.dot(vertDiff) / edgeLen; + + // If the collision happens outside the boundaries of the edge, ignore this edge. + if (-radius > distanceAlongEdge || edgeLen + radius < distanceAlongEdge) { lastVert = thisVert; continue; } - var oneOverTwoA = 0.5 / a; - var discriminantSqrt = Math.sqrt(discriminant); + // If the collision is within the edge, resolve the collision and continue. + if (distanceAlongEdge >= 0.0 && distanceAlongEdge <= edgeLen) { + finalT = edgeCollisionTime; + currentFinalPos = localpos.add(relLocalVel.multiply(finalT)); + lastVert = thisVert; + found = true; + continue; + } + } + + // This is what happens when we collide with a corner + + a = relLocalVel.lengthSq(); + + // Build a quadratic equation to solve for the collision time + var posVertDiff = localpos.sub(thisVert); + b = 2 * posVertDiff.dot(relLocalVel); + c = posVertDiff.length() - radSq; + discriminant = b * b - (4 * a * c); + + // If it's quadratic and has a solution ... + if (a != 0.0 && discriminant >= 0.0) { + oneOverTwoA = 0.5 / a; + discriminantSqrt = Math.sqrt(discriminant); // Solve using the quadratic formula - var edgeCollisionTime = (discriminantSqrt - b) * oneOverTwoA; - var edgeCollisionTime2 = (-b - discriminantSqrt) * oneOverTwoA; + edgeCollisionTime = (-b + discriminantSqrt) * oneOverTwoA; + edgeCollisionTime2 = (-b - discriminantSqrt) * oneOverTwoA; // Make sure the 2 times are in ascending order if (edgeCollisionTime2 < edgeCollisionTime) { @@ -884,135 +922,60 @@ class Marble extends GameObject { edgeCollisionTime = temp; } - // If the collision doesn't happen on this time step, ignore this edge. - if (edgeCollisionTime2 <= 0.0001 || finalT <= edgeCollisionTime) { - lastVert = thisVert; - continue; - } - - // Check if the collision hasn't already happened - if (edgeCollisionTime >= 0.0) { - var edgeLen = vertDiff.length(); - - var relativeCollisionPos = velocity.multiply(edgeCollisionTime).add(position).sub(thisVert); - - var distanceAlongEdge = relativeCollisionPos.dot(vertDiff) / edgeLen; - - // If the collision happens outside the boundaries of the edge, ignore this edge. - if (-radius > distanceAlongEdge || edgeLen + radius < distanceAlongEdge) { - lastVert = thisVert; - continue; - } - - // If the collision is within the edge, resolve the collision and continue. - if (distanceAlongEdge >= 0.0 && distanceAlongEdge <= edgeLen) { - finalT = edgeCollisionTime; - finalPosition = velocity.multiply(edgeCollisionTime).add(position); - - lastContactPos = vertDiff.multiply(distanceAlongEdge / edgeLen).add(thisVert); - contactPoly = {v0: v0, v: v, v2: v2}; - - lastVert = thisVert; - continue; - } - } - - // This is what happens when we collide with a corner - - var speedSq = velocity.lengthSq(); - - // Build a quadratic equation to solve for the collision time - var posVertDiff = position.sub(thisVert); - var halfCornerB = posVertDiff.dot(velocity); - var cornerB = halfCornerB + halfCornerB; - - var fourA = speedSq * 4.0; - - var cornerDiscriminant = cornerB * cornerB - (posVertDiff.lengthSq() - radSq) * fourA; - - // If it's quadratic and has a solution ... - if (speedSq != 0.0 && cornerDiscriminant >= 0.0) { - var oneOver2A = 0.5 / speedSq; - var cornerDiscriminantSqrt = Math.sqrt(cornerDiscriminant); - - // Solve using the quadratic formula - var cornerCollisionTime = (cornerDiscriminantSqrt - cornerB) * oneOver2A; - var cornerCollisionTime2 = (-cornerB - cornerDiscriminantSqrt) * oneOver2A; - - // Make sure the 2 times are in ascending order - if (cornerCollisionTime2 < cornerCollisionTime) { - var temp = cornerCollisionTime2; - cornerCollisionTime2 = cornerCollisionTime; - cornerCollisionTime = temp; - } - - // If the collision doesn't happen on this time step, ignore this corner - if (cornerCollisionTime2 > 0.0001 && finalT > cornerCollisionTime) { - // Adjust to make sure very small negative times are counted as zero - if (cornerCollisionTime <= 0.0 && cornerCollisionTime > -0.0001) - cornerCollisionTime = 0.0; - - // Check if the collision hasn't already happened - if (cornerCollisionTime >= 0.0) { - // Resolve it and continue - finalT = cornerCollisionTime; - contactPoly = {v0: v0, v: v, v2: v2}; - finalPosition = velocity.multiply(cornerCollisionTime).add(position); - lastContactPos = thisVert; - } - } - } - - // We still need to check the other corner ... - // Build one last quadratic equation to solve for the collision time - var lastVertDiff = position.sub(lastVert); - var lastCornerHalfB = lastVertDiff.dot(velocity); - var lastCornerB = lastCornerHalfB + lastCornerHalfB; - var lastCornerDiscriminant = lastCornerB * lastCornerB - (lastVertDiff.lengthSq() - radSq) * fourA; - - // If it's not quadratic or has no solution, then skip this corner - if (speedSq == 0.0 || lastCornerDiscriminant < 0.0) { - lastVert = thisVert; - continue; - } - - var lastCornerOneOver2A = 0.5 / speedSq; - var lastCornerDiscriminantSqrt = Math.sqrt(lastCornerDiscriminant); - - // Solve using the quadratic formula - var lastCornerCollisionTime = (lastCornerDiscriminantSqrt - lastCornerB) * lastCornerOneOver2A; - var lastCornerCollisionTime2 = (-lastCornerB - lastCornerDiscriminantSqrt) * lastCornerOneOver2A; - - // Make sure the 2 times are in ascending order - if (lastCornerCollisionTime2 < lastCornerCollisionTime) { - var temp = lastCornerCollisionTime2; - lastCornerCollisionTime2 = lastCornerCollisionTime; - lastCornerCollisionTime = temp; - } - // If the collision doesn't happen on this time step, ignore this corner - if (lastCornerCollisionTime2 <= 0.0001 || finalT <= lastCornerCollisionTime) { - lastVert = thisVert; - continue; + if (edgeCollisionTime2 > 0.0001 && finalT > edgeCollisionTime) { + // Adjust to make sure very small negative times are counted as zero + if (edgeCollisionTime <= 0.0 && edgeCollisionTime > -0.0001) + edgeCollisionTime = 0.0; + + // Check if the collision hasn't already happened + if (edgeCollisionTime >= 0.0) { + // Resolve it and continue + finalT = edgeCollisionTime; + currentFinalPos = localpos.add(relLocalVel.multiply(finalT)); + lastContactPos = thisVert; + found = true; + } } + } - // Adjust to make sure very small negative times are counted as zero - if (lastCornerCollisionTime <= 0.0 && lastCornerCollisionTime > -0.0001) - lastCornerCollisionTime = 0.0; - - // Check if the collision hasn't already happened - if (lastCornerCollisionTime < 0.0) { - lastVert = thisVert; - continue; - } - - // Resolve it and continue - finalT = lastCornerCollisionTime; - finalPosition = velocity.multiply(lastCornerCollisionTime).add(position); - lastContactPos = lastVert; - contactPoly = {v0: v0, v: v, v2: v2}; + // We still need to check the other corner ... + // Build one last quadratic equation to solve for the collision time + posVertDiff = localpos.sub(lastVert); + b = 2 * posVertDiff.dot(relLocalVel); + c = posVertDiff.lengthSq() - radSq; + discriminant = b * b - (4 * a * c); + // If it's not quadratic or has no solution, then skip this corner + if (a == 0.0 || discriminant < 0.0) { lastVert = thisVert; + continue; + } + + oneOverTwoA = 0.5 / a; + discriminantSqrt = Math.sqrt(discriminant); + + // Solve using the quadratic formula + edgeCollisionTime = (-b + discriminantSqrt) * oneOverTwoA; + edgeCollisionTime2 = (-b - discriminantSqrt) * oneOverTwoA; + + // Make sure the 2 times are in ascending order + if (edgeCollisionTime2 < edgeCollisionTime) { + var temp = edgeCollisionTime2; + edgeCollisionTime2 = edgeCollisionTime; + edgeCollisionTime = temp; + } + + if (edgeCollisionTime2 > 0.0001 && edgeCollisionTime < finalT) { + if (edgeCollisionTime <= 0 && edgeCollisionTime > -0.0001) { + edgeCollisionTime = 0; + } + if (edgeCollisionTime >= 0) { + finalT = edgeCollisionTime; + currentFinalPos = localpos.add(relLocalVel.multiply(finalT)); + lastVert = thisVert; + found = true; + } } } @@ -1020,10 +983,11 @@ class Marble extends GameObject { } } } - + var deltaPosition = velocity.multiply(finalT); + var finalPosition = position.add(deltaPosition); position = finalPosition; - return {position: position, t: finalT}; + return {position: position, t: finalT, found: found}; } function getIntersectionTime(dt:Float, velocity:Vector) { @@ -1187,13 +1151,6 @@ class Marble extends GameObject { var piTime = timeState.currentAttemptTime; - // if (this.controllable) { - // for (interior in pathedInteriors) { - // // interior.pushTickState(); - // interior.recomputeVelocity(piTime + 0.032, 0.032); - // } - // } - _bounceYet = false; var contactTime = 0.0; @@ -1214,21 +1171,9 @@ class Marble extends GameObject { } } - var intersectData = testMove(velocity, this.getAbsPos().getPosition(), timeStep, _radius, true); // this.getIntersectionTime(timeStep, velocity); - var intersectT = intersectData.t; - - if (intersectT < timeStep && intersectT >= 0.0001) { - // trace('CCD AT t = ${intersectT}'); - // intersectT *= 0.8; // We uh tick the shit to not actually at the contact time cause bruh - // intersectT /= 2; - var diff = timeStep - intersectT; - // this.velocity = this.velocity.sub(A.multiply(diff)); - // this.omega = this.omega.sub(a.multiply(diff)); - timeStep = intersectT; - // this.setPosition(intersectData.position.x, intersectData.position.y, intersectData.position.z); - } - + var stoppedPaths = false; var tempState = timeState.clone(); + tempState.dt = timeStep; it++; @@ -1257,6 +1202,21 @@ class Marble extends GameObject { } appliedImpulses = []; + var intersectData = testMove(velocity, this.getAbsPos().getPosition(), timeStep, _radius, true); // this.getIntersectionTime(timeStep, velocity); + var intersectT = intersectData.t; + if (intersectData.found && intersectT > 0.001) { + var diff = timeStep - intersectT; + this.velocity = this.velocity.sub(A.multiply(diff)); + this.omega = this.omega.sub(a.multiply(diff)); + // var mo = new h3d.prim.Sphere(); + // mo.addNormals(); + // mo.scale(_radius); + // var mCol = new h3d.scene.Mesh(mo); + // mCol.setPosition(intersectData.position.x, intersectData.position.y, intersectData.position.z); + // this.level.scene.addChild(mCol); + timeStep = intersectT; + } + piTime += timeStep; if (this.controllable) { for (interior in pathedInteriors) { diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index 675dba55..c4755276 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -1115,8 +1115,6 @@ class MarbleWorld extends Scheduler { // spherebounds.addSpherePos(gjkCapsule.p2.x, gjkCapsule.p2.y, gjkCapsule.p2.z, gjkCapsule.radius); // var contacts = this.collisionWorld.radiusSearch(marble.getAbsPos().getPosition(), marble._radius); var contacts = marble.contactEntities; - var newImmunity = []; - var calledShapes = []; var inside = []; var contactsphere = new SphereCollisionEntity(marble); @@ -1127,18 +1125,6 @@ class MarbleWorld extends Scheduler { if (contact.go is DtsObject) { var shape:DtsObject = cast contact.go; - var contacttest = shape.colliders.filter(x -> x != null).map(x -> x.sphereIntersection(contactsphere, timeState)); - var contactlist:Array = []; - for (l in contacttest) { - contactlist = contactlist.concat(l); - } - - if (!calledShapes.contains(shape) && !this.shapeImmunity.contains(shape) && contactlist.length != 0) { - calledShapes.push(shape); - newImmunity.push(shape); - shape.onMarbleContact(timeState); - } - shape.onMarbleInside(timeState); if (!this.shapeOrTriggerInside.contains(contact.go)) { this.shapeOrTriggerInside.push(contact.go); @@ -1182,7 +1168,6 @@ class MarbleWorld extends Scheduler { } } } - this.shapeImmunity = newImmunity; } function touchFinish() { diff --git a/src/collision/Collision.hx b/src/collision/Collision.hx index 716837e8..f2673d63 100644 --- a/src/collision/Collision.hx +++ b/src/collision/Collision.hx @@ -139,50 +139,124 @@ class Collision { return res; } + return res; + } - // var r1 = IntersectLineSphere(v0, v1, center, radius); - // if (r1 != null) { - // res.result = true; - // res.point = r1; - // res.normal = center.sub(r1).normalized(); - // return res; - // } - // var r2 = IntersectLineSphere(v1, v2, center, radius); - // if (r2 != null) { - // res.result = true; - // res.point = r2; - // res.normal = center.sub(r2).normalized(); - // return res; - // } - // var r3 = IntersectLineSphere(v2, v0, center, radius); - // if (r3 != null) { - // res.result = true; - // res.point = r3; - // res.normal = center.sub(r3).normalized(); - // return res; - // } - // Check points - // if (center.sub(v0).lengthSq() < radiusSq) { - // res.result = true; - // res.point = v0; - // res.normal = center.sub(v0).normalized(); - // // center.sub(v0).normalized(); - // return res; - // } - // if (center.sub(v1).lengthSq() < radiusSq) { - // res.result = true; - // res.point = v1; - // res.normal = center.sub(v1).normalized(); - // return res; - // } - // if (center.sub(v2).lengthSq() < radiusSq) { - // res.result = true; - // res.point = v2; - // res.normal = center.sub(v2).normalized(); - // return res; - // } - // Check plane - // var p = PlaneF.ThreePoints(toDifPoint(v0), toDifPoint(v1), toDifPoint(v2)); + public static function TriangleSphereIntersection(A:Vector, B:Vector, C:Vector, N:Vector, P:Vector, r:Float, edgeData:Int, edgeConcavities:Array) { + var res:ITSResult = { + result: false, + point: null, + normal: null + }; + + var v0 = A; + var v1 = B; + var v2 = C; + A = A.sub(P); + B = B.sub(P); + C = C.sub(P); + var ca = C.sub(A); + var ba = B.sub(A); + var radiusSq = r * r; + var cp = ba.cross(ca); + var aDotCp = A.dot(cp); + var cpLenSq = cp.lengthSq(); + if (aDotCp * aDotCp > radiusSq * cpLenSq) { + return res; + } + + var aSq = A.dot(A); + var aDotB = A.dot(B); + var aDotC = A.dot(C); + var bSq = B.dot(B); + var bDotC = B.dot(C); + var cSq = C.dot(C); + + if (aSq > radiusSq && aDotB > aSq && aDotC > aSq) { + return res; + } + if (bSq > radiusSq && aDotB > bSq && bDotC > bSq) { + return res; + } + if (cSq > radiusSq && aDotC > cSq && bDotC > cSq) { + return res; + } + + var cSubB = C.sub(B); + var aSubC = A.sub(C); + var baSq = ba.lengthSq(); + var cSubBSq = cSubB.lengthSq(); + var aSubCSq = aSubC.lengthSq(); + var aTest = A.multiply(baSq).sub(ba.multiply(aDotB - aSq)); + var bTest = B.multiply(cSubBSq).sub(cSubB.multiply(bDotC - bSq)); + var cTest = C.multiply(aSubCSq).sub(aSubC.multiply(aDotC - cSq)); + var rhs = C.multiply(baSq).sub(aTest); + var rhs2 = A.multiply(cSubBSq).sub(bTest); + var rhs3 = B.multiply(aSubCSq).sub(cTest); + + if (aTest.dot(aTest) > radiusSq * baSq * baSq && aTest.dot(rhs) > 0) { + return res; + } + if (bTest.dot(bTest) > radiusSq * cSubBSq * cSubBSq && bTest.dot(rhs2) > 0) { + return res; + } + if (cTest.dot(cTest) > radiusSq * aSubCSq * aSubCSq && cTest.dot(rhs3) > 0) { + return res; + } + + var lhs = P.sub(v0); + var baca = ba.dot(ca); + var caSq = ca.lengthSq(); + var lhsBa = lhs.dot(ba); + var lhsCa = lhs.dot(ca); + var len = baSq * caSq - baca * baca; + var d1 = (caSq * lhsBa - baca * lhsCa) / len; + var d2 = (baSq * lhsCa - baca * lhsBa) / len; + + if (1 - d1 - d2 >= 0 && d1 >= 0 && d2 >= 0) { + res.result = true; + res.normal = N.clone(); + res.point = P.sub(N.multiply(P.sub(v0).dot(N))); + } else { + var closestPt = P.sub(N.multiply(P.sub(v0).dot(N))); + var r1 = ClosestPointLine(v0, v1, closestPt); + var r2 = ClosestPointLine(v1, v2, closestPt); + var r3 = ClosestPointLine(v2, v0, closestPt); + + var chosenEdge = 0; // Bitfield + + var chosenPt:Vector; + if (r1.distanceSq(P) < r2.distanceSq(P)) { + chosenPt = r1; + chosenEdge = 1; + } else { + chosenPt = r2; + chosenEdge = 2; + } + if (chosenPt.distanceSq(P) < r3.distanceSq(P)) + res.point = chosenPt; + else { + chosenEdge = 4; + res.point = r3; + } + res.normal = P.sub(res.point).normalized(); + res.result = true; + + if (res.normal.dot(N) > 0.8) { + // Internal edge + if (chosenEdge & edgeData > 0) { + chosenEdge -= 1; + if (chosenEdge > 2) + chosenEdge--; + // if (edgeNormals[chosenEdge].length() < 0.5) { + // res.normal = center.sub(res.point).normalized(); + // } else + if (edgeConcavities[chosenEdge]) { // Our edge is concave + res.normal = N.clone(); + } + } + } + } return res; } diff --git a/src/collision/CollisionEntity.hx b/src/collision/CollisionEntity.hx index afb93e58..77f9a910 100644 --- a/src/collision/CollisionEntity.hx +++ b/src/collision/CollisionEntity.hx @@ -199,7 +199,7 @@ class CollisionEntity implements IOctreeObject { var surfacenormal = surface.normals[surface.indices[i]].transformed3x3(transform).normalized(); - var res = Collision.IntersectTriangleSphere(v0, v, v2, surfacenormal, position, radius, edgeData, edgeConcavities); + var res = Collision.TriangleSphereIntersection(v0, v, v2, surfacenormal, position, radius, edgeData, edgeConcavities); var closest = res.point; // var closest = Collision.ClosestPtPointTriangle(position, radius, v0, v, v2, surfacenormal); if (closest != null) { diff --git a/src/shapes/AbstractBumper.hx b/src/shapes/AbstractBumper.hx index 76e0e5cc..01b0c69b 100644 --- a/src/shapes/AbstractBumper.hx +++ b/src/shapes/AbstractBumper.hx @@ -8,6 +8,11 @@ import src.Util; class AbstractBumper extends DtsObject { var lastContactTime = Math.NEGATIVE_INFINITY; + public function new() { + super(); + this.enableCollideCallbacks = true; + } + override function update(timeState:src.TimeState) { // Override the keyframe var currentCompletion = getCurrentCompletion(timeState); diff --git a/src/shapes/Gem.hx b/src/shapes/Gem.hx index ef246612..3f74daee 100644 --- a/src/shapes/Gem.hx +++ b/src/shapes/Gem.hx @@ -15,6 +15,7 @@ class Gem extends DtsObject { dtsPath = "data/shapes/items/gem.dts"; ambientRotate = true; isCollideable = false; + this.isBoundingBoxCollideable = true; pickedUp = false; useInstancing = true; showSequences = false; // Gems actually have an animation for the little shiny thing, but the actual game ignores that. I get it, it was annoying as hell. diff --git a/src/shapes/PowerUp.hx b/src/shapes/PowerUp.hx index 4338883c..4740a710 100644 --- a/src/shapes/PowerUp.hx +++ b/src/shapes/PowerUp.hx @@ -37,6 +37,7 @@ abstract class PowerUp extends DtsObject { public function new(element:MissionElementItem) { super(); + this.isBoundingBoxCollideable = true; this.isCollideable = false; this.ambientRotate = true; this.element = element; diff --git a/src/shapes/Trapdoor.hx b/src/shapes/Trapdoor.hx index d56f0db7..6c8b155f 100644 --- a/src/shapes/Trapdoor.hx +++ b/src/shapes/Trapdoor.hx @@ -24,6 +24,7 @@ class Trapdoor extends DtsObject { this.isTSStatic = false; this.identifier = "Trapdoor"; this.hasNonVisualSequences = true; + this.enableCollideCallbacks = true; } public override function init(level:MarbleWorld, onFinish:Void->Void) {