mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 17:35:34 -04:00
handy tool
This commit is contained in:
parent
ec408822c5
commit
8e6eb98afe
704
direct/src/showutil/TexMemWatcher.py
Normal file
704
direct/src/showutil/TexMemWatcher.py
Normal file
@ -0,0 +1,704 @@
|
||||
from pandac.PandaModules import *
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
import math
|
||||
import copy
|
||||
|
||||
class TexMemWatcher(DirectObject):
|
||||
"""
|
||||
This class creates a separate graphics window that displays an
|
||||
approximation of the current texture memory, showing the textures
|
||||
that are resident and/or active, and an approximation of the
|
||||
amount of texture memory consumed by each one. It's intended as a
|
||||
useful tool to help determine where texture memory is being spent.
|
||||
|
||||
Although it represents the textures visually in a 2-d space, it
|
||||
doesn't actually have any idea how textures are physically laid
|
||||
out in memory--but it has to lay them out somehow, so it makes
|
||||
something up. It occasionally rearranges the texture display when
|
||||
it feels it needs to, without regard to what the graphics card is
|
||||
actually doing. This tool can't be used to research texture
|
||||
memory fragmentation issues.
|
||||
"""
|
||||
|
||||
def __init__(self, gsg = None, limit = None):
|
||||
DirectObject.__init__(self)
|
||||
|
||||
# If no GSG is specified, use the main GSG.
|
||||
if gsg is None:
|
||||
gsg = base.win.getGsg()
|
||||
elif isinstance(gsg, GraphicsOutput):
|
||||
# If we were passed a window, use that window's GSG.
|
||||
gsg = gsg.getGsg()
|
||||
|
||||
self.gsg = gsg
|
||||
|
||||
# Now open a new window just to render the output.
|
||||
self.winSize = (300, 300)
|
||||
name = 'Texture Memory'
|
||||
props = WindowProperties()
|
||||
props.setSize(*self.winSize)
|
||||
props.setTitle(name)
|
||||
props.setFullscreen(False)
|
||||
|
||||
fbprops = FrameBufferProperties.getDefault()
|
||||
flags = GraphicsPipe.BFFbPropsOptional | GraphicsPipe.BFRequireWindow
|
||||
|
||||
self.win = base.graphicsEngine.makeOutput(base.pipe, name, 0, fbprops,
|
||||
props, flags)
|
||||
assert self.win
|
||||
|
||||
# We don't need to clear the color buffer, since we'll be
|
||||
# filling it with a texture. But we can clear the depth
|
||||
# buffer; we use the depth buffer to cut a hole in the matte.
|
||||
self.win.setClearColor(False)
|
||||
self.win.setClearDepth(True)
|
||||
|
||||
self.win.setWindowEvent('tex-mem-window')
|
||||
self.accept('tex-mem-window', self.windowEvent)
|
||||
|
||||
# Make a render2d in this new window.
|
||||
self.render2d = NodePath('render2d')
|
||||
self.render2d.setDepthTest(False)
|
||||
self.render2d.setDepthWrite(False)
|
||||
self.render2d.setTwoSided(True)
|
||||
|
||||
# And a camera to view it.
|
||||
self.dr = self.win.makeDisplayRegion()
|
||||
cam = Camera('cam2d')
|
||||
self.lens = OrthographicLens()
|
||||
self.lens.setNearFar(-1000, 1000)
|
||||
cam.setLens(self.lens)
|
||||
|
||||
self.cam = self.render2d.attachNewNode(cam)
|
||||
self.dr.setCamera(self.cam)
|
||||
|
||||
self.canvas = self.render2d.attachNewNode('canvas')
|
||||
self.background = None
|
||||
self.overflowing = False
|
||||
|
||||
self.task = taskMgr.doMethodLater(0.5, self.updateTextures, 'TexMemWatcher')
|
||||
|
||||
self.setLimit(limit)
|
||||
|
||||
def setLimit(self, limit):
|
||||
self.limit = limit
|
||||
self.dynamicLimit = False
|
||||
|
||||
if limit is None:
|
||||
# If no limit was specified, use the specified graphics
|
||||
# memory limit, if any.
|
||||
lruLimit = self.gsg.getPreparedObjects().getGraphicsMemoryLimit()
|
||||
if lruLimit < 2**32 - 1:
|
||||
# Got a real lruLimit. Use it.
|
||||
self.limit = lruLimit
|
||||
|
||||
else:
|
||||
# No LRU limit either, so there won't be a practical
|
||||
# limit to the TexMemWatcher. We'll determine our
|
||||
# limit on-the-fly instead.
|
||||
|
||||
self.dynamicLimit = True
|
||||
|
||||
# The actual height of the canvas, including the overflow
|
||||
# area. The texture memory itself is restricted to (0..1)
|
||||
# vertically; anything higher than 1 is overflow.
|
||||
self.top = 1.25
|
||||
if self.dynamicLimit:
|
||||
# Actually, we'll never exceed texture memory, so never mind.
|
||||
self.top = 1
|
||||
|
||||
self.lens.setFilmSize(1, self.top)
|
||||
self.lens.setFilmOffset(0.5, self.top / 2.0) # lens covers 0..1 in x and y
|
||||
|
||||
self.makeWindowBackground()
|
||||
self.reconfigureWindow()
|
||||
|
||||
def cleanup(self):
|
||||
# Remove the window.
|
||||
if self.win:
|
||||
base.graphicsEngine.removeWindow(self.win)
|
||||
self.win = None
|
||||
|
||||
if self.task:
|
||||
taskMgr.remove(self.task)
|
||||
self.task = None
|
||||
|
||||
self.ignoreAll()
|
||||
|
||||
self.canvas.getChildren().detach()
|
||||
self.texRecords = {}
|
||||
self.texPlacements = {}
|
||||
|
||||
|
||||
def windowEvent(self, win):
|
||||
if win == self.win:
|
||||
props = win.getProperties()
|
||||
if not props.getOpen():
|
||||
# User closed window.
|
||||
self.cleanup()
|
||||
return
|
||||
|
||||
size = (props.getXSize(), props.getYSize())
|
||||
if size != self.winSize:
|
||||
self.winSize = size
|
||||
self.reconfigureWindow()
|
||||
|
||||
def reconfigureWindow(self):
|
||||
""" Resets everything for a new window size. """
|
||||
|
||||
self.background.setTexScale(TextureStage.getDefault(),
|
||||
self.winSize[0] / 20.0, self.winSize[1] / (20.0 * self.top))
|
||||
self.repack()
|
||||
|
||||
def makeWindowBackground(self):
|
||||
""" Creates a tile to use for coloring the background of the
|
||||
window, so we can tell what empty space looks like. """
|
||||
|
||||
if self.background:
|
||||
self.background.detachNode()
|
||||
self.background = None
|
||||
|
||||
# We start with a simple checkerboard texture image.
|
||||
p = PNMImage(2, 2, 1)
|
||||
p.setGray(0, 0, 0.40)
|
||||
p.setGray(1, 1, 0.40)
|
||||
p.setGray(0, 1, 0.80)
|
||||
p.setGray(1, 0, 0.80)
|
||||
|
||||
tex = Texture('check')
|
||||
tex.load(p)
|
||||
tex.setMagfilter(tex.FTNearest)
|
||||
|
||||
self.background = self.render2d.attachNewNode('background')
|
||||
|
||||
cm = CardMaker('background')
|
||||
cm.setFrame(0, 1, 0, 1)
|
||||
cm.setUvRange((0, 0), (1, 1))
|
||||
self.background.attachNewNode(cm.generate())
|
||||
|
||||
cm.setFrame(0, 1, 1, self.top)
|
||||
cm.setUvRange((0, 1), (1, self.top))
|
||||
bad = self.background.attachNewNode(cm.generate())
|
||||
bad.setColor((0.8, 0.2, 0.2, 1))
|
||||
|
||||
self.background.setBin('fixed', -100)
|
||||
self.background.setTexture(tex)
|
||||
|
||||
|
||||
def updateTextures(self, task):
|
||||
""" Gets the current list of resident textures and adds new
|
||||
textures or removes old ones from the onscreen display, as
|
||||
necessary. """
|
||||
|
||||
pgo = self.gsg.getPreparedObjects()
|
||||
totalSize = 0
|
||||
|
||||
texRecords = []
|
||||
neverVisited = copy.copy(self.texRecords)
|
||||
for tex in self.gsg.getPreparedTextures():
|
||||
# We have visited this texture; remove it from the
|
||||
# neverVisited list.
|
||||
if tex in neverVisited:
|
||||
del neverVisited[tex]
|
||||
|
||||
size = 0
|
||||
if tex.getResident(pgo):
|
||||
size = tex.getDataSizeBytes(pgo)
|
||||
|
||||
tr = self.texRecords.get(tex, None)
|
||||
|
||||
if size:
|
||||
totalSize += size
|
||||
active = tex.getActive(pgo)
|
||||
if not tr:
|
||||
# This is a new texture; need to record it.
|
||||
tr = TexRecord(tex, size, active)
|
||||
texRecords.append(tr)
|
||||
else:
|
||||
tr.setActive(active)
|
||||
if tr.size != size:
|
||||
# The size has changed; reapply it.
|
||||
tr.setSize(size)
|
||||
self.unplaceTexture(tr)
|
||||
texRecords.append(tr)
|
||||
else:
|
||||
if tr:
|
||||
# This texture is no longer resident; need to remove it.
|
||||
self.unplaceTexture(tr)
|
||||
|
||||
# Now go through and make sure we unplace any textures that we
|
||||
# didn't visit at all this pass.
|
||||
for tr in neverVisited.values():
|
||||
self.unplaceTexture(tr)
|
||||
|
||||
self.totalSize = totalSize
|
||||
if totalSize > self.limit and self.dynamicLimit:
|
||||
# Actually, never mind on the update: we have exceeded the
|
||||
# dynamic limit computed before, and therefore we need to
|
||||
# repack.
|
||||
self.repack()
|
||||
|
||||
else:
|
||||
# Pack in just the newly-loaded textures.
|
||||
|
||||
# Sort the regions from largest to smallest to maximize
|
||||
# packing effectiveness.
|
||||
texRecords.sort(key = lambda tr: (-tr.w, -tr.h))
|
||||
|
||||
self.overflowing = False
|
||||
for tr in texRecords:
|
||||
self.placeTexture(tr)
|
||||
self.texRecords[tr.tex] = tr
|
||||
|
||||
return task.again
|
||||
|
||||
|
||||
def repack(self):
|
||||
""" Repacks all of the current textures. """
|
||||
|
||||
self.canvas.getChildren().detach()
|
||||
self.texRecords = {}
|
||||
self.texPlacements = {}
|
||||
self.w = 1
|
||||
self.h = 1
|
||||
|
||||
pgo = self.gsg.getPreparedObjects()
|
||||
totalSize = 0
|
||||
|
||||
for tex in self.gsg.getPreparedTextures():
|
||||
if tex.getResident(pgo):
|
||||
size = tex.getDataSizeBytes(pgo)
|
||||
if size:
|
||||
active = tex.getActive(pgo)
|
||||
tr = TexRecord(tex, size, active)
|
||||
self.texRecords[tex] = tr
|
||||
totalSize += size
|
||||
|
||||
self.totalSize = totalSize
|
||||
if not self.totalSize:
|
||||
return
|
||||
|
||||
if self.dynamicLimit:
|
||||
# Choose a suitable limit by rounding to the next power of two.
|
||||
self.limit = Texture.upToPower2(self.totalSize)
|
||||
|
||||
# Now make that into a 2-D rectangle of the appropriate shape,
|
||||
# such that w * h == limit.
|
||||
|
||||
# Window size
|
||||
x, y = self.winSize
|
||||
|
||||
# There should be a little buffer on the top so we can see if
|
||||
# we overflow.
|
||||
y /= self.top
|
||||
|
||||
r = float(y) / float(x)
|
||||
|
||||
# Region size
|
||||
w = math.sqrt(self.limit) / math.sqrt(r)
|
||||
h = w * r
|
||||
self.w = w
|
||||
self.h = h
|
||||
|
||||
self.canvas.setScale(1.0 / w, 1.0, 1.0 / h)
|
||||
|
||||
# Sort the regions from largest to smallest to maximize
|
||||
# packing effectiveness.
|
||||
texRecords = self.texRecords.values()
|
||||
texRecords.sort(key = lambda tr: (-tr.w, -tr.h))
|
||||
|
||||
self.overflowing = False
|
||||
for tr in texRecords:
|
||||
self.placeTexture(tr)
|
||||
|
||||
def unplaceTexture(self, tr):
|
||||
""" Removes the texture from its place on the canvas. """
|
||||
for tp in tr.placements:
|
||||
del self.texPlacements[tp]
|
||||
tr.placements = []
|
||||
|
||||
if tr.root:
|
||||
tr.root.detachNode()
|
||||
tr.root = None
|
||||
|
||||
def placeTexture(self, tr):
|
||||
""" Places the texture somewhere on the canvas where it will
|
||||
fit. """
|
||||
|
||||
if not self.overflowing:
|
||||
tp = self.findHole(tr.w, tr.h)
|
||||
if tp:
|
||||
tr.placements = [tp]
|
||||
tr.makeCard(self)
|
||||
self.texPlacements[tp] = tr
|
||||
return
|
||||
|
||||
# Couldn't find a hole; can we fit it if we rotate?
|
||||
tp = self.findHole(tr.h, tr.w)
|
||||
if tp:
|
||||
tp.rotated = True
|
||||
tr.placements = [tp]
|
||||
tr.makeCard(self)
|
||||
self.texPlacements[tp] = tr
|
||||
return
|
||||
|
||||
# Couldn't find a hole of the right shape; can we find a
|
||||
# single rectangular hole of the right area, but of any shape?
|
||||
tp = self.findArea(tr.h * tr.w)
|
||||
if tp:
|
||||
texCmp = cmp(tr.w, tr.h)
|
||||
holeCmp = cmp(tp.p[1] - tp.p[0], tp.p[3] - tp.p[2])
|
||||
if texCmp != 0 and holeCmp != 0 and texCmp != holeCmp:
|
||||
tp.rotated = True
|
||||
tr.placements = [tp]
|
||||
tr.makeCard(self)
|
||||
self.texPlacements[tp] = tr
|
||||
return
|
||||
|
||||
# Couldn't find a single rectangular hole. We'll have to
|
||||
# divide the texture up into several smaller pieces to cram it
|
||||
# in.
|
||||
tpList = self.findHolePieces(tr.h * tr.w)
|
||||
if tpList:
|
||||
tr.placements = tpList
|
||||
tr.makeCard(self)
|
||||
for tp in tpList:
|
||||
self.texPlacements[tp] = tr
|
||||
return
|
||||
|
||||
# Just let it overflow.
|
||||
self.overflowing = True
|
||||
tp = self.findHole(tr.w, tr.h, allowOverflow = True)
|
||||
if tp:
|
||||
tr.placements = [tp]
|
||||
tr.makeCard(self)
|
||||
self.texPlacements[tp] = tr
|
||||
return
|
||||
|
||||
# Something went wrong.
|
||||
assert False
|
||||
|
||||
def findHole(self, w, h, allowOverflow = False):
|
||||
""" Searches for a hole large enough for (w, h). If one is
|
||||
found, returns an appropriate TexPlacement; otherwise, returns
|
||||
None. """
|
||||
|
||||
if w > self.w:
|
||||
# It won't fit within the row at all.
|
||||
if not allowOverflow:
|
||||
return None
|
||||
# Just stack it on the top.
|
||||
y = 0
|
||||
if self.texPlacements:
|
||||
y = max(map(lambda tp: tp.p[3], self.texPlacements.keys()))
|
||||
tp = TexPlacement(0, w, y, y + h)
|
||||
return tp
|
||||
|
||||
y = 0
|
||||
while y + h <= self.h or allowOverflow:
|
||||
nextY = None
|
||||
|
||||
# Scan along the row at 'y'.
|
||||
x = 0
|
||||
while x + w <= self.w:
|
||||
# Consider the spot at x, y.
|
||||
tp = TexPlacement(x, x + w, y, y + h)
|
||||
overlap = self.findOverlap(tp)
|
||||
if not overlap:
|
||||
# Hooray!
|
||||
return tp
|
||||
|
||||
nextX = overlap.p[1]
|
||||
if nextY is None:
|
||||
nextY = overlap.p[3]
|
||||
else:
|
||||
nextY = min(nextY, overlap.p[3])
|
||||
|
||||
assert nextX > x
|
||||
x = nextX
|
||||
|
||||
assert nextY > y
|
||||
y = nextY
|
||||
|
||||
# Nope, wouldn't fit anywhere.
|
||||
return None
|
||||
|
||||
|
||||
def findArea(self, area):
|
||||
""" Searches for a rectangular hole that is at least area
|
||||
square units big, regardless of its shape. If one is found,
|
||||
returns an appropriate TexPlacement; otherwise, returns
|
||||
None. """
|
||||
|
||||
y = 0
|
||||
while y < self.h:
|
||||
nextY = self.h
|
||||
|
||||
# Scan along the row at 'y'.
|
||||
x = 0
|
||||
while x < self.w:
|
||||
nextX = self.w
|
||||
|
||||
# Consider the spot at x, y.
|
||||
|
||||
# How wide can we go? Start by trying to go all the
|
||||
# way to the edge of the region.
|
||||
tpw = self.w - x
|
||||
|
||||
# Now, given this particular width, how tall do we
|
||||
# need to go?
|
||||
tph = area / tpw
|
||||
|
||||
while y + tph < self.h:
|
||||
tp = TexPlacement(x, x + tpw, y, y + tph)
|
||||
overlap = self.findOverlap(tp)
|
||||
if not overlap:
|
||||
# Hooray!
|
||||
return tp
|
||||
|
||||
nextX = min(nextX, overlap.p[1])
|
||||
nextY = min(nextY, overlap.p[3])
|
||||
|
||||
# Shorten the available region.
|
||||
tpw = overlap.p[0] - x
|
||||
if tpw <= 0.0:
|
||||
break
|
||||
tph = area / tpw
|
||||
|
||||
assert nextX > x
|
||||
x = nextX
|
||||
|
||||
assert nextY > y
|
||||
y = nextY
|
||||
|
||||
# Nope, wouldn't fit anywhere.
|
||||
return None
|
||||
|
||||
def findHolePieces(self, area):
|
||||
""" Returns a list of holes whose net area sums to the given
|
||||
area, or None if there are not enough holes. """
|
||||
|
||||
# First, save the original value of self.texPlacements, since
|
||||
# we will be modifying that during this search.
|
||||
savedTexPlacements = copy.copy(self.texPlacements)
|
||||
|
||||
result = []
|
||||
|
||||
while area > 0:
|
||||
tp = self.findLargestHole()
|
||||
if not tp:
|
||||
break
|
||||
|
||||
l, r, b, t = tp.p
|
||||
tpArea = (r - l) * (t - b)
|
||||
if tpArea >= area:
|
||||
# we're done.
|
||||
shorten = (tpArea - area) / (r - l)
|
||||
tp.p = (l, r, b, t - shorten)
|
||||
result.append(tp)
|
||||
self.texPlacements = savedTexPlacements
|
||||
return result
|
||||
|
||||
# Keep going.
|
||||
area -= tpArea
|
||||
result.append(tp)
|
||||
self.texPlacements[tp] = None
|
||||
|
||||
# Huh, not enough room, or no more holes.
|
||||
self.texPlacements = savedTexPlacements
|
||||
return None
|
||||
|
||||
def findLargestHole(self):
|
||||
""" Searches for the largest available hole. """
|
||||
|
||||
holes = []
|
||||
|
||||
y = 0
|
||||
while y < self.h:
|
||||
nextY = self.h
|
||||
|
||||
# Scan along the row at 'y'.
|
||||
x = 0
|
||||
while x < self.w:
|
||||
nextX = self.w
|
||||
|
||||
# Consider the spot at x, y.
|
||||
|
||||
# How wide can we go? Start by trying to go all the
|
||||
# way to the edge of the region.
|
||||
tpw = self.w - x
|
||||
|
||||
# And how tall can we go? Start by trying to go to
|
||||
# the top of the region.
|
||||
tph = self.h - y
|
||||
|
||||
while tpw > 0.0 and tph > 0.0:
|
||||
tp = TexPlacement(x, x + tpw, y, y + tph)
|
||||
overlap = self.findOverlap(tp)
|
||||
if not overlap:
|
||||
# Here's a hole.
|
||||
holes.append((tpw * tph, tp))
|
||||
break
|
||||
|
||||
nextX = min(nextX, overlap.p[1])
|
||||
nextY = min(nextY, overlap.p[3])
|
||||
|
||||
# We've been intersected either on the top or the
|
||||
# right. We need to shorten either width or
|
||||
# height. Which way results in the largest
|
||||
# remaining area?
|
||||
|
||||
tpw0 = overlap.p[0] - x
|
||||
tph0 = overlap.p[2] - y
|
||||
|
||||
if tpw0 * tph > tpw * tph0:
|
||||
# Shortening width results in larger.
|
||||
tpw = tpw0
|
||||
else:
|
||||
# Shortening height results in larger.
|
||||
tph = tph0
|
||||
|
||||
assert nextX > x
|
||||
x = nextX
|
||||
|
||||
assert nextY > y
|
||||
y = nextY
|
||||
|
||||
if not holes:
|
||||
# No holes to be found.
|
||||
return None
|
||||
|
||||
# Return the biggest hole
|
||||
return max(holes)[1]
|
||||
|
||||
def findOverlap(self, tp):
|
||||
""" If there is another placement that overlaps the indicated
|
||||
TexPlacement, returns it. Otherwise, returns None. """
|
||||
|
||||
for other in self.texPlacements.keys():
|
||||
if other.intersects(tp):
|
||||
return other
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class TexRecord:
|
||||
def __init__(self, tex, size, active):
|
||||
self.tex = tex
|
||||
self.active = active
|
||||
self.root = None
|
||||
self.placements = []
|
||||
|
||||
self.setSize(size)
|
||||
|
||||
def setSize(self, size):
|
||||
self.size = size
|
||||
x = self.tex.getXSize()
|
||||
y = self.tex.getYSize()
|
||||
r = float(y) / float(x)
|
||||
|
||||
# Card size
|
||||
w = math.sqrt(self.size) / math.sqrt(r)
|
||||
h = w * r
|
||||
|
||||
self.w = w
|
||||
self.h = h
|
||||
|
||||
|
||||
def setActive(self, flag):
|
||||
self.active = flag
|
||||
if self.active:
|
||||
self.matte.clearColor()
|
||||
else:
|
||||
self.matte.setColor((0.4, 0.4, 0.4, 1))
|
||||
|
||||
def makeCard(self, tmw):
|
||||
if self.root:
|
||||
self.root.detachNode()
|
||||
|
||||
root = NodePath('root')
|
||||
|
||||
# A card to display the texture.
|
||||
card = root.attachNewNode('card')
|
||||
|
||||
# A matte to frame the texture and indicate its status.
|
||||
matte = root.attachNewNode('matte')
|
||||
|
||||
# A wire frame to ring the matte and separate the card from
|
||||
# its neighbors.
|
||||
frame = root.attachNewNode('frame')
|
||||
|
||||
|
||||
for p in self.placements:
|
||||
l, r, b, t = p.p
|
||||
cx = (l + r) * 0.5
|
||||
cy = (b + t) * 0.5
|
||||
shrinkMat = Mat4.translateMat(-cx, 0, -cy) * Mat4.scaleMat(0.9) * Mat4.translateMat(cx, 0, cy)
|
||||
|
||||
cm = CardMaker('card')
|
||||
cm.setFrame(l, r, b, t)
|
||||
if p.rotated:
|
||||
cm.setUvRange((0, 1), (0, 0), (1, 0), (1, 1))
|
||||
c = card.attachNewNode(cm.generate())
|
||||
c.setMat(shrinkMat)
|
||||
|
||||
cm = CardMaker('matte')
|
||||
cm.setFrame(l, r, b, t)
|
||||
matte.attachNewNode(cm.generate())
|
||||
|
||||
ls = LineSegs('frame')
|
||||
ls.setColor(0, 0, 0, 1)
|
||||
ls.moveTo(l, 0, b)
|
||||
ls.drawTo(r, 0, b)
|
||||
ls.drawTo(r, 0, t)
|
||||
ls.drawTo(l, 0, t)
|
||||
ls.drawTo(l, 0, b)
|
||||
f1 = frame.attachNewNode(ls.create())
|
||||
f2 = f1.copyTo(frame)
|
||||
f2.setMat(shrinkMat)
|
||||
|
||||
# Instead of enabling transparency, we set a color blend
|
||||
# attrib. We do this because plain transparency would also
|
||||
# enable an alpha test, which we don't want; we want to draw
|
||||
# every pixel.
|
||||
card.setAttrib(ColorBlendAttrib.make(
|
||||
ColorBlendAttrib.MAdd,
|
||||
ColorBlendAttrib.OIncomingAlpha,
|
||||
ColorBlendAttrib.OOneMinusIncomingAlpha))
|
||||
card.setBin('fixed', 0)
|
||||
card.setTexture(self.tex)
|
||||
card.setY(-1) # the card gets pulled back, so the matte will z-test it out.
|
||||
card.setDepthWrite(True)
|
||||
card.setDepthTest(True)
|
||||
#card.flattenStrong()
|
||||
self.card = card
|
||||
|
||||
matte.setBin('fixed', 10)
|
||||
matte.setDepthTest(True)
|
||||
#matte.flattenStrong()
|
||||
self.matte = matte
|
||||
|
||||
frame.setBin('fixed', 20)
|
||||
#frame.flattenStrong()
|
||||
self.frame = frame
|
||||
|
||||
root.reparentTo(tmw.canvas)
|
||||
|
||||
self.root = root
|
||||
|
||||
class TexPlacement:
|
||||
def __init__(self, l, r, b, t):
|
||||
self.p = (l, r, b, t)
|
||||
self.rotated = False
|
||||
|
||||
def intersects(self, other):
|
||||
""" Returns True if the placements intersect, False
|
||||
otherwise. """
|
||||
|
||||
ml, mr, mb, mt = self.p
|
||||
tl, tr, tb, tt = other.p
|
||||
|
||||
return (tl < mr and tr > ml and
|
||||
tb < mt and tt > mb)
|
||||
|
Loading…
x
Reference in New Issue
Block a user