GeomNode vs CollisionPolygon

In the video lecture for Panda3d’s physics system, it’s stated by (???, sorry, didn’t catch name of presenter!) that while Panda3d’s collisionTraverser can iterate over GeomNodes, this isn’t the most optimal thing. I wonder what the difference is between a CollisionPolygon and a GeomNode in this case.

I am using GeoMipTerrains in my game for terrain, and in order to generate a collision mesh, I am using BruteForce mode so it will create a static mesh. However, this means that it’s not an actual set of CollisionSolids, but a PandaNode containing a bunch of GeomNodes.

I know I shouldn’t optimize early, but I am starting to think of ways to shave off frame time. (When there are a lot of bullets flying around, my frame rate is starting to dip.)

My question is this: How bad, performance-wise, is it to use GeomNodes over CollisionPolys?

It’s huge. Maybe 100-to-1 or something like that.

The reason it’s so horrible is that the collide-with-geometry code works by actually creating CollisionPolygons on the fly and testing against those temporary CollisionPolygons.

From time to time someone proposes reworking this to not be so expensive; and it’s perfectly doable. But such a proposal has never gotten off the ground, because at the end of the day, visible polygons are usually much smaller and more numerous than collision polygons anyway, so collide-with-geometry is almost never a good idea in a finished product, no matter how inexpensive the individual polygon test is.

The GeoMipTerrain is a special case. You don’t have CollisionPolygons in that case, and there’s no realistic way to produce them. Arguably, the GeoMipTerrain should provide a collision solid type that performs its own intersection test precisely, and efficiently. It doesn’t, but you can at least query the heightfield and use that for a poor-man’s Python-based intersection test.


Ah, well, that would explain why I get 15 FPS when I have a dozen or so bullets flying along parallel with my terrain collision mesh.

I ran a prelimary Pstats and saw a lot of time spent in the traverser. (Around 20ms in the first pass.)

I might investigate the optimized collision solid segment for GMT, but I think for the short term I will see about converting the GeomNodes generated by GMT into CollisionPolys.

I find that using a GMT for visual and another for collision mesh works pretty well as long as they share the same subdivision level. I end up with a quadtree structure that plays nicely with Panda3d’s collision system, and I get a 1-to-1 correspondence with my visual GMT. (Obviously, distant collision polys don’t match the visual mesh so well due to the visual mesh mipping, but it works well enough for the scenario in my game.)

Thanks for the advice, it’s given me some areas to investigate.

take a look at the eggoctree stuff; run the generated nodepath through mine there and it will return a node full of collision triangles…it is recursive so it will get all of them…

Yeah, huge difference between the two: After converting my geomnodes into collisionnodes with collisionpolys, my collision time went from 20ms down to around 7 ms.

Here’s what I am using:

def rebuildGeomNodesToColPolys (incomingNode):
    Converts GeomNodes into CollisionPolys in a straight 1-to-1 conversion

    Returns a new NodePath containing the CollisionNodes
    parent = NodePath('cGeomConversionParent')
    for c in incomingNode.findAllMatches('**/+GeomNode'):
        gni = 0
        geomNode = c.node()
        for g in range(geomNode.getNumGeoms()):
            geom = geomNode.getGeom(g).decompose()
            vdata = geom.getVertexData()
            vreader = GeomVertexReader(vdata, 'vertex')
            cChild = CollisionNode('cGeom-%s-gni%i' % (c.getName(), gni))
            gni += 1
            for p in range(geom.getNumPrimitives()):
                prim = geom.getPrimitive(p)
                for p2 in range(prim.getNumPrimitives()):
                    s = prim.getPrimitiveStart(p2)
                    e = prim.getPrimitiveEnd(p2)
                    v = []
                    for vi in range (s, e):
                        v.append (vreader.getData3f())
                    colPoly = CollisionPolygon(*v)

            parent.attachNewNode (cChild)

    return parent

Note: Ignores scale, so do at least a flattenLight() first. :slight_smile:

Thanks Mindstorm. I’ll check your octreefy modifications, I didn’t realize that someone had converted it to work with the GeomVertexData structures.

Yes, creating a CollisionHeightfield has been a requested feature for a while now. I haven’t found the time to implement it, yet, though. Would you volunteer to implement it?

Otherwise, you could do some math yourself and use getElevation where possible - that’s why it exists.

Yeah, that is true. But for now, using CollisionRays against a converted GMT BruteForce mesh works good 'nuff for my current game.

But, I’ll take a look at it. It’s definitely something that would be useful for Panda3d overall. And it would teach me a lot about the internals of the C++ collision system…

Mindstorm, I tried your modified octreefy script. Honestly, octreefying the BruteForced mesh doesn’t really do much. I had doubts, but when I ran tests I confirmed there was no real difference. But no surprise; Heightfields are essentially 2.5D structures, so the original quad-tree-like representation retained from the GeoMipTerrain is good enough.

Update: I totally did not notice the CollisionFloorMesh solid. I’m going to see what state this is in…