From 069fb87e7b0337ad55296cf519c454665b77128c Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 15 Feb 2006 19:20:55 +0000 Subject: [PATCH] control effect, etc., needs to be cycled --- panda/src/chan/movingPartBase.cxx | 23 +- panda/src/chan/movingPartMatrix.cxx | 18 +- panda/src/chan/movingPartScalar.cxx | 10 +- panda/src/chan/partBundle.I | 72 +++---- panda/src/chan/partBundle.cxx | 316 ++++++++++++++++------------ panda/src/chan/partBundle.h | 56 +++-- 6 files changed, 270 insertions(+), 225 deletions(-) diff --git a/panda/src/chan/movingPartBase.cxx b/panda/src/chan/movingPartBase.cxx index 2d5626660a..afbe30f69e 100644 --- a/panda/src/chan/movingPartBase.cxx +++ b/panda/src/chan/movingPartBase.cxx @@ -104,16 +104,19 @@ do_update(PartBundle *root, PartGroup *parent, // See if any of the channel values have changed since last time. - PartBundle::control_iterator bci; - for (bci = root->control_begin(); - !needs_update && bci != root->control_end(); - ++bci) { - AnimControl *control = (*bci); - int channel_index = control->get_channel_index(); - nassertr(channel_index >= 0 && channel_index < (int)_channels.size(), false); - AnimChannelBase *channel = _channels[channel_index]; - if (channel != (AnimChannelBase*)NULL) { - needs_update = control->channel_has_changed(channel); + { + PartBundle::CDReader cdata(root->_cycler); + PartBundle::ChannelBlend::const_iterator bci; + for (bci = cdata->_blend.begin(); + !needs_update && bci != cdata->_blend.end(); + ++bci) { + AnimControl *control = (*bci).first; + int channel_index = control->get_channel_index(); + nassertr(channel_index >= 0 && channel_index < (int)_channels.size(), false); + AnimChannelBase *channel = _channels[channel_index]; + if (channel != (AnimChannelBase*)NULL) { + needs_update = control->channel_has_changed(channel); + } } } diff --git a/panda/src/chan/movingPartMatrix.cxx b/panda/src/chan/movingPartMatrix.cxx index 08db8eeb5d..2cbd0c390e 100644 --- a/panda/src/chan/movingPartMatrix.cxx +++ b/panda/src/chan/movingPartMatrix.cxx @@ -63,15 +63,15 @@ make_initial_channel() const { //////////////////////////////////////////////////////////////////// void MovingPartMatrix:: get_blend_value(const PartBundle *root) { - const PartBundle::ChannelBlend &blend = root->get_blend_map(); + PartBundle::CDReader cdata(root->_cycler); - if (blend.empty()) { + if (cdata->_blend.empty()) { // No channel is bound; supply the default value. _value = _initial_value; - } else if (blend.size() == 1) { + } else if (cdata->_blend.size() == 1) { // A single value, the normal case. - AnimControl *control = (*blend.begin()).first; + AnimControl *control = (*cdata->_blend.begin()).first; int channel_index = control->get_channel_index(); nassertv(channel_index >= 0 && channel_index < (int)_channels.size()); @@ -87,7 +87,7 @@ get_blend_value(const PartBundle *root) { } else { // A blend of two or more values. - switch (root->get_blend_type()) { + switch (cdata->_blend_type) { case PartBundle::BT_single: case PartBundle::BT_linear: { @@ -96,7 +96,7 @@ get_blend_value(const PartBundle *root) { float net = 0.0f; PartBundle::ChannelBlend::const_iterator cbi; - for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; float effect = (*cbi).second; nassertv(effect != 0.0f); @@ -135,7 +135,7 @@ get_blend_value(const PartBundle *root) { float net = 0.0f; PartBundle::ChannelBlend::const_iterator cbi; - for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; float effect = (*cbi).second; nassertv(effect != 0.0f); @@ -184,7 +184,7 @@ get_blend_value(const PartBundle *root) { float net = 0.0f; PartBundle::ChannelBlend::const_iterator cbi; - for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; float effect = (*cbi).second; nassertv(effect != 0.0f); @@ -232,7 +232,7 @@ get_blend_value(const PartBundle *root) { float net = 0.0f; PartBundle::ChannelBlend::const_iterator cbi; - for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; float effect = (*cbi).second; nassertv(effect != 0.0f); diff --git a/panda/src/chan/movingPartScalar.cxx b/panda/src/chan/movingPartScalar.cxx index abccd4a047..ba625cc638 100644 --- a/panda/src/chan/movingPartScalar.cxx +++ b/panda/src/chan/movingPartScalar.cxx @@ -48,15 +48,15 @@ MovingPartScalar:: //////////////////////////////////////////////////////////////////// void MovingPartScalar:: get_blend_value(const PartBundle *root) { - const PartBundle::ChannelBlend &blend = root->get_blend_map(); + PartBundle::CDReader cdata(root->_cycler); - if (blend.empty()) { + if (cdata->_blend.empty()) { // No channel is bound; supply the default value. _value = _initial_value; - } else if (blend.size() == 1) { + } else if (cdata->_blend.size() == 1) { // A single value, the normal case. - AnimControl *control = (*blend.begin()).first; + AnimControl *control = (*cdata->_blend.begin()).first; int channel_index = control->get_channel_index(); nassertv(channel_index >= 0 && channel_index < (int)_channels.size()); @@ -75,7 +75,7 @@ get_blend_value(const PartBundle *root) { float net = 0.0f; PartBundle::ChannelBlend::const_iterator cbi; - for (cbi = blend.begin(); cbi != blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; float effect = (*cbi).second; nassertv(effect != 0.0f); diff --git a/panda/src/chan/partBundle.I b/panda/src/chan/partBundle.I index ef223131f9..d06d343e84 100644 --- a/panda/src/chan/partBundle.I +++ b/panda/src/chan/partBundle.I @@ -25,7 +25,8 @@ //////////////////////////////////////////////////////////////////// INLINE PartBundle::BlendType PartBundle:: get_blend_type() const { - return _blend_type; + CDReader cdata(_cycler); + return cdata->_blend_type; } //////////////////////////////////////////////////////////////////// @@ -39,50 +40,41 @@ get_node() const { return _node; } + //////////////////////////////////////////////////////////////////// -// Function: PartBundle::control_begin -// Access: Public -// Description: Returns an iterator that can be used to traverse the -// entire set of AnimControls currently in effect. +// Function: PartBundle::set_control_effect +// Access: Published +// Description: Sets the amount by which the character is affected by +// the indicated AnimControl (and its associated +// animation). Normally, this will only be zero or one. +// Zero indicates the animation does not affect the +// character, and one means it does. +// +// If the blend_type is not BT_single (see +// set_blend_type()), it is possible to have multiple +// AnimControls in effect simultaneously. In this case, +// the effect is a weight that indicates the relative +// importance of each AnimControl to the final +// animation. //////////////////////////////////////////////////////////////////// -INLINE PartBundle::control_iterator PartBundle:: -control_begin() const { - return _blend.begin(); +void PartBundle:: +set_control_effect(AnimControl *control, float effect) { + nassertv(Thread::get_current_pipeline_stage() == 0); + + CDWriter cdata(_cycler); + do_set_control_effect(control, effect, cdata); } - //////////////////////////////////////////////////////////////////// -// Function: PartBundle::control_end -// Access: Public -// Description: Returns an iterator that can be used to traverse the -// entire set of AnimControls currently in effect. +// Function: PartBundle::get_control_effect +// Access: Published +// Description: Returns the amount by which the character is affected +// by the indicated AnimControl and its associated +// animation. See set_control_effect(). //////////////////////////////////////////////////////////////////// -INLINE PartBundle::control_iterator PartBundle:: -control_end() const { - return _blend.end(); +INLINE float PartBundle:: +get_control_effect(AnimControl *control) const { + CDReader cdata(_cycler); + return do_get_control_effect(control, cdata); } - -//////////////////////////////////////////////////////////////////// -// Function: PartBundle::control_size -// Access: Public -// Description: Returns the number of entries between control_begin() -// and control_end(): the number of AnimControls -// currently in effect. -//////////////////////////////////////////////////////////////////// -INLINE PartBundle::control_size_type PartBundle:: -control_size() const { - return _blend.size(); -} - - -//////////////////////////////////////////////////////////////////// -// Function: PartBundle::get_blend_map -// Access: Public -// Description: Returns the entire mapping of AnimControls to blend -// effect. -//////////////////////////////////////////////////////////////////// -INLINE const PartBundle::ChannelBlend &PartBundle:: -get_blend_map() const { - return _blend; -} diff --git a/panda/src/chan/partBundle.cxx b/panda/src/chan/partBundle.cxx index 4be166dc66..fa311aeba2 100644 --- a/panda/src/chan/partBundle.cxx +++ b/panda/src/chan/partBundle.cxx @@ -41,12 +41,10 @@ TypeHandle PartBundle::_type_handle; //////////////////////////////////////////////////////////////////// PartBundle:: PartBundle(const PartBundle ©) : - PartGroup(copy), - _blend_type(copy._blend_type) + PartGroup(copy) { - _last_control_set = NULL; - _net_blend = 0.0f; - _anim_changed = false; + CDWriter cdata(_cycler, true); + cdata->_blend_type = copy.get_blend_type(); } //////////////////////////////////////////////////////////////////// @@ -58,11 +56,6 @@ PartBundle(const PartBundle ©) : //////////////////////////////////////////////////////////////////// PartBundle:: PartBundle(const string &name) : PartGroup(name) { - _blend_type = BT_single; - - _last_control_set = NULL; - _net_blend = 0.0f; - _anim_changed = false; } //////////////////////////////////////////////////////////////////// @@ -99,19 +92,23 @@ make_copy() const { //////////////////////////////////////////////////////////////////// void PartBundle:: set_blend_type(BlendType bt) { - if (_blend_type != bt) { - _blend_type = bt; + nassertv(Thread::get_current_pipeline_stage() == 0); - if (_blend_type == BT_single && control_size() > 1) { + CDReader cdata(_cycler); + if (cdata->_blend_type != bt) { + CDWriter cdataw(_cycler, cdata); + cdataw->_blend_type = bt; + + if (cdataw->_blend_type == BT_single && cdataw->_blend.size() > 1) { // If we just changed to a single blend type, i.e. no blending, // we should eliminate all the AnimControls other than the // most-recently-added one. - nassertv(_last_control_set != NULL); - clear_and_stop_intersecting(_last_control_set); + nassertv(cdataw->_last_control_set != NULL); + clear_and_stop_intersecting(cdataw->_last_control_set, cdataw); } - _anim_changed = true; + cdataw->_anim_changed = true; } } @@ -132,84 +129,17 @@ set_blend_type(BlendType bt) { //////////////////////////////////////////////////////////////////// void PartBundle:: clear_control_effects() { - if (!_blend.empty()) { - _blend.clear(); - _net_blend = 0.0f; - _anim_changed = true; + nassertv(Thread::get_current_pipeline_stage() == 0); + + CDReader cdata(_cycler); + if (!cdata->_blend.empty()) { + CDWriter cdataw(_cycler, cdata); + cdataw->_blend.clear(); + cdataw->_net_blend = 0.0f; + cdataw->_anim_changed = true; } } - -//////////////////////////////////////////////////////////////////// -// Function: PartBundle::set_control_effect -// Access: Published -// Description: Sets the amount by which the character is affected by -// the indicated AnimControl (and its associated -// animation). Normally, this will only be zero or one. -// Zero indicates the animation does not affect the -// character, and one means it does. -// -// If the blend_type is not BT_single (see -// set_blend_type()), it is possible to have multiple -// AnimControls in effect simultaneously. In this case, -// the effect is a weight that indicates the relative -// importance of each AnimControl to the final -// animation. -//////////////////////////////////////////////////////////////////// -void PartBundle:: -set_control_effect(AnimControl *control, float effect) { - nassertv(control->get_part() == this); - - if (effect == 0.0f) { - // An effect of zero means to eliminate the control. - ChannelBlend::iterator cbi = _blend.find(control); - if (cbi != _blend.end()) { - _blend.erase(cbi); - _anim_changed = true; - } - - } else { - // Otherwise we define it. - - // If we currently have BT_single, we only allow one AnimControl - // at a time. Stop all of the other AnimControls. - if (get_blend_type() == BT_single) { - clear_and_stop_intersecting(control); - } - - if (get_control_effect(control) != effect) { - _blend[control] = effect; - _anim_changed = true; - } - _last_control_set = control; - } - - recompute_net_blend(); -} - - -//////////////////////////////////////////////////////////////////// -// Function: PartBundle::get_control_effect -// Access: Published -// Description: Returns the amount by which the character is affected -// by the indicated AnimControl and its associated -// animation. See set_control_effect(). -//////////////////////////////////////////////////////////////////// -float PartBundle:: -get_control_effect(AnimControl *control) { - nassertr(control->get_part() == this, 0.0f); - - ChannelBlend::iterator cbi = _blend.find(control); - if (cbi == _blend.end()) { - // The control is not in effect. - return 0.0f; - } else { - return (*cbi).second; - } -} - - - //////////////////////////////////////////////////////////////////// // Function: PartBundle::output // Access: Published, Virtual @@ -261,6 +191,8 @@ write(ostream &out, int indent_level) const { PT(AnimControl) PartBundle:: bind_anim(AnimBundle *anim, int hierarchy_match_flags, const PartSubset &subset) { + nassertr(Thread::get_current_pipeline_stage() == 0, NULL); + if ((hierarchy_match_flags & HMF_ok_wrong_root_name) == 0) { // Make sure the root names match. if (get_name() != anim->get_name()) { @@ -308,15 +240,22 @@ bind_anim(AnimBundle *anim, int hierarchy_match_flags, //////////////////////////////////////////////////////////////////// bool PartBundle:: update() { - bool any_changed = do_update(this, NULL, false, _anim_changed); + bool anim_changed; + { + CDReader cdata(_cycler); + anim_changed = cdata->_anim_changed; + } + bool any_changed = do_update(this, NULL, false, anim_changed); // Now update all the controls for next time. + CDWriter cdata(_cycler, false); ChannelBlend::const_iterator cbi; - for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; control->mark_channels(); } - _anim_changed = false; + + cdata->_anim_changed = false; return any_changed; } @@ -333,12 +272,14 @@ force_update() { bool any_changed = do_update(this, NULL, true, true); // Now update all the controls for next time. + CDWriter cdata(_cycler, false); ChannelBlend::const_iterator cbi; - for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { AnimControl *control = (*cbi).first; control->mark_channels(); } - _anim_changed = false; + + cdata->_anim_changed = false; return any_changed; } @@ -353,31 +294,128 @@ force_update() { //////////////////////////////////////////////////////////////////// void PartBundle:: control_activated(AnimControl *control) { + nassertv(Thread::get_current_pipeline_stage() == 0); nassertv(control->get_part() == this); + CDReader cdata(_cycler); // If (and only if) our blend type is BT_single, which means no // blending, then starting an animation implicitly enables it. - if (get_blend_type() == BT_single) { - set_control_effect(control, 1.0f); + if (cdata->_blend_type == BT_single) { + CDWriter cdataw(_cycler, cdata); + do_set_control_effect(control, 1.0f, cdataw); } } +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::do_set_control_effect +// Access: Private +// Description: The private implementation of set_control_effect(). +//////////////////////////////////////////////////////////////////// +void PartBundle:: +do_set_control_effect(AnimControl *control, float effect, CData *cdata) { + nassertv(control->get_part() == this); + + if (effect == 0.0f) { + // An effect of zero means to eliminate the control. + ChannelBlend::iterator cbi = cdata->_blend.find(control); + if (cbi != cdata->_blend.end()) { + cdata->_blend.erase(cbi); + cdata->_anim_changed = true; + } + + } else { + // Otherwise we define it. + + // If we currently have BT_single, we only allow one AnimControl + // at a time. Stop all of the other AnimControls. + if (cdata->_blend_type == BT_single) { + clear_and_stop_intersecting(control, cdata); + } + + if (do_get_control_effect(control, cdata) != effect) { + cdata->_blend[control] = effect; + cdata->_anim_changed = true; + } + cdata->_last_control_set = control; + } + + recompute_net_blend(cdata); +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::do_get_control_effect +// Access: Private +// Description: The private implementation of get_control_effect(). +//////////////////////////////////////////////////////////////////// +float PartBundle:: +do_get_control_effect(AnimControl *control, const CData *cdata) const { + nassertr(control->get_part() == this, 0.0f); + + ChannelBlend::const_iterator cbi = cdata->_blend.find(control); + if (cbi == cdata->_blend.end()) { + // The control is not in effect. + return 0.0f; + } else { + return (*cbi).second; + } +} + + //////////////////////////////////////////////////////////////////// // Function: PartBundle::recompute_net_blend -// Access: Protected +// Access: Private // Description: Recomputes the total blending amount after a control // effect has been adjusted. This value must be kept // up-to-date so we can normalize the blending amounts. //////////////////////////////////////////////////////////////////// void PartBundle:: -recompute_net_blend() { - _net_blend = 0.0f; +recompute_net_blend(CData *cdata) { + cdata->_net_blend = 0.0f; ChannelBlend::const_iterator bti; - for (bti = _blend.begin(); bti != _blend.end(); ++bti) { - _net_blend += (*bti).second; + for (bti = cdata->_blend.begin(); bti != cdata->_blend.end(); ++bti) { + cdata->_net_blend += (*bti).second; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::clear_and_stop_intersecting +// Access: Private +// Description: Removes and stops all the currently activated +// AnimControls that animate some joints also animated +// by the indicated AnimControl. This is a special +// internal function that's only called when _blend_type +// is BT_single, to automatically stop all the other +// currently-executing animations. +//////////////////////////////////////////////////////////////////// +void PartBundle:: +clear_and_stop_intersecting(AnimControl *control, CData *cdata) { + double new_net_blend = 0.0f; + ChannelBlend new_blend; + bool any_changed = false; + + ChannelBlend::iterator cbi; + for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) { + AnimControl *ac = (*cbi).first; + if (ac == control || + !ac->get_bound_joints().has_bits_in_common(control->get_bound_joints())) { + // Save this control--it's either the target control, or it has + // no joints in common with the target control. + new_blend.insert(new_blend.end(), (*cbi)); + new_net_blend += (*cbi).second; + } else { + // Remove and stop this control. + ac->stop(); + any_changed = true; + } + } + + if (any_changed) { + cdata->_net_blend = new_net_blend; + cdata->_blend.swap(new_blend); + cdata->_anim_changed = true; } } @@ -423,40 +461,42 @@ register_with_read_factory() } //////////////////////////////////////////////////////////////////// -// Function: PartBundle::clear_and_stop_intersecting -// Access: Protected -// Description: Removes and stops all the currently activated -// AnimControls that animate some joints also animated -// by the indicated AnimControl. This is a special -// internal function that's only called when _blend_type -// is BT_single, to automatically stop all the other -// currently-executing animations. +// Function: PartBundle::CData::Constructor +// Access: Public +// Description: //////////////////////////////////////////////////////////////////// -void PartBundle:: -clear_and_stop_intersecting(AnimControl *control) { - double new_net_blend = 0.0f; - ChannelBlend new_blend; - bool any_changed = false; - - ChannelBlend::iterator cbi; - for (cbi = _blend.begin(); cbi != _blend.end(); ++cbi) { - AnimControl *ac = (*cbi).first; - if (ac == control || - !ac->get_bound_joints().has_bits_in_common(control->get_bound_joints())) { - // Save this control--it's either the target control, or it has - // no joints in common with the target control. - new_blend.insert(new_blend.end(), (*cbi)); - new_net_blend += (*cbi).second; - } else { - // Remove and stop this control. - ac->stop(); - any_changed = true; - } - } - - if (any_changed) { - _net_blend = new_net_blend; - _blend.swap(new_blend); - _anim_changed = true; - } +PartBundle::CData:: +CData() { + _blend_type = BT_single; + _last_control_set = NULL; + _net_blend = 0.0f; + _anim_changed = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::CData::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PartBundle::CData:: +CData(const PartBundle::CData ©) : + _blend_type(copy._blend_type), + _last_control_set(copy._last_control_set), + _blend(copy._blend), + _net_blend(copy._net_blend), + _anim_changed(copy._anim_changed) +{ + // Note that this copy constructor is not used by the PartBundle + // copy constructor! Any elements that must be copied between + // PartBundles should also be explicitly copied there. +} + +//////////////////////////////////////////////////////////////////// +// Function: PartBundle::CData::make_copy +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +CycleData *PartBundle::CData:: +make_copy() const { + return new CData(*this); } diff --git a/panda/src/chan/partBundle.h b/panda/src/chan/partBundle.h index f20761537c..829832ad3e 100644 --- a/panda/src/chan/partBundle.h +++ b/panda/src/chan/partBundle.h @@ -25,7 +25,6 @@ #include "animControl.h" #include "partSubset.h" #include "pointerTo.h" -#include "iterator_types.h" class AnimBundle; class PartBundleNode; @@ -44,9 +43,6 @@ public: // do_update() call to specify the channels that are in effect. typedef pmap ChannelBlend; - typedef first_of_pair_iterator control_iterator; - typedef ChannelBlend::size_type control_size_type; - protected: // The copy constructor is protected; use make_copy() or copy_subgraph(). PartBundle(const PartBundle ©); @@ -105,8 +101,8 @@ PUBLISHED: INLINE PartBundleNode *get_node() const; void clear_control_effects(); - void set_control_effect(AnimControl *control, float effect); - float get_control_effect(AnimControl *control); + INLINE void set_control_effect(AnimControl *control, float effect); + INLINE float get_control_effect(AnimControl *control) const; virtual void output(ostream &out) const; virtual void write(ostream &out, int indent_level) const; @@ -116,15 +112,6 @@ PUBLISHED: const PartSubset &subset = PartSubset()); public: - // The following functions may be used to traverse the set of - // controls applied to the PartBundle. Beware! These are not safe - // to use outside of PANDA.DLL. - INLINE control_iterator control_begin() const; - INLINE control_iterator control_end() const; - INLINE control_size_type control_size() const; - - INLINE const ChannelBlend &get_blend_map() const; - // The following functions aren't really part of the public // interface; they're just public so we don't have to declare a // bunch of friends. @@ -133,17 +120,37 @@ public: bool force_update(); virtual void control_activated(AnimControl *control); -protected: - void recompute_net_blend(); - void clear_and_stop_intersecting(AnimControl *control); +private: + class CData; + + void do_set_control_effect(AnimControl *control, float effect, CData *cdata); + float do_get_control_effect(AnimControl *control, const CData *cdata) const; + void recompute_net_blend(CData *cdata); + void clear_and_stop_intersecting(AnimControl *control, CData *cdata); - BlendType _blend_type; PartBundleNode *_node; - AnimControl *_last_control_set; - ChannelBlend _blend; - float _net_blend; - bool _anim_changed; + // This is the data that must be cycled between pipeline stages. + class CData : public CycleData { + public: + CData(); + CData(const CData ©); + + virtual CycleData *make_copy() const; + virtual TypeHandle get_parent_type() const { + return PartBundle::get_class_type(); + } + + BlendType _blend_type; + AnimControl *_last_control_set; + ChannelBlend _blend; + float _net_blend; + bool _anim_changed; + }; + + PipelineCycler _cycler; + typedef CycleDataReader CDReader; + typedef CycleDataWriter CDWriter; public: static void register_with_read_factory(); @@ -170,6 +177,9 @@ private: static TypeHandle _type_handle; friend class PartBundleNode; + friend class MovingPartBase; + friend class MovingPartMatrix; + friend class MovingPartScalar; }; inline ostream &operator <<(ostream &out, const PartBundle &bundle) {