Results 1 to 11 of 11
  1. #1

    Is it possible to find the direction a tri is facing?

    I'm writing some code and I only want it to operate on tri-s facing up. So if they have an angle off the Z axis of more than 90 degrees then they're ignored. Is there any easy way to check this?



  2. #2
    Member Secrop's Avatar
    Join Date
    Dec 2011
    Location
    Berlin
    Posts
    1,915
    It's called the Face Normal...

    Normal = Normalize(cross(vert1-vert0, vert2-vert0))
    where vert0, vert1 and vert2 are the positions of the triangle vertices.

    If in the end, the Z component of the Normal is positive, then the triangle is facing up... more exactly, since the Normal is normalized, the Z component corresponds to the cosine of the angle between the Normal and the Z axis; 1 means the normal and the Z axis are the same, 0 means they are perpendicular, and -1 means the normal is pointing to -Z.



  3. #3
    Thanks! That's really helpful. Does that return a vector? I've never used those before.



  4. #4
    Member Secrop's Avatar
    Join Date
    Dec 2011
    Location
    Berlin
    Posts
    1,915
    yes, the cross product between two vectors (in this case two edges of the triangle,) returns a vector perpendicular to both vectors with a length equal to the product of the lenght of both vectors and the sine of the angle between them. (but since we are normalizing the cross product, the final lenght is just equal to 1)



  5. #5
    Hi again, sorry, it took me longer than I thought to get to this part of my code.

    I realise this:

    Normal = Normalize(cross(vert1-vert0, vert2-vert0))
    Is probably pseudocode, I'm having trouble translating it into actual code. I'm not very used to python, or any weakly typed languages, so I'm not sure how much I have to specify and how much I don't.

    Here's what I have so far in the relevant code block:

    #Extract verts
    v1 = obj.vertices[obj.polygons[x].vertices[0]]
    v2 = obj.vertices[obj.polygons[x].vertices[1]]
    v3 = obj.vertices[obj.polygons[x].vertices[2]]

    print ("Polygon no." + str(x))
    print ("V1 = " + str(v1.co))
    print ("V2 = " + str(v2.co))
    print ("V3 = " + str(v3.co))
    print ("")

    #Create normals
    ???

    #Find face normal
    faceNormal.cross(v2-v1, v3-v1)

    #Normalise result
    faceNormal.normalize()

    #Check if it faces up
    if faceNormal.z > 0:
    Print ("Polygon faces up!")
    else:
    Print ("Polygon is not facing up ")

    Am I supposed to instantiate two new normals using the obj's edges, then cross them? Or do you literally just feed the verts into the cross function? If the latter, do I have to instantiate faceNormal as a normal first?

    Thanks in advance for any help. I'm way out of my depth.



  6. #6
    Member RLGUY's Avatar
    Join Date
    Mar 2017
    Location
    Vancouver
    Posts
    10
    Hey, hope I'm able to help with this.

    The Blender object stores face normals already calculated so you do not need to involve the cross product if you do not want to.

    Here is an example script that loops through the active object's polygons and calculates whether the polygon is pointing 'up'.

    Code:
    import bpy
    
    obj = bpy.context.scene.objects.active
    
    for p in obj.data.polygons:
        # This operation converts the object's local model normal to the world normal (How it is rotated in the 3D viewport)
        normal = obj.matrix_world * p.normal
    
        print("The normal of this polygon is:", normal)
        if normal.z > 0:
            print("This polygon faces up!")
        else:
            print("This polygon is not facing up")
    And if you do want to compute the normal using the vertices, you can use the 'mathutils' library for vector operations like cross and normalize:

    Code:
    import bpy
    import mathutils
    
    
    obj = bpy.context.scene.objects.active
    
    
    for x,p in enumerate(obj.data.polygons):
        #Extract verts
        v1 = obj.matrix_world * obj.data.vertices[p.vertices[0]].co
        v2 = obj.matrix_world * obj.data.vertices[p.vertices[1]].co
        v3 = obj.matrix_world * obj.data.vertices[p.vertices[2]].co
    
    
        print("-------")
        print("Polygon no. " + str(x))
        print("V1 = " + str(v1))
        print("V2 = " + str(v2))
        print("V3 = " + str(v3))
        print("")
    
    
        #Find face normal
        face_normal = mathutils.Vector.cross(v2-v1, v3-v1)
    
    
        #Normalise result
        mathutils.Vector.normalize(face_normal)
        print("Face Normal: " + str(face_normal))
    
    
        #Check if it faces up
        if face_normal.z > 0:
            print ("Polygon faces up!")
        else:
            print ("Polygon is not facing up ")
    Last edited by RLGUY; 02-Dec-17 at 20:20.



  7. #7
    Thanks so much for your reply. I've managed to get the face normal using your code, and it's almost working, but for some reason it's reporting horizontal polygons as being upward facing (z > 0), even though the Z coord of their normal is 0 or -0.

    Here's the code (the stuff about the vertices is just for debugging, I'll be removing it later):

    Code:
        #Keeps track of loop for debugging. Deleteme later
        x=1
        
        #Loop through faces, determining which ones face up
        for p in obj.data.polygons:
            #Deleteme
            v1 = obj.data.vertices[p.vertices[0]]
            v2 = obj.data.vertices[p.vertices[1]]
            v3 = obj.data.vertices[p.vertices[2]]
            
            #Deleteme
            print ("Polygon no." + str(x))
            print ("V1 = " + str(v1.co))
            print ("V2 = " + str(v2.co))
            print ("V3 = " + str(v3.co))
            
            #Deleteme
            x += 1
            
            # This operation converts the object's local model normal to the world normal (How it is rotated in the 3D viewport)
            normal = obj.matrix_world * p.normal
            
            #Determines global orientation of polygon
            print("Normal = ", str(normal))
            if normal.z > 0:
                print("This polygon faces up!")
            else:
                print("This polygon is not facing up")
            
            #Deleteme
            print ("")
    And the output when applied to a standard cube which has been converted to tris (another part of my code does this):

    Code:
    Beginning enforce draft
    Faces converted to tris
    
    Polygon no.1
    V1 = <Vector (1.0000, -1.0000, -1.0000)>
    V2 = <Vector (-1.0000, -1.0000, -1.0000)>
    V3 = <Vector (-1.0000, 1.0000, -1.0000)>
    Normal =  <Vector (0.0000, 0.0000, -1.0000)>
    This polygon is not facing up
    
    
    Polygon no.2
    V1 = <Vector (-1.0000, 1.0000, 1.0000)>
    V2 = <Vector (-1.0000, -1.0000, 1.0000)>
    V3 = <Vector (1.0000, -1.0000, 1.0000)>
    Normal =  <Vector (0.0000, 0.0000, 1.0000)>
    This polygon faces up!
    
    
    Polygon no.3
    V1 = <Vector (1.0000, 1.0000, 1.0000)>
    V2 = <Vector (1.0000, -1.0000, 1.0000)>
    V3 = <Vector (1.0000, -1.0000, -1.0000)>
    Normal =  <Vector (1.0000, -0.0000, 0.0000)>
    This polygon faces up!
    
    
    Polygon no.4
    V1 = <Vector (1.0000, -1.0000, 1.0000)>
    V2 = <Vector (-1.0000, -1.0000, 1.0000)>
    V3 = <Vector (-1.0000, -1.0000, -1.0000)>
    Normal =  <Vector (-0.0000, -1.0000, 0.0000)>
    This polygon faces up!
    
    
    Polygon no.5
    V1 = <Vector (-1.0000, -1.0000, -1.0000)>
    V2 = <Vector (-1.0000, -1.0000, 1.0000)>
    V3 = <Vector (-1.0000, 1.0000, 1.0000)>
    Normal =  <Vector (-1.0000, 0.0000, -0.0000)>
    This polygon is not facing up
    
    
    Polygon no.6
    V1 = <Vector (1.0000, 1.0000, -1.0000)>
    V2 = <Vector (-1.0000, 1.0000, -1.0000)>
    V3 = <Vector (-1.0000, 1.0000, 1.0000)>
    Normal =  <Vector (0.0000, 1.0000, 0.0000)>
    This polygon faces up!
    
    
    Polygon no.7
    V1 = <Vector (1.0000, 1.0000, -1.0000)>
    V2 = <Vector (1.0000, -1.0000, -1.0000)>
    V3 = <Vector (-1.0000, 1.0000, -1.0000)>
    Normal =  <Vector (0.0000, 0.0000, -1.0000)>
    This polygon is not facing up
    
    
    Polygon no.8
    V1 = <Vector (1.0000, 1.0000, 1.0000)>
    V2 = <Vector (-1.0000, 1.0000, 1.0000)>
    V3 = <Vector (1.0000, -1.0000, 1.0000)>
    Normal =  <Vector (0.0000, 0.0000, 1.0000)>
    This polygon faces up!
    
    
    Polygon no.9
    V1 = <Vector (1.0000, 1.0000, -1.0000)>
    V2 = <Vector (1.0000, 1.0000, 1.0000)>
    V3 = <Vector (1.0000, -1.0000, -1.0000)>
    Normal =  <Vector (1.0000, 0.0000, -0.0000)>
    This polygon is not facing up
    
    
    Polygon no.10
    V1 = <Vector (1.0000, -1.0000, -1.0000)>
    V2 = <Vector (1.0000, -1.0000, 1.0000)>
    V3 = <Vector (-1.0000, -1.0000, -1.0000)>
    Normal =  <Vector (-0.0000, -1.0000, -0.0000)>
    This polygon is not facing up
    
    
    Polygon no.11
    V1 = <Vector (-1.0000, 1.0000, -1.0000)>
    V2 = <Vector (-1.0000, -1.0000, -1.0000)>
    V3 = <Vector (-1.0000, 1.0000, 1.0000)>
    Normal =  <Vector (-1.0000, 0.0000, -0.0000)>
    This polygon is not facing up
    
    
    Polygon no.12
    V1 = <Vector (1.0000, 1.0000, 1.0000)>
    V2 = <Vector (1.0000, 1.0000, -1.0000)>
    V3 = <Vector (-1.0000, 1.0000, 1.0000)>
    Normal =  <Vector (0.0000, 1.0000, 0.0000)>
    This polygon faces up!
    As you can see on polygon number 3, the z coord of the normal is 0, but it still detects it as being greater than zero. What's going on here?
    Last edited by Peter Rabbit; 03-Dec-17 at 11:35.



  8. #8
    Member RLGUY's Avatar
    Join Date
    Mar 2017
    Location
    Vancouver
    Posts
    10
    The script reporting the triangle as up for a z coord of '0.0' could be due to a precision issue with how computers do math. Computers aren't able to make calculations with 100% precision, so the z coordinate might have actually been calculated to something like 0.00000000003213 instead of an exact 0, which is what may be causing the triangle to be reported as 'up'.

    You could change the condition to normal.z > some_very_small_number to prevent horizontal triangles from being reported:

    Code:
    // Here 1e-6 is the same as 10^-6 = 0.000001
    if normal.z > 1e-6:
        print("This polygon faces up!")
    else:
        print("This polygon is not facing up")



  9. #9
    Hmmm, so it could be a double, but it's being written to console as a float? Sounds weird, but I suppose that would work.

    Edit: Yep, that works, thanks!



  10. #10
    Member emu's Avatar
    Join Date
    Mar 2007
    Location
    GMT +1 (Czech Republic)
    Posts
    357
    I have just a brief code review.
    Originally Posted by RLGUY View Post
    (...)
    #Find face normal
    face_normal = mathutils.Vector.cross(v2-v1, v3-v1)
    #Normalise result
    mathutils.Vector.normalize(face_normal)
    There is much easier way to do this:
    Code:
    face_normal = mathutils.geometry.normal(v1, v2, v3)
    The method outputs a normalized result and it works with any number of vertices, not only triangles. You can just say:
    Code:
    mathutils.geometry.normal(v.co for v in polygon.vertices)
    Obviously, you need to import mathutils.geometry in the beginning of the script.

    Another note: if you really want to calculate the cross product, just use (v2 - v1).cross(v3 - v1).
    Last edited by emu; 03-Dec-17 at 14:17.
    Since I've got an interesting signature, I don't have to write interesting posts.



  11. #11
    Thanks, in the end I went with RL guy's solution which seemed simpler.



Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •