SOLVED Determine if a polygon is facing the camera

How can I determine if a polygon on a polygonal object is facing the current (user or editor) camera?
I know there is a BackfaceCulling function but it always returns me with True.

hello,

I've marked this thread as a question so when you considered it as solved, please change the state 🙂
again, use our forum functionalities

The python documentation is not the best for this function. The passed data must be in the same space. Internally it just do a Dot product and check if it's positive or negative.

   #inside a python tag  

    #retrieves the object where this tag is attached    
    obj = op.GetObject()
    # gets the Matrix of this object
    objMg = obj.GetMg()
    
    # retrieves the BaseDraw of view 0 (uselly the perspective)
    bd = doc.GetBaseDraw(0)
    
    # retrieves the scene camera
    cam = bd.GetSceneCamera(doc)
    
    #get the camera matrix
    camMg = cam.GetMg()
    
    # the Z axis of the object will be the normal of the plane XY
    normal = objMg.v3
        
    # Get the vector that is pointing toward the camera from object.
    objPos = camMg.off - objMg.off 
    
    #Normalize the vector isn't necessary but it's alway a good idea to debug/understand what's going on
    normal.Normalize()
    objPos.Normalize()

    
    print  bd.BackfaceCulling(normal, objPos), normal.Dot(objPos)

Cheers,
Manuel.

Thank you Manuel.
Your code is for an object, not a face.
So, I tried to adapt it and this is what I got (and it doesn't really work):

def main():
    selected=doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0)

    if selected==[]: return
    
    bd = doc.GetActiveBaseDraw()
    cam = bd.GetSceneCamera(doc)
    if cam == None: cam = bd.GetEditorCamera()
    cam_mg = cam.GetMg() # in case I need it
    cam_off = cam_mg.off # in case I need it

    for op in selected:
        if op.GetType() != 5100: continue
        mg = op.GetMg()
        faces = op.GetAllPolygons()
        points = op.GetAllPoints()
        
        selection = op.GetPolygonS()
        selection.DeselectAll()
        
        for i,poly in enumerate(faces):
            a,b,c,d = poly.a,poly.b,poly.c,poly.d
            pta = points[a]
            ptb = points[b]
            ptc = points[c]
            ptd = points[d]
            
            v1 = pta-ptb
            v2 = ptb-ptc
            
            normal = v1.Cross(v2)
            normal.Normalize()
            normal = normal * mg
            
            if c != d:
                center = c4d.Vector((pta.x+ptb.x+ptc.x+ptd.x)/4.0,(pta.y+ptb.y+ptc.y+ptd.y)/4.0,(pta.z+ptb.z+ptc.z+ptd.z)/4.0)
            else:
                center = c4d.Vector((pta.x+ptb.x+ptc.x)/3.0,(pta.y+ptb.y+ptc.y)/3.0,(pta.z+ptb.z+ptc.z)/3.0)
    
            center = center * mg
            
            is_vis = bd.BackfaceCulling(normal, center)
            
            if (is_vis == True):
                selection.Select(i)
                
    c4d.EventAdd()

What could be wrong?

That wasn't clear, sorry. Pick the center of the polygon and get the vector pointing towards the camera. You have to pass that vector.

 # Get the vector that is pointing toward the camera from object.
    objPos = camMg.off - objMg.off 

The function do a Dot product between those vectors (the dot product return an angle).
So the normal of the polygon and the vector from that polygon to the camera.

Now you should be able to change your code.
let me know.

import c4d

def main():

    #Cheks the document and an object is selected
    if doc is None:
        raise ValueError("there no document")

    if op is None:
        raise ValueError("there no object selected")    

    #Checks if it's a polygon object
    if not op.IsInstanceOf(c4d.Opolygon):
        raise ValueError("object must be a polygonObject")

    #Retrieves the camera     
    bd = doc.GetActiveBaseDraw()
    if bd is None:
        raise ValueError("error while retrieving the active BaseDraw of the document")


    cam = bd.GetSceneCamera(doc)
    if cam == None: 
        cam = bd.GetEditorCamera()

    #Gets the camera position.
    cam_mg = cam.GetMg() 
    cam_off = cam_mg.off 

    
    
    opMg = op.GetMg()
    
    faces = op.GetAllPolygons()
    points = op.GetAllPoints()
    
    selection = op.GetPolygonS()
    selection.DeselectAll()
    
    #Checks for each polygon if they face the camera or not.
    for i, poly in enumerate(faces):

        #calculate the normal of the polygon.
        a, b, c, d = poly.a, poly.b, poly.c, poly.d
        pta = points[a]
        ptb = points[b]
        ptc = points[c]
        ptd = points[d]
        
        v1 = pta-ptb
        v2 = ptb-ptc
        
        normal = v1.Cross(v2)
        normal.Normalize()

        #Gets the normal in the world coordinates.
        normal = normal * opMg
        
        #Calculates the center of the polygon.
        if c != d:
            center = c4d.Vector( pta.x + ptb.x + ptc.x + ptd.x , pta.y + ptb.y + ptc.y + ptd.y, pta.z + ptb.z + ptc.z + ptd.z) / 4.0
        else:
            center = c4d.Vector(pta.x + ptb.x + ptc.x, pta.y+ptb.y+ptc.y, pta.z + ptb.z + ptc.z) / 3.0

        #Gets the center coordinates in the globalspace.
        center = center * opMg
        #Gets the vector pointing towards the camera.
        direction = cam_off - center
        
        #Checks if the polygon is visible.
        isVisible = bd.BackfaceCulling(normal, direction)
        
        if isVisible:
            selection.Select(i)
            
    c4d.EventAdd()

if __name__ == '__main__':
    main()

The c++ is a bit more clear, both the normal and the center need to be in camera space. And the center in camera space is the vector from the camera pointing to the center.

Cheers,
Manuel

Once again, thank you, Manuel.
I had to do a few changes and it is working now.
I even made it select the faces that are within a certain angle from the camera.
Here is my code:

            for i,poly in enumerate(faces):
                a,b,c,d = poly.a,poly.b,poly.c,poly.d
                pta = points[a]*mg
                ptb = points[b]*mg
                ptc = points[c]*mg
                ptd = points[d]*mg
    
                v1 = pta-ptb
                v2 = ptb-ptc
    
                normal = v1.Cross(v2)
                normal.Normalize()
    
                if c != d:
                    center = c4d.Vector((pta.x+ptb.x+ptc.x+ptd.x)/4.0,(pta.y+ptb.y+ptc.y+ptd.y)/4.0,(pta.z+ptb.z+ptc.z+ptd.z)/4.0)
                else:
                    center = c4d.Vector((pta.x+ptb.x+ptc.x)/3.0,(pta.y+ptb.y+ptc.y)/3.0,(pta.z+ptb.z+ptc.z)/3.0)
    
                direction = cam_off - center
                norm_dir = direction.GetNormalized()
                angle = NINETY - normal.Dot(norm_dir)
    
                if (angle > 0.0 and angle < max_ang):
                    selection.Select(i)