The GraphicsOutput class

Don't mind the mess!

We're currently in the process of migrating the Panda3D Manual to a new service. This is a temporary layout in the meantime.

Top
Python C++

This page is not in the table of contents.

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 panda3d.core 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 success and False otherwise. The picture is saved in the directory of the script you are running.

from panda3d.core 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:

winThe GraphicsOutput object that you want to make the camera for
sortThe sort value of the camera. Decides the order in which DisplayRegions in the same window are drawn. See API for more information.
sceneDue to deprecation of other functions this parameter does not affect anything.
displayRegionThe 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.
aspectRatioThe aspectRatio of the GraphicsOutput. When this is left to None makeCamera uses the aspectRatio of the default window.
camNameThe 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()
Top