2
$\begingroup$

I've been working on retopo tools. I have created polygon loops with "bmesh.faces.new()". But i get a problem with face normal orientation. Some faces get inverted direction.

Here is my test: http://youtu.be/AfsMQRLa7Zo

Here is the code where i create faces: https://github.com/mifth/mifthtools/blob/4f3fec160c820dc66516a94ffa070261bafb9962/blender/addons/mira_tools/mi_curve_surfaces.py#L489-L501

Also, i added recalculation of normals "bmesh.ops.recalc_face_normals()" but it did not help me.

It would be great if you have some ideas how to fix it. Thanks.

$\endgroup$
2
  • $\begingroup$ You will probably need to keep track of the view matrix 'eye position' and let that decide whether you index a face CW or CCW from that perspective, as it will decide the orientation of the Face Normal. $\endgroup$
    – zeffii
    Commented May 30, 2015 at 21:42
  • $\begingroup$ or .. make the face, check if it is back or front facing from current viewing position and reverse the indices if it is back facing. Depending on your algorithm you could then automatically draw subsequent faces in the same orientation.. $\endgroup$
    – zeffii
    Commented May 30, 2015 at 22:00

1 Answer 1

2
$\begingroup$

something like this would let you determine if the polygon is back or front facing in the current view context, given a bm and a polygon to check:

def is_front_facing(context, bm, polygon):
    """
    When deciding if a polygon is facing the camera, you need 
    only calculate the dot product of the normal vector of     
    that polygon, with a vector from the camera to one of the 
    polygon's vertices. 

    - If the dot product is less than zero, the polygon is facing the camera. 
    - If the value is greater than zero, it is facing away from the camera.
    """

    region = context.region  
    rv3d = context.space_data.region_3d  

    # neat eye location code with the help of paleajed
    eye = Vector(rv3d.view_matrix[2][:3])
    eye.length = rv3d.view_distance
    eye_location = rv3d.view_location + eye  

    pnormal = obj.matrix_world.to_3x3() * polygon.normal
    world_coordinate = obj.matrix_world * polygon.verts[0].co

    result_vector = eye_location - world_coordinate
    dot_value = pnormal.dot(result_vector)            

    return not (dot_value < 0.0)

I ripped this out of some old code, it might help to see it in context if it doesn't work. It will be important to establish which rv3d to use, because a polygon that is back-facing if viewed from the top, will be reversed if viewed from the bottom.

$\endgroup$
5
  • 1
    $\begingroup$ Should be pnormal = obj.matrix_world.to_3x3() * polygon.normal also, normalizing wont change the dot sign (so isn't needed). $\endgroup$
    – ideasman42
    Commented May 31, 2015 at 9:03
  • $\begingroup$ "pnormal = obj.matrix_world.to_3x3() * polygon.normal" will not be correct if the object has unproportional scale or rotation. The normal formula will be correct only for non scaled and non rotate objects. Is it posible to fix? $\endgroup$
    – mifth
    Commented May 31, 2015 at 19:46
  • $\begingroup$ "pnormal = obj.matrix_world.to_3x3() * polygon.normal" will not be correct if the object has unproportional scale. For rotation it's ok. But for scale - it will be incorrect. For example if an object has "scale = 0.2, 1.0, 1.0" - normal will be distorted and incorrect with matrix_world.to_3x3() . $\endgroup$
    – mifth
    Commented May 31, 2015 at 20:10
  • $\begingroup$ obj.matrix_world also produces unexpected results with sheer matrices - but i don't know if that's a bug or intended limited behaviour of matrix_world @ideasman42 $\endgroup$
    – zeffii
    Commented Jun 1, 2015 at 5:34
  • $\begingroup$ You can normalize the matrix, but it may be less trouble to project all points onto screen-space, then get the sign of the 2d polygons cross product. $\endgroup$
    – ideasman42
    Commented Jun 1, 2015 at 10:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .