Получение обнаружения столкновений OBB-Triangle прямо в движке OpenGL - PullRequest
0 голосов
/ 10 февраля 2019

Я хочу реализовать столкновение на местности.У меня есть класс под названием GeoTerrain.Его конструктор берет изображение в качестве параметра, а затем строит ландшафт из треугольников с вершинами, высокими для каждого светлого пикселя и низкими для каждого темного пикселя.Это ставит меня в положение, в котором я должен реализовать надлежащее обнаружение столкновений для OBB, когда они попадают на местность.Но мое временное решение далеко не надежно.

Вот мой текущий подход: у каждого поверхностного треугольника есть свой маленький хитбокс, который состоит из 6 вершин (3 вершины на поверхности треугольника и три дополнительные вершины, которые в основном являются3 вершины поверхности, но смещенные вниз по нормали к поверхности (так, чтобы они находились под видимой поверхностью).

Почему: потому что я не знаю, как иначе вычислить минимальный вектор перевода без объема хитбокса.

Извините за длинный код - я пытался комментировать столько, сколько необходимо:

// This method resides in the Hitbox class.
// The hitbox instance then checks against the other object (parameter):
private IntersectionObject TestIntersectionTerrain(GeoTerrain terra)
{
    Vector3 mtv = new Vector3(0, 0, 0);
    Vector3 mtvTotal = new Vector3(0, 0, 0);
    float shape1Min, shape1Max, shape2Min, shape2Max;

    // Select all triangles within reach of the OBB hitbox:
    List<GeoTriangle> triangles = terra.GetTrianglesForHitbox(this);

    // Loop through all triangles and check collision
    // (cannot be more than 8 triangles, right now)
    foreach (GeoTriangle triangle in triangles)
    {
        bool bothOverlap = false;
        mtv = Vector3.Zero;
        bool error;
        bool breakDone = false;
        float mtvDistance = float.MaxValue;
        float mtvDirection = 1;

        // loop through all hitbox normals (three normals):
        for (int i = 0; i < mNormals.Length; i++)
        {
            error = false;

            // project the current vertices of objects' hitbox and triangle hitbox to find
            // both shapes' minimum and maximum:
            SATtest(CurrentHitBoxNormals[i], CurrentHitBoxVertices, out shape1Min, out shape1Max);
            SATtest(CurrentHitBoxNormals[i], triangle.VerticesHitbox, out shape2Min, out shape2Max);
            if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
            {
                bothOverlap = false;
                breakDone = true;
                break;
            }
            else
            {
                // calculate MTV: 
                CalculateOverlap(
                    CurrentHitBoxNormals[i], // normals (here, the meshes' hitbox normals)
                    ref shape1Min,
                    ref shape1Max,
                    ref shape2Min,
                    ref shape2Max, 
                    out error,
                    ref mtvDistance,
                    ref mtv,
                    ref mtvDirection,
                    mCenterTranslated, // object's hitbox volume center (world space)
                    triangle.CenterHitbox); // triangle's hitbox volume center (world space)
            }

            // do the same but now for the triangle's normal
            // (right now this unnecessarily also gets looped 3 times):
            SATtest(triangle.Normal, CurrentHitBoxVertices, out shape1Min, out shape1Max);
            SATtest(triangle.Normal, triangle.VerticesHitbox, out shape2Min, out shape2Max);
            if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
            {
                bothOverlap = false;
                breakDone = true;
            }
            else
            {
                CalculateOverlap(triangle.Normal, ref shape1Min, ref shape1Max, ref shape2Min, ref shape2Max,
                    out error, ref mtvDistance, ref mtv, ref mtvDirection, mCenterTranslated, triangle.CenterHitbox);
                bothOverlap = true;
            }
        }
        if (bothOverlap && !breakDone && mtv != Vector3.Zero)
        {
            // add the current mtv to the total MTV (of all triangles)
            // but only add more to it, if the current MTV has a bigger shift in
            // one direction than the previous MTVs:
            mtvTotal.X = Math.Abs(mtv.X) > Math.Abs(mtvTotal.X) ? mtv.X : mtvTotal.X;
            mtvTotal.Y = Math.Abs(mtv.Y) > Math.Abs(mtvTotal.Y) ? mtv.Y : mtvTotal.Y;
            mtvTotal.Z = Math.Abs(mtv.Z) > Math.Abs(mtvTotal.Z) ? mtv.Z : mtvTotal.Z;
        }

    }

    if (mtvTotal != Vector3.Zero)
    {
        IntersectionObject o = new IntersectionObject();
        o.IntersectingGameObject = terra.Owner;
        o.MinimumTranslationVector = mtvTotal;
        o.MeshNumber = 0;
        o.MeshName = terra.Owner.Name;

        return o;
    }
    else
    {
        return null;
    }
}

private void CalculateOverlap(Vector3 axis, ref float shape1Min, ref float shape1Max, ref float shape2Min, ref float shape2Max, out bool error, ref float mtvDistance, ref Vector3 mtv, ref float mtvDirection, Vector3 posA, Vector3 posB)
{
    float d0 = (shape2Max - shape1Min); 
    float d1 = (shape1Max - shape2Min);
    float intersectionDepthScaled = (shape1Min < shape2Min)? (shape1Max - shape2Min) : (shape1Min - shape2Max);

    float axisLengthSquared = Vector3.Dot(axis, axis);
    if (axisLengthSquared < 1.0e-8f)
    {
        error = true;
        return;
    }
    float intersectionDepthSquared = (intersectionDepthScaled * intersectionDepthScaled) / axisLengthSquared;

    error = false;

    if (intersectionDepthSquared < mtvDistance || mtvDistance < 0)
    {
        mtvDistance = intersectionDepthSquared;
        mtv = axis * (intersectionDepthScaled / axisLengthSquared);
        float notSameDirection = Vector3.Dot(posA - posB, mtv);
        mtvDirection =  notSameDirection < 0 ? -1.0f : 1.0f;
        mtv = mtv * mtvDirection;
    }

}

Проблема в том, что это работает, но не по краям. При моем подходе (невидимый хитбокс под поверхностью треугольника)игрок иногда поражает невидимую стену, потому что MTV говорит ему двигаться вверх (хорошо) и в сторону, а также (не хорошо). Математически это правильно, потому что, если игрок погружается в объем поддельного треугольника, mtv, конечно, может решитьпри необходимости сдвиньте его назад более чем на одну ось.

Есть ли способ получить MTV только от проверки на плоский треугольник?Я нашел несколько ссылок, которые говорят мне, «если» есть пересечение.Но я не нашел понятного решения для получения MTV.Я думаю, что мне нужно выяснить, на сколько игрок должен быть сдвинут назад по нормали поверхности?


РЕДАКТИРОВАТЬ (2019-02-12 22:00)

Я обновил код в соответствии с первым ответом.Но я все еще получаю странное поведение при применении вычисленного MTV.Это мой текущий алгоритм:

Мой обновлен Подход алгоритма обнаружения столкновений пока таков:

  1. получить список треугольников, которые находятся близко к игроку
  2. получить вершины и нормали OBBs
  3. перебрать этот список треугольников и выполнить для каждого треугольника (максимум ~ 8 прямо сейчас):
  4. использовать нормаль треугольника и проектировать вершины треугольника иВершины OBB.
  5. используют нормальный # 1 OBB и вершины проектного треугольника и вершины OBB.
  6. используют нормальный # 2 OBB и вершины проектного треугольника и вершины OBB.
  7. используют нормальный OBB# 3 и проецировать вершины треугольника и вершины OBB.
  8. пересекать векторы, которые вы мне сказали (шаги с 5 по 13), и проецировать вершины треугольника и вершины OBB на нормализованную версию этих векторов
  9. Каждый шагвыполняется только в том случае, если предыдущий шаг привел к наложению.Затем длина перекрытия сохраняется в массиве (13 ячеек, по одной на каждый шаг)
  10. Если каждый проецирование приводило к перекрытию, найдите mtv, рассчитав минимальное перекрытие и соответствующий вектор оси.
  11. Умножьте этот вектор на перекрытие и используйте его в качестве MTV.

Это верно?

1 Ответ

0 голосов
/ 12 февраля 2019

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

В грубой силеТаким образом, SAT работает в два этапа, еще пара, чтобы получить MTV.

  • Найдите список минимальных осей разделения для испытаний.

    • Например, для OBB вам нужно будет рассмотреть три направления для граней,и три направления для краев.
  • Спроецируйте две выпуклые формы на ось разделения, чтобы получить интервалы проецирования.

    • например, для каждой фигуры поставьте точки все их вершины в направлении оси.

    • Значения min и max дадут вам интервалы для каждой фигуры.

  • , если два интервала не пересекаются, объекты являются разрозненными и не пересекаются.

  • , если все интервалы пересекаются по каждой оси вращения,объекты пересекаются.

  • , чтобы найти MTV, найти минимальное перекрытие между всеми интервалами оси речи.

  • перекрытие интервала в сочетании снаправление оси разделения, даст вам MTV.

Для выпуклых многогранников, оси разделения для проверки довольно просты.Это:

  • нормали поверхности для каждой фигуры, назовите их A и B.

  • - перекрестное произведение всех ребер A на каждоекрая B.

  • Есть оптимизация, например, нет необходимости проверять оси с противоположным направлением или аналогичным направлением.

  • в качестве примера, OBB нужно будет проверить на три нормали грани и три ребра.

ПРИМЕЧАНИЕ: MTV не всегда может быть лучшим решением вашей проблемы, особенно когда мы говорим о местности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...