Issues with CollisionHandlerFloor

I am trying to use a CollisionHandlerFloor to test ray collisions of characters against procedurally generated mesh.
Only about half the tris want to work whereas the other half cause it to assert bomb,

http://pastebin.com/MBwVpnM6
lines 99-102 are the tris that don’t cooperate

C:\Users\B-san\Desktop\Panda-AM>ppython amclient.py
      Known pipe types:
        wglGraphicsPipe
      (all display modules loaded.)
      CollisionTraverser, 2 colliders and 1 handlers:
        render/foggy/Bones/Ayuko_cln handled by CollisionHandlerFloor
          ray, o (0 0 0.125), d (0 0 -1)
        render/foggy/ArikoBones/Ariko_cln handled by CollisionHandlerFloor
          ray, o (0 0 0.125), d (0 0 -1)
       
      Assertion failed: !pos.is_nan() at line 375 of c:\buildslave\release_sdk_win32\b
      uild\panda3d\panda\src\pgraph\transformState.cxx
      Assertion failed: !pos.is_nan() at line 375 of c:\buildslave\release_sdk_win32\b
      uild\panda3d\panda\src\pgraph\transformState.cxx
      Traceback (most recent call last):
        File "C:\Panda3D-1.7.2\direct\showbase\ShowBase.py", line 1684, in __collision
      Loop
          self.cTrav.traverse(self.render)
      AssertionError: !pos.is_nan() at line 375 of c:\buildslave\release_sdk_win32\bui
      ld\panda3d\panda\src\pgraph\transformState.cxx
      :task(error): Exception occurred in PythonTask collisionLoop
      Traceback (most recent call last):
        File "amclient.py", line 322, in <module>
          app.run()
        File "C:\Panda3D-1.7.2\direct\showbase\ShowBase.py", line 2630, in run
          self.taskMgr.run()
        File "C:\Panda3D-1.7.2\direct\task\Task.py", line 502, in run
          self.step()
        File "C:\Panda3D-1.7.2\direct\task\Task.py", line 460, in step
          self.mgr.poll()
        File "C:\Panda3D-1.7.2\direct\showbase\ShowBase.py", line 1684, in __collision
      Loop
          self.cTrav.traverse(self.render)
      AssertionError: !pos.is_nan() at line 375 of c:\buildslave\release_sdk_win32\bui
      ld\panda3d\panda\src\pgraph\transformState.cxx

The error message implies that attempting to compute the intersection resulted in a NaN calculation. It’s not obvious why, but perhaps some of your vertices had NaN values in them; or maybe it’s as simple as degenerate (collinear) triangles in your mesh.

Try printing out the GeomTriangles and GeomVertexData you generated to confirm that the data doesn’t contain NaN’s and is sensible.

David

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()

I can’t run your code without the model and texture files it references. Are those necessary to demonstrate the problem?

David

Pandas and smileys would probably work as placeholders for the character models since it’s the rays and dynamic triangle mesh that is at issue, not the model meshes.

The sky could be removed entirely and any texture you have available could be switched in for the “ground” setTexture call.

PS: Just in case it’s not obvious I basically create the collision tri’s addTriangle (and add vertices) in the same code calling addData/addVertices (VertexWriter/Geom).

It would be simpler if there was a way to initialize the CollisionFloorMesh from an existing Geom.

PSS: I’m using Panda 1.7.2

Would you mind posting a version of your code that you and I can both paste in and run directly and demonstrate the crash, without either of us having to edit out the model references? That would make it easier for us to confirm that we are looking at precisely the same thing. Thanks!

David

The following code is modified to use only stuff in the existing Panda models and maps folders. The symptoms still remain.

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("models/maps/envir-ground.jpg")
        self.World.setTexture(self.myTex)
 
        self.camTop=self.camera.attachNewNode("tov")
        self.camBtm=self.camera.attachNewNode("bov")


        # Load character models.
        mobName='Ayuko'
        self.mob[mobName]=Actor("models/panda")
        act=self.mob[mobName]
        act.setHpr(180,0,0)
        act.flattenLight()
        act.setScale(2.54)
        act.setPos(-5*worldScale,-5*worldScale,zOff*worldScale)
        self.eyeLevel[mobName]=34.85
        act.reparentTo(self.foggy)
        mobName='Ariko'
        self.mob[mobName]=Actor("models/smiley")
        act=self.mob[mobName]
        act.setHpr(180,0,0)
        act.flattenLight()
        act.setScale(12)
        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):            
        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()

        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()

OK, found it. You were absolutely right (though the actual problem was in CollisionFloorMesh, and not in CollisionHandlerFloor). In any case, there was a possibility of a NaN getting introduced into the equation if the source point happened to be coincident with one of the vertices in the mesh.

I’ve just committed a fix. My apologies for the bug.

David

Will the change be in 1.7.2 or will I need to wait for next version (ie: 1.7.3)?

1.7.2 has already been released, so you will need to wait for the next version, or use the next buildbot release. Or, you could get the source and build it yourself.

Or, you could work around the problem by creating a set of CollisionPolygon objects instead of a CollisionFloorMesh (the performance envelope will be similar); or you could ensure that your ray is never coincident with any of the mesh vertices.

David