How Do I Do Viewports?

I want to put a small window in the upper right corner of my render window and render a separate scene than that in the render window. In OpenGL I would do this by setting up a 2nd viewport (glViewPort(…)). How would I do this in Panda? I seem to remember a ‘render to texture’ solution being posted previously but my searches haven’t found it. Anyone have that thread bookmarked? Are there other solutions to this problem?

Thanks,
Paul

Have you already seen the samples that come with Panda?

You really should have a look at them, there’s at least one example of rendering a different viewport to a texture.

(if you are under windows, you’ll find it here: Panda3D-1.3.2\samples\Feature-Tutorials–Render-To-Texture)

Yes, that is the ‘thread’ I was looking for (or so I thought it was a thread). Still, are there other options for multiple viewports other than render to texture?

Thx,
Paul

Yes. The easiest way is simply to create a new DisplayRegion on your window. This corresponds directly to a glViewport() call. Each DisplayRegion is capable of rendering a completely unrelated scene.

dr = base.win.makeDisplayRegion(left, right, bottom, top)

Where left, right, bottom, top are the extents of the rectangle, in the range 0…1 (not -1…1). For instance, the entire screen is 0, 1, 0, 1.

If you want to clear the color and/or depth buffer before drawing into the new DisplayRegion, you should also call:

dr.setClearColorActive(1)
dr.setClearDepthActive(1)

Once you have a DisplayRegion, you can set up a camera and a scene for it, just like the default DisplayRegion, and just like you would for an offscreen buffer.

I believe there are other examples of creating and using DisplayRegions in other forum posts.

David

Ok thanks for the help. I am having a hard time figuring out how to do all of this though. I have tinkered with this stuff for several days and scanned the examples and I still haven’t got it to work. Maybe you can shed some light on what I am missing here. My code is posted below.

Here is what I am trying to do:

I have a simple scene (mWorld) that I want to render and navigate through using the default trackball controls. In that world is a simple wire cube (mCube) that travels through space as a function of time. While I am able to see the entire world and move around in it with the trackball, I want to have a small viewport in the upper left corner that shows the cube euler angles (note: not it’s position) by animating the cube’s orientation against a static (fixed) axes.

In my code I create the viewport and a camera to render it then attach the static axes object to it but nothing gets rendered. I’m sure I am missing something so fundamental here … I would certainly appreciate a little guidance to figure out what it is.

Thanks,
Paul

Updated code posted below ...
  1. You forgot to set the camera for the DR :
      self.mVPDisplayRegion.setCamera(self.camNode)

and drag the camNode backward a little.
2. you can extract the tuple instead of passing each element :

   ls.moveTo(*P[0])
   ls.drawTo(*P[1])
   ls.drawTo(*P[2])
   ls.drawTo(*P[3])
   ls.drawTo(*P[0])

   ls.moveTo(*P[4])
   ls.drawTo(*P[5])
   ls.drawTo(*P[6])
   ls.drawTo(*P[7])
   ls.drawTo(*P[4])

   ls.moveTo(*P[1])
   ls.drawTo(*P[5])

   ls.moveTo(*P[2])
   ls.drawTo(*P[6])

   ls.moveTo(*P[0])
   ls.drawTo(*P[4])

   ls.moveTo(*P[3])
   ls.drawTo(*P[7])

Excellent, thanks! I have update the code so I deleted the code above and the latest follows. One more question … I figured out how to set the background color of the display region but even when I give it an alpha of say 0.5 or so it is still fully opaque. Know how to make that viewport region semi-transparent?

Thanks again,
Paul

# Standard imports

import sys
import math

# Panda imports

from pandac.PandaModules import *

from direct.gui.OnscreenText import OnscreenText 
from direct.showbase import DirectObject
from direct.showbase.DirectObject import DirectObject
import direct.directbase.DirectStart
from direct.task import Task

# Tweak stuff

CubeSpeed = 1

CubeRate = 22

BgColor = VBase4(0.0,0.1,0.55,1.0)
VPColor = VBase4(0.2,0.3,0.75,0.2)

VPRot = [32,24,36]

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

class World(DirectObject):

	# *** Create the alternate viewport and set it up to display a
	# *** simple static axes and the dynamic (HPR only cube).
	def createViewport(self):

		# Create the root of our scene graph for the viewport
		self.mVPRoot = NodePath("ViewportRoot")

		# Add a simple 3D axes model to the scene graph
		self.mCamAxes = CreateAxes('ViewportAxes',2)
		self.mCamAxes.setScale(2,2,2)
		self.mCamAxes.reparentTo(self.mVPRoot)
		self.mCamAxes.setHpr(VPRot[0],VPRot[1],VPRot[2])

		# Create the camera to render the viewport
		self.mVPCamera = Camera("mVPCamera")
		self.mVPCamera.setScene(self.mVPRoot)
		self.mVPCameraNode = self.mVPRoot.attachNewNode(self.mVPCamera)

		self.mVPCameraNode.setPos(0,-12,0)
		self.mVPCameraNode.setHpr(0,0,0)

		# Create the display region
		self.mVPDisplayRegion = \
			base.win.makeDisplayRegion(0.70,0.95,0.70,0.95)
		self.mVPDisplayRegion.setClearColorActive(1) 
		self.mVPDisplayRegion.setClearDepthActive(1)
		self.mVPDisplayRegion.setCamera(self.mVPCameraNode)
		self.mVPDisplayRegion.setClearColor(VPColor)

		self.mCube2 = CreateWireCube("Cube",1)
		self.mCube2.setColor(1,1,0)
		self.mCube2.setHpr(VPRot[0],VPRot[1],VPRot[2])
		self.mCube2.reparentTo(self.mVPRoot)

	# *** Constructor
	def __init__(self):

		base.setBackgroundColor(BgColor)
		self.accept('escape',sys.exit)

		# Setup the main 'world' scene
		self.mWorld = NodePath("World")
		self.mWorld.reparentTo(render)

		self.mSceneSize = 12.0

		self.mMainSceneAxes = CreateAxes('MainAxes',4)
		self.mMainSceneAxes.setScale(\
			self.mSceneSize,
			self.mSceneSize,
			self.mSceneSize
		)
		self.mMainSceneAxes.reparentTo(self.mWorld)

		self.mCube = CreateWireCube("Cube",1)
		self.mCube.setColor(1,1,0)
		self.mCube.reparentTo(self.mWorld)

		# Setup main camera
		base.camLens.setFar(9000)
		base.camLens.setFov(55)
		base.cam.setPos(2*self.mSceneSize,-3*self.mSceneSize,self.mSceneSize)
		base.cam.setHpr(30,-10,0)

		# Setup the alternate viewport
		self.createViewport()

		# Setup time update task to control dynamics of cube.
		taskMgr.add(self.timeUpdate,'TimeUpdate')

		self.accept('arrow_up',self.up)
		self.accept('arrow_up-repeat',self.up)
		self.accept('arrow_down',self.down)
		self.accept('arrow_down-repeat',self.down)

		self.accept('arrow_left',self.left)
		self.accept('arrow_left-repeat',self.left)
		self.accept('arrow_right',self.right)
		self.accept('arrow_right-repeat',self.right)

		self.accept('+',self.lSpin)
		self.accept('+-repeat',self.lSpin)
		self.accept('-',self.rSpin)
		self.accept('--repeat',self.rSpin)

	def up(self):
		VPRot[0] = VPRot[0] + 1

	def down(self):
		VPRot[0] = VPRot[0] - 1

	def left(self):
		VPRot[1] = VPRot[1] + 1

	def right(self):
		VPRot[1] = VPRot[1] - 1

	def lSpin(self):
		VPRot[2] = VPRot[2] + 1

	def rSpin(self):
		VPRot[2] = VPRot[2] - 1

	# *** Time update task
	def timeUpdate(self,task):

		# *** Update the traveling cube dynamics
		x = task.time*CubeSpeed
		y = task.time*CubeSpeed
		z = math.sin(task.time/5.0)

		hpr = self.mCube.getHpr()
		h = hpr[0]
		p = r = task.time*CubeRate

		self.mCube.setPos(x,y,z)
		self.mCube.setHpr(h,p,r)
		self.mCube2.setHpr(h+VPRot[0],p+VPRot[1],r+VPRot[2])
		self.mCamAxes.setHpr(VPRot[0],VPRot[1],VPRot[2])

		return Task.cont

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

def CreateWireCube(nodeName,lineThickness=1):

	ls = LineSegs(nodeName)
	ls.setThickness(lineThickness)

	ls.moveTo(0.5,-0.5,-0.5) 
	ls.drawTo(0.5,0.5,-0.5)
	ls.drawTo(0.5,0.5,0.5)
	ls.drawTo(0.5,-0.5,0.5)
	ls.drawTo(0.5,-0.5,-0.5) 

	ls.moveTo(-0.5,-0.5,-0.5)
	ls.drawTo(-0.5,0.5,-0.5)
	ls.drawTo(-0.5,0.5,0.5)
	ls.drawTo(-0.5,-0.5,0.5)
	ls.drawTo(-0.5,-0.5,-0.5)

	ls.moveTo(0.5,0.5,-0.5)
	ls.drawTo(-0.5,0.5,-0.5)

	ls.moveTo(0.5,0.5,0.5)
	ls.drawTo(-0.5,0.5,0.5)

	ls.moveTo(0.5,-0.5,-0.5) 
	ls.drawTo(-0.5,-0.5,-0.5)

	ls.moveTo(0.5,-0.5,0.5)
	ls.drawTo(-0.5,-0.5,0.5)

	return NodePath(ls.create())

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

def CreateAxes(nodeName,lineThickness=1):

	ls = LineSegs(nodeName)
	ls.setThickness(lineThickness)

	ls.setColor(1.0, 0.0, 0.0, 1.0)
	ls.moveTo(0.0, 0.0, 0.0)
	ls.drawTo(1.0, 0.0, 0.0)

	ls.setColor(0.0, 1.0, 0.0, 1.0)
	ls.moveTo(0.0,0.0,0.0)
	ls.drawTo(0.0, 1.0, 0.0)

	ls.setColor(0.0, 0.0, 1.0, 1.0)
	ls.moveTo(0.0,0.0,0.0)
	ls.drawTo(0.0, 0.0, 1.0)

	return NodePath(ls.create())

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

world = World()

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Run the program

run()

You can’t make a DisplayRegion semi-transparent. If you want this effect, you’ll have to go the render-to-texture route.

David

Cool, thanks!

Paul

Let me amend that: you can achieve semi-transparent DisplayRegions, of a sort, if your scene is simple enough.

That is, you can simply avoid doing the color clear (you can still clear the depth buffer), and then draw all of your objects in a semi-transparent mode, for instance with something like “render.setAlphaScale(0.5); render.setTransparency(TransparencyAttrib.MAlpha)”. Of course you would use whatever node is the root of your DisplayRegion instead of render.

If you want to set a background color without doing a color clear, just draw a big polygon that fills the entire region.

Of course, with this trick, you are drawing a scene full of semitransparent objects, which is not exactly the same as a semitransparent scene full of opaque objects. But if your objects aren’t self-occluding, it may be close enough. You can also play games with the render order to draw things front-to-back so that you don’t see objects through the objects in front of them.

David