###########################################################
# Raycaster - Railshooter Module
# Traces a Ray and detects Collisions with Ray
###########################################################

# DESCRIPTION:
# This Class traces a Ray and picks objects that collide
# with this ray.  The pick function returns the nodepath that is 
# in the front-most position.  

    	
from direct.showbase.ShowBaseGlobal import *


########################################
# Raycaster Class
########################################
# Define a Raycaster Class to trace Rays
class Raycaster:
    def __init__( self, objectToPickFrom, ignorableNodes=[] ):

	self.objectToPickFrom = objectToPickFrom
	self.ignorableNodes = ignorableNodes


	########################################
	# Create the Collision Ray
	########################################

	# Make the Collision Ray Node
	self.rayCollisionNode = CollisionNode( "rayNode" )
	self.rayCollisionNodePath = objectToPickFrom.attachNewNode( self.rayCollisionNode )
	# self.rayCollisionNodePath.hide()

	# Ray Collides only against BitMask 1
	self.rayCollisionNode.setFromCollideMask(BitMask32.bit(1)) 

	# Create the Collision Ray
	self.ray = CollisionRay()
	self.ray.setOrigin( Point3( 0, 0, 0 ) )
	self.ray.setDirection( Vec3( 0, 1, 0 ) )
	self.rayCollisionNode.addSolid( self.ray )

	# Create the Collision Handler
	self.cq = CollisionHandlerQueue()
	self.numEntries = 0
	self.ct = CollisionTraverser()
	self.ct.addCollider( self.rayCollisionNodePath, self.cq )

	# add objectToPickFrom to self.ignorableNodes so that we don't hit it
	self.ignorableNodes.append( objectToPickFrom.node() )


    ########################################
    # Define Methods
    ########################################

    # Pick which nodePath the ray is colliding with and return the 
    # nodePath.
    def pick( self, targetNodePath=render ):

	self.ct.traverse( targetNodePath )
	self.numEntries = self.cq.getNumEntries()
	self.cq.sortEntries()

	# Check Entries from Traverser
	for i in range( 0, self.numEntries ):
	    entry = self.cq.getEntry( i )
	    node = entry.getIntoNode()
 	    nodePath = targetNodePath.findPathTo( node )

	    # We have a Collision!
	    if (not self.isIgnorable(nodePath)):

		# return the object we hit
		return nodePath

	# Else, nothing hit
	return None


    # Define which Nodepaths are Excluded from the Ray Picking
    def isIgnorable( self, nodePath ):
	if( nodePath.node() in self.ignorableNodes ):
	    return True
	ancestry = map( lambda x: x.node(), nodePath.getAncestry() )
	for item in self.ignorableNodes:
	    if( item in ancestry ):
		return True
	return False

