Skip to content

Instantly share code, notes, and snippets.

@Kif11
Created June 7, 2017 17:11
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Kif11/247f6b05e8d3a6c3ffb193b8c6f4dec7 to your computer and use it in GitHub Desktop.
Save Kif11/247f6b05e8d3a6c3ffb193b8c6f4dec7 to your computer and use it in GitHub Desktop.
Maya script to find if object located within camera frustum
import maya.cmds as cmds
import maya.OpenMaya as OpenMaya
import math
# Find if object located within camera frustum
# Usage:
# from obj_in_frust import in_frustum
# in_frustum('camera1', 'pCube1')
class Plane(object):
def __init__(self, normalisedVector):
# OpenMaya.MVector.__init__()
self.vector= normalisedVector
self.distance= 0.0
def relativeToPlane(self, point):
# Converting the point as a vector from the origin to its position
pointVec= OpenMaya.MVector( point.x, point.y, point.z )
val= (self.vector * pointVec) + self.distance
if (val > 0.0):
return 1 # In front
elif (val < 0.0):
return -1 # Behind
return 0 # On the plane
class Frustum(object):
def __init__(self, cameraName):
# Initialising selected transforms into its associated dagPaths
selectionList= OpenMaya.MSelectionList()
objDagPath= OpenMaya.MDagPath()
selectionList.add( cameraName )
selectionList.getDagPath(0, objDagPath)
self.camera= OpenMaya.MFnCamera(objDagPath)
self.nearClip = self.camera.nearClippingPlane()
self.farClip = self.camera.farClippingPlane()
self.aspectRatio= self.camera.aspectRatio()
left_util= OpenMaya.MScriptUtil()
left_util.createFromDouble( 0.0 )
ptr0= left_util.asDoublePtr()
right_util= OpenMaya.MScriptUtil()
right_util.createFromDouble( 0.0 )
ptr1= right_util.asDoublePtr()
bot_util= OpenMaya.MScriptUtil()
bot_util.createFromDouble( 0.0 )
ptr2= bot_util.asDoublePtr()
top_util= OpenMaya.MScriptUtil()
top_util.createFromDouble( 0.0 )
ptr3= top_util.asDoublePtr()
stat= self.camera.getViewingFrustum(self.aspectRatio, ptr0, ptr1, ptr2, ptr3, False, True)
planes= []
left= left_util.getDoubleArrayItem(ptr0, 0)
right= right_util.getDoubleArrayItem(ptr1, 0)
bottom= bot_util.getDoubleArrayItem(ptr2, 0)
top = top_util.getDoubleArrayItem(ptr3, 0)
## planeA = right plane
a= OpenMaya.MVector(right, top, -self.nearClip)
b= OpenMaya.MVector(right, bottom, -self.nearClip)
c= (a ^ b).normal() ## normal of plane = cross product of vectors a and b
planeA = Plane(c)
planes.append(planeA)
## planeB = left plane
a = OpenMaya.MVector(left, bottom, -self.nearClip)
b = OpenMaya.MVector(left, top, -self.nearClip)
c= (a ^ b).normal()
planeB= Plane( c )
planes.append( planeB )
##planeC = bottom plane
a = OpenMaya.MVector(right, bottom, -self.nearClip)
b = OpenMaya.MVector(left, bottom, -self.nearClip)
c= (a ^ b).normal()
planeC= Plane( c )
planes.append( planeC )
##planeD = top plane
a = OpenMaya.MVector(left, top, -self.nearClip)
b = OpenMaya.MVector(right, top, -self.nearClip)
c= (a ^ b).normal()
planeD= Plane( c )
planes.append( planeD )
# planeE = far plane
c = OpenMaya.MVector(0, 0, 1)
planeE= Plane( c )
planeE.distance= self.farClip
planes.append( planeE )
# planeF = near plane
c = OpenMaya.MVector(0, 0, -1)
planeF= Plane( c )
planeF.distance= self.nearClip
planes.append( planeF )
self.planes = planes
self.numPlanes = 6
def relativeToFrustum(self, pointsArray):
numInside= 0
numPoints= len( pointsArray )
for j in range( 0, 6 ):
numBehindThisPlane= 0
for i in range( 0, numPoints ):
if (self.planes[j].relativeToPlane(pointsArray[i]) == -1): # Behind
numBehindThisPlane += 1
if numBehindThisPlane == numPoints:
##all points were behind the same plane
return False
elif (numBehindThisPlane==0):
numInside += 1
if (numInside == self.numPlanes):
return True # Inside
return True # Intersect
def in_frustum(cameraName, objectName):
"""
returns: True if withing the frustum of False if not
"""
selectionList = OpenMaya.MSelectionList()
camDagPath = OpenMaya.MDagPath()
selectionList.add( cameraName )
selectionList.getDagPath(0, camDagPath)
cameraDagPath = OpenMaya.MFnCamera( camDagPath )
camInvWorldMtx = camDagPath.inclusiveMatrixInverse()
fnCam = Frustum(cameraName)
points = []
# For node inobjectList
selectionList = OpenMaya.MSelectionList()
objDagPath = OpenMaya.MDagPath()
selectionList.add(objectName)
selectionList.getDagPath( 0, objDagPath )
fnDag = OpenMaya.MFnDagNode(objDagPath)
obj = objDagPath.node()
dWorldMtx = objDagPath.exclusiveMatrix()
bbox = fnDag.boundingBox()
minx = bbox.min().x
miny = bbox.min().y
minz = bbox.min().z
maxx = bbox.max().x
maxy = bbox.max().y
maxz = bbox.max().z
# Getting points relative to the cameras transmformation matrix
points.append( bbox.min() * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(maxx, miny, minz) * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(maxx, miny, maxz) * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(minx, miny, maxz) * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(minx, maxy, minz) * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(maxx, maxy, minz) * dWorldMtx * camInvWorldMtx )
points.append( bbox.max() * dWorldMtx * camInvWorldMtx )
points.append( OpenMaya.MPoint(minx, maxy, maxz) * dWorldMtx * camInvWorldMtx )
return fnCam.relativeToFrustum(points)
@SatishGodaPearl
Copy link

Thank you for sharing this recipe :)

@Kif11
Copy link
Author

Kif11 commented Jan 11, 2020

I'm glad it helped!

@manuelspk
Copy link

I just wanted to say thank you. Works like a charm and It's exactly what I need. Thanks!

@loucifer00
Copy link

Hi, I'm a total noob and I had a question how to apply this to use as a geo camera frustum cull.
I executed the script via the script editor and nothing seemed to happen?

@linez69
Copy link

linez69 commented Nov 8, 2022

thanks, that's awesome!

@iamkartiks69
Copy link

iamkartiks69 commented Apr 26, 2023

First of all thanks a lot it really helped but I need your help with one thing, I used you script to change Level of Detail attribute of the object and set it to boundingbox if it's not in frustum, so do you have any idea how can i use it on dynamic moving camera, for ex: If a camera is moving it'll keep changing object's attribute depending upon if it's in the frustum or not ?

Thanks & Regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment