Collisions with GeoMIP Terrain

Can somebody point me towards an example that has GeoMIP Terrain working with collisions? I’m getting really lost in trying to find a way to make this work.

Right now I’ve got a model that can walk around, and I added a CollisionRay to it, but I can’t figure out how to make it interact with the GeoMIP terrain I have.

The only thread with anything useful I’ve been able to find it … sc&start=0, which helped me with a few issues, but I can’t seem to get it to notice collisions with the ground.

Here’s the code:

import direct.directbase.DirectStart                   
import sys, os, math, random    
from direct.showbase.DirectObject       import DirectObject             
from pandac.PandaModules                import *       
from pandac.PandaModules                import Vec3, Vec4, BitMask32     

from panda3d.core import CollisionTraverser, CollisionNode, CollisionHandlerQueue, CollisionRay

from 	        import Actor         
from direct.gui.DirectGui               import DirectFrame   
from direct.gui.OnscreenText            import OnscreenText   
class Game(DirectObject):
    def __init__(self):
        self.cTrav = CollisionTraverser()
        self.direction = "Stopped"
        self.facingDirection = self.model.getH( )
        self.turningDirection = 0;
        taskMgr.add( self.movingTask, 'movingTask' )
        taskMgr.add( self.turningTask, 'turningTask' )
        self.accept('w', self.setMovingDirection, ["Forward"])
        self.accept('w-up', self.setMovingDirection, ["Stopped"])
        self.accept('s', self.setMovingDirection, ["Backward"])
        self.accept('s-up', self.setMovingDirection, ["Stopped"])    
        self.accept('a', self.setTurningDirection, [1])
        self.accept('a-up', self.setTurningDirection, [0])      
        self.accept('d', self.setTurningDirection, [-1])
        self.accept('d-up', self.setTurningDirection, [0])
        self.accept('space', self.jump)

    def jump(self):
        print "Jump"
        self.model.setZ(self.model, 100)
    def setMovingDirection(self, dir):
        self.direction = dir
        if dir == "Forward":
            self.modelWalk.resume( )
        if dir == "Backward":
            self.modelWalk.resume( )
        if dir == "Stopped":
            self.modelWalk.pause( )
    def setTurningDirection(self, dir):
        self.turningDirection = dir
    def turningTask(self, task):
        self.model.setH( self.facingDirection + self.turningDirection )
        self.facingDirection = self.model.getH( )
        return task.cont

    def movingTask(self, task):
        speed = 0
        if self.direction == "Forward":
            speed = -5
        if self.direction == "Backward":
            speed = 5
        startpos = self.model.getPos()            
        self.model.setY(self.model, speed)
        #check for collisions      
        entries = []
        for i in range(self.modelGroundHandler.getNumEntries()):
            entry = self.modelGroundHandler.getEntry(i)
            print entry
        if self.modelGroundHandler.getNumEntries() > 0: 
            entry = self.modelGroundHandler.getEntry(0) 
            print entry 
        return task.cont

    def loadModel(self):
        self.model = Actor( "tiny/tiny",
                            {"walk": "tiny/tiny"})

        self.modelGroundRay = CollisionRay()
        self.modelGroundCol = CollisionNode('modelRay')
        self.modelGroundColNp = self.model.attachNewNode(self.modelGroundCol)
        self.modelGroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.modelGroundColNp, self.modelGroundHandler)

        self.model.reparentTo( render )
        self.model.setScale( .02 )
        self.model.setH( 90 )         
        self.model.setPos(10.0, 55.0, 75.0)
        taskMgr.add( self.cameraFollowTask, 'cameraFollowTask')
        self.modelWalk = self.model.actorInterval( "walk" )
        self.modelWalk.loop( )
        self.modelWalk.pause( )
    def loadTerrain(self):
        self.terrain = GeoMipTerrain("map1")
        self.root = self.terrain.getRoot()
        taskMgr.add(self.updateTerrainTask, 'RegenTerrain')
    def debugConsole(self):
        self.textDebug = TextNode('DebugConsole')
        self.textDebug.setFrameColor(0, 0, 0, 1)
        self.textDebug.setCardColor(0, 0, 0, 1)
        self.textDebug.setCardAsMargin(0.4, 0.4, 0.4, 0.4)
        self.textNodePath = aspect2d.attachNewNode(self.textDebug)
        self.textNodePath.setPos(0.02, 0, -0.04)
        self.textDebug.setText("Initializing debug console...")
        taskMgr.doMethodLater(1, self.updateDebugConsoleTask, 'RegenConsole')
    def updateDebugConsoleTask(self, task):
        fps = "FPS: " + str(round(globalClock.getAverageFrameRate(), 0)) + "\nCamera XYZ: " + str(camera.getPos( )) + "\nFAC: " + str(self.facingDirection)
        task.delayTime = 1
        return task.again

    def updateTerrainTask(self, task):
        return task.cont

    def cameraFollowTask( self, task ):
        offset = Vec3( 0, 75, 40 )
        desiredPos = self.model.getPos( ) + self.model.getQuat( ).xform( offset )
        return task.cont

Game = Game()


This is what I get when I run it and move around:

As you can see, it’s hitting the GeoMip terrain and so is the ray (the white line right beside him). But it’s not recording it as a collision at all.

OK, so I went back to the thread I mentioned in my original post and saw how they changed the BitMask on the actor model.

This is what I was originally using:


And this is what I changed it to:


And this is the result:

Yay! I’m registering collisions, but now it’s saying I’ve got a collision every frame even when the model is not in contact with the terrain. It also seemed to change the ray length from a tiny height (I had it about the height of the Actor model before) to really really long (I suspect it’s hitting the terrain right from the start due to how long it is).

So how to I reduce the length of the ray so it’s the right size?

Edit: It has to be the length of the ray now. You can see in the picture above at the very bottom of the screen that it’s lighting up the terrain where the ray hits it. I put this code in to do that:


But I’ve also noticed a second problem - the ray appears to be getting collisions from the model it’s attached to, you can see here:

Not only is the terrain showing collisions with the ray, but the right arm of the model is too.

Any suggestions?

geomipterrain was created to create rendering-optimized meshes.
those usualy are highly unsuited for collisions and you are best off not using them for collision.

if you only need the elevation below a certain point or nodepath there is the yourgeomipterrain.getElevation() function. it’s a highly optimized function based on the input-data and is a lot faster than performing “real” collision against the geometry.
you can directly supply a nodepath as argument and it’ll return the position of the collision point.

ps: at least i think it was called getElevation. you may want to check the api-reference for details.

Hmm…I found a few examples of that. But does it save that much processing power/resources?

Edit: The reason I ask is that the documentation ( … MipMapping) says that GeoMIP “improves culling and collision detection” so I thought it would be more suitable to use.

long answer short. yes, it does collision a LOT faster than testing against triangles. if you set up your collision geometry in a bad way the speed advantage may be up to 3 or 4 orders of magnitude faster. aside from that it’s a very robust detection. no falling through the ground. additionally to that, it’s a lot easier to deal with than with the rest of the collision system. with a single call you have your point. no masks, no collision solids, no traverse calls or collision queues needed.

if you can, use it. it’s advantages are over 9000.

Bugger. Has anybody gotten this work and can show an example? I’m really close, I just can’t figure out what is wrong. It’s detecting collisions, but it looks like it always thinks I’m colliding with the terrain even if I’m way above it.