Deteksi tabrakan 3D tercepat antara dua kotak terikat berorientasi (OBB)

10

Saya berada di titik dalam permainan saya di mana saya perlu menambahkan sistem tabrakan. Saya mencoba jBullet, dan sementara itu berhasil, itu bukan apa yang saya cari. Saya hanya ingin cara sederhana untuk menguji apakah dua pohon berorientasi kotak berlari (OBB) bertabrakan.

Saya akan melakukan tabrakan dengan menggunakan pohon itu. Buat AABB untuk broadphase, lalu jika itu lulus tes jika setiap OBB di pohon bertabrakan dengan pohon lain.

Saya memang menemukan beberapa hal di internet, tetapi saya tidak dapat memahaminya sepenuhnya. Apa yang saya minta adalah situs web atau sumber daya yang menjelaskan tabrakan OBB 3D dengan baik?

Saya belajar bahwa GJK lebih cepat dari SAT, dan tampaknya bisa memberi tahu saya seberapa jauh kotak-kotak itu saling menembus. Saya menemukan beberapa barang GJK, tetapi itu bukan kotak; sebaliknya, hal-hal yang lebih kompleks dan membingungkan.

Saya hanya ingin dapat membuat OBB dari 3 vektor: pusat, ukuran, dan rotasi setiap sumbu. Kemudian bisa menguji tabrakan dengan itu. Terima kasih sebelumnya atas semua yang Anda posting.

andrew3ds
sumber
Anda yakin perlu kotak pembatas? Jika Anda memiliki pohon tulang, dan Anda memiliki pusat dan gagasan kasar tentang volume masing-masing tulang, bola bundar lebih mudah dan jauh lebih cepat untuk diimplementasikan.
johnwbyrd

Jawaban:

0

Saya melihat tautan dalam komentar Anda yang menunjukkan karakter dengan OBB yang disilang di sekeliling mesh. Kadang-kadang itu bisa dilakukan dengan bola pembatas dan kemudian tidak ada masalah orientasi dan tes bola biasanya lebih cepat.

Karakter Anda, jika menunjukkan struktur pembatas seperti di tautan Anda akan lebih mirip lelaki Michelin .

Steve H.
sumber
0

Ini adalah contoh kerja aktual dari AABB, yang langsung dari mesin gim saya:

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;

namespace GrimoireEngine.Framework.Maths
{
    public struct BoundingBox : IEquatable<BoundingBox>
    {
        public Vector3 Min;
        public Vector3 Max;

        public const int CornerCount = 8;

        public static Vector3 MaxVector3
        {
            get
            {
                return new Vector3(float.MaxValue);
            }
        }

        public static Vector3 MinVector3
        {
            get
            {
                return new Vector3(float.MinValue);
            }
        }

        public static BoundingBox Identity
        {
            get
            {
                return new BoundingBox(Vector3.Zero, Vector3.One);
            }
        }

        public static BoundingBox Zero
        {
            get
            {
                return new BoundingBox();
            }
        }

        public BoundingBox(Vector3 min, Vector3 max)
        {
            Min = min;
            Max = max;
        }

        public BoundingBox(
            float minX, float minY, float minZ,
            float maxX, float maxY, float maxZ)
            : this(
                new Vector3(minX, minY, minZ),
                new Vector3(maxX, maxY, maxZ))
        { }

        public bool Collides(BoundingBox box)
        {
            if (box.Max.X < Min.X
                || box.Min.X > Max.X
                || box.Max.Y < Min.Y
                || box.Min.Y > Max.Y
                || box.Max.Z < Min.Z
                || box.Min.Z > Max.Z)
            {
                return false;
            }
            if (box.Min.X >= Min.X
                && box.Max.X <= Max.X
                && box.Min.Y >= Min.Y
                && box.Max.Y <= Max.Y
                && box.Min.Z >= Min.Z
                && box.Max.Z <= Max.Z)
            {
                return true;
            }
            return true;
        }

        public ContainmentType Contains(BoundingBox box)
        {
            if (box.Max.X < Min.X
                || box.Min.X > Max.X
                || box.Max.Y < Min.Y
                || box.Min.Y > Max.Y
                || box.Max.Z < Min.Z
                || box.Min.Z > Max.Z)
            {
                return ContainmentType.Disjoint;
            }
            if (box.Min.X >= Min.X
                && box.Max.X <= Max.X
                && box.Min.Y >= Min.Y
                && box.Max.Y <= Max.Y
                && box.Min.Z >= Min.Z
                && box.Max.Z <= Max.Z)
            {
                return ContainmentType.Contains;
            }
            return ContainmentType.Intersects;
        }

        public bool Collides(BoundingFrustum frustum)
        {
            int i;
            bool contained;
            Vector3[] corners = frustum.GetCorners();
            for (i = 0; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = false;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = true;
                }
                else
                {
                    contained = true;
                }
                if (contained == false)
                {
                    break;
                }
            }
            if (i == corners.Length)
            {
                return true;
            }
            if (i != 0)
            {
                return true;
            }
            i++;
            for (; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = false;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = true;
                }
                else
                {
                    contained = true;
                }
                if (contained != true)
                {
                    return true;
                }
            }
            return true;
        }

        public ContainmentType Contains(BoundingFrustum frustum)
        {
            int i;
            ContainmentType contained;
            Vector3[] corners = frustum.GetCorners();
            for (i = 0; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = ContainmentType.Disjoint;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = ContainmentType.Intersects;
                }
                else
                {
                    contained = ContainmentType.Contains;
                }
                if (contained == ContainmentType.Disjoint)
                {
                    break;
                }
            }
            if (i == corners.Length)
            {
                return ContainmentType.Contains;
            }
            if (i != 0)
            {
                return ContainmentType.Intersects;
            }
            i++;
            for (; i < corners.Length; i++)
            {
                if (corners[i].X < Min.X
                    || corners[i].X > Max.X
                    || corners[i].Y < Min.Y
                    || corners[i].Y > Max.Y
                    || corners[i].Z < Min.Z
                    || corners[i].Z > Max.Z)
                {
                    contained = ContainmentType.Disjoint;
                }
                else if (corners[i].X == Min.X
                         || corners[i].X == Max.X
                         || corners[i].Y == Min.Y
                         || corners[i].Y == Max.Y
                         || corners[i].Z == Min.Z
                         || corners[i].Z == Max.Z)
                {
                    contained = ContainmentType.Intersects;
                }
                else
                {
                    contained = ContainmentType.Contains;
                }
                if (contained != ContainmentType.Contains)
                {
                    return ContainmentType.Intersects;
                }
            }
            return ContainmentType.Contains;
        }

        public bool Collides(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X >= sphere.Radius
                && sphere.Center.Y - Min.Y >= sphere.Radius
                && sphere.Center.Z - Min.Z >= sphere.Radius
                && Max.X - sphere.Center.X >= sphere.Radius
                && Max.Y - sphere.Center.Y >= sphere.Radius
                && Max.Z - sphere.Center.Z >= sphere.Radius)
            {
                return true;
            }
            double dmin = 0;
            double e = sphere.Center.X - Min.X;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.X - Max.X;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Y - Min.Y;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Y - Max.Y;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Z - Min.Z;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return false;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Z - Max.Z;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return false;
                    }
                    dmin += e * e;
                }
            }
            return dmin <= sphere.Radius * sphere.Radius;
        }

        public ContainmentType Contains(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X >= sphere.Radius
                && sphere.Center.Y - Min.Y >= sphere.Radius
                && sphere.Center.Z - Min.Z >= sphere.Radius
                && Max.X - sphere.Center.X >= sphere.Radius
                && Max.Y - sphere.Center.Y >= sphere.Radius
                && Max.Z - sphere.Center.Z >= sphere.Radius)
            {
                return ContainmentType.Contains;
            }
            double dmin = 0;
            double e = sphere.Center.X - Min.X;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.X - Max.X;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Y - Min.Y;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Y - Max.Y;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            e = sphere.Center.Z - Min.Z;
            if (e < 0)
            {
                if (e < -sphere.Radius)
                {
                    return ContainmentType.Disjoint;
                }
                dmin += e * e;
            }
            else
            {
                e = sphere.Center.Z - Max.Z;
                if (e > 0)
                {
                    if (e > sphere.Radius)
                    {
                        return ContainmentType.Disjoint;
                    }
                    dmin += e * e;
                }
            }
            return dmin <= sphere.Radius * sphere.Radius ? ContainmentType.Intersects : ContainmentType.Disjoint;
        }

        public bool Collides(Vector3 point)
        {
            if (point.X < Min.X
                || point.X > Max.X
                || point.Y < Min.Y
                || point.Y > Max.Y
                || point.Z < Min.Z
                || point.Z > Max.Z)
            {
                return false;
            }
            if (point.X == Min.X
                || point.X == Max.X
                || point.Y == Min.Y
                || point.Y == Max.Y
                || point.Z == Min.Z
                || point.Z == Max.Z)
            {
                return true;
            }
            return true;
        }

        public ContainmentType Contains(Vector3 point)
        {
            ContainmentType result;
            if (point.X < Min.X
                || point.X > Max.X
                || point.Y < Min.Y
                || point.Y > Max.Y
                || point.Z < Min.Z
                || point.Z > Max.Z)
            {
                result = ContainmentType.Disjoint;
            }
            else if (point.X == Min.X
                     || point.X == Max.X
                     || point.Y == Min.Y
                     || point.Y == Max.Y
                     || point.Z == Min.Z
                     || point.Z == Max.Z)
            {
                result = ContainmentType.Intersects;
            }
            else
            {
                result = ContainmentType.Contains;
            }
            return result;
        }

        public bool Equals(BoundingBox other)
        {
            return (Min == other.Min) && (Max == other.Max);
        }

        public override bool Equals(object obj)
        {
            return (obj is BoundingBox) && Equals((BoundingBox)obj);
        }

        public Vector3[] GetCorners()
        {
            return new[] {
                new Vector3(Min.X, Max.Y, Max.Z),
                new Vector3(Max.X, Max.Y, Max.Z),
                new Vector3(Max.X, Min.Y, Max.Z),
                new Vector3(Min.X, Min.Y, Max.Z),
                new Vector3(Min.X, Max.Y, Min.Z),
                new Vector3(Max.X, Max.Y, Min.Z),
                new Vector3(Max.X, Min.Y, Min.Z),
                new Vector3(Min.X, Min.Y, Min.Z)
            };
        }

        public override int GetHashCode()
        {
            return Min.GetHashCode() + Max.GetHashCode();
        }

        public bool Intersects(BoundingBox box)
        {
            if ((Max.X >= box.Min.X) && (Min.X <= box.Max.X))
            {
                if ((Max.Y < box.Min.Y) || (Min.Y > box.Max.Y))
                {
                    return false;
                }
                return (Max.Z >= box.Min.Z) && (Min.Z <= box.Max.Z);
            }
            return false;
        }

        public bool Intersects(BoundingFrustum frustum)
        {
            return frustum.Intersects(this);
        }

        public bool Intersects(BoundingSphere sphere)
        {
            if (sphere.Center.X - Min.X > sphere.Radius
                && sphere.Center.Y - Min.Y > sphere.Radius
                && sphere.Center.Z - Min.Z > sphere.Radius
                && Max.X - sphere.Center.X > sphere.Radius
                && Max.Y - sphere.Center.Y > sphere.Radius
                && Max.Z - sphere.Center.Z > sphere.Radius)
            {
                return true;
            }
            double dmin = 0;
            if (sphere.Center.X - Min.X <= sphere.Radius)
            {
                dmin += (sphere.Center.X - Min.X) * (sphere.Center.X - Min.X);
            }
            else if (Max.X - sphere.Center.X <= sphere.Radius)
            {
                dmin += (sphere.Center.X - Max.X) * (sphere.Center.X - Max.X);
            }
            if (sphere.Center.Y - Min.Y <= sphere.Radius)
            {
                dmin += (sphere.Center.Y - Min.Y) * (sphere.Center.Y - Min.Y);
            }
            else if (Max.Y - sphere.Center.Y <= sphere.Radius)
            {
                dmin += (sphere.Center.Y - Max.Y) * (sphere.Center.Y - Max.Y);
            }
            if (sphere.Center.Z - Min.Z <= sphere.Radius)
            {
                dmin += (sphere.Center.Z - Min.Z) * (sphere.Center.Z - Min.Z);
            }
            else if (Max.Z - sphere.Center.Z <= sphere.Radius)
            {
                dmin += (sphere.Center.Z - Max.Z) * (sphere.Center.Z - Max.Z);
            }
            return dmin <= sphere.Radius * sphere.Radius;
        }

        public PlaneIntersectionType Intersects(Plane plane)
        {
            Vector3 positiveVertex;
            Vector3 negativeVertex;
            if (plane.Normal.X >= 0)
            {
                positiveVertex.X = Max.X;
                negativeVertex.X = Min.X;
            }
            else
            {
                positiveVertex.X = Min.X;
                negativeVertex.X = Max.X;
            }
            if (plane.Normal.Y >= 0)
            {
                positiveVertex.Y = Max.Y;
                negativeVertex.Y = Min.Y;
            }
            else
            {
                positiveVertex.Y = Min.Y;
                negativeVertex.Y = Max.Y;
            }
            if (plane.Normal.Z >= 0)
            {
                positiveVertex.Z = Max.Z;
                negativeVertex.Z = Min.Z;
            }
            else
            {
                positiveVertex.Z = Min.Z;
                negativeVertex.Z = Max.Z;
            }
            float distance = plane.Normal.X * negativeVertex.X + plane.Normal.Y * negativeVertex.Y + plane.Normal.Z * negativeVertex.Z + plane.D;
            if (distance > 0)
            {
                return PlaneIntersectionType.Front;
            }
            distance = plane.Normal.X * positiveVertex.X + plane.Normal.Y * positiveVertex.Y + plane.Normal.Z * positiveVertex.Z + plane.D;
            if (distance < 0)
            {
                return PlaneIntersectionType.Back;
            }
            return PlaneIntersectionType.Intersecting;
        }

        public float? Intersects(Ray ray)
        {
            return ray.Intersects(this);
        }

        public static bool operator ==(BoundingBox a, BoundingBox b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(BoundingBox a, BoundingBox b)
        {
            return !a.Equals(b);
        }

        public override string ToString()
        {
            return "{{Min:" + Min + " Max:" + Max + "}}";
        }

        public void DrawImmediate()
        {
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Max.Y, Min.Z);
            GL.Vertex3(Min.X, Max.Y, Min.Z);
            GL.Vertex3(Min.X, Min.Y, Min.Z);
            GL.Vertex3(Max.X, Min.Y, Min.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Min.Y, Max.Z);
            GL.Vertex3(Max.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Max.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Max.Y, Min.Z);
            GL.Vertex3(Max.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Max.Z);
            GL.Vertex3(Min.X, Max.Y, Min.Z);
            GL.End();
            GL.Begin(PrimitiveType.LineLoop);
            GL.Vertex3(Max.X, Min.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Max.Z);
            GL.Vertex3(Min.X, Min.Y, Min.Z);
            GL.Vertex3(Max.X, Min.Y, Min.Z);
            GL.End();
        }
    }
}

Cukup hapus metode tipe Bounding lainnya.

Krythic
sumber
3
Ini tidak terlihat seperti OOB bagi saya, tetapi AABB, yang sepertinya bukan yang dicari OP?
Tyyppi_77
@ Tyyppi_77 Judul pertanyaan berbunyi "Algoritma Bounding Box Tercepat". Ini adalah masalah dengan pertanyaan yang ambigu.
Krythic
3
Badan pertanyaan tampaknya menunjukkan dengan sangat jelas bahwa OP bertanya tentang OOB: "Saya hanya ingin dapat membuat OBB".
Tyyppi_77
-2

Molly Rocket selamanya adalah temanmu.

http://mollyrocket.com/849

Tapi sepertinya Anda salah memahami penggunaan umum kotak pembatas. Anda tidak benar-benar menggunakannya untuk sistem tabrakan fisika. Terutama ketika itu bisa sangat tidak efisien untuk penggunaan semacam itu.

Mungkin Anda memikirkan permintaan tabrakan Grafik Adegan? Di mana Anda memeriksa apakah suatu objek memasuki QuadTree, atau Octree, dan Anda dengan cepat membangun kembali grafik Anda.

moonshineTheleocat
sumber
Maaf jika saya salah paham. Cara saya berpikir untuk melakukan tabrakan adalah dengan memiliki setiap tulang di jala memiliki kotak pembatas, yang masing-masing akan bergerak bersama dengan matriks tulang. Misalnya: tautan . Tampaknya lebih cepat daripada harus melakukan tabrakan trimesh, terutama jika mesh akan memiliki animasi kerangka. Saya tidak akan memiliki fisika yang kompleks, hanya panggilan balik tabrakan sederhana untuk memberitahukan tabrakan antara jerat.
andrew3ds
1
Jawaban Anda sangat bergantung pada tautan eksternal. Saya sarankan Anda memperbarui jawabannya untuk memasukkan informasi yang relevan di sini kalau-kalau tautannya turun suatu hari.
MichaelHouse
Oh Kotak hit adalah yang Anda inginkan. Kotak hit tidak harus berupa pohon, dan tidak juga benar-benar AAB. Mereka pada dasarnya adalah jerat tabrakan yang tak terlihat yang melekat pada tulang. Perpustakaan fisika yang Anda gunakan sebelumnya dapat dengan mudah membantu Anda melakukan ini juga. Tapi ya, GTK masih bisa bekerja dengan cukup baik dalam sistem semacam ini. Terutama jika Anda ingin tahu apa yang melakukan apa.
moonshineTheleocat