0

I've attempted to write a Comparer class for Vector3s (or, rather, VertexPositionNormalTextures) which will sort a list of them into clockwise order in order to fix my backface culling issues. I've tried to adapt a very common method on the web which gets a signed angle between two vectors. However, at runtime my list complains that the sorting method is inconsistent, which is to say that either two items are compared inconsistently on different passes, or an item is compared to itself and not found equal.

Here's the comparer class:

public class ClockwiseSorter : IComparer<VertexPositionNormalTexture>
    {
        private Vector3 center;
        public ClockwiseSorter(Vector3 c)
        {
            center = c;
        }
        public int Compare(VertexPositionNormalTexture a, VertexPositionNormalTexture b)
        {
            Vector3 normal = Vector3.Cross(a.Position - center, b.Position - center);
            normal.Normalize();
            double theta = GetSignedAngleBetween2DVectors(a.Position - center, b.Position - center, Vector3.Cross(a.Position - center, normal));

            if (theta > 0)
                return -1;
            else if (theta < 0)
                return 1;
            else
                return 0;
        }

        /// Find the angle between two vectors. This will not only give the angle difference, but the direction.
        /// For example, it may give you -1 radian, or 1 radian, depending on the direction. Angle given will be the 
        /// angle from the FromVector to the DestVector, in radians.
        /// </summary>
        /// <param name="FromVector">Vector to start at.</param>
        /// <param name="DestVector">Destination vector.</param>
        /// <param name="DestVectorsRight">Right vector of the destination vector</param>
        /// <returns>Signed angle, in radians</returns>        
        /// <remarks>All three vectors must lie along the same plane.</remarks>
        public static double GetSignedAngleBetween2DVectors(Vector3 FromVector, Vector3 DestVector, Vector3 DestVectorsRight)
        {
            FromVector.Normalize();
            DestVector.Normalize();
            DestVectorsRight.Normalize();

            float forwardDot = Vector3.Dot(FromVector, DestVector);
            float rightDot = Vector3.Dot(FromVector, DestVectorsRight);

            // Keep dot in range to prevent rounding errors
            forwardDot = MathHelper.Clamp(forwardDot, -1.0f, 1.0f);

            double angleBetween = Math.Acos(forwardDot);

            if (rightDot < 0.0f)
                angleBetween *= -1.0f;

            return angleBetween;
        }

    }

I get the center vector by averaging the values of all the vectors:

        private Vector3 AverageVectors(List<VertexPositionNormalTexture> vectors)
    {
        Vector3 sum = Vector3.Zero;
        foreach (VertexPositionNormalTexture vector in vectors)
            sum += vector.Position;
        return (sum / vectors.Count);
    }

Am I screwing up somewhere, not thinking about this correctly? Or have I calculated the normal or center incorrectly? Backface culling has been nothing but a headache for me, I'm kind of desperate to move on...

A-Type
  • 1,148
  • 1
  • 7
  • 17
  • Am I correct in assuming that you're sorting three vertices at a time? Any more than that just doesn't make sense for backface-culling problems. – TreDubZedd Sep 30 '11 at 15:05
  • I'm actually sorting 4. It's a little odd, yes. I'm sorting at the quad level instead of the triangle level, however I could easily transition to the triangle level if that's easier. – A-Type Sep 30 '11 at 16:49

1 Answers1

1

If you have a triangle with vertex A,B and C,

the triangle normal is calcultated with the cross product operation

N = (B-A) x (C-A)

now you have to determine what direction is facing the normal.

enter image description here

for this can be useful the center of the mesh as you use in your code, though if mesh is not convex it can provide erroneous results.

You have to determine the angle between the normal and the vector build from the center of triangle to the center of the mesh.

TriangleCenter= (A+B+C) / 3;

P = MeshCenter - TriangleCenter;

P.Normalize();
N.Normalize();

the dot product  d = P.N = |P||N|.cos(alfa)

if (d>0) P and N are in the same direction  (AntiClockWise order) 
if (d<0) P and N are in opposite direction  (Clockwise order)
Blau
  • 5,742
  • 1
  • 18
  • 27
  • To be honest I'm a little confused as to why myself now that I've gotten some sleep.. I think I'm confusing two methods. My thinking was to take the angle of each around the center. I'm afraid I don't understand the relevancy of your drawing. Still, I'll go off the final point: So I have all 3 points of a triangle and I want them to be in clockwise order according to the normal of the face. What is the 'right way' to calculate that? – A-Type Sep 30 '11 at 16:51
  • the position of center c1 or c2 is arbitrary, for the same triangle the order is different if center is at right or left from AB. Your calcs. To see how determine it you can see this [answer](http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1165943#1165943). – Blau Sep 30 '11 at 17:57
  • That seems like a nice solution for 2D vectors, but I'm not sure how to do it with 3D vectors. – A-Type Sep 30 '11 at 18:59