diff --git a/panda/src/chan/animChannel.I b/panda/src/chan/animChannel.I index ae25438b33..896300f711 100644 --- a/panda/src/chan/animChannel.I +++ b/panda/src/chan/animChannel.I @@ -82,16 +82,17 @@ get_value(int, TYPENAME AnimChannel::ValueType &) { #endif //////////////////////////////////////////////////////////////////// -// Function: AnimChannel::get_value_no_scale +// Function: AnimChannel::get_value_no_scale_share // Access: Public, Virtual // 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 void AnimChannel:: -get_value_no_scale(int frame, ValueType &value) { +get_value_no_scale_shear(int frame, ValueType &value) { get_value(frame, value); } @@ -104,10 +105,60 @@ get_value_no_scale(int frame, ValueType &value) { //////////////////////////////////////////////////////////////////// template void AnimChannel:: -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 +void AnimChannel:: +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 +void AnimChannel:: +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 +void AnimChannel:: +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 +void AnimChannel:: +get_shear(int, LVecBase3f &shear) { + nassertv(false); } diff --git a/panda/src/chan/animChannel.h b/panda/src/chan/animChannel.h index a38a9b2841..0dc855834c 100644 --- a/panda/src/chan/animChannel.h +++ b/panda/src/chan/animChannel.h @@ -47,12 +47,13 @@ public: PUBLISHED: 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; diff --git a/panda/src/chan/animChannelMatrixDynamic.cxx b/panda/src/chan/animChannelMatrixDynamic.cxx index cd940f8fca..96bd343298 100644 --- a/panda/src/chan/animChannelMatrixDynamic.cxx +++ b/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 // Description: Gets the value of the channel at the indicated frame, -// without any scale information. +// without any scale or shear information. //////////////////////////////////////////////////////////////////// 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), _value->get_hpr(), _value->get_pos()); } else { @@ -101,11 +101,56 @@ get_value_no_scale(int frame, LMatrix4f &mat) { // Description: Gets the scale value at the indicated frame. //////////////////////////////////////////////////////////////////// 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(); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/chan/animChannelMatrixDynamic.h b/panda/src/chan/animChannelMatrixDynamic.h index f541437742..b6e17b11b7 100644 --- a/panda/src/chan/animChannelMatrixDynamic.h +++ b/panda/src/chan/animChannelMatrixDynamic.h @@ -48,8 +48,13 @@ protected: public: virtual bool has_changed(int last_frame, int this_frame); 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: void set_value(const LMatrix4f &value); diff --git a/panda/src/chan/animChannelMatrixXfmTable.cxx b/panda/src/chan/animChannelMatrixXfmTable.cxx index f56045bea0..a1f17280a9 100644 --- a/panda/src/chan/animChannelMatrixXfmTable.cxx +++ b/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 // Description: Gets the value of the channel at the indicated frame, -// without any scale information. +// without any scale or shear information. //////////////////////////////////////////////////////////////////// void AnimChannelMatrixXfmTable:: -get_value_no_scale(int frame, LMatrix4f &mat) { +get_value_no_scale_shear(int frame, LMatrix4f &mat) { float components[num_matrix_components]; components[0] = 1.0f; components[1] = 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()) { components[i] = get_default_value(i); } else { @@ -135,23 +138,99 @@ get_value_no_scale(int frame, LMatrix4f &mat) { // Description: Gets the scale value at the indicated frame. //////////////////////////////////////////////////////////////////// void AnimChannelMatrixXfmTable:: -get_scale(int frame, float scale[3]) { +get_scale(int frame, LVecBase3f &scale) { for (int i = 0; i < 3; i++) { if (_tables[i].empty()) { - scale[i] = get_default_value(i); + scale[i] = 1.0f; } else { 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 // Access: Public // 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:: set_table(char table_id, const CPTA_float &table) { diff --git a/panda/src/chan/animChannelMatrixXfmTable.h b/panda/src/chan/animChannelMatrixXfmTable.h index a01d4a7e32..eb3330369d 100644 --- a/panda/src/chan/animChannelMatrixXfmTable.h +++ b/panda/src/chan/animChannelMatrixXfmTable.h @@ -47,8 +47,13 @@ public: virtual bool has_changed(int last_frame, int this_frame); 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); diff --git a/panda/src/chan/movingPartMatrix.cxx b/panda/src/chan/movingPartMatrix.cxx index 9f2e33bc08..1656794eaa 100644 --- a/panda/src/chan/movingPartMatrix.cxx +++ b/panda/src/chan/movingPartMatrix.cxx @@ -70,74 +70,181 @@ get_blend_value(const PartBundle *root) { } else { // 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]); - - _value += v * effect; - scale += s * effect; - net += effect; + 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; - nassertv(net != 0.0f); - _value /= net; - scale /= net; + 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; - // Now rebuild the matrix with the correct scale values. + 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; - LVector3f false_scale, shear, hpr, translate; - decompose_matrix(_value, false_scale, shear, hpr, translate); - compose_matrix(_value, scale, shear, hpr, translate); + // There should be no need to normalize the quaternion, + // assuming all of the input quaternions were already + // normalized. + + _value = LMatrix4f::scale_shear_mat(scale, shear) * quat; + _value.set_row(3, pos); + } + break; } } } diff --git a/panda/src/chan/movingPartMatrix.h b/panda/src/chan/movingPartMatrix.h index 0178e6e3ee..b60897bff5 100644 --- a/panda/src/chan/movingPartMatrix.h +++ b/panda/src/chan/movingPartMatrix.h @@ -24,6 +24,7 @@ #include "movingPart.h" #include "animChannel.h" #include "animChannelFixed.h" +#include "cmath.h" EXPORT_TEMPLATE_CLASS(EXPCL_PANDA, EXPTP_PANDA, MovingPart); diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index 3fd64319a0..6e51356216 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -82,13 +82,22 @@ PUBLISHED: BT_linear, // 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_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);