Panda Bullet

Hi,
I’m implementing a picker by following the former Demos/OpenGL/DemoApplication.cpp code in bullet 2.83. But I’m having trouble with BulletWorld.clearTickCallback(), that is when I try to clear a previously set tick callback (with BulletWorld.setTickCallback()) I get this error:

...
Assertion failed: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx
Traceback (most recent call last):
  File "/Projects/physics_bullet_tes2.py", line 173, in simulationTask
    bulletWorld.doPhysics(globalClock.getDt())
AssertionError: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx
:task(error): Exception occurred in PythonTask Physics Simulation
Traceback (most recent call last):
  File "/Projects/physics_bullet_tes2.py", line 222, in <module>
    app.run()
  File "/usr/share/panda3d/direct/showbase/ShowBase.py", line 2984, in run
    self.taskMgr.run()
  File "/usr/share/panda3d/direct/task/Task.py", line 509, in run
    self.step()
  File "/usr/share/panda3d/direct/task/Task.py", line 466, in step
    self.mgr.poll()
  File "/Projects/physics_bullet_tes2.py", line 173, in simulationTask
    bulletWorld.doPhysics(globalClock.getDt())
AssertionError: world->getWorldUserInfo() at line 932 of panda/src/bullet/bulletWorld.cxx

Here is the example code:

from panda3d.core import Vec3, NodePath, CardMaker, BitMask32, LMatrix4f, \
                LPoint3f, LVector3f, TransformState, PythonCallbackObject
from panda3d.bullet import BulletSphereShape, BulletConstraint, \
                BulletRigidBodyNode, BulletSphericalConstraint, BulletWorld, \
                BulletPlaneShape, BulletGenericConstraint
from direct.showbase.ShowBase import ShowBase

class Picker:
    def __init__(self, app, world, pickKeyOn, pickKeyOff,
                 csIsSpherical=True, cfm=0.5, erp=0.5, debug=False):
        # get bullet world reference
        self.mWorld = world
        # get render, camera node paths
        self.app = app
        self.mRender = self.app.render
        self.mCamera = self.app.camera
        self.mCamLens = self.app.camLens
        # (re)set picking logic data
        self.mCsPick = None
        self.mPivotCamDist = 0.0
        self.mPickedBody = None
        self.mCsIsSpherical = csIsSpherical
        self.mCfm = cfm
        self.mErp = erp
        # setup event callback for picking body
        self.mPickKeyOn = pickKeyOn
        self.mPickKeyOff = pickKeyOff
        self.app.accept(self.mPickKeyOn, self.pickBody, [self.mPickKeyOn])
        self.app.accept(self.mPickKeyOff, self.pickBody, [self.mPickKeyOff])
        # debug
        self.debug = debug
    
    def pickBody(self, event):
        # handle body picking
        if event == self.mPickKeyOn:
            # get the mouse watcher
            mwatcher = self.app.mouseWatcherNode
            if mwatcher.hasMouse():
                # Get to and from pos in camera coordinates
                pMouse = mwatcher.getMouse()
                #
                pFrom, pTo = (LPoint3f(), LPoint3f())
                if self.mCamLens.extrude(pMouse, pFrom, pTo):
                    # Transform to global coordinates
                    pFrom = self.mRender.getRelativePoint(self.mCamera, pFrom)
                    pTo = self.mRender.getRelativePoint(self.mCamera, pTo)
                    # cast a ray to detect a body
                    result = self.mWorld.rayTestClosest(pFrom, pTo, BitMask32.allOn())
                    #
                    if result.hasHit():
                        # possible hit objects:
                        # - BulletRigidBodyNode
                        # - BulletCharacterControllerNode
                        # - BulletVehicle
                        # - BulletConstraint
                        # - BulletSoftBodyNode
                        # - BulletGhostNode
                        if result.getNode().isOfType(BulletRigidBodyNode.getClassType()):
                            self.mPickedBody = result.getNode()
                            if not (self.mPickedBody.isStatic() or self.mPickedBody.isKinematic()):
                                # set body as active and not deactivable
                                self.mPickedBody.setDeactivationEnabled(False)
                                # get global pivot pos
                                pivotPos = result.getHitPos()
                                # get the initial distance from camera
                                self.mPivotCamDist = (pivotPos - self.mCamera.getPos(self.mRender)).length()
                                # compute pivot pos relative to body
                                bodyNP = NodePath(self.mPickedBody)
                                pivotLocalPos = bodyNP.getRelativePoint(self.mRender, pivotPos)
                                # create constraint
                                if self.mCsIsSpherical:
                                    # spherical
                                    self.mCsPick = BulletSphericalConstraint(self.mPickedBody, pivotLocalPos)
                                else:
                                    # generic
                                    self.mCsPick = BulletGenericConstraint(self.mPickedBody,
                                                        TransformState.makePos(pivotLocalPos), True)
                                    # set parameters (in Bullet environment)
                                    dof6 = self.mCsPick
                                    dof6.setAngularLimit(0, 0.0, 0.0)
                                    # define the 'strength' of our constraint (each axis)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 0)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 1)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 2)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 3)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 4)
                                    dof6.setParam(BulletConstraint.CPStopCfm, self.mCfm, 5)
                                    # define the 'error reduction' of our constraint (each axis)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 0)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 1)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 2)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 3)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 4)
                                    dof6.setParam(BulletConstraint.CPStopErp, self.mErp, 5)
                                # and attach it to the world
                                self.mWorld.attach(self.mCsPick);
                                # set callback
                                self.mWorld.setTickCallback(PythonCallbackObject(self.movePicked), True);
                        # print hit body
                        self.PRINT_DEBUG_HIT(result)
                    else:
                        print("Nothing hit!")
        else:
            if self.mCsPick is not None:
                # unset callback
                # ##ERROR
                self.mWorld.clearTickCallback()
                # remove constraint from world
                self.mWorld.remove(self.mCsPick)
                # delete constraint
                self.mCsPick = None
                # set body as inactive and deactivable
                self.mPickedBody.setDeactivationEnabled(True);
    
    def movePicked(self, data):
        # handle picked body if any
        if self.mCsPick is not None:
            # get the mouse watcher
            mwatcher = self.app.mouseWatcherNode
            if mwatcher.hasMouse():
                # Get to and from pos in camera coordinates
                pMouse = mwatcher.getMouse()
                #
                pNear, pFar = (LPoint3f(), LPoint3f())
                if self.mCamLens.extrude(pMouse, pNear, pFar):
                    # Transform to global coordinates
                    pNear = self.mRender.getRelativePoint(self.mCamera, pNear)
                    pFar = self.mRender.getRelativePoint(self.mCamera, pFar)
                    # new pivot (b) pos
                    vecFarNear = pFar - pNear
                    vecFarNear.normalize()
                    pivotPos = self.mCamera.getPos(self.mRender) + vecFarNear * self.mPivotCamDist;
                    if self.mCsIsSpherical:
                        self.mCsPick.setPivotB(pivotPos)
                    else:
                        # (in Bullet environment)
                        dof6 = self.mCsPick
                        dof6.setFrames(dof6.getFrameA().setPos(pivotPos), dof6.getFrameB())
    
    def PRINT_DEBUG_HIT(self, result):
        if self.debug:
            print(result.getNode().getType().getName() + 
                  '\n\t| panda node: ' + str(result.getNode().getName()) + 
                  '\n\t| hit pos: (' + 
                  str(result.getHitPos().getX()) + ', ' + 
                  str(result.getHitPos().getY()) + ', ' + 
                  str(result.getHitPos().getZ()) + ')'
                  '\n\t| hit normal: (' + 
                  str(result.getHitNormal().getX()) + ', ' + 
                  str(result.getHitNormal().getY()) + ', ' + 
                  str(result.getHitNormal().getZ()) + ')'
                  '\n\t| hit fraction: "' + 
                  str(result.getHitFraction()) + '"'
                  '\n\t| from pos: (' + 
                  str(result.getFromPos().getX()) + ', ' + 
                  str(result.getFromPos().getY()) + ', ' + 
                  str(result.getFromPos().getZ()) + ')'
                  '\n\t| to pos: (' + 
                  str(result.getToPos().getX()) + ', ' + 
                  str(result.getToPos().getY()) + ', ' + 
                  str(result.getToPos().getZ()) + ')')
           
if __name__ == '__main__':
    app = ShowBase()

    # Setup our physics world
    bulletWorld = BulletWorld()
    bulletWorld.setGravity(0, 0, -9.81)
    # add the simulation task
    def simulationTask(task):
        global globalClock, bulletWorld
        # do physics step
        bulletWorld.doPhysics(globalClock.getDt())
        #
        return task.cont
    #
    app.taskMgr.add(simulationTask, "Physics Simulation")
   
    # Add a plane
    cm = CardMaker("plane")
    cm.setFrame(-10000, 10000, -10000, 10000)
    plane = NodePath(cm.generate())
    plane.setP(-90.0)
    plane.setZ(1.0)
    plane.setColor(LVector3f(0.0, 0.5, 0.5))
    # Add a plane to collide with    
    planeShape = BulletPlaneShape(Vec3(0, 0, 1), 1)
    planeBody = BulletRigidBodyNode('Plane')
    planeBody.setRestitution(0.1)
    planeBody.setFriction(0.8)
    planeBody.addShape(planeShape)
    planeBody.setStatic(True)  # static
    bulletWorld.attachRigidBody(planeBody)
    planeBodyNP = app.render.attachNewNode(planeBody)
    planeBodyNP.setPos(0, 0, 0)
    plane.reparentTo(planeBodyNP)
 
    # add a body
    pandaActor = app.loader.loadModel("smiley")
    pandaActor.setScale(2.0)
    # set rigid body
    pandaShape = BulletSphereShape(2.0)
    pandaBody = BulletRigidBodyNode('smiley body')
    pandaBody.setMass(1.0)
    pandaBody.setRestitution(0.99)
    pandaBody.setFriction(0.8)
    pandaBody.addShape(pandaShape)     
    bulletWorld.attachRigidBody(pandaBody)
    pandaBodyNP = app.render.attachNewNode(pandaBody)
    pandaBodyNP.setPos(0.0, 0.0, 90.0)
    pandaActor.reparentTo(pandaBodyNP)

    #set camera
    app.camera.setPos(0.0, -250, 75.0)
    app.camera.lookAt(0.0, 200.0, 0.0)
    mat = LMatrix4f(app.camera.getTransform().getMat())
    mat.invertInPlace()
    app.mouseInterfaceNode.setMat(mat)
    #add a picker
    picker = Picker(app, bulletWorld, "p", "p-up",
                 csIsSpherical=True, cfm=0.5, erp=0.5, debug=True)
    app.run()

here the picker will pick (in this case by typing “p”) a (not static) rigid body and move it with the mouse by setting a constraint and a tick callback ; when unpicking it (in this case by releasing the p key, i.e. “p-up”) it destroy the constraint and clear the tick callback, but the above error is produced.

I’d like some info on optimization.

I have a a lot of geometry that could only fit a BulletTriangleMeshShape, if I was to use the default panda collision system I’d arrange the geoms in a octree, but I have no idea how to do that with a Bullet shape.

Do I even need to?
Googlin I’ve found this topic: bulletphysics.org/Bullet/php … f=9&t=1633 one of the people there says:

Is this magic “btBvhTriangleMeshShapes” available in Panda3D? It would be sweet if I could just dump all my collision triangles into one geom and feed it to Bullet.

the BulletTriangleMeshshape is a wrapper for either btBvhTriangleMeshShape or btGImpactMeshShape, depending on the “dynamic” flag passed in the constructor. So yes, for static collision geoemetry this shape is available.

I’ve got a new problem, this time with the BulletVehicle. As my vehicle start moving the wheels (and collision solid) keeps bouncing and shaking. It looks like as if the wheels where square :cry:

I tried tuning the wheels parameters (suspension stiffness, damping relaxation, damping compression, friction slip and roll influence), but none of that changes this shuttering.

If I change the number of steps in doPhysics the shaking frequency just changes, but it never goes away.

I need to get this working for PyWeek… Help!

EDIT: Well it turns out the magic formula is itsy bitsy, teenie weenie (yellow polka dot) steps. With 10 steps each 0.001s it works ok.

Hello,

I’ve decided to re-visit the problem I was having with my softbody falling through the floor. I have en ellipsoid and a solid sphere for comparison. The ellipsoid at first works correctly but at a certain point (after applying some velocity) the ellipsoid falls through the floor. It’s as if the plane is only so large, but as you can see in the attached video the solid sphere collides correctly with the plane. I’ve include the code before but I’ll add it again below. I wondered if anyone had suggestions on how to fix it?

The video is here:
https://www.youtube.com/watch?v=IXQitgb571g

void Move(const Event* event, void* data);
void MoveSolid(const Event* event, void* data);
 
BulletWorld *get_physics_world() {
    // physics_world is supposed to be an global variable,
    // but declaring global variables is not cool
    // for good programmers lol, instead, should use static keyword.
    static BulletWorld *physics_world = new BulletWorld();
    return physics_world;
}
 
AsyncTask::DoneStatus update_scene(GenericAsyncTask* task, void* data) {
    // Get dt (from Python example) and apply to do_physics(float, int, int);
    ClockObject *co = ClockObject::get_global_clock();
    get_physics_world()->do_physics(co->get_dt(), 10, 1.0 / 180.0);
 
    return AsyncTask::DS_cont;
}
 
int main(int argc, char *argv[]) {
    // All variables.
    PandaFramework framework;
    PT(WindowFramework) window;
    NodePath camera;
    PT(AsyncTaskManager) task_mgr;
 
    // Init everything :D
    framework.open_framework(argc, argv);
//    framework->set_window_title("Bullet Physics");
 
    window = framework.open_window();
    window->enable_keyboard();
    window->setup_trackball();
 
    camera = window->get_camera_group();
    task_mgr = AsyncTaskManager::get_global_ptr();
 
    // Make physics simulation.
    // Static world stuff.
    get_physics_world()->set_gravity(0, 0, -9.81f);

	 BulletSoftBodyWorldInfo info = get_physics_world()->get_world_info();
	 info.set_air_density(1.2);
	 info.set_water_density(0);
	 info.set_water_normal(LVector3(0,0,0));

	LPoint3 centre = LPoint3(0,0,0);
	LVector3 radius = LVector3(1,1,1);// * 1.5;

	PT(BulletSoftBodyNode) bodyNode = BulletSoftBodyNode::make_ellipsoid(info,centre,radius,128);
	bodyNode->get_cfg().clear_all_collision_flags();
	bodyNode->set_name("Ellipsoid");
	BulletSoftBodyMaterial material = bodyNode->get_material(0);
	material.setLinearStiffness(0.5);
	bodyNode->get_cfg().set_dynamic_friction_coefficient(1);
	bodyNode->get_cfg().set_damping_coefficient(0.001);
	bodyNode->get_cfg().set_pressure_coefficient(2500);
	bodyNode->get_cfg().set_volume_conversation_coefficient(0.5);
	bodyNode->set_total_mass(25);
	bodyNode->set_pose(true,false);

	bodyNode->generate_clusters(16); //16
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_sdf_rigid_soft,true);
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_cluster_soft_soft,true);
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_cluster_self,false);

	//bodynode getters
	float mass = bodyNode->get_mass(0);
	int clusters = bodyNode->get_num_clusters();
	int materials = bodyNode->get_num_materials();
	int nodes = bodyNode->get_num_nodes();
	float totalMass = bodyNode->get_total_mass();
	float volume = bodyNode->get_volume();

	printf("mass: %f\n",mass);
	printf("clusters: %d\n",clusters);
	printf("materials: %f\n",materials);
	printf("nodes: %f\n",nodes);
	printf("total mass: %f\n",totalMass);
	printf("volume: %f\n",volume);
	//

	NodePath bodyNP = window->get_render().attach_new_node(bodyNode);
	bodyNP.set_pos(0,0,0);
	bodyNP.set_h(0.0);
	get_physics_world()->attach_soft_body(bodyNode);

	static const GeomVertexFormat* fmt = GeomVertexFormat::get_v3n3t2();
	PT(Geom) geom = BulletHelper::make_geom_from_faces(bodyNode,fmt,true);
	bodyNode->link_geom(geom);
	
	PT(BulletDebugNode) visNode = new BulletDebugNode("EllipsoidVisual");
	//visNode->add_geom(geom);
	NodePath visNP = bodyNP.attach_new_node(visNode);
	//visNode = new BulletDebugNode("Debug");
	
	PT(BulletPlaneShape) floor_shape = new BulletPlaneShape(LVecBase3f(0, 0, 1), 1);
    PT(BulletRigidBodyNode) floor_rigid_node = new BulletRigidBodyNode("Ground");
 
    floor_rigid_node->add_shape(floor_shape);
 
    NodePath np_ground = window->get_render().attach_new_node(floor_rigid_node);
    np_ground.set_pos(0, 0, -2);
    get_physics_world()->attach_rigid_body(floor_rigid_node);
	visNode->add_child(floor_rigid_node);

	PN_stdfloat sphereRadius = 0.5;
	PT(BulletSphereShape) sphere_shape = new BulletSphereShape(sphereRadius);
	PT(BulletRigidBodyNode) sphere_rigid_node = new BulletRigidBodyNode("SphereSolid");
	sphere_rigid_node->add_shape(sphere_shape);
	NodePath np_sphere = window->get_render().attach_new_node(sphere_rigid_node);
	np_sphere.set_pos(5, 0, 0);
	sphere_rigid_node->set_mass(10);
	get_physics_world()->attach_rigid_body(sphere_rigid_node);
 
    visNode->show_bounding_boxes(false);
	visNode->show_constraints(true);
	visNode->show_normals(true);
	visNode->show_wireframe(true);

	get_physics_world()->set_debug_node(visNode);
	visNP.show();
	np_ground.show();

	//visNP.set_collide_mask(CollideMask::all_on());
	bodyNP.set_collide_mask(CollideMask::all_on());
	np_ground.set_collide_mask(CollideMask::all_on());

    PT(GenericAsyncTask) task;
    task = new GenericAsyncTask("Scene update", &update_scene, (void*) NULL);
    task_mgr->add(task);

	framework.define_key("w", "forward", Move,  bodyNode);
	framework.define_key("e", "forward", MoveSolid,  sphere_rigid_node);
 
    framework.main_loop();
    framework.close_framework();
 
    return (0);
}

void Move(const Event* event, void* data)
{
	BulletSoftBodyNode* node = static_cast<BulletSoftBodyNode*>(data);
	node->set_velocity(LVector3f(1,0,0));
}

void MoveSolid(const Event* event, void* data)
{
	BulletRigidBodyNode* node = static_cast<BulletRigidBodyNode*>(data);
	node->set_linear_velocity(LVector3f(1,0,0));
}

Yeah, I can reproduce it. Strange. It just falls off the edge of the plane, as if it is not infinite (it should be). Calling set_margin on the shape with a sufficiently high number seems to work around this.

I wonder if this is a bug in Bullet. I did find this other thread when googling about it:
bulletphysics.org/Bullet/phpBB3/ … php?t=9332

Thanks rdb, that had me stumped for ages. You’re right, set_margin seems to extend the plane. Interesting.

Cheers,
Zobbo

It looks like the bullet kinematic character controller got updated recently.

github.com/bulletphysics/bullet … 50e99d783e

Some new methods have been added for linear and angular dampening, max penetration depth, setting the jump speed and jump direction!

	void setLinearDamping(btScalar d) { m_linearDamping = btClamped(d, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); }
 	btScalar getLinearDamping() const { return  m_linearDamping; }
 	void setAngularDamping(btScalar d) { m_angularDamping = btClamped(d, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); }
 	btScalar getAngularDamping() const { return  m_angularDamping; }

Unfortunately I don’t know enough about c++ to expose the new methods…

Hello,

I’m back playing with my physics example again. I have two issues. One is that the softbody doesn’t stop moving at all. I’ve tried deactivating the softbody but that doesn’t do anything (presumably because the softbody is continually colliding with the plane). The other way I tried to stop it was to increase damping dynamically. This helps somewhat but the softbody still moves and I can see it being difficult to apply and revert the damping during gameplay (having to predict when the ball is hit or collides).

The other issue is that the collision with the plane seems to be higher than with the solid, as you can see in this video http://youtu.be/V-3k_Fc9v10. I’m not sure what to do about that one.

Here’s the code:

#include "pandaFramework.h"
#include "windowFramework.h"
#include "nodePath.h"
#include "clockObject.h"
 
#include "asyncTask.h"
#include "genericAsyncTask.h"
 
#include "bulletWorld.h"
#include "bulletPlaneShape.h"
#include "bulletBoxShape.h"
#include "bulletSoftBodyWorldInfo.h"
#include "bulletSoftBodyMaterial.h"
#include "bulletSoftBodyConfig.h"
#include "GeomVertexFormat.h"
#include "bulletHelper.h"
#include "bulletSphereShape.h"

void Move(const Event* event, void* data);
void MoveSolid(const Event* event, void* data);
void Deactivate(const Event* event, void* data);
 
BulletWorld *get_physics_world() {
    // physics_world is supposed to be an global variable,
    // but declaring global variables is not cool
    // for good programmers lol, instead, should use static keyword.
    static BulletWorld *physics_world = new BulletWorld();
    return physics_world;
}
 
AsyncTask::DoneStatus update_scene(GenericAsyncTask* task, void* data) {
    // Get dt (from Python example) and apply to do_physics(float, int, int);
    ClockObject *co = ClockObject::get_global_clock();
    get_physics_world()->do_physics(co->get_dt(), 10, 1.0 / 180.0);
 
    return AsyncTask::DS_cont;
}
 
int main(int argc, char *argv[]) {
    // All variables.
    PandaFramework framework;
    PT(WindowFramework) window;
    NodePath camera;
    PT(AsyncTaskManager) task_mgr;
 
    // Init everything :D
    framework.open_framework(argc, argv);
//    framework->set_window_title("Bullet Physics");
 
    window = framework.open_window();
    window->enable_keyboard();
    window->setup_trackball();
 
    camera = window->get_camera_group();
    task_mgr = AsyncTaskManager::get_global_ptr();
 
    // Make physics simulation.
    // Static world stuff.
    get_physics_world()->set_gravity(0, 0, -9.81f);

	 BulletSoftBodyWorldInfo info = get_physics_world()->get_world_info();
	 info.set_air_density(1.2);
	 info.set_water_density(0);
	 info.set_water_normal(LVector3(0,0,0));

	LPoint3 centre = LPoint3(0,0,0);
	LVector3 radius = LVector3(1,1,1);// * 1.5;

	PT(BulletSoftBodyNode) bodyNode = BulletSoftBodyNode::make_ellipsoid(info,centre,radius,128);
	bodyNode->get_cfg().clear_all_collision_flags();
	bodyNode->set_name("Ellipsoid");
	BulletSoftBodyMaterial material = bodyNode->get_material(0);
	material.setLinearStiffness(0.5);
	bodyNode->get_cfg().set_dynamic_friction_coefficient(1);
	bodyNode->get_cfg().set_damping_coefficient(0.001);//was 0.001
	bodyNode->get_cfg().set_pressure_coefficient(2500);
	bodyNode->get_cfg().set_volume_conversation_coefficient(0.5);
	bodyNode->set_total_mass(25);
	bodyNode->set_pose(true,false);

	bodyNode->generate_clusters(16); //16
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_sdf_rigid_soft,true);
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_cluster_soft_soft,true);
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_cluster_self,false);
	bodyNode->get_cfg().set_collision_flag(BulletSoftBodyConfig::CF_vertex_face_soft_soft,true);

	bodyNode->set_deactivation_time(1000);

	//bodynode getters
	float mass = bodyNode->get_mass(0);
	int clusters = bodyNode->get_num_clusters();
	int materials = bodyNode->get_num_materials();
	int nodes = bodyNode->get_num_nodes();
	float totalMass = bodyNode->get_total_mass();
	float volume = bodyNode->get_volume();

	printf("mass: %f\n",mass);
	printf("clusters: %d\n",clusters);
	printf("materials: %f\n",materials);
	printf("nodes: %f\n",nodes);
	printf("total mass: %f\n",totalMass);
	printf("volume: %f\n",volume);
	//

	NodePath bodyNP = window->get_render().attach_new_node(bodyNode);
	bodyNP.set_pos(0,0,0);
	bodyNP.set_h(0.0);
	get_physics_world()->attach_soft_body(bodyNode);

	static const GeomVertexFormat* fmt = GeomVertexFormat::get_v3n3t2();
	PT(Geom) geom = BulletHelper::make_geom_from_faces(bodyNode,fmt,true);
	bodyNode->link_geom(geom);
	
	PT(BulletDebugNode) visNode = new BulletDebugNode("EllipsoidVisual");
	//visNode->add_geom(geom);
	NodePath visNP = bodyNP.attach_new_node(visNode);
	//visNode = new BulletDebugNode("Debug");
	
	PT(BulletPlaneShape) floor_shape = new BulletPlaneShape(LVecBase3f(0, 0, 1), 1);
    PT(BulletRigidBodyNode) floor_rigid_node = new BulletRigidBodyNode("Ground");

	floor_shape->set_margin(100);
 
    floor_rigid_node->add_shape(floor_shape);
 
    NodePath np_ground = window->get_render().attach_new_node(floor_rigid_node);
    np_ground.set_pos(0, 0, -2);
    get_physics_world()->attach_rigid_body(floor_rigid_node);
	visNode->add_child(floor_rigid_node);

	PN_stdfloat sphereRadius = 0.5;
	PT(BulletSphereShape) sphere_shape = new BulletSphereShape(sphereRadius);
	PT(BulletRigidBodyNode) sphere_rigid_node = new BulletRigidBodyNode("SphereSolid");
	sphere_rigid_node->add_shape(sphere_shape);
	NodePath np_sphere = window->get_render().attach_new_node(sphere_rigid_node);
	np_sphere.set_pos(5, 0, 0);
	sphere_rigid_node->set_mass(10);
	get_physics_world()->attach_rigid_body(sphere_rigid_node);
 
    visNode->show_bounding_boxes(false);
	visNode->show_constraints(true);
	visNode->show_normals(true);
	visNode->show_wireframe(true);

	get_physics_world()->set_debug_node(visNode);
	visNP.show();
	np_ground.show();

	//visNP.set_collide_mask(CollideMask::all_on());
	bodyNP.set_collide_mask(CollideMask::all_on());
	np_ground.set_collide_mask(CollideMask::all_on());

    PT(GenericAsyncTask) task;
    task = new GenericAsyncTask("Scene update", &update_scene, (void*) NULL);
    task_mgr->add(task);

	framework.define_key("w", "forward", Move,  bodyNode);
	framework.define_key("e", "forward", MoveSolid,  sphere_rigid_node);
	framework.define_key("r", "deacvtivate",Deactivate,bodyNode);
 
    framework.main_loop();
    framework.close_framework();
 
    return (0);
}

void Move(const Event* event, void* data)
{
	BulletSoftBodyNode* node = static_cast<BulletSoftBodyNode*>(data);
	node->set_velocity(LVector3f(5,0,0));
}

void MoveSolid(const Event* event, void* data)
{
	BulletRigidBodyNode* node = static_cast<BulletRigidBodyNode*>(data);
	node->set_linear_velocity(LVector3f(5,0,0));
}

void Deactivate(const Event* event, void* data)
{
	BulletSoftBodyNode* node = static_cast<BulletSoftBodyNode*>(data);
	node->get_cfg().set_damping_coefficient(1.0);
}

Looking at at the bullet forums it seems softbodies never sleep and the recommended way to halt a soft body is to use append_anchor which works quite well, so first problem solved. I’m just a bit stuck on the gap between the softbody collision and the rigid plane.