terrain for vehicle in Bullet?

Hello. I’m working with the code “18_Vehicle” This tutorial Bullet for Panda3D:
enn0x.p3dp.com/bullet-samples.zip.
I need this car set to roll on uneven ground that I have named “myterrain. egg” . How I can do this? I read the manual of “Bullet Continuous Collision Detection” but I could not solve my problem. Anyone can help me with this code please?
Thank you!

code axample:

 def setup(self):
    self.worldNP = render.attachNewNode('World')

    # World
    self.debugNP = self.worldNP.attachNewNode(BulletDebugNode('Debug'))
    self.debugNP.show()

    self.world = BulletWorld()
    self.world.setGravity(Vec3(0, 0, -9.81))
    self.world.setDebugNode(self.debugNP.node())

    # Plane
    shape = BulletPlaneShape(Vec3(0, 0, 1), 0)

    np = self.worldNP.attachNewNode(BulletRigidBodyNode('Ground'))
    np.node().addShape(shape)
    np.setPos(0, 0, -1)
    np.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(np.node())

    # Chassis
    shape = BulletBoxShape(Vec3(0.6, 1.4, 0.5))
    ts = TransformState.makePos(Point3(0, 0, 0.5))

    np = self.worldNP.attachNewNode(BulletRigidBodyNode('Vehicle'))
    np.node().addShape(shape, ts)
    np.setPos(0, 0, 1)
    np.node().setMass(800.0)
    np.node().setDeactivationEnabled(False)

    self.world.attachRigidBody(np.node())

    #np.node().setCcdSweptSphereRadius(1.0)
    #np.node().setCcdMotionThreshold(1e-7) 

    # Vehicle
    self.vehicle = BulletVehicle(self.world, np.node())
    self.vehicle.setCoordinateSystem(ZUp)
    self.world.attachVehicle(self.vehicle)

    self.yugoNP = loader.loadModel('models/yugo/yugo.egg')
    self.yugoNP.reparentTo(np)

    # Right front wheel
    np = loader.loadModel('models/yugo/yugotireR.egg')
    np.reparentTo(self.worldNP)
    self.addWheel(Point3( 0.70,  1.05, 0.3), True, np)

    # Left front wheel
    np = loader.loadModel('models/yugo/yugotireL.egg')
    np.reparentTo(self.worldNP)
    self.addWheel(Point3(-0.70,  1.05, 0.3), True, np)

    # Right rear wheel
    np = loader.loadModel('models/yugo/yugotireR.egg')
    np.reparentTo(self.worldNP)
    self.addWheel(Point3( 0.70, -1.05, 0.3), False, np)

    # Left rear wheel
    np = loader.loadModel('models/yugo/yugotireL.egg')
    np.reparentTo(self.worldNP)
    self.addWheel(Point3(-0.70, -1.05, 0.3), False, np)

    # Steering info
    self.steering = 0.0            # degree
    self.steeringClamp = 45.0      # degree
    self.steeringIncrement = 120.0 # degree per second

  def addWheel(self, pos, front, np):
    wheel = self.vehicle.createWheel()

    wheel.setNode(np.node())
    wheel.setChassisConnectionPointCs(pos)
    wheel.setFrontWheel(front)

    wheel.setWheelDirectionCs(Vec3(0, 0, -1))
    wheel.setWheelAxleCs(Vec3(1, 0, 0))
    wheel.setWheelRadius(0.25)
    wheel.setMaxSuspensionTravelCm(40.0)

    wheel.setSuspensionStiffness(40.0)
    wheel.setWheelsDampingRelaxation(2.3)
    wheel.setWheelsDampingCompression(4.4)
    wheel.setFrictionSlip(100.0);
    wheel.setRollInfluence(0.1)

Continuous collision detection has nothing to do with this.

Look at the sample. Here is the part where you load the “terrain” that the vehicle drives on:

    # Plane
    shape = BulletPlaneShape(Vec3(0, 0, 1), 0)

    np = self.worldNP.attachNewNode(BulletRigidBodyNode('Ground'))
    np.node().addShape(shape)
    np.setPos(0, 0, -1)
    np.setCollideMask(BitMask32.allOn()) 

The example uses a simple terrain, a plane. Replace this part with code where you create a Bullet triangle mesh shape (and rigid body) from your .egg file. See the other samples (e.g. 02_shapes) for how to do it.

ok enn0x Thanks. I solved my problem!
But I have another problem. How to add texture to myterrain. egg? I only see the field in “Wireframe”, My myterrain. egg file is assigned a texture, but do not get to appear. Thanks for your help!

I guess that the wireframe you see is not the GeomNode loaded from the .egg file, but the bullet debug render (which shows Bullet collision shapes CREATED FROM the GeomNode). Have you reparented the NodePath returned from the loader to the scene graph?

Ok the problem is that terrain of collision that appears in wireframe not match with my “geom” reparented to “render” or “np” . you need to set the scale of “geom” to match?

    geom = loader.loadModel('models/myterrain.egg')
    geom.reparentTo(np)
    #geom.reparentTo(render)
    
    Geom=geom.findAllMatches('**/+GeomNode').getPath(0).node().getGeom(0)
     
    mesh = BulletTriangleMesh()
    mesh.addGeom(Geom)
    shape = BulletTriangleMeshShape(mesh, dynamic=False)
    np = self.worldNP.attachNewNode(BulletRigidBodyNode('Mesh'))   
    np.node().addShape(shape)
    np.setPos(0, 0, 0.1)
    np.setScale(10, 10, 10)
    np.setCollideMask(BitMask32.allOn())

    self.world.attachRigidBody(np.node())

There are a couple of things in you code which might go wrong:

geom.reparentTo(np) 
...
np = self.worldNP.attachNewNode(BulletRigidBodyNode('Mesh'))

The terrain you load is reparented to ‘np’, which is later replace by the Bullet node. ‘np’ might have a transform. Also, only replace if you know what you do. Otherwise use different variables.

...geom.findAllMatches('**/+GeomNode').getPath(0)...

This returns a NodePath found BELOW the NodePath ‘geom’. This NothPath might have a transform relative to ‘geom’. You have to use this transform when adding the shape to the rigid body (addShape) or when setting the transform of the rigid body (setPos in your case).

Geom=geom.findAllMatches('**/+GeomNode').getPath(0).node().getGeom(0) 

A common mistake is that users miss the fact that a NodePath loaded via the loader might have more than one GeomNode, and a GeomNode might have more than one Geom.

np.setScale(10, 10, 10) 

do not scale Bullet nodes. It is possible in some cases, but not recommended. You can either scale the visible geometry AFTER you used it as a blueprint for the collision shape, or scale it flatten the geometry BEFORE using it as a blueprint.