多边形汤 - 椭球碰撞假阳性(Kasper Fauerby)

时间:2017-02-21 11:27:21

标签: c++ collision-detection

我正试图在我的游戏中实现Kasper Fauerby的碰撞检测和响应,但我在检测部分遇到了一些问题。

顶点和边缘碰撞都会返回误报,此时我只是在碰撞即将发生时(半径为1),我只是不将我的自由凸轮速度添加到它的位置。

当我的下一个位置位于三角形的平面上时,它看起来像顶点延伸得更远。但是当我向顶点方向移动时,这只会影响我。我可以平行移动到三角形的法线而不会与顶点碰撞。

目前我将代码一直删除到代码Kasper provided in his paper,但这里是我正在使用的代码:

bool MeshCollider::CheckTriangleEllipsoid(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, Ellipsoid *colPackage)
{
    // Make the plane containing this triangle.
    Collision::PLANE trianglePlane(p1, p2, p3);
    // Is triangle front-facing to the velocity vector?
    // We only check front-facing triangles
    // (your choice of course)
    if (trianglePlane.isFrontFacingTo(
    colPackage->normalizedVelocity)) {
    // Get interval of plane intersection:
        double t0, t1;
        bool embeddedInPlane = false;
        // Calculate the signed distance from sphere
        // position to triangle plane
        double signedDistToTrianglePlane = trianglePlane.signedDistanceTo(colPackage->ePosition);
        // cache this as we’re going to use it a few times below:
        float normalDotVelocity = glm::dot(trianglePlane.normal, colPackage->eVelocity);
        // if sphere is travelling parrallel to the plane:
        if (normalDotVelocity == 0.0f) {
            if (fabs(signedDistToTrianglePlane) >= 1.0f) {
            // Sphere is not embedded in plane.
            // No collision possible:
                return false;
            }
            else {
            // sphere is embedded in plane.
            // It intersects in the whole range [0..1]
                embeddedInPlane = true;
                t0 = 0.0;
                t1 = 1.0;
            }
        }
        else {
        // N dot D is not 0. Calculate intersection interval:
            t0 = (-1.0 - signedDistToTrianglePlane) / normalDotVelocity;
            t1 = (1.0 - signedDistToTrianglePlane) / normalDotVelocity;
            // Swap so t0 < t1
            if (t0 > t1) {
                double temp = t1;
                t1 = t0;
                t0 = temp;
            }
            // Check that at least one result is within range:
            if (t0 > 1.0f || t1 < 0.0f) {
                // Both t values are outside values [0,1]
                // No collision possible:
                return false;
            }
            // Clamp to [0,1]
            if (t0 < 0.0) t0 = 0.0;
            if (t1 < 0.0) t1 = 0.0;
            if (t0 > 1.0) t0 = 1.0;
            if (t1 > 1.0) t1 = 1.0;
        }
        // OK, at this point we have two time values t0 and t1
        // between which the swept sphere intersects with the
        // triangle plane. If any collision is to occur it must
        // happen within this interval.
        glm::vec3 collisionPoint;
        bool foundCollison = false;
        float t = 1.0;
        // First we check for the easy case - collision inside
        // the triangle. If this happens it must be at time t0
        // as this is when the sphere rests on the front side
        // of the triangle plane. Note, this can only happen if
        // the sphere is not embedded in the triangle plane.

        if(!embeddedInPlane) {
            glm::vec3 planeIntersectionPoint =
            (colPackage->ePosition - trianglePlane.normal)
            + (float)t0 * colPackage->eVelocity;
            if (checkPointInTriangle(planeIntersectionPoint,
                p1,
                p2,
                p3))
            {
                foundCollison = true;

                t = t0;
                collisionPoint = planeIntersectionPoint;
            }
        }

        // if we haven’t found a collision already we’ll have to
        // sweep sphere against points and edges of the triangle.
        // Note: A collision inside the triangle (the check above)
        // will always happen before a vertex or edge collision!
        // This is why we can skip the swept test if the above
        // gives a collision!


        if (foundCollison == false) {
        // some commonly used terms:
            glm::vec3 velocity = colPackage->eVelocity;
            glm::vec3 base = colPackage->ePosition;

            float velocitySquaredLength = glm::length(velocity);
            float a, b, c; // Params for equation
            float newT;

            // For each vertex or edge a quadratic equation have to
            // be solved. We parameterize this equation as
            // a*t^2 + b*t + c = 0 and below we calculate the
            // parameters a,b and c for each test.
            // Check against points:
            a = velocitySquaredLength;
            // P1
            b = 2.0 * (glm::dot(velocity, base - p1));
            c = glm::length(p1 - base) - 1.0;
            if (getLowestRoot(a, b, c, t, &newT)) {
                t = newT;
                foundCollison = true;
                collisionPoint = p1;
                std::cout << "point 1\n";
            }
            // P2
            b = 2.0 * (glm::dot(velocity, base - p2));
            c = glm::length(p2 - base) - 1.0;
            if (getLowestRoot(a, b, c, t, &newT)) {
                t = newT;
                foundCollison = true;
                collisionPoint = p2;
                std::cout << "point 2\n";
            }

            // P3
            b = 2.0*(glm::dot(velocity, base - p3));
            c = glm::length(p3 - base) - 1.0 ;
            if (getLowestRoot(a, b, c, t, &newT)) {
                t = newT;
                foundCollison = true;
                collisionPoint = p3;
                std::cout << "point 3\n";
            }

            // Check agains edges:
            // p1 -> p2:
            glm::vec3 edge = p2 - p1;
            glm::vec3 baseToVertex = p1 - base;
            float edgeSquaredLength = glm::length(edge);
            float edgeDotVelocity = glm::dot(edge, velocity);
            float edgeDotBaseToVertex = glm::dot(edge, baseToVertex);
            // Calculate parameters for equation
            a = edgeSquaredLength * -velocitySquaredLength +
            edgeDotVelocity*edgeDotVelocity;
            b = edgeSquaredLength*(2*glm::dot(velocity, baseToVertex)) -
            2.0*edgeDotVelocity*edgeDotBaseToVertex;
            c = edgeSquaredLength*(1 - glm::length(baseToVertex)) +
            edgeDotBaseToVertex*edgeDotBaseToVertex;
            // Does the swept sphere collide against infinite edge?
            if(getLowestRoot(a, b, c, t, &newT)) {
            // Check if intersection is within line segment:
                float f = (edgeDotVelocity*newT - edgeDotBaseToVertex) /
                edgeSquaredLength;
                if (f >= 0.0 && f <= 1.0) {
                // intersection took place within segment.
                    t = newT;
                    foundCollison = true;
                    collisionPoint = p1 + f*edge;
                    std::cout << "p1 p2\n";
                }
            }

            // p2 -> p3:
            edge = p3 - p2;
            baseToVertex = p2 - base;
            edgeSquaredLength = glm::length(edge);
            edgeDotVelocity = glm::dot(edge, velocity);
            edgeDotBaseToVertex = glm::dot(edge, baseToVertex);
            // Calculate parameters for equation
            a = edgeSquaredLength * -velocitySquaredLength +
            edgeDotVelocity*edgeDotVelocity;
            b = edgeSquaredLength*(2*glm::dot(velocity, baseToVertex)) -
            2.0*edgeDotVelocity*edgeDotBaseToVertex;
            c = edgeSquaredLength*(1 - glm::length(baseToVertex)) +
            edgeDotBaseToVertex*edgeDotBaseToVertex;
            if (getLowestRoot(a, b, c, t, &newT)) {
                float f = (edgeDotVelocity*newT - edgeDotBaseToVertex) /
                edgeSquaredLength;
                if (f >= 0.0 && f <= 1.0) {
                    t = newT;
                    foundCollison = true;
                    collisionPoint = p2 + f*edge;
                    std::cout << "p2 p3\n";
                }
            }
            // p3 -> p1:
            edge = p1 - p3;
            baseToVertex = p3 - base;
            edgeSquaredLength = glm::length(edge);
            edgeDotVelocity = glm::dot(edge, velocity);
            edgeDotBaseToVertex = glm::dot(edge, baseToVertex);
            // Calculate parameters for equation
            a = edgeSquaredLength * -velocitySquaredLength +
            edgeDotVelocity*edgeDotVelocity;
            b = edgeSquaredLength*(2*glm::dot(velocity, baseToVertex)) -
            2.0*edgeDotVelocity*edgeDotBaseToVertex;
            c = edgeSquaredLength*(1 - glm::length(baseToVertex)) +
            edgeDotBaseToVertex*edgeDotBaseToVertex;
            if (getLowestRoot(a, b, c, t, &newT)) {
                float f = (edgeDotVelocity*newT - edgeDotBaseToVertex) /
                edgeSquaredLength;
                if (f >= 0.0 && f <= 1.0) {
                    t = newT;
                    foundCollison = true;
                    collisionPoint = p3 + f*edge;
                    std::cout << "p3 p1\n";
                }
            }
        }

        if (foundCollison)
            std::cout << t << " t";
        return foundCollison;// Exit here since I'm not using collision response yet.


        // Set result:
        if (foundCollison == true) {
        // distance to collision: ’t’ is time of collision
            float distToCollision = t * colPackage->velocity.length();
            // Does this triangle qualify for the closest hit?
            // it does if it’s the first hit or the closest
            if (colPackage->foundCollision == false ||
            distToCollision < colPackage->nearestDistance) {
            // Collision information nessesary for sliding
                colPackage->nearestDistance = distToCollision;
                colPackage->intersectionPoint = collisionPoint;
                colPackage->foundCollision = true;
            }
        }
    } // if not backface
    return false;
}

我假设论文中提供的代码确实有效。人们似乎在卡斯帕的论文上取得了成功。不幸的是不是我。

到目前为止,我尝试的是:

  • 现在重复实施两次(如果我在某处犯了错误)。
  • 尝试碰撞检测的单独部分,找出导致问题的原因。

现在我只是出于想法,一切对我来说都是正确的,但它仍然给我误报。

1 个答案:

答案 0 :(得分:0)

发现问题:

我使用的是glm :: length而不是返回平方长度的函数。 这导致误报。