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
    May 30 '15 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
    May 30 '15 at 22:00
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
    May 31 '15 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
    May 31 '15 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
    May 31 '15 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
    Jun 1 '15 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
    Jun 1 '15 at 10:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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