CollisionTraverser.traverse() AssertionError

Im trying to track the position of the mouse in 3D space using a CollisionRay. I’ve created a basic terrain generated via code, and I want where on the 3D terrain my curser is pointed at, but when I try it crashes and I get the following error:

Assertion failed: tris->is_of_type(GeomTriangles::get_class_type()) at line 1275 of panda/src/collide/collisionTraverser.cxx

This happens as I call:

self.traverser.transverse(render)

Where

self.traverser

is the traverser I create at game start.

base.cTrav = CollisionTraverser()

So I’m wondering exactly what that error mean. Have I forgotten to add something to the dynamic terrain, or is something else going on?

Here is the traverse code:

class MouseController:
    def __init__(self, traverser):
        self.traverser = traverser
        self.handler = CollisionHandlerQueue()

        self.pickerNode = CollisionNode("mouseRay")
        self.pickerNP = base.camera.attachNewNode(self.pickerNode)
        self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask())
        self.pickerRay = CollisionRay()
        self.pickerNode.addSolid(self.pickerRay)
        self.traverser.addCollider(self.pickerNP, self.handler)

        self.object_counter = 0
        self.registered_objects = []

    def register_object(self, obj):
        print(type(obj))
        obj.setPythonTag("MouseCollisionObject", str(self.object_counter))
        self.object_counter += 1
        self.registered_objects.append(obj)

    def getCameraCollision(self, tracked_object):
        if tracked_object not in self.registered_objects:
            return TypeError("Object not tracked")

        mpos = base.mouseWatcherNode.getMouse()

        self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
        self.traverser.showCollisions(render)
        self.traverser.traverse(render)
        # Assume for simplicity's sake that myHandler is a CollisionHandlerQueue.

        if self.handler.getNumEntries() > 0:
            # This is so we get the closest object.
            for obj in self.self.handler.getEntries():
                print("Iterator\n")
                picked_object = obj.getIntoNodePath()
                print("find Tag\n")
                picked_object = obj.findNetTag("MouseCollisionObject")
                print("Before if \n")
                if not picked_object.isEmpty and picked_object == tracked_object:
                    return obj.getSurfacePoint(render), obj.getSurfaceNormal(render)
                else:
                    return None, None

And the mesh to node object creation code i use for the terrain:

def construct_pandas_object(self):
        vertex_format = GeomVertexFormat.get_v3n3c4t2()
        vertex_data = GeomVertexData("surface", vertex_format, Geom.UH_static)
        pos_writer = GeomVertexWriter(vertex_data, "vertex")
        normal_writer = GeomVertexWriter(vertex_data, "normal")
        col_writer = GeomVertexWriter(vertex_data, "color")
        uv_writer = GeomVertexWriter(vertex_data, "texcoord")

        tris_prim = GeomTriangles(Geom.UH_static)
        tris_prim.reserve_num_vertices(len(self.faces))
        print(f"vertices {len(self.vertices)}")
        print(f"normals {len(self.normals)}")
        print(f"faces {len(self.faces)}")

        geometry = Geom(vertex_data)

        for i, face in enumerate(self.faces):
            tris_prim.add_vertices(*face)

            if i < len(self.vertices):
                pos_writer.add_data3(tuple(self.vertices[i]))
                col_writer.add_data4((1.0, 1.0, 1.0, 1.0))
                uv_writer.add_data2((0, 0))
                normal_writer.add_data3(tuple(self.normals[i]))


        geometry.add_primitive(tris_prim)

        node = GeomNode("surface")
        node.add_geom(geometry)

        return NodePath(node)

A quick test showed that there is no problem with this in panda.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import (GeomVertexFormat, GeomVertexData, GeomVertexWriter, GeomTriangles, Geom, GeomNode, 
NodePath, CollisionTraverser, CollisionHandlerQueue, CollisionNode, CollisionRay, BitMask32)

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        node = self.construct()
        node.reparentTo(render)
        node.setCollideMask(BitMask32.bit(1))

        base.accept('mouse1', self.pick)

        self.CurPicker = CollisionTraverser()
        self.CurPicker.showCollisions(render)
        self.CurPickerOr = CollisionHandlerQueue()
        CurPickerNod = CollisionNode('mouseRay')
        CurPickerCam = camera.attachNewNode(CurPickerNod)
        CurPickerNod.setFromCollideMask(BitMask32.bit(1))
        CurPickerNod.setIntoCollideMask(BitMask32.allOff())
        self.CurPickerRay = CollisionRay()
        CurPickerNod.addSolid(self.CurPickerRay)
        self.CurPicker.addCollider(CurPickerCam, self.CurPickerOr)

    def pick(self):
       if base.mouseWatcherNode.hasMouse():
           mpos = base.mouseWatcherNode.getMouse()
           self.CurPickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
           self.CurPicker.traverse(render)
           self.CurPickerOr.sortEntries()
           if (self.CurPickerOr.getNumEntries() > 0):
               info = self.CurPickerOr.getEntry(0)
               print (info.getSurfacePoint(render))

    def construct(self):
        vdata = GeomVertexData('name', GeomVertexFormat.getV3(), Geom.UHStatic)
        vdata.setNumRows(4)

        vertex = GeomVertexWriter(vdata, 'vertex')
        vertex.addData3(-1.0, -1.0, 0.0)
        vertex.addData3(1.0, -1.0, 0.0)
        vertex.addData3(1.0, 1.0, 0.0)
        vertex.addData3(-1.0, 1.0, 0.0)

        prim = GeomTriangles(Geom.UHStatic)
        prim.addVertices(0, 1, 3)
        prim.addVertices(3, 1, 2)
        prim.closePrimitive()

        geom = Geom(vdata)
        geom.addPrimitive(prim)

        node = GeomNode('gnode')
        node.addGeom(geom)

        return NodePath(node)

game = Game()
game.run()

Maybe it’s in your code that you didn’t show.

I’m sorry for the delay, haven’t been at my pc till now. Found the cause.

Used make_lines_in_place to get a grid on the terrain, which caused it to crash. Likely because it works without using triangles(?).

        lines = self.terrain.copy_to(pivot)

        lines.node().modify_geom(0).make_lines_in_place()
        lines.set_render_mode_thickness(3)
        lines.set_color(1.0, 1.0, 1.0)

I wonder if there is a way I can still use this code without it crashing someway?

I think you should exclude it from the CollisionTraverser crawl. Perhaps you should manually add the objects that should collide in the CollisionTraverser. Or use a separate node instead of a render node.

And it is better to duplicate the geometry for this purpose.

You can also simply enable the display of geometry lines.

node.setRenderModeWireframe()

Something like this.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import (GeomVertexFormat, GeomVertexData, GeomVertexWriter, GeomTriangles, Geom, GeomNode, 
NodePath, CollisionTraverser, CollisionHandlerQueue, CollisionNode, CollisionRay, BitMask32)

class Game(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        self.col_all = NodePath("col")
        self.col_all.reparentTo(render)

        node = self.construct()
        node.reparentTo(self.col_all)
        node.setCollideMask(BitMask32.bit(1))
        node.setRenderModeWireframe()

        base.accept('mouse1', self.pick)

        self.CurPicker = CollisionTraverser()
        self.CurPicker.showCollisions(self.col_all)
        
        self.CurPickerOr = CollisionHandlerQueue()
        
        CurPickerNod = CollisionNode('mouseRay')
        
        CurPickerCam = camera.attachNewNode(CurPickerNod)
        
        CurPickerNod.setFromCollideMask(BitMask32.bit(1))
        CurPickerNod.setIntoCollideMask(BitMask32.allOff())
        
        self.CurPickerRay = CollisionRay()
        CurPickerNod.addSolid(self.CurPickerRay)
        
        self.CurPicker.addCollider(CurPickerCam, self.CurPickerOr)

    def pick(self):
       if base.mouseWatcherNode.hasMouse():
           mpos = base.mouseWatcherNode.getMouse()
           self.CurPickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())
           self.CurPicker.traverse(self.col_all)
           self.CurPickerOr.sortEntries()
           if (self.CurPickerOr.getNumEntries() > 0):
               info = self.CurPickerOr.getEntry(0)
               print (info.getSurfacePoint(render))


    def construct(self):
        vdata = GeomVertexData('name', GeomVertexFormat.getV3(), Geom.UHStatic)
        vdata.setNumRows(4)
        prim = GeomTriangles(Geom.UHStatic)
        vertex = GeomVertexWriter(vdata, 'vertex')

        vertex.addData3(-1.0, -1.0, 0.0)
        vertex.addData3(1.0, -1.0, 0.0)
        vertex.addData3(1.0, 1.0, 0.0)
        vertex.addData3(-1.0, 1.0, 0.0)

        prim.addVertices(0, 1, 3)
        prim.addVertices(3, 1, 2)

        prim.closePrimitive()

        geom = Geom(vdata)
        geom.addPrimitive(prim)

        node = GeomNode('gnode')
        node.addGeom(geom)

        return NodePath(node)

game = Game()
game.run()
1 Like

Thanks, that seems to work.

Have a nice day

The problem is with make_lines_in_place(). It fails to properly update the primitive type.

I will check in a fix for 1.10.10. In the meantime, you can take the GeomPrimitive created by make_lines_in_place() and attach it to a new Geom with the same vertex data.

1 Like