Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Do you have any reliable 3d polygonal face normal calculation code?

Any language will do, I'll port it to make it work. Even if you find some code in a 3d game engine and post it here I'll be more than grateful.

Edit:

I'm using this but it fails when faces are 90 degrees upright or similar.

        // the normal point
        var x:Number = 0;
        var y:Number = 0;
        var z:Number = 0;

        // if is a triangle with 3 points
        if (points.length == 3) {

            // read vertices of triangle
            var Ax:Number, Bx:Number, Cx:Number;
            var Ay:Number, By:Number, Cy:Number;
            var Az:Number, Bz:Number, Cz:Number;
            Ax = points[0].x;   Bx = points[1].x;   Cx = points[2].x;
            Ay = points[0].y;   By = points[1].y;   Cy = points[2].y;
            Az = points[0].z;   Bz = points[1].z;   Cz = points[2].z;

            // calculate normal of a triangle
            x = (By - Ay) * (Cz - Az) - (Bz - Az) * (Cy - Ay);
            y = (Bz - Az) * (Cx - Ax) - (Bx - Ax) * (Cz - Az);
            z = (Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax);

        // if is a polygon with 4+ points
        }else if (points.length > 3){

            // calculate normal of a polygon using all points
            var n:int = points.length;          
            x = 0;
            y = 0;
            z = 0

            // ensure all points above 0
            var minx:Number = 0, miny:Number = 0, minz:Number = 0;
            for (var p:int = 0, pl:int = points.length; p < pl; p++) {
                var po:_Point3D = points[p] = points[p].clone();
                if (po.x < minx) {  minx = po.x;     }
                if (po.y < miny) {  miny = po.y;     }
                if (po.z < minz) {  minz = po.z;     }
            }
            if (minx > 0 || miny > 0 || minz > 0){
                for (p = 0; p < pl; p++) {
                    po = points[p];
                    po.x -= minx;
                    po.y -= miny;
                    po.z -= minz;
                }
            }

            var cur:int = 1, prev:int = 0, next:int = 2;
            for (var i:int = 1; i <= n; i++) {

                // using Newell method
                x += points[cur].y * (points[next].z - points[prev].z);
                y += points[cur].z * (points[next].x - points[prev].x);
                z += points[cur].x * (points[next].y - points[prev].y);
                cur = (cur+1) % n;
                next = (next+1) % n;
                prev = (prev+1) % n;
            }
        }

        // length of the normal
        var length:Number = Math.sqrt(x * x + y * y + z * z);

        // if area is 0
        if (length == 0) {
            return null;

        }else{
            // turn large values into a unit vector
            x = x / length;
            y = y / length;
            z = z / length;
        }

Edit:

I can somehow display 3d arrows for each calculated normal and go so far as to document test cases where it worked and failed. But I was looking for a simpler solution, simply by using code that someone else has created and found to be working. Is OpenGL all you have? Doesn't anyone have any triangle/polygonal normal calculation code at all?

To Jari:

I'm using this code to calculate normals, as built from OpenGL's pseudo code. Is there something I've done wrong? and what would happen if the points are not perfectly inplane? would it fail and return a random normal or just change the normal slightly?

            n = points.length;
            x = y = z = 0;
            for (i = 0; i < n; i++) {
                j = (i + 1) % n;
                x += (points[i].y - points[j].y) * (points[i].z + points[j].z);
                y += (points[i].z - points[j].z) * (points[i].x + points[j].x);
                z += (points[i].x - points[j].x) * (points[i].y + points[j].y);
            }
share|improve this question
    
Out of curiosity, what is the language of the code snippet? –  Ricket Feb 6 '11 at 6:25
    
@Ricket - ActionScript 3, similar to JavaScript. Why? does it look neat and elegent? :) –  Jarvis Feb 6 '11 at 7:25
1  
Be specific. How does it fail? Just for triangles, just for quads, or for both? Can you give some specific test cases? –  Peter Taylor Feb 7 '11 at 13:31
    
@Peter - It fails for quads especially when faces are exactly upright and the x/y/z values are the same. So I ended up rotating faces a little bit for the normal to be calculated exactly. I think it has something to do with the multiplies in the Newell section, they depend on either having some value. What if x/y/z is 0? you see? the entire multiply would fail and return a value of 0. –  Jarvis Feb 7 '11 at 21:43
    
@Jenko, those multiplies are on the RHS of a +=, so it isn't necessarily a problem if some of them are zero. –  Peter Taylor Feb 7 '11 at 23:23

5 Answers 5

up vote 2 down vote accepted
+100

I'm successfully using this for quads / triangles.

Loop through each face, and pass in 3 verticies. If you have a quad ABCD pass in ABD. For example for the front facing face on this cube, I would pass in, v2, v3, v0

// cube ///////////////////////////////////////////////////////////////////////
//    v6----- v5
//   /|      /|
//  v1------v0|
//  | |     | |
//  | |v7---|-|v4
//  |/      |/
//  v2------v3

This is in C++ which supports operator overloading, so if in AS3 convert to Vector3d, which I notice you're not using now, might as well. ( I could see why you aren't, sometimes when trying to make found code work i also want to rule out any variables that could make it fail )

//  Modified from http://www.fullonsoftware.co.uk/snippets/content/Math_-_Calculating_Face_Normals.pdf
Vec3f RibbonMesh::calcNormal( const Vec3f &p1, const Vec3f &p2, const Vec3f &p3 )
{
    Vec3f V1= (p2 - p1);
    Vec3f V2 = (p3 - p1);
    Vec3f surfaceNormal;
    surfaceNormal.x = (V1.y*V2.z) - (V1.z-V2.y);
    surfaceNormal.y = - ( (V2.z * V1.x) - (V2.x * V1.z) );
    surfaceNormal.z = (V1.x-V2.y) - (V1.y-V2.x);


    // Dont forget to normalize if needed
    return surfaceNormal;
}

Edit: Found source where I got this from

share|improve this answer
    
Thanks for the code! I'll try it out and let you know my results.. –  Jarvis Feb 12 '11 at 21:13
    
Did you have any luck? –  onedayitwillmake Feb 15 '11 at 5:24
    
Since yours is the only decent looking code, and since Jari has nothing but advice to give I'm going to award you the bounty although I've not had the time to try out your snippet. Thanks for sending code when I asked for code! –  Jarvis Feb 17 '11 at 1:14
    
@Jenko - so you basically punished me for not writing code ouright to you, instead rewarding some other answer which you didn't even verify it. Thanks. –  Jari Komppa Feb 17 '11 at 6:11
    
@Jari - Hey! don't take offense. No punishment and no bad feelings. Just that this poor guy took the trouble to get some 'unique' looking code that I could try out and might really help. Your advice, however rare, is something that I'll have to work out myself .. nothing 'cut-paste' about that. I just wanted to award the bounty to someone who might value it more. Besides you have gotten a lot of score on the answer itself! kudos on that and have a nice day :) ... and moreover it would look awful on me to award the bounty to someone who didn't even answer the title of the question, "any code?" –  Jarvis Feb 18 '11 at 10:30

It may be mathematically equivalent - check it if you want - but the code given there is not the same as the pseudocode for Newell's method given on the OpenGL wiki. See http://www.opengl.org/wiki/Calculating_a_Surface_Normal

share|improve this answer
    
OpenGL's version was even worse and it failed for more cases, I haven't visualised the normals by drawing arrows, but it did give me many more silly errors. –  Jarvis Feb 8 '11 at 15:34

OpenGL wiki's pseudocode (as referenced by Peter Taylor, as well) is correct. What you want is the normal of the plane the triangle, quad or other polygon represents. All you need are two edges of any planar polygon that share a common vertex.

The number of edges does not matter. All you need is to get enough information to define the plane, and then get the normal of said plane.

share|improve this answer
    
As to the code you posted, I don't have time right now to go deeper into it, but at least - you only need to care about three points (and two edges). Anything more will just confuse the issue. –  Jari Komppa Feb 12 '11 at 8:51
    
thanks for the nice reply, sorry for being harsh. do i really need just 3 points? but i thought more would be helpful if its a convex polygon? sorry for being dumb about things because i'm a developer, not a mathematician, so the more you can talk to me about which piece of code to use the easier it will be for me. thanks. –  Jarvis Feb 12 '11 at 10:23
1  
Think of the polygon as something that's laying on an infinite plane. To know where the plane is, you just need three points. More points won't define the plane any better. –  Jari Komppa Feb 12 '11 at 12:08

A cross product ought to be perfectly reliable for any planar polygon. If you've got problems with that, it's almost certainly just a bug in your code.

As others have said, it would be useful to know why you think your normals are wrong.

share|improve this answer
    
Can you see if there's anything wrong with the openGL code I posted? any idea if 3 points would be sufficient for a poly and there may be no need to use the multi-point code above? –  Jarvis Feb 12 '11 at 9:51
1  
If all points lie in a plane, then 3 points are sufficient unless the three points lie along a straight line. –  Jason Goemaat Feb 12 '11 at 10:27

I'll post my solution too, it takes the average of the normals of all triangles that a vertex belongs to.

It takes a mesh with vertices and indices and calculates the normals for all vertices.

for(int i = 0; i < indices.Length; i += 3)
{
   Vector3 v0 = vertices[indicies[i]].Position;
   Vector3 v1 = vertices[indicies[i+1]].Position;
   Vector3 v2 = vertices[indicies[i+2]].Position;

   Vector3 normal = Vector3.Normalize(Vector3.Cross(v2 - v0, v1 - v0)); //This is the normal of the triangle if that's all you're interested in.

   vertices[indicies[i]].Normal += normal;
   vertices[indicies[i+1]].Normal += normal;
   vertices[indicies[i+2]].Normal += normal;
}
for(int i = 0; i < vertices.Length; i++)
{
   vertices[i].Normal = Vector3.Normalize(vertices[i].Normal);
}
share|improve this answer
2  
+1 - Thanks for the code! Who downvoted this? you have a problem with code? his snippet is functional .... cross product of the 2 vectors normalised. –  Jarvis Feb 14 '11 at 1:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.