Hi, I’ve been struggling w/this issue myself. I would like to have a 3d scene sandwiched between a foreground 2d scene and a background 2d scene. After reading the above posts I did a bit of research on my own and came up w/the following results.
Josh’s method works great but only when all of the cameras have the same lens type. This method does not seem to work in a mixed-lens environment.
Below is a detailed account of how I arrived at this conclusion. If anyone can help me figure out how to get this to work properly or has suggestions, I would be very grateful.
I did the following:
a.) Create 3 cameras that are all parented to sibling nodes in the scene graph. One for a foreground, midground and background layer.
b.) Mask out everything for each camera except for one nodepath so each camera only displays what’s in the nodepath it was assigned to (not sure why I had to do this, I thought having the above scene heriarchy would do this)
c.)Provide some controls that allow me to cycle the depth of the layers using Josh’s method and toggle the lenses between Perspective and Orthographic.
I noticed the following:
When all of the layers use the same lens, I have no problem configuring the layers in any order.
When I toggle the lenses to be mixed the orthographic layers ALWAYS appear in front of the perspective layers.
Below is my code: (Sorry for the length, I wanted to be thorough)
To run it you should just have to copy/paste it into a file and then copy the following files from C:\Panda3D-1.3.0\models\maps to a subfolder named “samples” in whatever dir you run this out of
“envir-reeds.png”
“envir-tree2.png”
“envir-mountain1.png”
The desired result is to have the reeds and the mountain cameras be Orthographic (2d) and the tree camera use a Perspective Lens.
The reeds should appear in front of the tree and the tree to appear in front of the mountain.
When this demo launches all of the cameras are Perspective and the affect appears to work. But if you press the [6] button to make the mountain camera use an Orthographic Lens, the mountains pop up in front of the trees and reeds.
Controls:
[1] Toggle the reeds camera on/off
[2] Toggle the tree camera on/off
[3] Toggle the mountain camera on/off
[4] Toggle the reeds camera lens to be Perspective/Orthographic
[5] Toggle the tree camera lens to be Perspective/Orthographic
[6] Toggle the mountain camera lens to be Perspective/Orthographic
[c] Cycle the depth of the cameras using Josh’s method
import direct.directbase.DirectStart
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
#hideFromCameras(...)
#uses drawmasks to hide a nodepath from two cameras.
#I had to do it this way bc. I couldn't figure out how
#to setup the mask so the node was only visible to one camera
#instead I had to specify all cameras that it wasn't visible to
def hideFromCameras(camera1,camera2,nodePath):
camera1Bits = BitArray()
camera1Bits.setWord(0,camera1.node().getCameraMask())
camera2Bits = BitArray()
camera2Bits.setWord(0,camera2.node().getCameraMask())
showMask =~(camera1Bits|camera2Bits)
hideMask = camera1Bits|camera2Bits
clearMask = BitArray.allOff()
nodePath.node().adjustDrawMask(showMask.getWord(0),
hideMask.getWord(0),
clearMask.getWord(0))
#cleanScene(...)
#Disables all of the standard cameras and removes them from the scene
#I wanted to try to set everything up from scratch w/none of panda's default stuff
def cleanScene():
base.cam2d.node().setActive(0)
base.cam.node().setActive(0)
camera.hide()
render.node().removeAllChildren()
render.ls()
#class CameraTester
# Lab class for figuring out how to do interesting stuff w/ multiple cameras in panda
#
class CameraTester(DirectObject):
#I'm setting up the scene so we can have 3 seperate cameras each pointing at a
#different and totally independant part of the scene graph
#to do this I create a new node for each camera to render and make them siblings to a one overarching node that's
#attached to "render"
def __init__(self):
#First setup the heirarchy. One root node w/3 siblings (one for each camera to render)
self._rootNode = PandaNode("root")
self._foregroundRoot= PandaNode("foregroundRoot")
self._midgroundRoot = PandaNode("midgroundRoot")
self._backgroundRoot = PandaNode("backgroundRoot")
self._rootNodePath = render.attachNewNode(self._rootNode )
self._fgRootNodePath = self._rootNodePath.attachNewNode(self._foregroundRoot)
self._mgRootNodePath = self._rootNodePath.attachNewNode(self._midgroundRoot)
self._bgRootNodePath = self._rootNodePath.attachNewNode(self._backgroundRoot)
#Now create some brand spanking new cameras one for each layer
self._foregroundCamera = base.makeCamera(base.win,camName='camFor')
self._midgroundCamera= base.makeCamera(base.win,camName='camMid')
self._backgroundCamera= base.makeCamera(base.win,camName='camBak')
#Make them render ONLY their little corner of the scenegraph
self._foregroundCamera.reparentTo(self._fgRootNodePath)
self._midgroundCamera.reparentTo(self._mgRootNodePath)
self._backgroundCamera.reparentTo(self._bgRootNodePath)
#Create some stuff for them to render
#I probably don't need sperate cardmakers for each of these, but their here and it shouldn't matter
self._foregroundCards= self.createCardMaker("foregroundCard")
self._midgroundCards= self.createCardMaker("midgroundCard")
self._backgroundCards = self.createCardMaker("backgroundCard")
#Create each card using textures found in C:\Panda3D-1.3.0\models\maps
self._foregroundCard =self.setupCard(self._foregroundCards,
self._fgRootNodePath,
"sample/envir-reeds.png")
self._midgroundCard =self.setupCard(self._midgroundCards,
self._mgRootNodePath,
"sample/envir-tree2.png")
self._backgroundCard =self.setupCard(self._backgroundCards,
self._bgRootNodePath,
"sample/envir-mountain1.png")
#for some reason pointing each camera at its own nodepath didn't keep them from rendering the other parts of the
#nodepath so I also explicitly hide everything else in the scene from the cameras exept their personal nodepath
#Here I'm setting up each camera w/an exclusive mask
fg = BitArray.bit(0)
mg = BitArray.bit(1)
bg = BitArray.bit(2)
print "foreground bits: "+repr(fg.getWord(0))
print "midground bits: "+repr(mg.getWord(0))
print "background bits: "+repr(bg.getWord(0))
self._foregroundCamera.node().setCameraMask(fg.getWord(0))
self._midgroundCamera.node().setCameraMask(mg.getWord(0))
self._backgroundCamera.node().setCameraMask(bg.getWord(0))
#Now use my helper function to hide each nodepath from the other cameras
hideFromCameras(self._midgroundCamera,self._backgroundCamera,self._fgRootNodePath)
hideFromCameras(self._foregroundCamera,self._backgroundCamera,self._mgRootNodePath)
hideFromCameras(self._foregroundCamera,self._midgroundCamera,self._bgRootNodePath)
#Press d to cycle the order of the different cameras
self.accept("d", self.displaySortCycleCameras)
#Turn the cameras on/off
self.accept("1", self.toggleCameraActive,[self._foregroundCamera])
self.accept("2", self.toggleCameraActive,[self._midgroundCamera])
self.accept("3", self.toggleCameraActive,[self._backgroundCamera])
#Toggle each camera between Perspective/ Orthographic
self.accept("4", self.toggleCameraLens,[self._foregroundCamera])
self.accept("5", self.toggleCameraLens,[self._midgroundCamera])
self.accept("6", self.toggleCameraLens,[self._backgroundCamera])
#Creates a card attached to a camera node and sets some default settings
def setupCard(self,cardmaker,camera,texture):
outputNode = camera.attachNewNode(cardmaker.generate())
outputNode.setTexture(loader.loadTexture(texture))
outputNode.setTransparency(1)
outputNode.setPos(0, 2, 0)
outputNode.setScale(1)
return outputNode
#Prints stats on the sorting stuff done on this camera
def printSortStats(self,camera):
print "NodePath Sort: "+str(camera.getSort())
print "Bin: "+str(camera.getBinName())
print "Bin Order: "+str(camera.getBinDrawOrder())
print "Display Region Sort: "+str(camera.node().getDisplayRegion(0).getSort())
#Displays the current foreground , midground and background cameras
def printCamStats(self):
print "Foreground Camera is: "+repr( self._foregroundCamera)
print "Midground Camera is: "+repr(self._midgroundCamera)
print "Background Camera is: "+repr(self._backgroundCamera)
#Sets up the display region sort order as per Josh Yolen's suggestion
#To make the foreground camera appear in front of the others and the midground appear in front of the background
#This method works fine as long as all of the cameras have the same lens type
#When the cameras have different lenses however the orthographic lenses get rendered first always
def displaysort(self):
print "Display Depth Sorting cameras"
self._foregroundCamera.node().getDisplayRegion(0).setSort(10)
self._midgroundCamera.node().getDisplayRegion(0).setSort(20)
self._backgroundCamera.node().getDisplayRegion(0).setSort(30)
self.printCamStats()
#Cycle the cameras and sort them using the displayRegion Method
def displaySortCycleCameras(self):
self.cycleCameras()
self.displaysort()
#Cycles the cameras so foreground->midground->background->(new)foreground
def cycleCameras(self):
#copy the current midground
oldMid = self._midgroundCamera
#Move the foreground to the midground
self._midgroundCamera = self._foregroundCamera
#Move the background to the foreground
self._foregroundCamera = self._backgroundCamera
#Move the old midground to the background
self._backgroundCamera = oldMid
#Use [1,2,3] to deactivate each camera. This proves that each scene is being rendered to
#and independant camera
def toggleCameraActive(self,camera):
if(camera.node().isActive()):
print "De-Activating Camera: "+repr(camera)
camera.node().setActive(0)
else:
print "Activating Camera: "+repr(camera)
camera.node().setActive(1)
#Use [4,5,6] to toggle the lens of each camera
def toggleCameraLens(self,camera):
if(camera.node().getLens().isOrthographic()):
print "Setting Camera: "+repr(camera)+" to Perspective Lens"
camera.node().setLens(PerspectiveLens())
else:
print "Setting Camera: "+repr(camera)+" to Orthographic Lens"
orthoLens = OrthographicLens()
orthoLens.setFilmSize(2,2)
orthoLens.setAspectRatio(base.camLens.getAspectRatio())
camera.node().setLens(orthoLens)
#Create a new cardmaker w/default settings
def createCardMaker(self, name):
cardMaker = CardMaker(name)
cardMaker.setFrame(-base.camLens.getAspectRatio(),
base.camLens.getAspectRatio(),
-1,1)
return cardMaker
cleanScene() #Hide/Disable all of the default stuff so I can play
camTester = CameraTester()
run()