Switch all normals to "outside"



  • On 08/03/2013 at 06:17, xxxxxxxx wrote:

    Hi all,

    I have a little problem.

    I've a big amount of objects.
    Each object is modified with the following instructions:
       Select all points and optimize
       Select all polygons, untriangulate and generate N-Gons.
    The result are many objects. But not always the normals point to outside.

    So: I'm looking for an Idea how to select these objects where the normals point to inside of the object.

    Does anybody have an idea how to solve the problem??

    Thanks a lot



  • On 08/03/2013 at 06:48, xxxxxxxx wrote:

    it depends. you have only quite limited access to the polygon normals in python.
    in pseudocoode:

    if ((obj_world_coords + point[n] + normal_point[n]).GetLength() > 
       (obj_world_coords + point[n] ).GetLength()) :
    	#positive
    else :
     #negative
    

    you can get the normals with PolygonObject.CreatePhongNormals or you can calculate
    the normals for yourself for a polygon, but when the normals are being flipped due to
    some modeling operations the results returned won't be the same as in the editor, so
    a phong tag is the best way, if you can create one or there is already one.

    for ply in polygonObject.GetAllPolygons() : 
    	a, b, c = polygonObject.GetPoint(ply.a), polygonObject.GetPoint(ply.b),  polygonObject.GetPoint(ply.c)
    	normal = ((a - b) % (b - c)).GetNormalized()
    


  • On 10/03/2013 at 03:09, xxxxxxxx wrote:

    Hi Ronald,

    I've been thinking about this yesterday and came up with an algorithm that seems to be
    consistent with non-overlapping (meaning no self-intersections!) and closed meshes.

    The c4dtools package now includes this algorithm since version 1.2.5 in the
    c4dtools.misc.normalalign module. The source-code can be found here.

    The idea is to shoot rays from three different positions in the world into the polygon-object
    for each polygon and test the angle between the polygon-normal and the ray. When the
    face is "on the opposite side" of the object, therefore the polygon's normal is intended to
    backface the camera, this is figured out by how many intersections have occure before the ray
    hit the polygon that is currently being tested.

    The ray's must be shot from three different positions since otherwise we would not be able to
    tell if a polygon's normal points into the right direction when it's surface normal is exactly orthogonal to the ray shot in.

    Here's an excerpt from the code:

    def test_object_normals(op, info=None, logger=None) :
        r"""
        Tests the polygon-object *op*'s normals if they're pointing to the
        in or outside of the object. Returns a list of boolean variables
        where each index defines wether the associated polygon's normal
        is pointing into the right direction or not.
      
        The algorithm works best on completely closed shapes with one
        segment only. The polygon-object should also be valid, therefore not
        more than two polygons per edge, etc. The results might be incorrect
        with an invalid mesh structure.
      
        :param op: A :class:`c4d.PolygonObject` instance to test.
        :param info: A :class:`~c4dtools.utils.PolygonObjectInfo` instance for
                the passed object, or None to generate on demand.
        :return:   :class:`list` of `bool` and the PolygonObjectInfo
                instance.
        """
      
        if not info:
            info = PolygonObjectInfo()
            info.init(op)
        if info.polycount <= 0:
            return []
      
        collider = GeRayCollider()
        if not collider.Init(op) :
            raise RuntimeError('GeRayCollider could not be initialized.')
      
        mg = op.GetMg()
        mp = op.GetMp()
        size = op.GetRad()
      
        # Define three camera position for the object. We could simply use
        # one if there wouldn't be the special case where a polygon's normal
        # is exactly in an angle of 90°, where we can not define wether the
        # normal is correctly aligned or not.
      
        maxp = mp + size + c4d.Vector(size.GetLength() * 2)
        cam1 = c4d.Vector(maxp.x, 0, 0)
        cam2 = c4d.Vector(0, maxp.y, 0)
        cam3 = c4d.Vector(0, 0, maxp.z)
      
        # Check each polygon from each camera position for the angle between
        # them. If one of the angles is greater than 90°, the face is pointing
        # into the wrong direction.
      
        result = []
        iterator = enumerate(zip(info.normals, info.midpoints))
        for index, (normal, midpoint) in iterator:
            normal_aligned = False
      
            for cam in [cam1, cam2, cam3]:
      
                # Compute the direction vector from the cam to the midpoint
                # of the polygon and the ray length to garuantee a hit with
                # the polygon.
                direction = (midpoint - cam)
                length = direction.GetLengthSquared()
                direction.Normalize()
      
                # Compute the intersections point from the cam to the midpoint
                # of the polygon.
                collider.Intersect(cam, direction, length)
                intersections = {}
                for i in xrange(collider.GetIntersectionCount()) :
                    isect = collider.GetIntersection(i)
      
                    # The GeRayCollider class may yield doubled intersections,
                    # we filter them out this way.
                    if isect['face_id'] not in intersections:
                        intersections[isect['face_id']] = isect
      
                # Sort the intersections by distance to the cam.
                intersections = sorted(
                        intersections.values(),
                        key=lambda x: x['distance'])
      
                # Find the intersection with the current polygon and how
                # many polygons have been intersected before this polygon
                # was intersection.
                isect_index = -1
                isect = None
                for i, isect in enumerate(intersections) :
                    if isect['face_id'] == index:
                        isect_index = i
                        break
      
                # We actually *have* to find an intersection, it would be
                # a strange error if we wouldn't have found one.
                if isect_index < 0:
                    if logger:
                        message = "No intersection with face %d from cam %s"
                        logger.warning(message % (index, cam))
                    continue
      
                angle = VectorAngle(normal, direction * -1)
      
                # If there has been one intersection with another face before
                # the intersection with the current polygon, the polygon is
                # assumed to be intended to face away from the camera. Same for
                # all other odd numbers of intersection that have occured
                # before the intersection with the current face.
                if isect_index % 2:
                    angle = (math.pi / 2) - angle
      
                if not xor(isect['backface'], isect_index % 2) :
                    normal_aligned = True
      
            result.append(normal_aligned)
      
        return result, info
    

    Note : Requires utf-8 encoding when used, since the comments contain the non-ascii character  °  .

    -Niklas


Log in to reply