[Solved] Problems when projecting segment onto OBB axis.

Started by
3 comments, last by Spa8nky 14 years, 5 months ago
Here is the ray/intersection test I am using:

            // Turn ray into segment
            Vector3 A = Origin;
            Vector3 B = Origin + Direction * 1000.0f;        // Max length of ray (can be reduced)

            // Segment mid point
            Vector3 m = (A + B) * 0.5f;

            // Segment halflength vector
            Vector3 d = A - m;

            // Translate OBB and segment to origin
            m = m - oBB.Centre;

            // Project the segment onto the separating axes
            float adx = Math.Abs(Vector3.Dot(oBB.Axis_X, d));
            float ady = Math.Abs(Vector3.Dot(oBB.Axis_Y, d));
            float adz = Math.Abs(Vector3.Dot(oBB.Axis_Z, d));

            // Obb axes are othogonal
            Console.WriteLine(Vector3.Cross(oBB.Axis_X, oBB.Axis_Y));
            Console.WriteLine(oBB.Axis_Z);

            // Try world coordinates as separating values
            if (Math.Abs(m.X) > oBB.Extents.X + adx)
            {
                return false;
            }
            if (Math.Abs(m.Y) > oBB.Extents.Y + ady)
            {
                return false;
            }
            if (Math.Abs(m.Z) > oBB.Extents.Z + adz)
            {
                return false;
            }

            // Add in an epsilon term to counteract arithmetic errors when segment is (near parallel to a coordinate axis
            adx += GameConstants.EPSILON;
            ady += GameConstants.EPSILON;
            adz += GameConstants.EPSILON;

            // Try cross products of segment direction vector with coordinate axes
            if (Math.Abs(m.Y * d.Z - m.Z * d.Y) > oBB.Extents.Y * adz + oBB.Extents.Z * ady)
            {
                return false;
            }
            if (Math.Abs(m.Z * d.X - m.X * d.Z) > oBB.Extents.X * adz + oBB.Extents.Z * adx)
            {
                return false;
            }
            if (Math.Abs(m.X * d.Y - m.Y * d.X) > oBB.Extents.X * ady + oBB.Extents.Y * adx)
            {
                return false;
            }

            return true;


The problem is the intersection test is not returning true when the OBB rotation is anything but axis aligned (Matrix.Identity). Have the following lines not projected the ray/segment onto the OBB axes correctly: // Project the segment onto the separating axes float adx = Math.Abs(Vector3.Dot(oBB.Axis_X, d)); float ady = Math.Abs(Vector3.Dot(oBB.Axis_Y, d)); float adz = Math.Abs(Vector3.Dot(oBB.Axis_Z, d)); Or have I made a different mistake entirely? Thank you. [Edited by - Spa8nky on November 10, 2009 9:41:44 AM]
Advertisement
Ok, I've now solved the problem but I have another:

When returning the surface normal of the OBB from a given point, is the following correct:

        /// <summary>        /// Return the surface normal for any given point on the OBB        /// </summary>        /// <param name="point"></param>        /// <returns></returns>        public Vector3 GetNormalFromPoint(Vector3 point)        {            Vector3 normal = Vector3.Zero;            float min = float.MaxValue;            float distance;            point -= Centre;            distance = Math.Abs(Extents.X - Math.Abs(point.X));            if (distance < min)            {                min = distance;                normal = Math.Sign(point.X) * Axis_X;       // Rotation.M11, Rotation.M12, Rotation.M13            }            distance = Math.Abs(Extents.Y - Math.Abs(point.Y));            if (distance < min)            {                min = distance;                normal = Math.Sign(point.Y) * Axis_Y;       // Rotation.M21, Rotation.M22, Rotation.M23            }            distance = Math.Abs(Extents.Z - Math.Abs(point.Z));            if (distance < min)            {                min = distance;                normal = Math.Sign(point.Z) * Axis_Z;       // Rotation.M31, Rotation.M32, Rotation.M33            }            return normal;        }


If not, then please feel free to scorn :)

Thanks

EDIT: The method above isn't correct :(

[Edited by - Spa8nky on November 9, 2009 7:47:55 PM]
I got it, woohoo!

        /// <summary>        /// Return the surface normal for any given point on the OBB        /// </summary>        /// <param name="point"></param>        /// <returns></returns>        public Vector3 GetNormalFromPoint(Vector3 point)        {            Vector3 normal = Vector3.Zero;            float min = float.MaxValue;            float distance;            point -= Centre;            // Compute translation vector t            Vector3 t = point - Centre;            t.X = Vector3.Dot(point, Axis_X);            t.Y = Vector3.Dot(point, Axis_Y);            t.Z = Vector3.Dot(point, Axis_Z);            distance = Math.Abs(Extents.X - Math.Abs(t.X));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.X) * Axis_X;       // Rotation.M11, Rotation.M12, Rotation.M13            }            distance = Math.Abs(Extents.Y - Math.Abs(t.Y));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.Y) * Axis_Y;       // Rotation.M21, Rotation.M22, Rotation.M23            }            distance = Math.Abs(Extents.Z - Math.Abs(t.Z));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.Z) * Axis_Z;       // Rotation.M31, Rotation.M32, Rotation.M33            }            return normal;        }


Seems to work just fine! Any comments?
Just that
Vector3 t = point - Centre;
is overwritten immediately after that line. So I assume that
Vector3 t;
would be already sufficient.
Oh yes, thank you.

The code now reads as follows:

        /// <summary>        /// Return the surface normal for any given point on the OBB        /// </summary>        /// <param name="point"></param>        /// <returns></returns>        public Vector3 GetNormalFromPoint(Vector3 point)        {            Vector3 normal = Vector3.Zero;            float min = float.MaxValue;            float distance;            // Compute translation vector t            point -= Centre;            Vector3 t;            // Bring translation into OBB's coordinate frame            t.X = Vector3.Dot(point, Axis_X);            t.Y = Vector3.Dot(point, Axis_Y);            t.Z = Vector3.Dot(point, Axis_Z);            distance = Math.Abs(Extents.X - Math.Abs(t.X));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.X) * Axis_X;       // Rotation.M11, Rotation.M12, Rotation.M13            }            distance = Math.Abs(Extents.Y - Math.Abs(t.Y));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.Y) * Axis_Y;       // Rotation.M21, Rotation.M22, Rotation.M23            }            distance = Math.Abs(Extents.Z - Math.Abs(t.Z));            if (distance < min)            {                min = distance;                normal = Math.Sign(t.Z) * Axis_Z;       // Rotation.M31, Rotation.M32, Rotation.M33            }            return normal;        }


I think this is about as straight forward as I can make this method. If I try simplifying it:

            // Compute translation vector t            Vector3 t = point - Centre;            // Bring translation into OBB's coordinate frame            t.X = Vector3.Dot(t, Axis_X);            t.Y = Vector3.Dot(t, Axis_Y);            t.Z = Vector3.Dot(t, Axis_Z);


The code won't work as I am changing t each time.



This topic is closed to new replies.

Advertisement