Теорема о разделяющей оси ложно срабатывает - PullRequest
0 голосов
/ 15 декабря 2018

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

Я следую этому учебникуОднако, учебник только в 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 голосов
/ 15 декабря 2018

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

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

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

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

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

...