| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- from pandac.PandaModules import *
- from direct.task import Task
- from direct.directnotify.DirectNotifyGlobal import *
- from direct.showbase.DirectObject import DirectObject
- import math
- class BufferViewer(DirectObject):
- notify = directNotify.newCategory('BufferViewer')
- def __init__(self):
- """Access: private. Constructor."""
- self.enabled = 0
- self.sizex = 0
- self.sizey = 0
- self.position = "lrcorner"
- self.layout = "hline"
- self.include = "all"
- self.exclude = "none"
- self.cullbin = "fixed"
- self.cullsort = 10000
- self.cards = []
- self.cardindex = 0
- self.task = 0
- self.window = 0
- self.dirty = 1
- self.accept("render-texture-targets-changed", self.refreshReadout)
- if (ConfigVariableBool("show-buffers", 0).getValue()):
- self.enable(1)
- def refreshReadout(self):
- """Force the readout to be refreshed. This is usually invoked
- by GraphicsOutput::add_render_texture (via an event handler).
- However, it is also possible to invoke it manually. Currently,
- the only time I know of that this is necessary is after a
- window resize (and I ought to fix that)."""
- self.dirty = 1
- def isValidTextureSet(self, list):
- """Access: private. Returns true if the parameter is a
- list of GraphicsOutput and Texture, or the keyword 'all'."""
- if (isinstance(x,list)):
- for elt in x:
- if (self.isValidTextureSet(x)==0):
- return 0
- else:
- return (x=="all") or (isinstance(x,Texture)) or (isinstance(x,GraphicsOutput))
- def isEnabled(self):
- """Returns true if the buffer viewer is currently enabled."""
- return self.enabled
- def enable(self, x):
- """Turn the buffer viewer on or off. The initial state of the
- buffer viewer depends on the Config variable 'show-buffers'."""
- if (x != 0) and (x != 1):
- BufferViewer.notify.error('invalid parameter to BufferViewer.enable')
- return
- self.enabled = x
- self.dirty = 1
- if (x) and (self.task == 0):
- self.task = taskMgr.add(self.maintainReadout, "buffer-viewer-maintain-readout",
- priority=1)
- def toggleEnable(self):
- """Toggle the buffer viewer on or off. The initial state of the
- enable flag depends on the Config variable 'show-buffers'."""
- self.enable(1-self.enabled)
- def setCardSize(self, x, y):
- """Set the size of each card. The units are relative to
- render2d (ie, 1x1 card is not square). If one of the
- dimensions is zero, then the viewer will choose a value
- for that dimension so as to ensure that the aspect ratio
- of the card matches the aspect ratio of the source-window.
- If both dimensions are zero, the viewer uses a heuristic
- to choose a reasonable size for the card. The initial
- value is (0,0)."""
- if (x < 0) or (y < 0):
- BufferViewer.notify.error('invalid parameter to BufferViewer.setCardSize')
- return
- self.sizex = x
- self.sizey = y
- self.dirty = 1
-
- def setPosition(self, pos):
- """Set the position of the cards. The valid values are:
- * llcorner - put them in the lower-left corner of the window
- * lrcorner - put them in the lower-right corner of the window
- * ulcorner - put them in the upper-left corner of the window
- * urcorner - put them in the upper-right corner of the window
- * window - put them in a separate window
- The initial value is 'lrcorner'."""
- valid=["llcorner","lrcorner","ulcorner","urcorner","window"]
- if (valid.count(pos)==0):
- BufferViewer.notify.error('invalid parameter to BufferViewer.setPosition')
- BufferViewer.notify.error('valid parameters are: llcorner, lrcorner, ulcorner, urcorner, window')
- return
- if (pos == "window"):
- BufferViewer.notify.error('BufferViewer.setPosition - "window" mode not implemented yet.')
- return
- self.position = pos
- self.dirty = 1
- def setLayout(self, lay):
- """Set the layout of the cards. The valid values are:
- * vline - display them in a vertical line
- * hline - display them in a horizontal line
- * vgrid - display them in a vertical grid
- * hgrid - display them in a horizontal grid
- * cycle - display one card at a time, using selectCard/advanceCard
- The default value is 'hline'."""
- valid=["vline","hline","vgrid","hgrid","cycle"]
- if (valid.count(lay)==0):
- BufferViewer.notify.error('invalid parameter to BufferViewer.setLayout')
- BufferViewer.notify.error('valid parameters are: vline, hline, vgrid, hgrid, cycle')
- return
- self.layout = lay
- self.dirty = 1
- def selectCard(self, i):
- """Only useful when using setLayout('cycle'). Sets the index
- that selects which card to display. The index is taken modulo
- the actual number of cards."""
- self.cardindex = i
- self.dirty = 1
- def advanceCard(self):
- """Only useful when using setLayout('cycle'). Increments the index
- that selects which card to display. The index is taken modulo
- the actual number of cards."""
- self.cardindex += 1
- self.dirty = 1
-
- def setInclude(self, x):
- """Set the include-set for the buffer viewer. The include-set
- specifies which of the render-to-texture targets to display.
- Valid inputs are the string 'all' (display every render-to-texture
- target), or a list of GraphicsOutputs or Textures. The initial
- value is 'all'."""
- if (self.isValidTextureSet(x)==0):
- BufferViewer.notify.error('setInclude: must be list of textures and buffers, or "all"')
- return
- self.include = x
- self.dirty = 1
- def setExclude(self, x):
- """Set the exclude-set for the buffer viewer. The exclude-set
- should be a list of GraphicsOutputs and Textures to ignore.
- The exclude-set is subtracted from the include-set (so the excludes
- effectively override the includes.) The initial value is the
- empty list."""
- if (self.isValidTextureSet(x)==0):
- BufferViewer.notify.error('setExclude: must be list of textures and buffers')
- return
- self.exclude = x
- self.dirty = 1
- def setSort(self, bin, sort):
- """Set the cull-bin and sort-order for the output cards. The
- default value is 'fixed', 10000."""
- self.cullbin = bin
- self.cullsort = sort
- self.dirty = 1
- def analyzeTextureSet(self, x, set):
- """Access: private. Converts a list of GraphicsObject,
- GraphicsEngine, and Texture into a table of Textures."""
- if (isinstance(x,list)):
- for elt in x:
- self.analyzeTextureSet(elt, set)
- elif (isinstance(x, Texture)):
- set[x] = 1
- elif (isinstance(x, GraphicsOutput)):
- for itex in range(x.countTextures()):
- tex = x.getTexture(itex)
- set[tex] = 1
- elif (isinstance(x, GraphicsEngine)):
- for iwin in range(x.getNumWindows()):
- win = x.getWindow(iwin)
- self.analyzeTextureSet(win, set)
- elif (x=="all"):
- self.analyzeTextureSet(base.graphicsEngine, set)
- else: return
- def makeFrame(self, sizex, sizey):
- """Access: private. Each texture card is displayed with
- a two-pixel wide frame (a ring of black and a ring of white).
- This routine builds the frame geometry. It is necessary to
- be precise so that the frame exactly aligns to pixel
- boundaries, and so that it doesn't overlap the card at all."""
- format=GeomVertexFormat.getV3cp()
- vdata=GeomVertexData('card-frame', format, Geom.UHDynamic)
- vwriter=GeomVertexWriter(vdata, 'vertex')
- cwriter=GeomVertexWriter(vdata, 'color')
-
- ringoffset = [0,1,1,2]
- ringbright = [0,0,1,1]
- for ring in range(4):
- offsetx = (ringoffset[ring]*2.0) / float(sizex)
- offsety = (ringoffset[ring]*2.0) / float(sizey)
- bright = ringbright[ring]
- vwriter.addData3f(-1-offsetx, 0, -1-offsety)
- vwriter.addData3f(1+offsetx, 0, -1-offsety)
- vwriter.addData3f(1+offsetx, 0, 1+offsety)
- vwriter.addData3f(-1-offsetx, 0, 1+offsety)
- cwriter.addData3f(bright,bright,bright)
- cwriter.addData3f(bright,bright,bright)
- cwriter.addData3f(bright,bright,bright)
- cwriter.addData3f(bright,bright,bright)
- triangles=GeomTriangles(Geom.UHDynamic)
- for i in range(2):
- delta = i*8
- triangles.addVertices(0+delta,4+delta,1+delta)
- triangles.addVertices(1+delta,4+delta,5+delta)
- triangles.addVertices(1+delta,5+delta,2+delta)
- triangles.addVertices(2+delta,5+delta,6+delta)
- triangles.addVertices(2+delta,6+delta,3+delta)
- triangles.addVertices(3+delta,6+delta,7+delta)
- triangles.addVertices(3+delta,7+delta,0+delta)
- triangles.addVertices(0+delta,7+delta,4+delta)
- triangles.closePrimitive()
- geom=Geom(vdata)
- geom.addPrimitive(triangles)
- geomnode=GeomNode("card-frame")
- geomnode.addGeom(geom)
- return NodePath(geomnode)
- def maintainReadout(self, task):
- """Access: private. Whenever necessary, rebuilds the entire
- display from scratch. This is only done when the configuration
- parameters have changed."""
- # If nothing has changed, don't update.
- if (self.dirty==0):
- return Task.cont
- self.dirty = 0
- # Delete the old set of cards.
- for card in self.cards:
- card.removeNode()
- self.cards = []
- # If not enabled, return.
- if (self.enabled == 0):
- self.task = 0
- return Task.done
-
- # Generate the include and exclude sets.
- exclude = {}
- include = {}
- self.analyzeTextureSet(self.exclude,exclude)
- self.analyzeTextureSet(self.include,include)
-
- # Generate a list of cards and the corresponding windows.
- cards = []
- wins = []
- for iwin in range(base.graphicsEngine.getNumWindows()):
- win = base.graphicsEngine.getWindow(iwin)
- for itex in range(win.countTextures()):
- tex = win.getTexture(itex)
- if (include.has_key(tex)) and (exclude.has_key(tex)==0):
- card = win.getTextureCard()
- card.setTexture(tex)
- cards.append(card)
- wins.append(win)
- self.cards = cards
- if (len(cards)==0): return
- ncards = len(cards)
- # Decide how many rows and columns to use for the layout.
- if (self.layout == "hline"):
- rows = 1
- cols = ncards
- elif (self.layout == "vline"):
- rows = ncards
- cols = 1
- elif (self.layout == "hgrid"):
- rows = int(math.sqrt(ncards))
- cols = rows
- if (rows * cols < ncards): cols += 1
- if (rows * cols < ncards): rows += 1
- elif (self.layout == "vgrid"):
- rows = int(math.sqrt(ncards))
- cols = rows
- if (rows * cols < ncards): rows += 1
- if (rows * cols < ncards): cols += 1
- elif (self.layout == "cycle"):
- rows = 1
- cols = 1
- else:
- BufferViewer.notify.error('shouldnt ever get here in BufferViewer.maintainReadout')
- # Choose an aspect ratio for the cards. All card size
- # calculations are done in pixel-units, using integers,
- # in order to ensure that everything ends up neatly on
- # a pixel boundary.
- aspectx = wins[0].getXSize()
- aspecty = wins[0].getYSize()
- for win in wins:
- if (win.getXSize()*aspecty) != (win.getYSize()*aspectx):
- aspectx = 1
- aspecty = 1
- # Choose a card size. If the user didn't specify a size,
- # use a heuristic, otherwise, just follow orders. The
- # heuristic uses an initial card size of 42.66666667% of
- # the screen vertically, which comes to 256 pixels on
- # an 800x600 display. Then, it double checks that the
- # readout will fit on the screen, and if not, it shrinks it.
-
- if (float(self.sizex)==0.0) and (float(self.sizey)==0.0):
- sizey = int(0.4266666667 * base.win.getYSize())
- sizex = (sizey * aspectx) / aspecty
- v_sizey = (base.win.getYSize() - (rows-1) - (rows*2)) / rows
- v_sizex = (v_sizey * aspectx) / aspecty
- if (v_sizey < sizey) or (v_sizex < sizex):
- sizey = v_sizey
- sizex = v_sizex
- h_sizex = (base.win.getXSize() - (cols-1) - (cols*2)) / cols
- h_sizey = (h_sizex * aspecty) / aspectx
- if (h_sizey < sizey) or (h_sizex < sizex):
- sizey = h_sizey
- sizex = h_sizex
- else:
- sizex = int(self.sizex * 0.5 * base.win.getXSize())
- sizey = int(self.sizey * 0.5 * base.win.getYSize())
- if (sizex == 0): sizex = (sizey*aspectx) / aspecty
- if (sizey == 0): sizey = (sizex*aspecty) / aspectx
- # Convert from pixels to render2d-units.
- fsizex = (2.0*sizex) / float(base.win.getXSize())
- fsizey = (2.0*sizey) / float(base.win.getYSize())
- fpixelx = 2.0 / float(base.win.getXSize())
- fpixely = 2.0 / float(base.win.getYSize())
- # Choose directional offsets
- if (self.position == "llcorner"):
- dirx = -1.0
- diry = -1.0
- elif (self.position == "lrcorner"):
- dirx = 1.0
- diry = -1.0
- elif (self.position == "ulcorner"):
- dirx = -1.0
- diry = 1.0
- elif (self.position == "urcorner"):
- dirx = 1.0
- diry = 1.0
- else:
- BufferViewer.notify.error('window mode not implemented yet')
-
- # Create the frame
- frame = self.makeFrame(sizex,sizey)
- # Now, position the cards on the screen.
- # For each card, create a frame consisting of eight quads.
-
- for r in range(rows):
- for c in range(cols):
- index = c + r*cols
- if (index < ncards):
- index = (index + self.cardindex) % len(cards)
- posx = dirx * (1.0 - fsizex*0.5 - 3*fpixelx - (c*(fsizex+6*fpixelx)))
- posy = diry * (1.0 - fsizey*0.5 - 3*fpixely - (r*(fsizey+6*fpixely)))
- placer = NodePath("card-structure")
- placer.setPos(posx,0,posy)
- placer.setScale(fsizex*0.5,1.0,fsizey*0.5)
- placer.setBin(self.cullbin, self.cullsort)
- placer.reparentTo(render2d)
- frame.instanceTo(placer)
- cards[index].reparentTo(placer)
- cards[index] = placer
- return Task.cont
|