Buffers and windows, encapsulated in the GraphicsBuffer
and GraphicsWindow
classes are almost interchangable in Panda. In fact most operations in the GraphicsEngine
class are defined on and return GraphicsOutput
objects, the class that both GraphicsBuffer
and GraphicsWindow
inherit from. Therefore, we will discuss the properties of GraphicsOutput
objects first.
The first very important note is that none of these classes are meant to be constructed directly, i.e.:
myOutput=GraphicsOutput()
myWindow=GraphicsWindow()
myBuffer=GraphicsBuffer()
will not work. Refer to The Graphics Engine for how to create these objects. Furthermore, since GraphicsOutput
is an abstract class, GraphicsWindow
objects will be used in code examples.
All GraphicsOutput
objects have getGsg()
, getPipe()
, and getName()
which return respectively their GraphicsStateGuardian, GraphicsPipe, and name. You can also get the width and height using getXSize()
and getYSize()
.
from pandac.PandaModules import GraphicsWindow
#assume we already have a window setup and in myWindow
myWindowGsg=myWindow.getGsg()
myWindowPipe=myWindow.getPipe()
myWindowName=myWindow.getName()
myWindowWidth=myWindow.getXSize()
myWindowLength=myWindow.getYSize()
You can also save a screenshot from any GraphicsOutput
by using saveScreenShot(fileName)
, where fileName
is the name of the picture(the format of the picture is specified by the extension of filename
). Returns True
upon succes and False
otherwise. The picture is saved in the directory of the script you are running.
from pandac.PandaModules import Filename
myWindow.saveScreenShot(Filename('hello.bmp'))
This naturally flows into rendering into a texture. We'll start with copying a scene. If you want to get a texture that simply copies what's in its GraphicsOutput
object, you must first make a call to setupCopyTexture()
. You can then get the texture by using getTexture()
. You can now apply the texture to a NodePath as you would a texture loaded from memory. Thanks to the magic of pointers, the texture automatically updates itself if the contents of its GraphicsOutput
change. If you do not want this behaviour you should use detachTexture()
when you no longer want the texture to be updated. However, since the first frame is always blank, the best way to use detachTexture()
is in a do-later task or event.
myWindow.setupCopyTexture()
myTexture=myWindow.getTexture()
#assume myModel is already setup
myModel.setTexture(myTexture)
#and if you want to stop the texture from updating itself
def stopUpdating():
global myWindow
myWindow.detachTexture()
taskMgr.doMethodLater(1,stopUpdating,'stops updating')
While this is helpful, you may want to render an entirely new scene into a GraphicsOutput
and then place it on screen (i.e. you have a televsion in your main scene and want to generate the show on the spot). The first thing you do is create a GraphicsOutput
to hold the scene. You do this by calling makeTextureBuffer
. It makes a GraphicsOutput
specifically for rendering a scene and then retrieving it by getTexture()
.
makeTextureBuffer(name, xSize, ySize)
The arguments name
, xSize
, and ySize
mean the same things they do for makeWindow and makeBuffer.
You then have to create a new camera for the new scene, using
base.makeCamera(win, sort=0, scene=None,
displayRegion=(0,1,0,1), aspectRatio=None, camName='cam')
Here's a break down of what the parameters mean:
win | The GraphicsOutput object that you want to make the camera for |
sort | The sort value of the camera. Decides the order in which DisplayRegion s in the same window are drawn. See API for more information. |
scene | Due to deprecation of other functions this parameter does not affect anything. |
displayRegion | The area of the new GraphicsOutput that you want to cover in the form (left start point, right end point, bottom start point, top end point). (0,0) represent the bottom left of the screen and (1,1) represents the top right. Therefore (0,1,0,1) represents the entire area. Arguments must be between 0 and 1. |
aspectRatio | The aspectRatio of the GraphicsOutput . When this is left to None makeCamera uses the aspectRatio of the default window. |
camName | The name of the node that represents this camera in the scene graph |
Cameras render whatever is connected to their ancestors in the scene graph. Therefore if you want a truly independent scene you have to start a new scene graph. Create a dummy NodePath and now reparentTo
the new camera to this node. Now you can treat the new scene and the new camera like you would render and your scene gets drawn to your GraphicsOuptut
.
However, any state changes you make to the NodePath camera
will no longer affect your new camera. Also, since the standard mouse controls work on the camera
NodePath, these will not work either. You can alternatively use the Camera class method setScene(scenePath)
, where scenePath
is the top of the scene graph you want to draw. This preserves the standard heirarchy stated in Camera Control.
# I use a GraphicsBuffer only because this is a process you
# probably want the user to see
myBuffer=myWindow.makeTextureBuffer("Another Scene", 800,600)
# You must pass a string to the NodePath constructor or
# attempts to set it as a parent will remove the child from the graph
myNewScene=NodePath("myRender")
myNewCamera=base.makeCamera(myBuffer)
myNewCamera.reparentTo(myNewScene)
#or myNewCamera.node().setScene(myNewScene)
#You can now get a texture that represents anything
# you do in this new scene (that is still automatically updated)
myTexture=myBuffer.getTexture()