Browse Source

Redesign of the Bullet <--> scene graph synchronisation.

enn0x 14 years ago
parent
commit
d0c8ccd901

+ 26 - 0
panda/src/bullet/bulletBodyNode.I

@@ -225,3 +225,29 @@ get_shape(int idx) const {
   return _shapes[idx];
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BulletBodyNode::set_debug_enabled
+//       Access: Published
+//  Description: Enables or disables the debug visualisation for
+//               this collision object. By default the debug 
+//               visualisation is enabled.
+////////////////////////////////////////////////////////////////////
+INLINE void BulletBodyNode::
+set_debug_enabled(const bool enabled) {
+
+  set_collision_flag(btCollisionObject::CF_DISABLE_VISUALIZE_OBJECT, !enabled);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BulletBodyNode::is_debug_enabled
+//       Access: Published
+//  Description: Returns TRUE if the debug visualisation is enabled
+//               for this collision object, and FALSE if the debug
+//               visualisation is disabled.
+////////////////////////////////////////////////////////////////////
+INLINE bool BulletBodyNode::
+is_debug_enabled() const {
+
+  return !get_collision_flag(btCollisionObject::CF_DISABLE_VISUALIZE_OBJECT);
+}
+

+ 7 - 26
panda/src/bullet/bulletBodyNode.cxx

@@ -35,9 +35,6 @@ BulletBodyNode(const char *name) : PandaNode(name) {
   // Shape
   _shape = new btEmptyShape();
 
-  // Transform changed callback
-  _disable_transform_changed = false;
-
   // Default collide mask
   set_into_collide_mask(CollideMask::all_on());
 }
@@ -162,22 +159,6 @@ output(ostream &out) const {
   if (is_kinematic()) out << " kinematic";
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: BulletBodyNode::transform_changed
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-void BulletBodyNode::
-transform_changed() {
-
-  // Apply scale to collision shape
-  CPT(TransformState) ts = this->get_transform();
-  if (ts->has_scale()) {
-    LVecBase3f scale = ts->get_scale();
-    _shape->setLocalScaling(LVecBase3f_to_btVector3(scale));
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: BulletBodyNode::add_shape
 //       Access: Published
@@ -194,7 +175,7 @@ add_shape(BulletShape *shape, CPT(TransformState) ts) {
   // Transform
   btTransform trans = btTransform::getIdentity();
   if (ts) {
-    trans = LMatrix4f_to_btTrans(ts->get_mat());
+    trans = TransformState_to_btTrans(ts);
   }
 
   // Root shape
@@ -447,14 +428,14 @@ set_active(bool active, bool force) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletBodyNode::set_disable_deactivation
+//     Function: BulletBodyNode::set_deactivation_enabled
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletBodyNode::
-set_disable_deactivation(bool disable, bool force) {
+set_deactivation_enabled(const bool enabled, const bool force) {
 
-  int state = (disable) ? DISABLE_DEACTIVATION : WANTS_DEACTIVATION;
+  int state = (enabled) ? WANTS_DEACTIVATION : DISABLE_DEACTIVATION;
 
   if (force) {
     get_object()->forceActivationState(state);
@@ -465,14 +446,14 @@ set_disable_deactivation(bool disable, bool force) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletBodyNode::get_disable_deactivation
+//     Function: BulletBodyNode::is_deactivation_enabled
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 bool BulletBodyNode::
-get_disable_deactivation() const {
+is_deactivation_enabled() const {
 
-  return (get_object()->getActivationState() & DISABLE_DEACTIVATION) != 0;
+  return (get_object()->getActivationState() & DISABLE_DEACTIVATION) == 0;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 5
panda/src/bullet/bulletBodyNode.h

@@ -78,8 +78,12 @@ PUBLISHED:
   void set_deactivation_time(float dt);
   float get_deactivation_time() const;
 
-  void set_disable_deactivation(bool disable, bool force=false);
-  bool get_disable_deactivation() const;
+  void set_deactivation_enabled(const bool enabled, const bool force=false);
+  bool is_deactivation_enabled() const;
+
+  // Debug Visualistion
+  INLINE void set_debug_enabled(const bool enabled);
+  INLINE bool is_debug_enabled() const;
 
   // Friction and Restitution
   INLINE float get_restitution() const;
@@ -121,9 +125,6 @@ protected:
   typedef PTA(PT(BulletShape)) BulletShapes;
   BulletShapes _shapes;
 
-  bool _disable_transform_changed;
-  virtual void transform_changed();
-
 private:
   virtual void shape_changed();
 

+ 53 - 57
panda/src/bullet/bulletCharacterControllerNode.cxx

@@ -24,17 +24,21 @@ TypeHandle BulletCharacterControllerNode::_type_handle;
 BulletCharacterControllerNode::
 BulletCharacterControllerNode(BulletShape *shape, float step_height, const char *name) : PandaNode(name) {
 
-  // Setup initial transform
+  // Synchronised transform
+  _sync = TransformState::make_identity();
+  _sync_disable = false;
+
+  // Initial transform
   btTransform trans = btTransform::getIdentity();
 
-  // Get convex shape
+  // Get convex shape (for ghost object)
   if (!shape->is_convex()) {
     bullet_cat.error() << "a convex shape is required!" << endl;
   }
 
   btConvexShape *convex = dynamic_cast<btConvexShape *>(shape->ptr());
 
-  // Setup ghost object
+  // Ghost object
   _ghost = new btPairCachingGhostObject();
   _ghost->setUserPointer(this);
 
@@ -43,25 +47,21 @@ BulletCharacterControllerNode(BulletShape *shape, float step_height, const char
   _ghost->setCollisionShape(convex);
   _ghost->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
 
-  // Setup up axis
+  // Up axis
   _up = get_default_up_axis();
 
-  // Movement
+  // Initialise movement
   _linear_velocity_is_local = false;
   _linear_velocity.set(0.0f, 0.0f, 0.0f);
   _angular_velocity = 0.0f;
 
-  // Setup character controller
+  // Character controller
   _character = new btKinematicCharacterController(_ghost, convex, step_height, _up);
   _character->setGravity((btScalar)9.81f);
 
   // Retain a pointer to the shape
   _shape = shape;
 
-  // The 'transform changed' hook has to be disabled when updating the node's
-  // transform from inside the post_step method!
-  _disable_transform_changed = false;
-
   // Default collide mask
   set_into_collide_mask(CollideMask::all_on());
 }
@@ -196,14 +196,17 @@ set_angular_velocity(float omega) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletCharacterControllerNode::pre_step
+//     Function: BulletCharacterControllerNode::sync_p2b
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletCharacterControllerNode::
-pre_step(float dt) {
+sync_p2b(float dt) {
 
- // Angular rotation
+  // Synchronise global transform
+  transform_changed();
+
+  // Angular rotation
   btScalar angle = dt * deg_2_rad(_angular_velocity);
 
   btMatrix3x3 m = _ghost->getWorldTransform().getBasis();
@@ -225,28 +228,32 @@ pre_step(float dt) {
   }
 
   _character->setVelocityForTimeInterval(v, dt);
-  //_character->setWalkDirection(v * dt);
-
   _angular_velocity = 0.0f;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletCharacterControllerNode::post_step
+//     Function: BulletCharacterControllerNode::sync_b2p
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletCharacterControllerNode::
-post_step() {
+sync_b2p() {
 
-  btTransform trans = _ghost->getWorldTransform();
-  CPT(TransformState) ts = btTrans_to_TransformState(trans);
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  LVecBase3f scale = np.get_net_transform()->get_scale();
 
-  _disable_transform_changed = true;
+  btTransform trans = _ghost->getWorldTransform();
+  CPT(TransformState) ts = btTrans_to_TransformState(trans, scale);
 
-  NodePath np = NodePath::any_path((PandaNode *)this);
-  np.set_transform(np.get_top(), ts);
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
 
-  _disable_transform_changed = false;
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+    _sync_disable = true;
+    np.set_transform(NodePath(), ts);
+    _sync_disable = false;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -257,25 +264,36 @@ post_step() {
 void BulletCharacterControllerNode::
 transform_changed() {
 
-  if (_disable_transform_changed) return;
+  if (_sync_disable) return;
 
-  // Get translation and heading
   NodePath np = NodePath::any_path((PandaNode *)this);
-  CPT(TransformState) ts = np.get_transform(np.get_top());
+  CPT(TransformState) ts = np.get_net_transform();
 
-  LPoint3f pos = ts->get_pos();
-  float heading = ts->get_hpr().get_x();
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
 
-  // Set translation
-  _character->warp(LVecBase3f_to_btVector3(pos));
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
 
-  // Set Heading
-  btMatrix3x3 m = _ghost->getWorldTransform().getBasis();
-  btVector3 up = m[_up];
+    // Get translation, heading and scale
+    LPoint3f pos = ts->get_pos();
+    float heading = ts->get_hpr().get_x();
+    LVecBase3f scale = ts->get_scale();
 
-  m = btMatrix3x3(btQuaternion(up, heading));
+    // Set translation
+    _character->warp(LVecBase3f_to_btVector3(pos));
 
-  _ghost->getWorldTransform().setBasis(m);
+    // Set Heading
+    btMatrix3x3 m = _ghost->getWorldTransform().getBasis();
+    btVector3 up = m[_up];
+
+    m = btMatrix3x3(btQuaternion(up, heading));
+
+    _ghost->getWorldTransform().setBasis(m);
+
+    // Set scale
+    _shape->set_local_scale(scale);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -408,25 +426,3 @@ set_use_ghost_sweep_test(bool value) {
   return _character->setUseGhostSweepTest(value);
 }
 
-/*
-////////////////////////////////////////////////////////////////////
-//     Function: BulletCharacterControllerNode::parents_changed
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-void BulletCharacterControllerNode::
-parents_changed() {
-
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BulletCharacterControllerNode::children_changed
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-void BulletCharacterControllerNode::
-children_changed() {
-
-}
-*/
-

+ 5 - 6
panda/src/bullet/bulletCharacterControllerNode.h

@@ -69,15 +69,16 @@ public:
   INLINE btPairCachingGhostObject *get_ghost() const;
   INLINE btKinematicCharacterController *get_character() const;
 
-  void pre_step(float dt);
-  void post_step();
+  void sync_p2b(float dt);
+  void sync_b2p();
 
 protected:
-  //virtual void parents_changed();
-  //virtual void children_changed();
   virtual void transform_changed();
 
 private:
+  CPT(TransformState) _sync;
+  bool _sync_disable;
+
   BulletUpAxis _up;
 
   btKinematicCharacterController *_character;
@@ -89,8 +90,6 @@ private:
   bool _linear_velocity_is_local;
   float _angular_velocity;
 
-  bool _disable_transform_changed;
-
 ////////////////////////////////////////////////////////////////////
 public:
   static TypeHandle get_class_type() {

+ 3 - 5
panda/src/bullet/bulletDebugNode.cxx

@@ -186,12 +186,12 @@ draw_mask_changed() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletDebugNode::post_step
+//     Function: BulletDebugNode::sync_b2p
 //       Access: Private
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 void BulletDebugNode::
-post_step(btDynamicsWorld *world) {
+sync_b2p(btDynamicsWorld *world) {
 
   if (is_overall_hidden()) return;
 
@@ -199,7 +199,7 @@ post_step(btDynamicsWorld *world) {
   world->debugDrawWorld();
 
   // Get inverse of this node's net transform
-  NodePath np = NodePath::any_path(this);
+  NodePath np = NodePath::any_path((PandaNode *)this);
   LMatrix4f m = np.get_net_transform()->get_mat();
   m.invert_in_place();
 
@@ -348,7 +348,6 @@ drawTriangle(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, cons
 void BulletDebugNode::DebugDraw::
 drawTriangle(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2, const btVector3 &n0, const btVector3 &n1, const btVector3 &n2, const btVector3 &color, btScalar alpha) {
 
-  // TODO
   bullet_cat.debug() << "drawTriangle(2) - not yet implemented!" << endl;
 }
 
@@ -374,7 +373,6 @@ drawContactPoint(const btVector3 &point, const btVector3 &normal, btScalar dista
 void BulletDebugNode::DebugDraw::
 draw3dText(const btVector3 &location, const char *text) {
 
-  // TODO
   bullet_cat.debug() << "draw3dText - not yet implemented!" << endl;
 }
 

+ 1 - 1
panda/src/bullet/bulletDebugNode.h

@@ -49,7 +49,7 @@ public:
   virtual bool safe_to_flatten_below() const;
 
 private:
-  void post_step(btDynamicsWorld *world);
+  void sync_b2p(btDynamicsWorld *world);
 
   struct Line {
     LVecBase3f _p0;

+ 50 - 40
panda/src/bullet/bulletGhostNode.cxx

@@ -25,19 +25,20 @@ TypeHandle BulletGhostNode::_type_handle;
 BulletGhostNode::
 BulletGhostNode(const char *name) : BulletBodyNode(name) {
 
-  // Setup initial transform
+  // Synchronised transform
+  _sync = TransformState::make_identity();
+  _sync_disable = false;
+
+  // Initial transform
   btTransform trans = btTransform::getIdentity();
 
-  // Setup ghost object
+  // Ghost object
   _ghost = new btPairCachingGhostObject();
   _ghost->setUserPointer(this);
   _ghost->setCollisionFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
   _ghost->setWorldTransform(trans);
   _ghost->setInterpolationWorldTransform(trans);
   _ghost->setCollisionShape(_shape);
-
-  // Autosync is off by default
-  _sync_transform = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -52,62 +53,71 @@ get_object() const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletGhostNode::parents_changed
+//     Function: BulletGhostNode::transform_changed
 //       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletGhostNode::
-parents_changed() {
-
-  // Enable autosync if one of the parents is suited for this
-  Parents parents = get_parents();
-  for (int i=0; i < parents.get_num_parents(); ++i) {
-    PandaNode *parent = parents.get_parent(i);
-    TypeHandle type = parent->get_type();
-
-    if (BulletRigidBodyNode::get_class_type() == type ||
-        BulletSoftBodyNode::get_class_type() == type ||
-        BulletGhostNode::get_class_type() == type ||
-        BulletCharacterControllerNode::get_class_type() == type) {
-      _sync_transform = true;
-      return;
-    }
-  }
+transform_changed() {
 
-  // None of the parents is suited for autosync
-  _sync_transform = false;
+  if (_sync_disable) return;
 
-  transform_changed();
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  CPT(TransformState) ts = np.get_net_transform();
+
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
+
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+
+    btTransform trans = TransformState_to_btTrans(ts);
+    _ghost->setWorldTransform(trans);
+    _ghost->setInterpolationWorldTransform(trans);
+
+    if (ts->has_scale()) {
+      LVecBase3f scale = ts->get_scale();
+      for (int i=0; i<get_num_shapes(); i++) {
+        PT(BulletShape) shape = _shapes[i];
+        shape->set_local_scale(scale);
+      }
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletGhostNode::transform_changed
-//       Access: Protected
+//     Function: BulletGhostNode::sync_p2b
+//       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletGhostNode::
-transform_changed() {
+sync_p2b() {
 
-  if (_disable_transform_changed) return;
-
-  btTransform trans = btTransform::getIdentity();
-  get_node_transform(trans, this);
-  _ghost->setWorldTransform(trans);
-  _ghost->setInterpolationWorldTransform(trans);
-
-  BulletBodyNode::transform_changed();
+  transform_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletGhostNode::pre_step
+//     Function: BulletGhostNode::sync_b2p
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletGhostNode::
-pre_step() {
+sync_b2p() {
+
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  LVecBase3f scale = np.get_net_transform()->get_scale();
+
+  btTransform trans = _ghost->getWorldTransform();
+  CPT(TransformState) ts = btTrans_to_TransformState(trans, scale);
+
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
 
-  if (_sync_transform) {
-    transform_changed();
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+    _sync_disable = true;
+    np.set_transform(NodePath(), ts);
+    _sync_disable = false;
   }
 }
 

+ 5 - 4
panda/src/bullet/bulletGhostNode.h

@@ -44,16 +44,17 @@ PUBLISHED:
 public:
   virtual btCollisionObject *get_object() const;
 
-  void pre_step();
+  void sync_p2b();
+  void sync_b2p();
 
 protected:
-  virtual void parents_changed();
   virtual void transform_changed();
 
 private:
-  btPairCachingGhostObject *_ghost;
+  CPT(TransformState) _sync;
+  bool _sync_disable;
 
-  bool _sync_transform;
+  btPairCachingGhostObject *_ghost;
 
 ////////////////////////////////////////////////////////////////////
 public:

+ 6 - 6
panda/src/bullet/bulletRigidBodyNode.I

@@ -21,8 +21,8 @@
 INLINE BulletRigidBodyNode::
 ~BulletRigidBodyNode() {
 
-  delete _body;
-  delete _motion;
+  delete _rigid->getMotionState();
+  delete _rigid;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -33,7 +33,7 @@ INLINE BulletRigidBodyNode::
 INLINE void BulletRigidBodyNode::
 set_linear_damping(float value) {
 
-  _body->setDamping(value, _body->getAngularDamping());
+  _rigid->setDamping(value, _rigid->getAngularDamping());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -44,7 +44,7 @@ set_linear_damping(float value) {
 INLINE void BulletRigidBodyNode::
 set_angular_damping(float value) {
 
-  _body->setDamping(_body->getLinearDamping(), value);
+  _rigid->setDamping(_rigid->getLinearDamping(), value);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -55,7 +55,7 @@ set_angular_damping(float value) {
 INLINE float BulletRigidBodyNode::
 get_linear_damping() const {
 
-  return _body->getLinearDamping();
+  return _rigid->getLinearDamping();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -66,6 +66,6 @@ get_linear_damping() const {
 INLINE float BulletRigidBodyNode::
 get_angular_damping() const {
 
-  return _body->getAngularDamping();
+  return _rigid->getAngularDamping();
 }
 

+ 78 - 78
panda/src/bullet/bulletRigidBodyNode.cxx

@@ -25,16 +25,19 @@ TypeHandle BulletRigidBodyNode::_type_handle;
 BulletRigidBodyNode::
 BulletRigidBodyNode(const char *name) : BulletBodyNode(name) {
 
-  // Setup motion state
-  _motion = new MotionState(this);
+  // Synchronised transform
+  _sync = TransformState::make_identity();
+  _sync_disable = false;
 
-  // Setup mass properties
+  // Mass properties
   btScalar mass(0.0);
   btVector3 inertia(0, 0, 0);
 
-  btRigidBody::btRigidBodyConstructionInfo ci(mass, _motion, _shape, inertia);
+  // Motion state and construction info
+  btDefaultMotionState *motion = new btDefaultMotionState();
+  btRigidBody::btRigidBodyConstructionInfo ci(mass, motion, _shape, inertia);
 
-  // Enable additional damping
+  // Additional damping
   if (bullet_additional_damping) {
     ci.m_additionalDamping = true;
     ci.m_additionalDampingFactor = bullet_additional_damping_linear_factor;
@@ -43,9 +46,9 @@ BulletRigidBodyNode(const char *name) : BulletBodyNode(name) {
     ci.m_additionalAngularDampingThresholdSqr = bullet_additional_damping_angular_threshold;
   }
 
-  // Setup rigid body
-  _body = new btRigidBody(ci);
-  _body->setUserPointer(this);
+  // Rigid body
+  _rigid = new btRigidBody(ci);
+  _rigid->setUserPointer(this);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +72,7 @@ output(ostream &out) const {
 btCollisionObject *BulletRigidBodyNode::
 get_object() const {
 
-  return _body;
+  return _rigid;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -97,10 +100,10 @@ set_mass(float mass) {
   btVector3 inertia(0.0f, 0.0f, 0.0f);
 
   if (mass > 0.0f) {
-    _body->getCollisionShape()->calculateLocalInertia(mass, inertia);
+    _rigid->getCollisionShape()->calculateLocalInertia(mass, inertia);
   }
 
-  _body->setMassProps(mass, inertia);
+  _rigid->setMassProps(mass, inertia);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -111,7 +114,7 @@ set_mass(float mass) {
 float BulletRigidBodyNode::
 get_mass() const {
 
-  btScalar invMass = _body->getInvMass();
+  btScalar invMass = _rigid->getInvMass();
 
   if (invMass == 0.0f) {
     return 0.0f;
@@ -132,7 +135,7 @@ apply_force(const LVector3f &force, const LPoint3f &pos) {
   nassertv_always(!force.is_nan());
   nassertv_always(!pos.is_nan());
 
-  _body->applyForce(LVecBase3f_to_btVector3(force),
+  _rigid->applyForce(LVecBase3f_to_btVector3(force),
                     LVecBase3f_to_btVector3(pos));
 }
 
@@ -146,7 +149,7 @@ apply_central_force(const LVector3f &force) {
 
   nassertv_always(!force.is_nan());
 
-  _body->applyCentralForce(LVecBase3f_to_btVector3(force));
+  _rigid->applyCentralForce(LVecBase3f_to_btVector3(force));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -159,7 +162,7 @@ apply_torque(const LVector3f &torque) {
 
   nassertv_always(!torque.is_nan());
 
-  _body->applyTorque(LVecBase3f_to_btVector3(torque));
+  _rigid->applyTorque(LVecBase3f_to_btVector3(torque));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -172,7 +175,7 @@ apply_torque_impulse(const LVector3f &torque) {
 
   nassertv_always(!torque.is_nan());
 
-  _body->applyTorqueImpulse(LVecBase3f_to_btVector3(torque));
+  _rigid->applyTorqueImpulse(LVecBase3f_to_btVector3(torque));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -186,7 +189,7 @@ apply_impulse(const LVector3f &impulse, const LPoint3f &pos) {
   nassertv_always(!impulse.is_nan());
   nassertv_always(!pos.is_nan());
 
-  _body->applyImpulse(LVecBase3f_to_btVector3(impulse),
+  _rigid->applyImpulse(LVecBase3f_to_btVector3(impulse),
                       LVecBase3f_to_btVector3(pos));
 }
 
@@ -200,18 +203,7 @@ apply_central_impulse(const LVector3f &impulse) {
 
   nassertv_always(!impulse.is_nan());
 
-  _body->applyCentralImpulse(LVecBase3f_to_btVector3(impulse));
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: BulletRigidBodyNode::parents_changed
-//       Access: Protected
-//  Description:
-////////////////////////////////////////////////////////////////////
-void BulletRigidBodyNode::
-parents_changed() {
-
-  transform_changed();
+  _rigid->applyCentralImpulse(LVecBase3f_to_btVector3(impulse));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -222,56 +214,65 @@ parents_changed() {
 void BulletRigidBodyNode::
 transform_changed() {
 
-  if (_disable_transform_changed) return;
+  if (_sync_disable) return;
 
-  btTransform trans = btTransform::getIdentity();
-  get_node_transform(trans, this);
-  _body->setWorldTransform(trans);
-  _body->setInterpolationWorldTransform(trans);
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  CPT(TransformState) ts = np.get_net_transform();
+
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
+
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+
+    btTransform trans = TransformState_to_btTrans(ts);
+    _rigid->setWorldTransform(trans);
+    _rigid->setInterpolationWorldTransform(trans);
 
-  BulletBodyNode::transform_changed();
+    if (ts->has_scale()) {
+      LVecBase3f scale = ts->get_scale();
+      for (int i=0; i<get_num_shapes(); i++) {
+        PT(BulletShape) shape = _shapes[i];
+        shape->set_local_scale(scale);
+      }
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletRigidBodyNode::MotionState::getWorldTransform
+//     Function: BulletRigidBodyNode::sync_p2b
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-void BulletRigidBodyNode::MotionState::
-getWorldTransform(btTransform &trans) const {
+void BulletRigidBodyNode::
+sync_p2b() {
 
-  get_node_transform(trans, _node);
+  transform_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletRigidBodyNode::MotionState::setWorldTransform
+//     Function: BulletRigidBodyNode::sync_b2p
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
-void BulletRigidBodyNode::MotionState::
-setWorldTransform(const btTransform &trans) {
+void BulletRigidBodyNode::
+sync_b2p() {
 
-  if (trans.getOrigin().getX() != trans.getOrigin().getX()) {
-    bullet_cat.error() << "setWorldTransform: trans is NAN!" << endl;
-    return;
-  }
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  LVecBase3f scale = np.get_net_transform()->get_scale();
 
-  LVecBase3f scale = _node->get_transform()->get_scale();
+  btTransform trans = _rigid->getWorldTransform();
   CPT(TransformState) ts = btTrans_to_TransformState(trans, scale);
 
-  // Disable transform_changed callback
-  _node->_disable_transform_changed = true;
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
 
-  if (_node->get_num_parents() == 0) {
-    _node->set_transform(ts);
-  }
-  else {
-    NodePath np = NodePath::any_path(_node);
-    np.set_transform(np.get_top(), ts);
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+    _sync_disable = true;
+    np.set_transform(NodePath(), ts);
+    _sync_disable = false;
   }
-
-  // Re-enable transform_changed callback again
-  _node->_disable_transform_changed = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -284,11 +285,10 @@ set_center_of_mass_pos(const LPoint3f &pos) {
 
   nassertv_always(!pos.is_nan());
 
-  btTransform xform = btTransform::getIdentity();
-  xform.setIdentity();
-  xform.setOrigin(LVecBase3f_to_btVector3(pos));
+  btTransform trans = btTransform::getIdentity();
+  trans.setOrigin(LVecBase3f_to_btVector3(pos));
 
-  _body->setCenterOfMassTransform(xform);
+  _rigid->setCenterOfMassTransform(trans);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -299,7 +299,7 @@ set_center_of_mass_pos(const LPoint3f &pos) {
 LPoint3f BulletRigidBodyNode::
 get_center_of_mass_pos() const {
 
-  return btVector3_to_LPoint3f(_body->getCenterOfMassPosition());
+  return btVector3_to_LPoint3f(_rigid->getCenterOfMassPosition());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -310,7 +310,7 @@ get_center_of_mass_pos() const {
 LVector3f BulletRigidBodyNode::
 get_linear_velocity() const {
 
-  return btVector3_to_LVector3f(_body->getLinearVelocity());
+  return btVector3_to_LVector3f(_rigid->getLinearVelocity());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -321,7 +321,7 @@ get_linear_velocity() const {
 LVector3f BulletRigidBodyNode::
 get_angular_velocity() const {
 
-  return btVector3_to_LVector3f(_body->getAngularVelocity());
+  return btVector3_to_LVector3f(_rigid->getAngularVelocity());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -334,7 +334,7 @@ set_linear_velocity(const LVector3f &velocity) {
 
   nassertv_always(!velocity.is_nan());
 
-  _body->setLinearVelocity(LVecBase3f_to_btVector3(velocity));
+  _rigid->setLinearVelocity(LVecBase3f_to_btVector3(velocity));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -347,7 +347,7 @@ set_angular_velocity(const LVector3f &velocity) {
 
   nassertv_always(!velocity.is_nan());
 
-  _body->setAngularVelocity(LVecBase3f_to_btVector3(velocity));
+  _rigid->setAngularVelocity(LVecBase3f_to_btVector3(velocity));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -358,7 +358,7 @@ set_angular_velocity(const LVector3f &velocity) {
 void BulletRigidBodyNode::
 clear_forces() {
 
-  _body->clearForces();
+  _rigid->clearForces();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -369,7 +369,7 @@ clear_forces() {
 float BulletRigidBodyNode::
 get_linear_sleep_threshold() const {
 
-  return _body->getLinearSleepingThreshold();
+  return _rigid->getLinearSleepingThreshold();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -380,7 +380,7 @@ get_linear_sleep_threshold() const {
 float BulletRigidBodyNode::
 get_angular_sleep_threshold() const {
 
-  return _body->getAngularSleepingThreshold();
+  return _rigid->getAngularSleepingThreshold();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -391,7 +391,7 @@ get_angular_sleep_threshold() const {
 void BulletRigidBodyNode::
 set_linear_sleep_threshold(float threshold) {
 
-  _body->setSleepingThresholds(_body->getLinearSleepingThreshold(), threshold);
+  _rigid->setSleepingThresholds(_rigid->getLinearSleepingThreshold(), threshold);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -402,7 +402,7 @@ set_linear_sleep_threshold(float threshold) {
 void BulletRigidBodyNode::
 set_angular_sleep_threshold(float threshold) {
 
-  _body->setSleepingThresholds(threshold, _body->getAngularSleepingThreshold());
+  _rigid->setSleepingThresholds(threshold, _rigid->getAngularSleepingThreshold());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -415,7 +415,7 @@ set_gravity(const LVector3f &gravity) {
 
   nassertv_always(!gravity.is_nan());
 
-  _body->setGravity(LVecBase3f_to_btVector3(gravity));
+  _rigid->setGravity(LVecBase3f_to_btVector3(gravity));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -426,7 +426,7 @@ set_gravity(const LVector3f &gravity) {
 LVector3f BulletRigidBodyNode::
 get_gravity() const {
 
-  return btVector3_to_LVector3f(_body->getGravity());
+  return btVector3_to_LVector3f(_rigid->getGravity());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -437,7 +437,7 @@ get_gravity() const {
 void BulletRigidBodyNode::
 set_linear_factor(const LVector3f &factor) {
 
-  _body->setLinearFactor(LVecBase3f_to_btVector3(factor));
+  _rigid->setLinearFactor(LVecBase3f_to_btVector3(factor));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -448,6 +448,6 @@ set_linear_factor(const LVector3f &factor) {
 void BulletRigidBodyNode::
 set_angular_factor(const LVector3f &factor) {
 
-  _body->setAngularFactor(LVecBase3f_to_btVector3(factor));
+  _rigid->setAngularFactor(LVecBase3f_to_btVector3(factor));
 }
 

+ 6 - 15
panda/src/bullet/bulletRigidBodyNode.h

@@ -82,28 +82,19 @@ public:
 
   virtual void output(ostream &out) const;
 
+  void sync_p2b();
+  void sync_b2p();
+
 protected:
-  virtual void parents_changed();
   virtual void transform_changed();
 
 private:
   virtual void shape_changed();
 
-  class MotionState : public btMotionState {
-
-  public:
-    MotionState(BulletRigidBodyNode *node) : _node(node) {};
-    ~MotionState() {};
-
-    virtual void getWorldTransform(btTransform &trans) const;
-    virtual void setWorldTransform(const btTransform &trans);
-
-  private:
-    BulletRigidBodyNode *_node;
-  };
+  CPT(TransformState) _sync;
+  bool _sync_disable;
 
-  btRigidBody *_body;
-  MotionState *_motion;
+  btRigidBody *_rigid;
 
 ////////////////////////////////////////////////////////////////////
 public:

+ 1 - 1
panda/src/bullet/bulletSoftBodyNode.I

@@ -21,7 +21,7 @@
 INLINE BulletSoftBodyNode::
 ~BulletSoftBodyNode() {
 
-  delete _body;
+  delete _soft;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 112 - 78
panda/src/bullet/bulletSoftBodyNode.cxx

@@ -32,12 +32,16 @@ TypeHandle BulletSoftBodyNode::_type_handle;
 BulletSoftBodyNode::
 BulletSoftBodyNode(btSoftBody *body, const char *name) : BulletBodyNode(name) {
 
-  // Setup body
-  _body = body;
-  _body->setUserPointer(this);
+  // Synchronised transform
+  _sync = TransformState::make_identity();
+  _sync_disable = false;
+
+  // Softbody
+  _soft = body;
+  _soft->setUserPointer(this);
 
   // Shape
-  btCollisionShape *shape_ptr = _body->getCollisionShape();
+  btCollisionShape *shape_ptr = _soft->getCollisionShape();
 
   nassertv(shape_ptr != NULL);
   nassertv(shape_ptr->getShapeType() == SOFTBODY_SHAPE_PROXYTYPE);
@@ -58,7 +62,7 @@ BulletSoftBodyNode(btSoftBody *body, const char *name) : BulletBodyNode(name) {
 btCollisionObject *BulletSoftBodyNode::
 get_object() const {
 
-  return _body;
+  return _soft;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -69,7 +73,7 @@ get_object() const {
 BulletSoftBodyConfig BulletSoftBodyNode::
 get_cfg() {
 
-  return BulletSoftBodyConfig(_body->m_cfg);
+  return BulletSoftBodyConfig(_soft->m_cfg);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -80,7 +84,7 @@ get_cfg() {
 BulletSoftBodyWorldInfo BulletSoftBodyNode::
 get_world_info() {
 
-  return BulletSoftBodyWorldInfo(*(_body->m_worldInfo));
+  return BulletSoftBodyWorldInfo(*(_soft->m_worldInfo));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -91,7 +95,7 @@ get_world_info() {
 int BulletSoftBodyNode::
 get_num_materials() const {
 
-  return _body->m_materials.size();
+  return _soft->m_materials.size();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -104,45 +108,45 @@ get_material(int idx) const {
 
   nassertr(idx >= 0 && idx < get_num_materials(), BulletSoftBodyMaterial::empty());
 
-  btSoftBody::Material *material = _body->m_materials[idx];
+  btSoftBody::Material *material = _soft->m_materials[idx];
   return BulletSoftBodyMaterial(*material);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::get_num_nodes
+//     Function: BulletSoftBodyNode::append_material
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-int BulletSoftBodyNode::
-get_num_nodes() const {
+BulletSoftBodyMaterial BulletSoftBodyNode::
+append_material() {
 
-  return _body->m_nodes.size();
+  btSoftBody::Material *material = _soft->appendMaterial();
+  nassertr(material, BulletSoftBodyMaterial::empty());
+
+  return BulletSoftBodyMaterial(*material);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::get_node
+//     Function: BulletSoftBodyNode::get_num_nodes
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-BulletSoftBodyNodeElement BulletSoftBodyNode::
-get_node(int idx) const {
+int BulletSoftBodyNode::
+get_num_nodes() const {
 
-  nassertr(idx >=0 && idx < get_num_nodes(), BulletSoftBodyNodeElement::empty());
-  return BulletSoftBodyNodeElement(_body->m_nodes[idx]);
+  return _soft->m_nodes.size();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::append_material
+//     Function: BulletSoftBodyNode::get_node
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
-BulletSoftBodyMaterial BulletSoftBodyNode::
-append_material() {
-
-  btSoftBody::Material *material = _body->appendMaterial();
-  nassertr(material, BulletSoftBodyMaterial::empty());
+BulletSoftBodyNodeElement BulletSoftBodyNode::
+get_node(int idx) const {
 
-  return BulletSoftBodyMaterial(*material);
+  nassertr(idx >=0 && idx < get_num_nodes(), BulletSoftBodyNodeElement::empty());
+  return BulletSoftBodyNodeElement(_soft->m_nodes[idx]);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -154,10 +158,10 @@ void BulletSoftBodyNode::
 generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) {
 
   if (material) {
-    _body->generateBendingConstraints(distance, &(material->get_material()));
+    _soft->generateBendingConstraints(distance, &(material->get_material()));
   }
   else {
-    _body->generateBendingConstraints(distance);
+    _soft->generateBendingConstraints(distance);
   }
 }
 
@@ -169,46 +173,63 @@ generate_bending_constraints(int distance, BulletSoftBodyMaterial *material) {
 void BulletSoftBodyNode::
 randomize_constraints() {
 
-  _body->randomizeConstraints();
+  _soft->randomizeConstraints();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::parents_changed
+//     Function: BulletSoftBodyNode::transform_changed
 //       Access: Protected
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletSoftBodyNode::
-parents_changed() {
+transform_changed() {
 
-  transform_changed();
+  if (_sync_disable) return;
+
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  CPT(TransformState) ts = np.get_net_transform();
+
+  LMatrix4f m_sync = _sync->get_mat();
+  LMatrix4f m_ts = ts->get_mat();
+
+  if (!m_sync.almost_equal(m_ts)) {
+    _sync = ts;
+
+    btTransform trans = TransformState_to_btTrans(ts);
+
+    trans *= _soft->m_initialWorldTransform.inverse();
+    _soft->transform(trans);
+
+    if (ts->has_scale()) {
+      LVecBase3f scale = ts->get_scale();
+      for (int i=0; i<get_num_shapes(); i++) {
+        PT(BulletShape) shape = _shapes[i];
+        shape->set_local_scale(scale);
+      }
+    }
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::transform_changed
-//       Access: Protected
+//     Function: BulletSoftBodyNode::sync_p2b
+//       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletSoftBodyNode::
-transform_changed() {
-
-  if (_disable_transform_changed) return;
-
-  btTransform trans = btTransform::getIdentity();
-  get_node_transform(trans, this);
-  trans *= _body->m_initialWorldTransform.inverse();
-  _body->transform(trans);
+sync_p2b() {
 
-  BulletBodyNode::transform_changed();
+  transform_changed();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletSoftBodyNode::post_step
+//     Function: BulletSoftBodyNode::sync_b2p
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletSoftBodyNode::
-post_step() {
+sync_b2p() {
 
+  // Render softbody
   if (_geom) {
     btTransform trans = btTransform::getIdentity();
     get_node_transform(trans, this);
@@ -221,7 +242,7 @@ post_step() {
     GeomVertexReader flips(vdata, BulletHelper::get_sb_flip());
 
     while (!vertices.is_at_end()) {
-      btSoftBody::Node node = _body->m_nodes[indices.get_data1i()];
+      btSoftBody::Node node = _soft->m_nodes[indices.get_data1i()];
       btVector3 v = trans.invXform(node.m_x);
       btVector3 n = node.m_n;
 
@@ -233,7 +254,7 @@ post_step() {
   }
 
   if (_curve) {
-    btSoftBody::tNodeArray &nodes(_body->m_nodes);
+    btSoftBody::tNodeArray &nodes(_soft->m_nodes);
 
     for (int i=0; i < nodes.size(); i++) {
       btVector3 pos = nodes[i].m_x;
@@ -242,7 +263,7 @@ post_step() {
   }
 
   if (_surface) {
-    btSoftBody::tNodeArray &nodes(_body->m_nodes);
+    btSoftBody::tNodeArray &nodes(_soft->m_nodes);
 
     int num_u = _surface->get_num_u_vertices();
     int num_v = _surface->get_num_v_vertices();
@@ -260,11 +281,18 @@ post_step() {
   // set_bounds does not store the pointer - it makes a copy using
   // volume->make_copy().
   BoundingBox bb = this->get_aabb();
-  CPT(TransformState) xform = TransformState::make_pos(bb.get_approx_center());
+  LVecBase3f pos = bb.get_approx_center();
+
+  NodePath np = NodePath::any_path((PandaNode *)this);
+  LVecBase3f scale = np.get_net_transform()->get_scale();
+
+  CPT(TransformState) ts = TransformState::make_pos(pos);
+  ts = ts->set_scale(scale);
 
-  _disable_transform_changed = true;
-  this->set_transform(xform);
-  _disable_transform_changed = false;
+  _sync = ts;
+  _sync_disable = true;
+  np.set_transform(NodePath(), ts);
+  _sync_disable = false;
 
   Thread *current_thread = Thread::get_current_thread();
   this->r_mark_geom_bounds_stale(current_thread);
@@ -289,7 +317,7 @@ get_closest_node_index(LVecBase3f point, bool local) {
     get_node_transform(trans, this);
   }
 
-  btSoftBody::tNodeArray &nodes(_body->m_nodes);
+  btSoftBody::tNodeArray &nodes(_soft->m_nodes);
   int node_idx = 0;
 
   for (int i=0; i<nodes.size(); ++i) {
@@ -317,6 +345,8 @@ link_geom(Geom *geom) {
   nassertv(geom->get_vertex_data()->has_column(InternalName::get_vertex()));
   nassertv(geom->get_vertex_data()->has_column(InternalName::get_normal()));
 
+  sync_p2b();
+
   _geom = geom;
 
   PT(GeomVertexData) vdata = _geom->modify_vertex_data();
@@ -362,7 +392,7 @@ unlink_geom() {
 void BulletSoftBodyNode::
 link_curve(NurbsCurveEvaluator *curve) {
 
-  nassertv(curve->get_num_vertices() == _body->m_nodes.size());
+  nassertv(curve->get_num_vertices() == _soft->m_nodes.size());
 
   _curve = curve;
 }
@@ -386,7 +416,7 @@ unlink_curve() {
 void BulletSoftBodyNode::
 link_surface(NurbsSurfaceEvaluator *surface) {
 
-  nassertv(surface->get_num_u_vertices() * surface->get_num_v_vertices() == _body->m_nodes.size());
+  nassertv(surface->get_num_u_vertices() * surface->get_num_v_vertices() == _soft->m_nodes.size());
 
   _surface = surface;
 }
@@ -413,7 +443,7 @@ get_aabb() const {
   btVector3 pMin;
   btVector3 pMax;
 
-  _body->getAabb(pMin, pMax);
+  _soft->getAabb(pMin, pMax);
 
   return BoundingBox(
     btVector3_to_LPoint3f(pMin),
@@ -429,7 +459,7 @@ get_aabb() const {
 void BulletSoftBodyNode::
 set_volume_mass(float mass) {
 
-  _body->setVolumeMass(mass);
+  _soft->setVolumeMass(mass);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -440,7 +470,7 @@ set_volume_mass(float mass) {
 void BulletSoftBodyNode::
 set_total_mass(float mass, bool fromfaces) {
 
-  _body->setTotalMass(mass, fromfaces);
+  _soft->setTotalMass(mass, fromfaces);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -451,7 +481,7 @@ set_total_mass(float mass, bool fromfaces) {
 void BulletSoftBodyNode::
 set_volume_density(float density) {
 
-  _body->setVolumeDensity(density);
+  _soft->setVolumeDensity(density);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -462,7 +492,7 @@ set_volume_density(float density) {
 void BulletSoftBodyNode::
 set_total_density(float density) {
 
-  _body->setTotalDensity(density);
+  _soft->setTotalDensity(density);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -473,7 +503,7 @@ set_total_density(float density) {
 void BulletSoftBodyNode::
 set_mass(int node, float mass) {
 
-  _body->setMass(node, mass);
+  _soft->setMass(node, mass);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -484,7 +514,7 @@ set_mass(int node, float mass) {
 float BulletSoftBodyNode::
 get_mass(int node) const {
 
-  return _body->getMass(node);
+  return _soft->getMass(node);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -495,7 +525,7 @@ get_mass(int node) const {
 float BulletSoftBodyNode::
 get_total_mass() const {
 
-  return _body->getTotalMass();
+  return _soft->getTotalMass();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -506,7 +536,7 @@ get_total_mass() const {
 float BulletSoftBodyNode::
 get_volume() const {
 
-  return _body->getVolume();
+  return _soft->getVolume();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -518,7 +548,7 @@ void BulletSoftBodyNode::
 add_force(const LVector3f &force) {
 
   nassertv(!force.is_nan());
-  _body->addForce(LVecBase3f_to_btVector3(force));
+  _soft->addForce(LVecBase3f_to_btVector3(force));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -530,7 +560,7 @@ void BulletSoftBodyNode::
 add_force(const LVector3f &force, int node) {
 
   nassertv(!force.is_nan());
-  _body->addForce(LVecBase3f_to_btVector3(force), node);
+  _soft->addForce(LVecBase3f_to_btVector3(force), node);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -542,7 +572,7 @@ void BulletSoftBodyNode::
 set_velocity(const LVector3f &velocity) {
 
   nassertv(!velocity.is_nan());
-  _body->setVelocity(LVecBase3f_to_btVector3(velocity));
+  _soft->setVelocity(LVecBase3f_to_btVector3(velocity));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -554,7 +584,7 @@ void BulletSoftBodyNode::
 add_velocity(const LVector3f &velocity) {
 
   nassertv(!velocity.is_nan());
-  _body->addVelocity(LVecBase3f_to_btVector3(velocity));
+  _soft->addVelocity(LVecBase3f_to_btVector3(velocity));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -566,7 +596,7 @@ void BulletSoftBodyNode::
 add_velocity(const LVector3f &velocity, int node) {
 
   nassertv(!velocity.is_nan());
-  _body->addVelocity(LVecBase3f_to_btVector3(velocity), node);
+  _soft->addVelocity(LVecBase3f_to_btVector3(velocity), node);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -577,7 +607,7 @@ add_velocity(const LVector3f &velocity, int node) {
 void BulletSoftBodyNode::
 generate_clusters(int k, int maxiterations) {
 
-  _body->generateClusters(k, maxiterations);
+  _soft->generateClusters(k, maxiterations);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -588,7 +618,7 @@ generate_clusters(int k, int maxiterations) {
 void BulletSoftBodyNode::
 release_clusters() {
 
-  _body->releaseClusters();
+  _soft->releaseClusters();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -599,7 +629,7 @@ release_clusters() {
 void BulletSoftBodyNode::
 release_cluster(int index) {
 
-  _body->releaseCluster(index);
+  _soft->releaseCluster(index);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -610,7 +640,7 @@ release_cluster(int index) {
 int BulletSoftBodyNode::
 get_num_clusters() const {
 
-  return _body->clusterCount();
+  return _soft->clusterCount();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -621,7 +651,7 @@ get_num_clusters() const {
 LVecBase3f BulletSoftBodyNode::
 cluster_com(int cluster) const {
 
-  return btVector3_to_LVecBase3f(_body->clusterCom(cluster));
+  return btVector3_to_LVecBase3f(_soft->clusterCom(cluster));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -632,7 +662,7 @@ cluster_com(int cluster) const {
 void BulletSoftBodyNode::
 set_pose(bool bvolume, bool bframe) {
 
-  _body->setPose(bvolume, bframe);
+  _soft->setPose(bvolume, bframe);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -643,11 +673,13 @@ set_pose(bool bvolume, bool bframe) {
 void BulletSoftBodyNode::
 append_anchor(int node, BulletRigidBodyNode *body, bool disable) {
 
-  nassertv(node < _body->m_nodes.size())
+  nassertv(node < _soft->m_nodes.size())
   nassertv(body);
 
+  body->sync_p2b();
+
   btRigidBody *ptr =(btRigidBody *)body->get_object();
-  _body->appendAnchor(node, ptr, disable);
+  _soft->appendAnchor(node, ptr, disable);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -658,12 +690,14 @@ append_anchor(int node, BulletRigidBodyNode *body, bool disable) {
 void BulletSoftBodyNode::
 append_anchor(int node, BulletRigidBodyNode *body, const LVector3f &pivot, bool disable) {
 
-  nassertv(node < _body->m_nodes.size())
+  nassertv(node < _soft->m_nodes.size())
   nassertv(body);
   nassertv(!pivot.is_nan());
 
+  body->sync_p2b();
+
   btRigidBody *ptr =(btRigidBody *)body->get_object();
-  _body->appendAnchor(node, ptr, LVecBase3f_to_btVector3(pivot), disable);
+  _soft->appendAnchor(node, ptr, LVecBase3f_to_btVector3(pivot), disable);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 3
panda/src/bullet/bulletSoftBodyNode.h

@@ -188,14 +188,17 @@ PUBLISHED:
 public:
   virtual btCollisionObject *get_object() const;
 
-  void post_step();
+  void sync_p2b();
+  void sync_b2p();
 
 protected:
-  virtual void parents_changed();
   virtual void transform_changed();
 
 private:
-  btSoftBody *_body;
+  btSoftBody *_soft;
+
+  CPT(TransformState) _sync;
+  bool _sync_disable;
 
   PT(Geom) _geom;
   PT(NurbsCurveEvaluator) _curve;

+ 2 - 2
panda/src/bullet/bulletVehicle.cxx

@@ -218,12 +218,12 @@ get_wheel(int idx) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: BulletVehicle::post_step
+//     Function: BulletVehicle::sync_b2p
 //       Access: Public
 //  Description:
 ////////////////////////////////////////////////////////////////////
 void BulletVehicle::
-post_step() {
+sync_b2p() {
 
   for (int i=0; i < get_num_wheels(); i++) {
     btWheelInfo info = _vehicle->getWheelInfo(i);

+ 1 - 1
panda/src/bullet/bulletVehicle.h

@@ -91,7 +91,7 @@ PUBLISHED:
 public:
   INLINE btRaycastVehicle *get_vehicle() const;
 
-  void post_step();
+  void sync_b2p();
 
 private:
   btRaycastVehicle *_vehicle;

+ 64 - 26
panda/src/bullet/bulletWorld.cxx

@@ -24,7 +24,8 @@ TypeHandle BulletWorld::_type_handle;
 PStatCollector BulletWorld::_pstat_physics("App:Bullet:DoPhysics");
 PStatCollector BulletWorld::_pstat_simulation("App:Bullet:DoPhysics:Simulation");
 PStatCollector BulletWorld::_pstat_debug("App:Bullet:DoPhysics:Debug");
-PStatCollector BulletWorld::_pstat_sb("App:Bullet:DoPhysics:SoftBodies");
+PStatCollector BulletWorld::_pstat_p2b("App:Bullet:DoPhysics:SyncP2B");
+PStatCollector BulletWorld::_pstat_b2p("App:Bullet:DoPhysics:SyncB2P");
 
 ////////////////////////////////////////////////////////////////////
 //     Function: BulletWorld::Constructor
@@ -140,47 +141,84 @@ do_physics(float dt, int substeps, float stepsize) {
 
   _pstat_physics.start();
 
-  // Preprocess characters
-  for (int i=0; i < get_num_characters(); i++) {
-    get_character(i)->pre_step(dt);
-  }
-
-  // Preprocess ghosts (autosync)
-  for (int i=0; i < get_num_ghosts(); i++) {
-    get_ghost(i)->pre_step();
-  }
+  // Synchronize Panda to Bullet
+  _pstat_p2b.start();
+  sync_p2b(dt);
+  _pstat_p2b.stop();
 
   // Simulation
   _pstat_simulation.start();
   _world->stepSimulation(dt, substeps, stepsize);
   _pstat_simulation.stop();
 
-  // Postprocess characters
+  // Synchronize Bullet to Panda
+  _pstat_b2p.start();
+  sync_b2p();
+  _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime);
+  _pstat_b2p.stop();
+
+  // Render debug
+  if (_debug) {
+    _pstat_debug.start();
+    _debug->sync_b2p(_world);
+    _pstat_debug.stop();
+  }
+
+  _pstat_physics.stop();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BulletWorld::sync_p2b
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BulletWorld::
+sync_p2b(float dt) {
+
+  for (int i=0; i < get_num_rigid_bodies(); i++) {
+    get_rigid_body(i)->sync_p2b();
+  }
+
+  for (int i=0; i < get_num_soft_bodies(); i++) {
+    get_soft_body(i)->sync_p2b();
+  }
+
+  for (int i=0; i < get_num_ghosts(); i++) {
+    get_ghost(i)->sync_p2b();
+  }
+
   for (int i=0; i < get_num_characters(); i++) {
-    get_character(i)->post_step();
+    get_character(i)->sync_p2b(dt);
   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BulletWorld::sync_b2p
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void BulletWorld::
+sync_b2p() {
 
-  // Postprocess vehicles
   for (int i=0; i < get_num_vehicles(); i++) {
-    get_vehicle(i)->post_step();
+    get_vehicle(i)->sync_b2p();
   }
 
-  // Render debug
-  _pstat_debug.start();
-  if (_debug) {
-    _debug->post_step(_world);
+  for (int i=0; i < get_num_rigid_bodies(); i++) {
+    get_rigid_body(i)->sync_b2p();
   }
-  _pstat_debug.stop();
 
-  // Render soft bodies
-  _pstat_sb.start();
   for (int i=0; i < get_num_soft_bodies(); i++) {
-    get_soft_body(i)->post_step();
+    get_soft_body(i)->sync_b2p();
   }
-  _info.m_sparsesdf.GarbageCollect(bullet_gc_lifetime);
-  _pstat_sb.stop();
 
-  _pstat_physics.stop();
+  for (int i=0; i < get_num_ghosts(); i++) {
+    get_ghost(i)->sync_b2p();
+  }
+
+  for (int i=0; i < get_num_characters(); i++) {
+    get_character(i)->sync_b2p();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -325,7 +363,7 @@ attach_ghost(BulletGhostNode *node) {
 
   // TODO group/filter settings...
 /*
-enum   CollisionFilterGroups { 
+enum CollisionFilterGroups { 
   DefaultFilter = 1, 
   StaticFilter = 2, 
   KinematicFilter = 4, 

+ 5 - 1
panda/src/bullet/bulletWorld.h

@@ -151,6 +151,9 @@ public:
   INLINE btDispatcher *get_dispatcher() const;
 
 private:
+  void sync_p2b(float dt);
+  void sync_b2p();
+
   typedef PTA(PT(BulletRigidBodyNode)) BulletRigidBodies;
   typedef PTA(PT(BulletSoftBodyNode)) BulletSoftBodies;
   typedef PTA(PT(BulletGhostNode)) BulletGhosts;
@@ -161,7 +164,8 @@ private:
   static PStatCollector _pstat_physics;
   static PStatCollector _pstat_simulation;
   static PStatCollector _pstat_debug;
-  static PStatCollector _pstat_sb;
+  static PStatCollector _pstat_p2b;
+  static PStatCollector _pstat_b2p;
 
   struct btFilterCallback : public btOverlapFilterCallback {
     virtual bool needBroadphaseCollision(