Here is some simple code that demonstrates the Assertion error that I have been getting. I have been working for two weeks to get it down to under 400 lines (the error is not always easy to invoke, I suspect it is a dangling pointer or other sort of pointer problem and can therefore appear to be intermittent -the following code seems to cause the error consistently)
# This code demonstrates the assertion error:
# Assertion failed: _states->find(this) == _saved_entry at line 1944 of c:\panda3d-1.7.0\panda\src\pgraph\transformState.cxx
# Assertion failed: _saved_entry == _states->end() at line 108 of c:\panda3d-1.7.0\panda\src\pgraph\transformState.cxx
# Author: Chris Hilder, email: cj.hilder@clear.net.nz, 24 July 2010
from direct.directbase import DirectStart
from direct.showbase.DirectObject import DirectObject
from direct.gui.DirectGui import *
from direct.interval.IntervalGlobal import *
from direct.actor.Actor import Actor
from direct.stdpy import threading
from direct.showbase.PythonUtil import Queue
from pandac.PandaModules import lookAt
from pandac.PandaModules import Thread # necessary for Thread.sleep() which does not exist in threading
from pandac.PandaModules import GeomVertexFormat, GeomVertexData
from pandac.PandaModules import Geom, GeomTriangles, GeomLines, GeomVertexWriter
from pandac.PandaModules import Texture, GeomNode, TextureStage, TexGenAttrib
from pandac.PandaModules import TextFont
from pandac.PandaModules import PerspectiveLens
from pandac.PandaModules import CardMaker
from pandac.PandaModules import Light, Spotlight
from pandac.PandaModules import TextNode
from pandac.PandaModules import Vec3, Vec4, Point3, Point4, Mat3, Mat4, TransformState
from pandac.PandaModules import ConfigVariableString
#-----------------------------------------------------------------------------
# A couple of drawing functions that create new geometry...
def _drawTri(A, B, C, colourA, colourB, colourC, alpha):
x1, y1, z1 = A
x2, y2, z2 = B
x3, y3, z3 = C
r1, g1, b1 = colourA
r2, g2, b2 = colourB
r3, g3, b3 = colourC
trianglePrimitive = GeomTriangles(Geom.UHStatic)
format = GeomVertexFormat.getV3c4() # standard vertex format with a 4-component color and a 3-component vertex position
vdata = GeomVertexData('triangle', format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
color = GeomVertexWriter(vdata, 'color')
vertex.addData3f(x1, y1, z1)
vertex.addData3f(x2, y2, z2)
vertex.addData3f(x3, y3, z3)
color.addData4f((r1, g1, b1, alpha))
color.addData4f((r2, g2, b2, alpha))
color.addData4f((r3, g3, b3, alpha))
trianglePrimitive .addVertex(0)
trianglePrimitive .addVertex(1)
trianglePrimitive .addVertex(2)
trianglePrimitive .closePrimitive()
triangle = Geom(vdata)
triangle.addPrimitive(trianglePrimitive )
snode = GeomNode('triangle')
snode.addGeom(triangle)
return snode
def _drawLine(start, end, startColour, endColour):
x1, y1, z1 = start
x2, y2, z2 = end
linePrimitive = GeomLines(Geom.UHStatic)
format = GeomVertexFormat.getV3c4() # standard vertex format with a 4-component color and a 3-component vertex position
vdata = GeomVertexData('line', format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
color = GeomVertexWriter(vdata, 'color')
vertex.addData3f(x1, y1, z1)
vertex.addData3f(x2, y2, z2)
color.addData4f(startColour)
color.addData4f(endColour)
linePrimitive.addVertex(0)
linePrimitive.addVertex(1)
linePrimitive.closePrimitive()
line = Geom(vdata)
line.addPrimitive(linePrimitive)
snode = GeomNode('line')
snode.addGeom(line)
return snode
#-----------------------------------------------------------------------------
class Turtle(object):
""" An implementation of turtlegraphics. """
def __init__(self, actor = "defaultActor", font = "defaultFont"):
if font == "defaultFont":
font = defaultFont
self.colourA = (0.0, 0.0, 0.0)
self.colourB = (0.0, 0.0, 0.0)
self.colourC = (0.0, 0.0, 0.0)
self.alpha = 1.0
self.pointA = Point3(0.0, 0.0, 0.0)
self.pointB = Point3(0.0, 0.0, 0.0)
self.pointC = Point3(0.0, 0.0, 0.0)
self.texture = None
self.drawingNode = render.attachNewNode("Drawing")
# This node has the drawing attached as it is generated by Turtle moves
self.locationNode = render.attachNewNode("Location")
# This node locates the Turtle in rendering space.
self.adjustmentNode = self.locationNode.attachNewNode("Adjustment")
# This node is used for calculating Turtle moves
self.penDown()
self.textFont = font
def __del__(self):
self.drawingNode.removeNode()
self.locationNode.removeNode()
def penUp(self):
self.penIsDown = False
def penDown(self):
self.penIsDown = True
def clear(self):
self.locationNode.setTransform(self.locationNode.getTransform().makeIdentity())
self.drawingNode.removeChildren()
########################################
# The above line is the trigger of the error.
# Replacing it with stash, detatchNode, or reparentTo sometimes stops the error
# from happening, but not always. After numerous experiments
# I conclude that any change to the scene graph,
# such as attaching a new geom node, is sufficient
# to trigger the error, provided the right situation
# has been set up. With the present geometry
# the removeChildren() is a perfect trigger.
########################################
def setPos(self, x=None, y=None, z=None):
"""Can take a tuple of 1, 2, or 3 coordinates, or separate parameters (1, 2, or 3 of them).
The first coordinate is x, the second y, and the third z. Coordinates left out remain unchanged."""
if isinstance(x, Vec3):
pos = Point3(x)
else:
pos = x
if isinstance(pos, tuple) or isinstance(pos, Point3):
if len(pos) == 2:
x, y = pos
z = self.locationNode.getZ()
pos = (x, y, z)
if len(pos) == 1:
x, y, z = self.locationNode.getPos()
x = pos[0]
pos = (x, y, z)
else:
if (not x is None) and (not y is None) and (not z is None):
pos = (pos, y, z)
else:
x1, y1, z1 = self.locationNode.getPos()
if not x is None:
x1 = float(x)
if not y is None:
y1 = float(y)
if not z is None:
z1 = float(z)
pos = (x1, y1, z1)
if self.penIsDown:
c1r, c1g, c1b = self.colourA
c2r, c2g, c2b = self.colourB
node =_drawLine(self.locationNode.getPos(render), pos,
Vec4(c1r, c1g, c1b, self.alpha), Vec4(c2r, c2g, c2b, self.alpha))
self.drawingNode.attachNewNode(node)
self.locationNode.setPos(pos)
def forward(self, steps):
steps = float(steps)
if self.penIsDown:
c1r, c1g, c1b = self.colourA
c2r, c2g, c2b = self.colourB
node =_drawLine(Vec3(0.0, 0.0, 0.0), Vec3(0.0, steps, 0.0),
Vec4(c1r, c1g, c1b, self.alpha), Vec4(c2r, c2g, c2b, self.alpha))
new = self.drawingNode.attachNewNode(node)
new.setPos(self.locationNode.getPos(render))
new.setHpr(self.locationNode.getHpr(render))
self.adjustmentNode.setPos(0.0, steps, 0.0)
self.locationNode.setPos(self.adjustmentNode.getPos(render))
self.adjustmentNode.setPos(0.0, 0.0, 0.0)
def back(self, steps):
self.forward(-steps)
def _rotate(self, angle, hpr):
self.adjustmentNode.setHpr(hpr)
self.locationNode.setHpr(self.adjustmentNode.getHpr(render))
self.adjustmentNode.setHpr(0.0, 0.0, 0.0)
def left(self, angle):
angle = float(angle)
self._rotate(angle, (angle, 0.0, 0.0))
def right(self, angle):
self.left(-angle)
def up(self, angle):
angle = float(angle)
self._rotate(angle, (0.0, angle, 0.0))
def down(self, angle):
self.up(-angle)
def rollRight(self, angle):
angle = float(angle)
self._rotate(angle, (0.0, 0.0, angle))
def rollLeft(self, angle):
self.rollRight(-angle)
def setFont(self, font):
self.textFont = font
def write(self, text, font = None):
if font is None:
font = self.textFont
if font is None:
print "Please tell me which font to use (or set a default font)."
else:
textNodePath = self.drawingNode.attachNewNode("Text Node Path")
textNodePath.setPos(self.locationNode.getPos())
textNodePath.setHpr(self.locationNode.getHpr())
textNodePath.setScale(self.adjustmentNode.getScale())
newNode = TextNode("Text")
newNode.setFont(font.font)
newNode.setText(text)
x, y, z = self.colourA
w = self.alpha
newNode.setTextColor(x, y, z, w)
newNodePath = textNodePath.attachNewNode(newNode.generate())
newNodePath.setPos(-2.0, 0.0, 0.0)
newNodePath.setHpr(90.0, -90.0, 0.0)
newNodePath.setScale(font.scale)
newNodePath.setTwoSided(True)
def setA(self, x = None, y = None, z = None):
self.pointA = self._convertToPoint3(x, y, z)
def setB(self, x = None, y = None, z = None):
self.pointB = self._convertToPoint3(x, y, z)
def setC(self, x = None, y = None, z = None):
self.pointC = self._convertToPoint3(x, y, z)
def setTexture(self, texture):
self.texture = texture
def triangle(self, A = None, B = None, C = None):
if A is None:
A = self.pointA
if B is None:
B = self.pointB
if C is None:
C = self.pointC
if A == B or B == C or C == A:
return
if self.texture is None:
tri = _drawTri(A, B, C, self.colourA, self.colourB, self.colourC, self.alpha)
else:
white = (1.0, 1.0, 1.0)
tri = _drawTri(A, B, C, white, white, white, self.alpha)
newNode = self.drawingNode.attachNewNode(tri)
newNode.setTwoSided(True)
if self.texture is not None:
newNode.setTexGen(TextureStage.getDefault(), TexGenAttrib.MWorldPosition)
newNode.setTexProjector(TextureStage.getDefault(), render, newNode);
newNode.setTexture(self.texture, 1)
newNode.setTexScale(TextureStage.getDefault(),0.0100,0.0100)
def _convertToPoint3(self, x = None, y = None, z = None):
if isinstance(x, Point3):
return x
if isinstance(x, Vec3):
return Point3(x)
if isinstance(x, tuple):
if len(x) == 2:
x1, y1 = x
z1 = self.locationNode.getZ()
return Point3(x1, y1, z1)
if len(x) == 1:
x1, y1, z1 = self.locationNode.getPos()
x1 = x[0]
return Point3(x1, y1, z1)
else:
if (not x is None) and (not y is None) and (not z is None):
return Point3(x, y, z)
else:
x1, y1, z1 = self.locationNode.getPos()
if not x is None:
x1 = float(x)
if not y is None:
y1 = float(y)
if not z is None:
z1 = float(z)
return Point3(x1, y1, z1)
def toss(self, speed = 1):
self.spinEffect = self.drawingNode.hprInterval(speed, Point3(self.drawingNode.getH()+360,self.drawingNode.getP()+360,self.drawingNode.getR()+360))
self.spinEffect.start()
#-----------------------------------------------------------------------------
# Some code to exercise the Turtle by generating some geometry.
# It generates some triangles with applied texture, some lines, and some text nodes.
# The exact make-up of the geometry does not seem to affect the occurrance of the error,
# but this particular example seems to cause the error reasonably reliably...
def tris():
for i in range(18):
tri()
x.right(15)
x.up(15)
def tri():
x.setTexture(blobby)
x.forward(100)
x.right(120)
x.setA()
x.write('Bug')
x.forward(100)
x.right(120)
x.setB()
x.write('Bug')
x.forward(100)
x.right(120)
x.setC()
x.write('Bugger')
x.triangle()
def triThing():
x.clear()
x.penDown()
tris()
x.toss(1)
def waitFor(seq):
Thread.sleep(0.04)
while seq.isPlaying():
Thread.sleep(0.04)
def causeBugToShow():
global x
x = Turtle(None)
triThing()
waitFor(x.spinEffect)
x.clear()
#-----------------------------------------------------------------------------
class CicadaFont(object):
""" This class is just a way of loading a font and associating a certain scale number with it. """
def __init__(self, fontFileName, fontScale = 24):
try:
self.font = loader.loadFont(fontFileName)
except:
print "Unable to load font:", fontFileName
self.scale = fontScale
class _ExecThread(threading.Thread):
""" The main worker thread, from which all commands are executed using eval(). """
def run(self):
while True:
while not _commandLineQueue.isEmpty():
command = _commandLineQueue.pop()
eval(command, globals())
Thread.sleep(0.04)
Thread.sleep(0.04)
_commandLineQueue = Queue()
_thread = _ExecThread()
_thread.start()
blobby = loader.loadTexture('models/maps/noise.rgb')
defaultFont = CicadaFont('models/cmtt12.egg', fontScale = 16)
base.disableMouse()
base.camera.setPos(100.0, 100.0, 1000.0)
base.camera.lookAt(100.0, 100.0, 0.0)
_commandLineQueue.push('causeBugToShow()')
run()