Maybe I need to handle the rays myself.
CollisionFloorHandler seems broken with trimeshes.
I was able to use conditionals to remove specific tris from the gridlike mesh and get it to run. The code will run for varying amounts of time (the mesh is not modified after initial generation) before it bombs due to the same assertion failure.
Edit: Modified the code to avoid any triangle that made it crash immediately, There were only two:
c=11,r=2
c=55,r=5
This is in a regularly defined loop so why would two tris of the grid be degenerate??? If the verteces were bad it would affect other neighbouring tris that used the same vertices.
Edit 2: The amount of time seems to vary from run to run before the assert fires. Sometimes it runs for quite a while other times the walker’s intervals do not even get all the way around.
Edit 3: The problem stays the same even with the random Z adjustment reenabled.
Leads me to suspect it’s not the vertices or the tris.
from math import pi, sin, cos, tan, sqrt
from functools import partial
from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
from direct.interval.IntervalGlobal import LerpFunc
from direct.filter.CommonFilters import CommonFilters
from direct.filter.FilterManager import FilterManager
from panda3d.core import Point3,VBase3,GeoMipTerrain,TextureStage,Fog
from panda3d.core import CollisionNode,CollisionTraverser,CollisionHandlerFloor
from panda3d.core import CollisionRay,CollisionFloorMesh
from panda3d.core import GeomVertexFormat,GeomVertexData,GeomVertexWriter
from panda3d.core import Geom,GeomTriangles,GeomNode
from panda3d.core import PStatClient
from panda3d.core import Quat,BitMask32
#from pandac.PandaModules import AntialiasAttrib
from random import random,seed
worldScale=26.02#/3.2808399 # 26.02 convert unit to m, /3.2808399 convert unit to ft
fogStart=15*worldScale
fogLimit=90*worldScale
zOff=0.5
Pi=pi
Pi2=2*Pi
d2r=Pi/180
r2d=180/Pi
class MyApp(ShowBase):
def __init__(self):
ShowBase.__init__(self)
self.mob={}
self.eyeLevel={}
self.worldCfm={}
self.worldClN={}
self.floor=CollisionHandlerFloor()
self.floor.setMaxVelocity(100)
self.floor.setOffset(0)
self.cTrav=CollisionTraverser()
# Set up fog/filters/camera
self.disableMouse()
self.camLens.setNear(30)
self.camLens.setFar(10000)
self.filters=CommonFilters(self.win, self.cam)
self.fog=Fog("worldfog")
self.fog.setColor(0.7,0.7,0.8)
self.fog.setLinearRange(0,fogLimit)
self.fog.setLinearFallback(45,fogStart,fogLimit)
self.fog.setLinearOnsetPoint(0,fogStart,0)
self.fog.setLinearOpaquePoint(0,fogLimit,0)
self.fogOb=camera.attachNewNode(self.fog)
self.foggy=render.attachNewNode("foggy")
self.foggy.setFog(self.fog)
self.camleg=Quat()
#self.render.setAntialias(AntialiasAttrib.MAuto)
# For dynamically generated meshes
self.vFmt=GeomVertexFormat.getV3t2()
self.vData=GeomVertexData('theWorld',self.vFmt,Geom.UHStatic)
self.vOp={}
self.vOp['v']=GeomVertexWriter(self.vData,'vertex')
#self.vOp['c']=GeomVertexWriter(self.vData,'color')
self.vOp['t']=GeomVertexWriter(self.vData,'texcoord')
self.vOp['vAdd']=self.vOp['v'].addData3f
#self.vOp['cAdd']=self.vOp['c'].addData4f
self.vOp['tAdd']=self.vOp['t'].addData2f
self.tWorld=GeomTriangles(Geom.UHStatic)
self.gWorld=Geom(self.vData)
self.gWorld.addPrimitive(self.tWorld)
self.nWorld=GeomNode('dynWorld')
self.nWorld.addGeom(self.gWorld)
self.World=self.foggy.attachNewNode(self.nWorld)
self.World.setScale(worldScale)
cell=0
self.World.setPos(0,0,0)
cln=self.World.attachNewNode(CollisionNode('floor_'+str(cell)+'_cln'))
self.worldClN[cell]=cln
self.worldCfm[cell]=CollisionFloorMesh()
# Set vertices
seed(729035)
for x in range(11):
for y in range(11):
k=random()*.6-.3
#k=0
k=k+zOff
self.vOp['vAdd'](x-5,y-5,k)
self.worldCfm[cell].addVertex((x-5,y-5,k))
self.vOp['tAdd'](x,y)
#self.vOp['cAdd'](1,1,1,1)
# Set triangles
for c in range(0,100,11):
for r in range(0,10):
# commented out to show which tris the collisionsystem doesn't like
if (c!=11 or r!=2) and (c!=55 or r!=5):
self.tWorld.addVertices(c+r,c+r+12,c+r+1)
#next line causes error if uncommented
#AssertionError: !pos.is_nan() at line 375 of
# c:\buildslave\release_sdk_win32\build\panda3d\panda\src\pgraph\transformState.cxx
self.worldCfm[cell].addTriangle(c+r,c+r+12,c+r+1)
self.tWorld.addVertices(c+r+12,c+r,c+r+11)
self.worldCfm[cell].addTriangle(c+r+12,c+r,c+r+11)
#self.tWorld.closePrimitive()
self.worldClN[cell].node().addSolid(self.worldCfm[cell])
self.worldClN[cell].node().setFromCollideMask(BitMask32(0x0000))
self.worldClN[cell].node().setIntoCollideMask(BitMask32(0xffff))
self.worldClN[cell].show()
self.myTex=self.loader.loadTexture("outdoor/dirt.png")
self.World.setTexture(self.myTex)
self.camTop=self.camera.attachNewNode("tov")
self.camBtm=self.camera.attachNewNode("bov")
# Use class 'CommonFilters' to enable a cartoon inking filter.
# This can fail if the video card is not powerful enough, if so,
# display an error and exit.
#self.separation = .667 # Pixels
#filterok = self.filters.setCartoonInk(separation=self.separation)
#if (filterok == False):
# addTitle("Toon Shader: Video card not powerful enough to do image postprocessing")
# return
# Load the sky
self.sky=self.loader.loadModel("skies/blue_sky_sphere")
self.sky.reparentTo(self.render)
self.skyH=0
self.skySpd=.005
# Load ocean
self.sea=self.loader.loadModel("models/water")
self.sea.reparentTo(self.foggy)
self.seaTS=self.sea.findAllTextureStages()[0]
self.seaSpeedU=.02
self.seaSpeedV=.023
self.seaShift=.01
self.seaOffU=0
self.seaOffV=0
# Load character models.
mobName='Ayuko'
self.mob[mobName]=Actor("models/Ayuko/mAyuko")
act=self.mob[mobName]
act.setScale(25.4)
act.setPos(-5*worldScale,-5*worldScale,zOff*worldScale)
self.eyeLevel[mobName]=34.85
act.reparentTo(self.foggy)
mobName='Ariko'
self.mob[mobName]=Actor("models/mAriko")
act=self.mob[mobName]
act.setScale(25.4)
act.setPos(0,0,zOff*worldScale)
self.eyeLevel[mobName]=32.5
act.reparentTo(self.foggy)
for nm in self.mob:
mob=self.mob[nm]
cln=mob.attachNewNode(CollisionNode(nm+'_cln'))
cln.node().addSolid(CollisionRay(0,0,1,0,0,-1))
cln.node().setFromCollideMask(BitMask32(0xffff))
cln.node().setIntoCollideMask(BitMask32(0x0000))
cln.show()
self.cTrav.addCollider(cln,self.floor)
self.floor.addCollider(cln,mob)
# Set a better initial camera
self.cH=180
self.cP=12.5
self.cDist=15*worldScale
# Camera Target
self.target='Ariko'
mob=self.mob['Ayuko']
#mob.loop("walk")
self.ayuSeq={}
#turnDur=sqrt(2)*4/6 # diagonal fraction of delay
turnDur=1
self.ayuSeq['1']=LerpFunc(setMaskedPos,
duration=4,
toData=Point3(-4*worldScale,3*worldScale,zOff*worldScale),
fromData=Point3(-4*worldScale,-3*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['2a']=LerpFunc(setMaskedPos,
duration=turnDur,
toData=Point3(-3*worldScale,4*worldScale,zOff*worldScale),
fromData=Point3(-4*worldScale,3*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['2b']=mob.hprInterval(turnDur,
Point3(270,0,0),
startHpr=Point3(360,0,0))
self.ayuSeq['3']=LerpFunc(setMaskedPos,
duration=4,
toData=Point3(3*worldScale,4*worldScale,zOff*worldScale),
fromData=Point3(-3*worldScale,4*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['4a']=LerpFunc(setMaskedPos,
duration=turnDur,
toData=Point3(4*worldScale,3*worldScale,zOff*worldScale),
fromData=Point3(3*worldScale,4*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['4b']=mob.hprInterval(turnDur,
Point3(180,0,0),
startHpr=Point3(270,0,0))
self.ayuSeq['5']=LerpFunc(setMaskedPos,
duration=4,
toData=Point3(4*worldScale,-3*worldScale,zOff*worldScale),
fromData=Point3(4*worldScale,3*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['6a']=LerpFunc(setMaskedPos,
duration=turnDur,
toData=Point3(3*worldScale,-4*worldScale,zOff*worldScale),
fromData=Point3(4*worldScale,-3*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['6b']=mob.hprInterval(turnDur,
Point3(90,0,0),
startHpr=Point3(180,0,0))
self.ayuSeq['7']=LerpFunc(setMaskedPos,
duration=4,
toData=Point3(-3*worldScale,-4*worldScale,zOff*worldScale),
fromData=Point3(3*worldScale,-4*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['8a']=LerpFunc(setMaskedPos,
duration=turnDur,
toData=Point3(-4*worldScale,-3*worldScale,zOff*worldScale),
fromData=Point3(-3*worldScale,-4*worldScale,zOff*worldScale),
extraArgs=[mob,'xy'])
self.ayuSeq['8b']=mob.hprInterval(turnDur,
Point3(0,0,0),
startHpr=Point3(90,0,0))
self.ayuSeq['seq']=Sequence(self.ayuSeq['1'],
Parallel(self.ayuSeq['2a'],self.ayuSeq['2b']),
self.ayuSeq['3'],
Parallel(self.ayuSeq['4a'],self.ayuSeq['4b']),
self.ayuSeq['5'],
Parallel(self.ayuSeq['6a'],self.ayuSeq['6b']),
self.ayuSeq['7'],
Parallel(self.ayuSeq['8a'],self.ayuSeq['8b']))
self.ayuSeq['seq'].loop()
#PStatClient.connect()
#render.analyze()
print self.cTrav
def onUpdate(self):
self.skyH+=self.skySpd
while self.skyH>=360:
self.skyH-=360
while self.skyH<0:
self.skyH+=360
self.sky.setH(self.skyH)
mob=self.mob[self.target]
aLoc=mob.getPos()
el=self.eyeLevel[self.target]
p=VBase3(0,-1,0)
self.camleg.setHpr(VBase3(180+mob.getH()+self.cH,180+self.cP,0))
p1=self.camleg.xform(p)
p1=p1*self.cDist
self.camera.setPos(aLoc[0]+p1[0],
aLoc[1]+p1[1],
aLoc[2]+p1[2]+el)
self.camera.lookAt(aLoc+(0,0,el))
#self.camera.lookAt(self.mob['Ayuko'].getPos()+(0,0,self.eyeLevel['Ayuko']))
self.mob['Ariko'].lookAt(self.mob['Ayuko'])
hpr=self.mob['Ariko'].getHpr()
self.seaOffU+=self.seaSpeedU
self.seaOffV+=self.seaSpeedV
while self.seaOffU>Pi2:
self.seaOffU-=Pi2
while self.seaOffV>Pi2:
self.seaOffV-=Pi2
self.sea.setTexOffset(self.seaTS,
sin(self.seaOffU)*self.seaShift,
sin(self.seaOffV)*self.seaShift)
vFov=self.camLens.getFov()[1]
nearP=self.camLens.getNear()
zd=nearP*tan(0.5*vFov*d2r)
self.camTop.setPos(0,nearP,zd)
self.camBtm.setPos(0,nearP,-zd)
tov=self.camTop.getZ(self.render)
bov=self.camBtm.getZ(self.render)
if tov<0 or bov<0:
self.filters.setBlurSharpen(amount=0.4)
render.setColorScale(0.60,0.90,1.00,1)
else:
self.filters.delBlurSharpen()
render.clearColorScale()
def setMaskedHpr(v3,ob=None,mask='hpr'):
while len(mask)<3:
mask+='-'
if mask[0]=='h' or mask[1]=='h' or mask[2]=='h':
ob.setH(v3[0])
if mask[0]=='p' or mask[1]=='p' or mask[2]=='p':
ob.setP(v3[1])
if mask[0]=='r' or mask[1]=='r' or mask[2]=='r':
ob.setR(v3[2])
def setMaskedPos(v3,ob=None,mask='xyz'):
while len(mask)<3:
mask+='-'
if mask[0]=='x' or mask[1]=='x' or mask[2]=='x':
ob.setX(v3[0])
if mask[0]=='y' or mask[1]=='y' or mask[2]=='y':
ob.setY(v3[1])
if mask[0]=='z' or mask[1]=='z' or mask[2]=='z':
ob.setZ(v3[2])
app = MyApp()
def updateTask(task):
app.onUpdate()
return task.cont
taskMgr.add(updateTask,"UpdaterTask")
app.run()