Browse Source

Remove shear from BT_normalized_linear, add BT_componentwise and BT_componentwise_quat

David Rose 20 years ago
parent
commit
34c1491fbb

+ 60 - 9
panda/src/chan/animChannel.I

@@ -82,16 +82,17 @@ get_value(int, TYPENAME AnimChannel<SwitchType>::ValueType &) {
 #endif
 #endif
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: AnimChannel::get_value_no_scale
+//     Function: AnimChannel::get_value_no_scale_share
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Returns the value associated with the current frame,
 //  Description: Returns the value associated with the current frame,
-//               with no scale components.  This only makes sense for
-//               a matrix-type channel, although for fiddly technical
-//               reasons the function exists for all channels.
+//               with no scale or share components.  This only makes
+//               sense for a matrix-type channel, although for fiddly
+//               technical reasons the function exists for all
+//               channels.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class SwitchType>
 template<class SwitchType>
 void AnimChannel<SwitchType>::
 void AnimChannel<SwitchType>::
-get_value_no_scale(int frame, ValueType &value) {
+get_value_no_scale_shear(int frame, ValueType &value) {
   get_value(frame, value);
   get_value(frame, value);
 }
 }
 
 
@@ -104,10 +105,60 @@ get_value_no_scale(int frame, ValueType &value) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 template<class SwitchType>
 template<class SwitchType>
 void AnimChannel<SwitchType>::
 void AnimChannel<SwitchType>::
-get_scale(int, float scale[3]) {
-  scale[0] = 1.0f;
-  scale[1] = 1.0f;
-  scale[2] = 1.0f;
+get_scale(int, LVecBase3f &scale) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannel::get_hpr
+//       Access: Public, Virtual
+//  Description: Returns the h, p, and r components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+template<class SwitchType>
+void AnimChannel<SwitchType>::
+get_hpr(int, LVecBase3f &hpr) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannel::get_quat
+//       Access: Public, Virtual
+//  Description: Returns the rotation component associated with the
+//               current frame, expressed as a quaternion.  As above,
+//               this only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+template<class SwitchType>
+void AnimChannel<SwitchType>::
+get_quat(int, LQuaternionf &quat) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannel::get_pos
+//       Access: Public, Virtual
+//  Description: Returns the x, y, and z translation components
+//               associated with the current frame.  As above, this
+//               only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+template<class SwitchType>
+void AnimChannel<SwitchType>::
+get_pos(int, LVecBase3f &pos) {
+  nassertv(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannel::get_shear
+//       Access: Public, Virtual
+//  Description: Returns the a, b, and c shear components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+template<class SwitchType>
+void AnimChannel<SwitchType>::
+get_shear(int, LVecBase3f &shear) {
+  nassertv(false);
 }
 }
 
 
 
 

+ 7 - 6
panda/src/chan/animChannel.h

@@ -47,12 +47,13 @@ public:
 PUBLISHED:
 PUBLISHED:
   virtual void get_value(int frame, ValueType &value)=0;
   virtual void get_value(int frame, ValueType &value)=0;
 
 
-  // These two functions only have meaning for matrix types.
-  virtual void get_value_no_scale(int frame, ValueType &value);
-  virtual void get_scale(int frame, float scale[3]);
-  // The second parameter above should really by LVector3f instead of
-  // float[3], but there seems to be a compiler bug in EGCS that
-  // doesn't like that.  So we have this kludge for now.
+  // These transform-component methods only have meaning for matrix types.
+  virtual void get_value_no_scale_shear(int frame, ValueType &value);
+  virtual void get_scale(int frame, LVecBase3f &scale);
+  virtual void get_hpr(int frame, LVecBase3f &hpr);
+  virtual void get_quat(int frame, LQuaternionf &quat);
+  virtual void get_pos(int frame, LVecBase3f &pos);
+  virtual void get_shear(int frame, LVecBase3f &shear);
 
 
   virtual TypeHandle get_value_type() const;
   virtual TypeHandle get_value_type() const;
 
 

+ 54 - 9
panda/src/chan/animChannelMatrixDynamic.cxx

@@ -80,14 +80,14 @@ get_value(int, LMatrix4f &mat) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: AnimChannelMatrixDynamic::get_value_no_scale
+//     Function: AnimChannelMatrixDynamic::get_value_no_scale_shear
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Gets the value of the channel at the indicated frame,
 //  Description: Gets the value of the channel at the indicated frame,
-//               without any scale information.
+//               without any scale or shear information.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void AnimChannelMatrixDynamic::
 void AnimChannelMatrixDynamic::
-get_value_no_scale(int frame, LMatrix4f &mat) {
-  if (_value->has_scale()) {
+get_value_no_scale_shear(int frame, LMatrix4f &mat) {
+  if (_value->has_scale() || _value->has_shear()) {
     compose_matrix(mat, LVecBase3f(1.0f, 1.0f, 1.0f),
     compose_matrix(mat, LVecBase3f(1.0f, 1.0f, 1.0f),
                    _value->get_hpr(), _value->get_pos());
                    _value->get_hpr(), _value->get_pos());
   } else {
   } else {
@@ -101,11 +101,56 @@ get_value_no_scale(int frame, LMatrix4f &mat) {
 //  Description: Gets the scale value at the indicated frame.
 //  Description: Gets the scale value at the indicated frame.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void AnimChannelMatrixDynamic::
 void AnimChannelMatrixDynamic::
-get_scale(int frame, float scale[3]) {
-  const LVecBase3f &sc = _value->get_scale();
-  scale[0] = sc[0];
-  scale[1] = sc[1];
-  scale[2] = sc[2]; 
+get_scale(int frame, LVecBase3f &scale) {
+  scale = _value->get_scale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixDynamic::get_hpr
+//       Access: Public, Virtual
+//  Description: Returns the h, p, and r components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixDynamic::
+get_hpr(int, LVecBase3f &hpr) {
+  hpr = _value->get_hpr();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixDynamic::get_quat
+//       Access: Public, Virtual
+//  Description: Returns the rotation component associated with the
+//               current frame, expressed as a quaternion.  As above,
+//               this only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixDynamic::
+get_quat(int, LQuaternionf &quat) {
+  quat = _value->get_quat();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixDynamic::get_pos
+//       Access: Public, Virtual
+//  Description: Returns the x, y, and z translation components
+//               associated with the current frame.  As above, this
+//               only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixDynamic::
+get_pos(int, LVecBase3f &pos) {
+  pos = _value->get_pos();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixDynamic::get_shear
+//       Access: Public, Virtual
+//  Description: Returns the a, b, and c shear components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixDynamic::
+get_shear(int, LVecBase3f &shear) {
+  shear = _value->get_shear();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 7 - 2
panda/src/chan/animChannelMatrixDynamic.h

@@ -48,8 +48,13 @@ protected:
 public:
 public:
   virtual bool has_changed(int last_frame, int this_frame);
   virtual bool has_changed(int last_frame, int this_frame);
   virtual void get_value(int frame, LMatrix4f &mat);
   virtual void get_value(int frame, LMatrix4f &mat);
-  virtual void get_value_no_scale(int frame, LMatrix4f &value);
-  virtual void get_scale(int frame, float scale[3]);
+
+  virtual void get_value_no_scale_shear(int frame, LMatrix4f &value);
+  virtual void get_scale(int frame, LVecBase3f &scale);
+  virtual void get_hpr(int frame, LVecBase3f &hpr);
+  virtual void get_quat(int frame, LQuaternionf &quat);
+  virtual void get_pos(int frame, LVecBase3f &pos);
+  virtual void get_shear(int frame, LVecBase3f &shear);
 
 
 PUBLISHED:
 PUBLISHED:
   void set_value(const LMatrix4f &value);
   void set_value(const LMatrix4f &value);

+ 88 - 9
panda/src/chan/animChannelMatrixXfmTable.cxx

@@ -106,19 +106,22 @@ get_value(int frame, LMatrix4f &mat) {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: AnimChannelMatrixXfmTable::get_value_no_scale
+//     Function: AnimChannelMatrixXfmTable::get_value_no_scale_shear
 //       Access: Public, Virtual
 //       Access: Public, Virtual
 //  Description: Gets the value of the channel at the indicated frame,
 //  Description: Gets the value of the channel at the indicated frame,
-//               without any scale information.
+//               without any scale or shear information.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void AnimChannelMatrixXfmTable::
 void AnimChannelMatrixXfmTable::
-get_value_no_scale(int frame, LMatrix4f &mat) {
+get_value_no_scale_shear(int frame, LMatrix4f &mat) {
   float components[num_matrix_components];
   float components[num_matrix_components];
   components[0] = 1.0f;
   components[0] = 1.0f;
   components[1] = 1.0f;
   components[1] = 1.0f;
   components[2] = 1.0f;
   components[2] = 1.0f;
+  components[3] = 0.0f;
+  components[4] = 0.0f;
+  components[5] = 0.0f;
 
 
-  for (int i = 3; i < num_matrix_components; i++) {
+  for (int i = 6; i < num_matrix_components; i++) {
     if (_tables[i].empty()) {
     if (_tables[i].empty()) {
       components[i] = get_default_value(i);
       components[i] = get_default_value(i);
     } else {
     } else {
@@ -135,23 +138,99 @@ get_value_no_scale(int frame, LMatrix4f &mat) {
 //  Description: Gets the scale value at the indicated frame.
 //  Description: Gets the scale value at the indicated frame.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void AnimChannelMatrixXfmTable::
 void AnimChannelMatrixXfmTable::
-get_scale(int frame, float scale[3]) {
+get_scale(int frame, LVecBase3f &scale) {
   for (int i = 0; i < 3; i++) {
   for (int i = 0; i < 3; i++) {
     if (_tables[i].empty()) {
     if (_tables[i].empty()) {
-      scale[i] = get_default_value(i);
+      scale[i] = 1.0f;
     } else {
     } else {
       scale[i] = _tables[i][frame % _tables[i].size()];
       scale[i] = _tables[i][frame % _tables[i].size()];
     }
     }
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixXfmTable::get_hpr
+//       Access: Public, Virtual
+//  Description: Returns the h, p, and r components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixXfmTable::
+get_hpr(int frame, LVecBase3f &hpr) {
+  for (int i = 0; i < 3; i++) {
+    if (_tables[i + 6].empty()) {
+      hpr[i] = 0.0f;
+    } else {
+      hpr[i] = _tables[i + 6][frame % _tables[i + 6].size()];
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixXfmTable::get_quat
+//       Access: Public, Virtual
+//  Description: Returns the rotation component associated with the
+//               current frame, expressed as a quaternion.  As above,
+//               this only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixXfmTable::
+get_quat(int frame, LQuaternionf &quat) {
+  LVecBase3f hpr;
+  for (int i = 0; i < 3; i++) {
+    if (_tables[i + 6].empty()) {
+      hpr[i] = 0.0f;
+    } else {
+      hpr[i] = _tables[i + 6][frame % _tables[i + 6].size()];
+    }
+  }
+
+  quat.set_hpr(hpr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixXfmTable::get_pos
+//       Access: Public, Virtual
+//  Description: Returns the x, y, and z translation components
+//               associated with the current frame.  As above, this
+//               only makes sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixXfmTable::
+get_pos(int frame, LVecBase3f &pos) {
+  for (int i = 0; i < 3; i++) {
+    if (_tables[i + 9].empty()) {
+      pos[i] = 0.0f;
+    } else {
+      pos[i] = _tables[i + 9][frame % _tables[i + 9].size()];
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AnimChannelMatrixXfmTable::get_shear
+//       Access: Public, Virtual
+//  Description: Returns the a, b, and c shear components associated
+//               with the current frame.  As above, this only makes
+//               sense for a matrix-type channel.
+////////////////////////////////////////////////////////////////////
+void AnimChannelMatrixXfmTable::
+get_shear(int frame, LVecBase3f &shear) {
+  for (int i = 0; i < 3; i++) {
+    if (_tables[i + 3].empty()) {
+      shear[i] = 0.0f;
+    } else {
+      shear[i] = _tables[i + 3][frame % _tables[i + 3].size()];
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: AnimChannelMatrixXfmTable::set_table
 //     Function: AnimChannelMatrixXfmTable::set_table
 //       Access: Public
 //       Access: Public
 //  Description: Assigns the indicated table.  table_id is one of 'i',
 //  Description: Assigns the indicated table.  table_id is one of 'i',
-//               'j', 'k', for scale, 'h', 'p', 'r', for rotation, and
-//               'x', 'y', 'z', for translation.  The new table must
-//               have either zero, one, or get_num_frames() frames.
+//               'j', 'k', for scale, 'a', 'b', 'c' for shear, 'h',
+//               'p', 'r', for rotation, and 'x', 'y', 'z', for
+//               translation.  The new table must have either zero,
+//               one, or get_num_frames() frames.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void AnimChannelMatrixXfmTable::
 void AnimChannelMatrixXfmTable::
 set_table(char table_id, const CPTA_float &table) {
 set_table(char table_id, const CPTA_float &table) {

+ 7 - 2
panda/src/chan/animChannelMatrixXfmTable.h

@@ -47,8 +47,13 @@ public:
 
 
   virtual bool has_changed(int last_frame, int this_frame);
   virtual bool has_changed(int last_frame, int this_frame);
   virtual void get_value(int frame, LMatrix4f &mat);
   virtual void get_value(int frame, LMatrix4f &mat);
-  virtual void get_value_no_scale(int frame, LMatrix4f &value);
-  virtual void get_scale(int frame, float scale[3]);
+
+  virtual void get_value_no_scale_shear(int frame, LMatrix4f &value);
+  virtual void get_scale(int frame, LVecBase3f &scale);
+  virtual void get_hpr(int frame, LVecBase3f &hpr);
+  virtual void get_quat(int frame, LQuaternionf &quat);
+  virtual void get_pos(int frame, LVecBase3f &pos);
+  virtual void get_shear(int frame, LVecBase3f &shear);
 
 
   static INLINE bool is_valid_id(char table_id);
   static INLINE bool is_valid_id(char table_id);
 
 

+ 168 - 61
panda/src/chan/movingPartMatrix.cxx

@@ -70,74 +70,181 @@ get_blend_value(const PartBundle *root) {
   } else {
   } else {
     // A blend of two or more values.
     // A blend of two or more values.
 
 
-    if (root->get_blend_type() == PartBundle::BT_linear) {
-      // An ordinary, linear blend.
-      _value = 0.0f;
-      float net = 0.0f;
-
-      PartBundle::ChannelBlend::const_iterator cbi;
-      for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
-        AnimControl *control = (*cbi).first;
-        float effect = (*cbi).second;
-        nassertv(effect != 0.0f);
-
-        int channel_index = control->get_channel_index();
-        nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
-        ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
-        nassertv(channel != NULL);
-
-        ValueType v;
-        channel->get_value(control->get_frame(), v);
-
-        _value += v * effect;
-        net += effect;
+    switch (root->get_blend_type()) {
+    case PartBundle::BT_single:
+      // This one shouldn't be possible.
+      nassertv(false);
+      
+    case PartBundle::BT_linear:
+      {
+        // An ordinary, linear blend.
+        _value = 0.0f;
+        float net = 0.0f;
+        
+        PartBundle::ChannelBlend::const_iterator cbi;
+        for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
+          AnimControl *control = (*cbi).first;
+          float effect = (*cbi).second;
+          nassertv(effect != 0.0f);
+          
+          int channel_index = control->get_channel_index();
+          nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
+          ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
+          nassertv(channel != NULL);
+          
+          ValueType v;
+          channel->get_value(control->get_frame(), v);
+          
+          _value += v * effect;
+          net += effect;
+        }
+        
+        nassertv(net != 0.0f);
+        _value /= net;
       }
       }
+      break;
 
 
-      nassertv(net != 0.0f);
-      _value /= net;
-
-    } else if (root->get_blend_type() == PartBundle::BT_normalized_linear) {
-      // A normalized linear blend.  This means we do a linear blend
-      // without scales, normalize the scale components of the
-      // resulting matrix to eliminate artificially-introduced scales,
-      // and then reapply the scales.
-
-      // Perhaps we should treat shear the same as a scale here?
-
-      _value = 0.0f;
-      LVector3f scale(0.0f, 0.0f, 0.0f);
-      float net = 0.0f;
-
-      PartBundle::ChannelBlend::const_iterator cbi;
-      for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
-        AnimControl *control = (*cbi).first;
-        float effect = (*cbi).second;
-        nassertv(effect != 0.0f);
-
-        int channel_index = control->get_channel_index();
-        nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
-        ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
-        nassertv(channel != NULL);
-
-        ValueType v;
-        channel->get_value_no_scale(control->get_frame(), v);
-        LVector3f s;
-        channel->get_scale(control->get_frame(), &s[0]);
+    case PartBundle::BT_normalized_linear:
+      {
+        // A normalized linear blend.  This means we do a linear blend
+        // without scales or shears, normalize the scale and shear
+        // components of the resulting matrix to eliminate
+        // artificially-introduced scales, and then reapply the
+        // scales and shears.
+        
+        _value = 0.0f;
+        LVecBase3f scale(0.0f, 0.0f, 0.0f);
+        LVecBase3f shear(0.0f, 0.0f, 0.0f);
+        float net = 0.0f;
+        
+        PartBundle::ChannelBlend::const_iterator cbi;
+        for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
+          AnimControl *control = (*cbi).first;
+          float effect = (*cbi).second;
+          nassertv(effect != 0.0f);
+          
+          int channel_index = control->get_channel_index();
+          nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
+          ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
+          nassertv(channel != NULL);
+          
+          ValueType v;
+          channel->get_value_no_scale_shear(control->get_frame(), v);
+          LVecBase3f iscale, ishear;
+          channel->get_scale(control->get_frame(), iscale);
+          channel->get_shear(control->get_frame(), ishear);
+          
+          _value += v * effect;
+          scale += iscale * effect;
+          shear += ishear * effect;
+          net += effect;
+        }
+        
+        nassertv(net != 0.0f);
+        _value /= net;
+        scale /= net;
+        shear /= net;
+        
+        // Now rebuild the matrix with the correct scale values.
+        
+        LVector3f false_scale, false_shear, hpr, translate;
+        decompose_matrix(_value, false_scale, false_shear, hpr, translate);
+        compose_matrix(_value, scale, shear, hpr, translate);
+      }
+      break;
 
 
-        _value += v * effect;
-        scale += s * effect;
-        net += effect;
+    case PartBundle::BT_componentwise:
+      {
+        // Componentwise linear, including componentwise H, P, and R.
+        LVecBase3f scale(0.0f, 0.0f, 0.0f);
+        LVecBase3f hpr(0.0f, 0.0f, 0.0f);
+        LVecBase3f pos(0.0f, 0.0f, 0.0f);
+        LVecBase3f shear(0.0f, 0.0f, 0.0f);
+        float net = 0.0f;
+        
+        PartBundle::ChannelBlend::const_iterator cbi;
+        for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
+          AnimControl *control = (*cbi).first;
+          float effect = (*cbi).second;
+          nassertv(effect != 0.0f);
+          
+          int channel_index = control->get_channel_index();
+          nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
+          ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
+          nassertv(channel != NULL);
+          
+          LVecBase3f iscale, ihpr, ipos, ishear;
+          channel->get_scale(control->get_frame(), iscale);
+          channel->get_hpr(control->get_frame(), ihpr);
+          channel->get_pos(control->get_frame(), ipos);
+          channel->get_shear(control->get_frame(), ishear);
+          
+          scale += iscale * effect;
+          hpr += ihpr * effect;
+          pos += ipos * effect;
+          shear += ishear * effect;
+          net += effect;
+        }
+        
+        nassertv(net != 0.0f);
+        scale /= net;
+        hpr /= net;
+        pos /= net;
+        shear /= net;
+        
+        compose_matrix(_value, scale, shear, hpr, pos);
       }
       }
+      break;
 
 
-      nassertv(net != 0.0f);
-      _value /= net;
-      scale /= net;
+    case PartBundle::BT_componentwise_quat:
+      {
+        // Componentwise linear, except for rotation, which is a
+        // quaternion.
+        LVecBase3f scale(0.0f, 0.0f, 0.0f);
+        LQuaternionf quat(0.0f, 0.0f, 0.0f, 0.0f);
+        LVecBase3f pos(0.0f, 0.0f, 0.0f);
+        LVecBase3f shear(0.0f, 0.0f, 0.0f);
+        float net = 0.0f;
+        
+        PartBundle::ChannelBlend::const_iterator cbi;
+        for (cbi = blend.begin(); cbi != blend.end(); ++cbi) {
+          AnimControl *control = (*cbi).first;
+          float effect = (*cbi).second;
+          nassertv(effect != 0.0f);
+          
+          int channel_index = control->get_channel_index();
+          nassertv(channel_index >= 0 && channel_index < (int)_channels.size());
+          ChannelType *channel = DCAST(ChannelType, _channels[channel_index]);
+          nassertv(channel != NULL);
+          
+          LVecBase3f iscale, ipos, ishear;
+          LQuaternionf iquat;
+          channel->get_scale(control->get_frame(), iscale);
+          channel->get_quat(control->get_frame(), iquat);
+          channel->get_pos(control->get_frame(), ipos);
+          channel->get_shear(control->get_frame(), ishear);
+          
+          scale += iscale * effect;
+          quat += iquat * effect;
+          pos += ipos * effect;
+          shear += ishear * effect;
+          net += effect;
+        }
+        
+        nassertv(net != 0.0f);
+        scale /= net;
+        quat /= net;
+        pos /= net;
+        shear /= net;
 
 
-      // Now rebuild the matrix with the correct scale values.
+        // There should be no need to normalize the quaternion,
+        // assuming all of the input quaternions were already
+        // normalized.
 
 
-      LVector3f false_scale, shear, hpr, translate;
-      decompose_matrix(_value, false_scale, shear, hpr, translate);
-      compose_matrix(_value, scale, shear, hpr, translate);
+        _value = LMatrix4f::scale_shear_mat(scale, shear) * quat;
+        _value.set_row(3, pos);
+      }
+      break;
     }
     }
   }
   }
 }
 }

+ 1 - 0
panda/src/chan/movingPartMatrix.h

@@ -24,6 +24,7 @@
 #include "movingPart.h"
 #include "movingPart.h"
 #include "animChannel.h"
 #include "animChannel.h"
 #include "animChannelFixed.h"
 #include "animChannelFixed.h"
+#include "cmath.h"
 
 
 EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MovingPart<ACMatrixSwitchType>);
 EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MovingPart<ACMatrixSwitchType>);
 
 

+ 15 - 6
panda/src/chan/partBundle.h

@@ -82,13 +82,22 @@ PUBLISHED:
     BT_linear,
     BT_linear,
 
 
     // BT_normalized_linear is a compromise on BT_linear.  The matrix
     // BT_normalized_linear is a compromise on BT_linear.  The matrix
-    // is blended linearly without the scale component, and the
-    // blended scale component is applied separately.  This keeps all
-    // of the character's body parts in the correct size and shape.
-    // However, if the hierarchy is disconnected, body parts can fly
-    // off.  It's essential the skeleton hierarchy be completely
-    // connected to use this blend mode successully.
+    // is blended linearly without the scale and shear components, and
+    // the blended scale and shear components are applied separately.
+    // This keeps all of the character's body parts in the correct
+    // size and shape.  However, if the hierarchy is disconnected,
+    // body parts can fly off.  It's essential the skeleton hierarchy
+    // be completely connected to use this blend mode successully.
     BT_normalized_linear,
     BT_normalized_linear,
+
+    // BT_componentwise linearly blends all components separately,
+    // including H, P, and R, and recomposes the matrix.
+    BT_componentwise,
+
+    // BT_componentwise_quat linearly blends all components
+    // separately, except for rotation which is blended as a
+    // quaternion.
+    BT_componentwise_quat,
   };
   };
 
 
   void set_blend_type(BlendType bt);
   void set_blend_type(BlendType bt);