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.
Solved Determine if a polygon is facing the camera
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
- Q&A New Functionality.
- How to Post Questions especially the tagging part.
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.
MAXON SDK Specialist
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
MAXON SDK Specialist
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)