分离轴定理错误触发

时间:2018-12-15 17:49:32

标签: c++ collision-detection collision separating-axis-theorem

我正在用C ++编写一些代码,使用分离轴定理来测试碰撞,并且在某些方向上错误地触发了碰撞的发生

我正在关注this教程,但是该教程仅适用于2D,尽管我认为它应该仍然相同,但我正在尝试以3D实施它。

我现在拥有的算法不会遗漏任何碰撞,但是对于两个盒子的某些方向,它认为实际上没有碰撞时会发生碰撞。可以看到一个例子,根据下面的代码,这两个盒子明显冲突。

False collision

代码是用C ++编写的

BoxCollider.h

class BoxCollider :
    public Collider
{
public:
    BoxCollider(Vector3 position, Vector3 rotation, Vector3 size);


    ~BoxCollider();

    void Update();

public:
    Vector3 rotation;
    Vector3 size;
    Matrix transformMatrix;

    std::vector<Vector3> points;

    Vector3 normals[3];
};

BoxCollider.cpp

BoxCollider::BoxCollider(Vector3 position, Vector3 rotation, Vector3 size) : rotation(rotation), size(size)
{
    this->position = position;
    points.resize(8);

}

BoxCollider::~BoxCollider()
{
}

void BoxCollider::Update()
{
    Transform* eTransform = m_entity->GetComponent<Transform>();
    transformMatrix.RotateYawPitchRoll(rotation + eTransform->rotation);
    Vector3 ePos = eTransform->position;

    points[0] = transformMatrix * (Vector3( 0.5, -0.5, -0.5) * size) + position + ePos;
    points[1] = transformMatrix * (Vector3( 0.5,  0.5, -0.5) * size) + position + ePos;
    points[2] = transformMatrix * (Vector3( 0.5, -0.5,  0.5) * size) + position + ePos;
    points[3] = transformMatrix * (Vector3( 0.5,  0.5,  0.5) * size) + position + ePos;
    points[4] = transformMatrix * (Vector3(-0.5, -0.5, -0.5) * size) + position + ePos;
    points[5] = transformMatrix * (Vector3(-0.5,  0.5, -0.5) * size) + position + ePos;
    points[6] = transformMatrix * (Vector3(-0.5, -0.5,  0.5) * size) + position + ePos;
    points[7] = transformMatrix * (Vector3(-0.5,  0.5,  0.5) * size) + position + ePos;

    normals[0] = transformMatrix * Vector3(1, 0, 0);
    normals[1] = transformMatrix * Vector3(0, 1, 0);
    normals[2] = transformMatrix * Vector3(0, 0, 1);
}

算法:

void EntityManager::CheckCollision(BoxCollider * col0, BoxCollider * col1)
{
    for (int i = 0; i < 3; i++) //First cube
    {
        Vector3 axis = col0->normals[i];
        axis = Vector3(axis.z, -axis.x, axis.y);


        Projection proj1 = GetProjection(col0->points, axis);
        Projection proj2 = GetProjection(col1->points, axis);

        float overlap = GetOverlap(proj1, proj2);

        if (overlap > 0.0) //The projections do not overlap
            return;
    }

    for (int i = 0; i < 3; i++) //First cube
    {
        Vector3 axis = col1->normals[i];
        axis = Vector3(axis.z, -axis.x, axis.y);

        Projection proj1 = GetProjection(col0->points, axis);
        Projection proj2 = GetProjection(col1->points, axis);

        float overlap = GetOverlap(proj1, proj2);

        if (overlap > 0.0) //The projections do not overlap
            return;
    }
}

float GetOverlap(Projection proj1, Projection proj2)
{
    float a = proj2.left - proj1.right;
    float b = proj1.left - proj2.right;

    return a > b ? a : b;
}

Projection GetProjection(std::vector<Vector3> points, Vector3 axis)
{
    float tmp = 0;
    float left = D3D10_FLOAT32_MAX, right = -D3D10_FLOAT32_MAX;

    for (int i = 0; i < points.size(); i++)
    {
        tmp = DotProduct(points[i], axis.Normalize());
        if (tmp < left)
        {
            left = tmp;
        }

        if (tmp > right)
        {
            right = tmp;
        }
    }

    return Projection(left, right, axis);
}

1 个答案:

答案 0 :(得分:3)

  

教程仅在2D中,我试图在3D中实现它,尽管我认为它应该仍然相同

不幸的是,事实并非如此。 3D情况更加复杂。要检查两个复杂的形状是否在3D中发生碰撞,您需要检查每个面的法线(执行此操作)以及与每个对象的边缘垂直的方向(您会错过这些方向)。

因此,对于具有边方向A0,A1,A2和B0,B1,B2的框(A和B),我们有:

  • A的3个法线
  • B的3个法线
  • 9个方向:A0 x B0,A0 x B1,A0 x B2,A1 x B0,A1 x B1,A1 x B2,A2 x B0,A2 x B1,A2 x B2

所以您只需要添加缺少的9张支票。

进一步的注意:您不需要混淆法线。我的意思是不需要此行:

    axis = Vector3(axis.z, -axis.x, axis.y);

在这种情况下,它没有任何危害。但是对于更复杂的形状,实际上可能会使测试不正确。