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)
    

Log in to reply