OBB冲突检测错误地检测到翻译向量

问题描述 投票:0回答:1

编辑:已通过stackoverflow.com进行了修复[CalculateOverlap()中缺少某些案例。添加了它们以供您查看。


我有一个C#Hitbox类(请参见下面的代码,尤其是CollisionTest()方法),该类使用SAT来检测两个OBB之间的冲突。这在以下情况下效果很好...

Working collision detection example

...但在以下情况下完全失败:

enter image description here

计算出的最小平移向量太高,导致对象跳跃而不是仅调整到碰撞对象。

为了改善算法我需要做什么?非常感谢您的帮助!

using OpenTK;

namespace KWEngine2.Collision3D
{
    public class Hitbox
    {
        private static Vector3[] STATICVERTICES = new Vector3[]
        { 
            new Vector3(-0.5f, -0.5f, +0.5f),
            new Vector3(+0.5f, -0.5f, +0.5f),
            new Vector3(+0.5f, -0.5f, -0.5f),
            new Vector3(-0.5f, -0.5f, -0.5f),

            new Vector3(+0.5f, +0.5f, +0.5f),
            new Vector3(-0.5f, +0.5f, +0.5f),
            new Vector3(-0.5f, +0.5f, -0.5f),
            new Vector3(+0.5f, +0.5f, -0.5f)
        };

        private static Vector3[] STATICNORMALS = new Vector3[]
        {
            new Vector3(1, 0, 0),
            new Vector3(0, 1, 0),
            new Vector3(0, 0, 1)
        };

        private static Vector3 STATICCENTER = new Vector3(0, 0, 0);

        // Holds the currently transformed vertices of the bounding box
        private Vector3[] _vertices = new Vector3[8];

        // Holds the currently transformed surface normals of the bounding box
        private Vector3[] _normals = new Vector3[3];

        // Holds the currently transformed center point of the bounding box
        private Vector3 _center = new Vector3(0, 0, 0);


        private static Matrix4 CreateModelMatrix(Vector3 position, Quaternion rotation, Vector3 scale)
        {
            // Create the basic 4x4 rotation matrix:
            Matrix4 m = Matrix4.CreateFromQuaternion(rotation);

            // Multiply the first three rows by the scale to add the given scale:
            m.Row0 *= scale.X;
            m.Row1 *= scale.Y;
            m.Row2 *= scale.Z;

            // Replace the lowest row with the position data:
            m.Row3.X = position.X;
            m.Row3.Y = position.Y;
            m.Row3.Z = position.Z;
            m.Row3.W = 1.0f;

            return m;
        }


        public Hitbox()
        {
            Update(Vector3.Zero, Quaternion.Identity, Vector3.One);
        }

        public Hitbox(Vector3 position, Quaternion rotation, Vector3 scale)
        {
            Update(position, rotation, scale);
        }

        public void Update(Vector3 position, Quaternion rotation, Vector3 scale)
        {
            Matrix4 modelMatrix = CreateModelMatrix(position, rotation, scale);

            for (int i = 0; i < 8; i++)
            {
                if (i < 3)
                {
                    Vector3.TransformNormal(ref STATICNORMALS[i], ref modelMatrix, out _normals[i]);
                }
                // transform the corner points by multiplying them by the model matrix:
                Vector3.TransformPosition(ref STATICVERTICES[i], ref modelMatrix, out _vertices[i]);
            }
            // transform the center point by multiplying it by the model matrix:
            Vector3.TransformPosition(ref STATICCENTER, ref modelMatrix, out _center);
        }

        public static bool CollisionTest(Hitbox a, Hitbox b, out Vector3 MTV)
        {

            float mtvDistance = float.MaxValue;
            float mtvDirection = 1;
            MTV = Vector3.Zero;

            for (int i = 0; i < 3; i++)
            {
                bool error;
                float shape1Min, shape1Max, shape2Min, shape2Max;
                SatTest(ref a._normals[i], ref a._vertices, out shape1Min, out shape1Max);
                SatTest(ref a._normals[i], ref b._vertices, out shape2Min, out shape2Max);
                if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
                {
                    return false;
                }
                else
                {
                    CalculateOverlap(ref a._normals[i], ref shape1Min, ref shape1Max, ref shape2Min, ref shape2Max,
                        out error, ref mtvDistance, ref MTV, ref mtvDirection, ref a._center, ref b._center);
                    if (error)
                        return false;
                }


                SatTest(ref b._normals[i], ref a._vertices, out shape1Min, out shape1Max);
                SatTest(ref b._normals[i], ref b._vertices, out shape2Min, out shape2Max);
                if (!Overlaps(shape1Min, shape1Max, shape2Min, shape2Max))
                {
                    return false;
                }
                else
                {
                    CalculateOverlap(ref b._normals[i], ref shape1Min, ref shape1Max, ref shape2Min, ref shape2Max,
                        out error, ref mtvDistance, ref MTV, ref mtvDirection, ref a._center, ref b._center);
                    if (error)
                        return false;
                }

            }
            return true;
        }

        private static void SatTest(ref Vector3 axisToTest, ref Vector3[] points, out float minAlong, out float maxAlong)
        {
            minAlong = float.MaxValue;
            maxAlong = float.MinValue;
            for (int i = 0; i < points.Length; i++)
            {
                float dotVal = Vector3.Dot(points[i], axisToTest);
                if (dotVal < minAlong) minAlong = dotVal;
                if (dotVal > maxAlong) maxAlong = dotVal;
            }
        }

        private static bool Overlaps(float min1, float max1, float min2, float max2)
        {
            return IsBetweenOrdered(min2, min1, max1) || IsBetweenOrdered(min1, min2, max2);
        }

        private static bool IsBetweenOrdered(float val, float lowerBound, float upperBound)
        {
            return lowerBound <= val && val <= upperBound;
        }

        private static void CalculateOverlap(ref 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, ref Vector3 posA, ref Vector3 posB)
        {
            // THIS PART IS NEW!
            float intersectionDepthScaled;
            if (shape1Min < shape2Min)
            {
                if (shape1Max > shape2Max)
                {
                    float diff1 = shape1Max - shape2Max;
                    float diff2 = shape2Min - shape1Min;
                    if(diff1 > diff2)
                    {
                        intersectionDepthScaled = shape2Max - shape1Min;
                    }
                    else
                    {
                        intersectionDepthScaled = shape2Min - shape1Max;
                    }

                }
                else
                {
                    intersectionDepthScaled = shape1Max - shape2Min; // default
                }

            }
            else
            {
                if(shape1Max < shape2Max)
                {
                    float diff1 = shape2Max - shape1Max;
                    float diff2 = shape1Min - shape2Min;
                    if (diff1 > diff2)
                    {
                        intersectionDepthScaled = shape1Max - shape2Min;
                    }
                    else
                    {
                        intersectionDepthScaled = shape1Min - shape2Max;
                    }
                }
                else
                {
                    intersectionDepthScaled = shape1Min - shape2Max; // default
                }

            }
            // END OF NEW PART

            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;
            }
        }
    }
}
c# opengl collision-detection opentk bounding-box
1个回答
0
投票

[似乎在某个地方(进入CalculateOverlap),您检查A的下限是否在B内。您错过了A的下限在B的下限以下并且A的上限在B的上限以上的情况。

© www.soinside.com 2019 - 2024. All rights reserved.