Теорема о разделяющей оси ложно срабатывает

Я пишу некоторый код на C++ для тестирования столкновений с использованием теоремы о разделительной оси, и в определенных ориентациях это ошибочно вызывает возникновение столкновения

Я следую этому уроку, однако он только в 2D, и я пытаюсь реализовать его в 3D, хотя я думаю, что он должен быть таким же.

Алгоритм, который у меня сейчас есть, не пропускает никаких столкновений, но для некоторых ориентаций двух блоков он думает, что они сталкиваются, когда они не затрагиваются. Пример можно увидеть здесь, эти два поля, очевидно, сталкиваются в соответствии с кодом ниже.

Код написан на 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 ответ

Решение

учебник только в 2D, и я пытаюсь реализовать его в 3D, хотя я думаю, что он должен быть таким же

К сожалению, это не так. 3D случай немного сложнее. Чтобы проверить, сталкиваются ли две сложные фигуры в 3D, вам нужно проверить каждую грань нормали (вы делаете это) и направления, которые перпендикулярны краю каждого объекта (вы пропускаете их).

Итак, для ящиков (A и B) с направлениями ребер A0, A1, A2 и B0, B1, B2 имеем:

  • 3 нормали А
  • 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);

В этом случае это не приносит никакого вреда. Но для более сложных форм это может сделать тест некорректным.

Другие вопросы по тегам