16 #include "partBundle.h"
17 #include "animBundle.h"
18 #include "animBundleNode.h"
19 #include "animControl.h"
21 #include "animPreloadTable.h"
22 #include "config_chan.h"
24 #include "string_utils.h"
27 #include "datagramIterator.h"
28 #include "bamReader.h"
29 #include "bamWriter.h"
30 #include "configVariableEnum.h"
31 #include "loaderOptions.h"
32 #include "bindAnimRequest.h"
40 (
"anim-blend-type", PartBundle::BT_normalized_linear,
41 PRC_DESC(
"The default blend type to use for blending animations between "
42 "frames, or between multiple animations. See interpolate-frames, "
43 "and also PartBundle::set_anim_blend_flag() and "
44 "PartBundle::set_frame_blend_flag()."));
57 _anim_preload = copy._anim_preload;
60 CDWriter cdata(_cycler,
true);
61 CDReader cdata_from(copy._cycler);
62 cdata->_blend_type = cdata_from->_blend_type;
63 cdata->_anim_blend_flag = cdata_from->_anim_blend_flag;
64 cdata->_frame_blend_flag = cdata_from->_frame_blend_flag;
65 cdata->_root_xform = cdata_from->_root_xform;
76 PartBundle(
const string &name) :
102 _anim_preload == other->_anim_preload) {
109 _anim_preload = other->_anim_preload;
115 anim_preload->add_anims_from(other->_anim_preload.get_read_pointer());
139 if (cdata->_anim_blend_flag != anim_blend_flag) {
141 cdataw->_anim_blend_flag = anim_blend_flag;
143 if (!anim_blend_flag && cdataw->_blend.size() > 1) {
148 nassertv(cdataw->_last_control_set != NULL);
149 clear_and_stop_intersecting(cdataw->_last_control_set, cdataw);
152 cdataw->_anim_changed =
true;
165 apply_transform(const TransformState *transform) {
166 if (transform->is_identity()) {
171 AppliedTransforms::iterator ati = _applied_transforms.find(transform);
172 if (ati != _applied_transforms.end()) {
173 if ((*ati).first.is_valid_pointer() &&
174 (*ati).second.is_valid_pointer()) {
176 return (*ati).second.p();
181 new_bundle->xform(transform->get_mat());
183 if (ati != _applied_transforms.end()) {
185 (*ati).first.refresh();
186 (*ati).second = new_bundle;
189 bool inserted = _applied_transforms.insert(AppliedTransforms::value_type(transform, new_bundle)).second;
190 nassertr(inserted, new_bundle);
195 new_bundle->force_update();
220 if (!cdata->_blend.empty()) {
222 cdataw->_blend.clear();
223 cdataw->_net_blend = 0.0f;
224 cdataw->_anim_changed =
true;
236 out << get_type() <<
" " << get_name();
246 write(ostream &out,
int indent_level)
const {
247 indent(out, indent_level)
248 << get_type() <<
" " << get_name() <<
" {\n";
249 write_descendants(out, indent_level + 2);
250 indent(out, indent_level) <<
"}\n";
278 bind_anim(
AnimBundle *anim,
int hierarchy_match_flags,
281 if (do_bind_anim(control, anim, hierarchy_match_flags, subset)) {
319 int hierarchy_match_flags, const
PartSubset &subset,
321 nassertr(loader != (
Loader *)NULL, NULL);
324 LoaderOptions::LF_report_errors |
325 LoaderOptions::LF_convert_anim);
326 string basename = filename.get_basename_wo_extension();
331 anim_index = anim_preload->find_anim(basename);
339 PT(
PandaNode) model = loader->load_sync(filename, anim_options);
349 PT(
AnimControl) control = bind_anim(anim, hierarchy_match_flags, subset);
354 control->set_anim_model(model);
360 PN_stdfloat frame_rate = anim_preload->get_base_frame_rate(anim_index);
361 int num_frames = anim_preload->get_num_frames(anim_index);
363 new
AnimControl(basename, this, frame_rate, num_frames);
365 if (!subset.is_include_empty()) {
369 find_bound_joints(joint_index,
false, bound_joints, subset);
370 control->set_bound_joints(bound_joints);
375 filename, anim_options, loader, control,
376 hierarchy_match_flags, subset);
377 request->set_priority(async_bind_priority);
378 loader->load_async(request);
393 ChannelBlend::const_iterator cbi;
394 for (cbi = cdata->_blend.begin();
395 cbi != cdata->_blend.end();
398 PN_stdfloat effect = (*cbi).second;
399 if (effect != 0.0f) {
419 freeze_joint(
const string &joint_name,
const TransformState *transform) {
426 cdata->_anim_changed =
true;
452 cdata->_anim_changed =
true;
478 cdata->_anim_changed =
true;
504 cdata->_anim_changed =
true;
529 cdata->_anim_changed =
true;
547 CDWriter cdata(_cycler,
false, current_thread);
548 bool any_changed =
false;
551 if (now > cdata->_last_update + _update_delay || cdata->_anim_changed) {
552 bool anim_changed = cdata->_anim_changed;
553 bool frame_blend_flag = cdata->_frame_blend_flag;
555 any_changed =
do_update(
this, cdata, NULL,
false, anim_changed,
559 ChannelBlend::const_iterator cbi;
560 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
565 cdata->_anim_changed =
false;
566 cdata->_last_update = now;
582 CDWriter cdata(_cycler,
false, current_thread);
583 bool any_changed =
do_update(
this, cdata, NULL,
true,
true, current_thread);
586 ChannelBlend::const_iterator cbi;
587 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
592 cdata->_anim_changed =
false;
608 nassertv(control->
get_part() ==
this);
614 if (!cdata->_anim_blend_flag) {
616 do_set_control_effect(control, 1.0f, cdataw);
630 int hierarchy_match_flags,
const PartSubset &subset) {
637 if ((hierarchy_match_flags & HMF_ok_wrong_root_name) == 0) {
639 if (get_name() != ptanim->get_name()) {
640 if (chan_cat.is_error()) {
642 <<
"Root name of part (" << get_name()
643 <<
") does not match that of anim (" << ptanim->get_name()
655 int channel_index = 0;
656 pick_channel_index(holes, channel_index);
658 if (!holes.empty()) {
659 channel_index = holes.front();
667 bind_hierarchy(ptanim, channel_index, joint_index,
669 control->
setup_anim(
this, anim, channel_index, bound_joints);
687 nassertv(find(_nodes.begin(), _nodes.end(), node) == _nodes.end());
688 _nodes.push_back(node);
702 Nodes::iterator ni = find(_nodes.begin(), _nodes.end(), node);
703 nassertv(ni != _nodes.end());
713 do_set_control_effect(
AnimControl *control, PN_stdfloat effect, CData *cdata) {
714 nassertv(control->
get_part() ==
this);
716 if (effect == 0.0f) {
718 ChannelBlend::iterator cbi = cdata->_blend.find(control);
719 if (cbi != cdata->_blend.end()) {
720 cdata->_blend.erase(cbi);
721 cdata->_anim_changed =
true;
729 if (!cdata->_anim_blend_flag) {
730 clear_and_stop_intersecting(control, cdata);
733 if (do_get_control_effect(control, cdata) != effect) {
734 cdata->_blend[control] = effect;
735 cdata->_anim_changed =
true;
737 cdata->_last_control_set = control;
740 recompute_net_blend(cdata);
748 PN_stdfloat PartBundle::
749 do_get_control_effect(
AnimControl *control,
const CData *cdata)
const {
750 nassertr(control->
get_part() ==
this, 0.0f);
752 ChannelBlend::const_iterator cbi = cdata->_blend.find(control);
753 if (cbi == cdata->_blend.end()) {
757 return (*cbi).second;
770 recompute_net_blend(CData *cdata) {
771 cdata->_net_blend = 0.0f;
773 ChannelBlend::const_iterator bti;
774 for (bti = cdata->_blend.begin(); bti != cdata->_blend.end(); ++bti) {
775 cdata->_net_blend += (*bti).second;
791 clear_and_stop_intersecting(
AnimControl *control, CData *cdata) {
792 double new_net_blend = 0.0f;
793 ChannelBlend new_blend;
794 bool any_changed =
false;
796 ChannelBlend::iterator cbi;
797 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
803 new_blend.insert(new_blend.end(), (*cbi));
804 new_net_blend += (*cbi).second;
813 cdata->_net_blend = new_net_blend;
814 cdata->_blend.swap(new_blend);
815 cdata->_anim_changed =
true;
831 do_update(
this, cdata, NULL,
true,
true, current_thread);
843 manager->
write_pointer(dg, _anim_preload.get_read_pointer());
872 PartBundle *me =
new PartBundle;
876 parse_params(params, scan, manager);
877 me->fillin(scan, manager);
891 PartGroup::fillin(scan, manager);
921 _blend_type = anim_blend_type;
922 _anim_blend_flag =
false;
923 _frame_blend_flag = interpolate_frames;
925 _last_control_set = NULL;
927 _anim_changed =
false;
937 CData(
const PartBundle::CData ©) :
938 _blend_type(copy._blend_type),
939 _anim_blend_flag(copy._anim_blend_flag),
940 _frame_blend_flag(copy._frame_blend_flag),
941 _root_xform(copy._root_xform),
942 _last_control_set(copy._last_control_set),
944 _net_blend(copy._net_blend),
945 _anim_changed(copy._anim_changed),
946 _last_update(copy._last_update)
960 return new CData(*
this);
969 void PartBundle::CData::
974 _root_xform.write_datagram(dg);
986 void PartBundle::CData::
988 _blend_type = (BlendType)scan.
get_uint8();
990 _frame_blend_flag = scan.
get_bool();
991 _root_xform.read_datagram(scan);
999 operator << (ostream &out, PartBundle::BlendType blend_type) {
1000 switch (blend_type) {
1001 case PartBundle::BT_linear:
1002 return out <<
"linear";
1004 case PartBundle::BT_normalized_linear:
1005 return out <<
"normalized_linear";
1007 case PartBundle::BT_componentwise:
1008 return out <<
"componentwise";
1010 case PartBundle::BT_componentwise_quat:
1011 return out <<
"componentwise_quat";
1015 <<
"Invalid BlendType value: " << (int)blend_type <<
"\n";
1016 nassertr(
false, out);
1025 operator >> (istream &in, PartBundle::BlendType &blend_type) {
1029 if (cmp_nocase_uh(word,
"linear") == 0) {
1030 blend_type = PartBundle::BT_linear;
1032 }
else if (cmp_nocase_uh(word,
"normalized_linear") == 0) {
1033 blend_type = PartBundle::BT_normalized_linear;
1035 }
else if (cmp_nocase_uh(word,
"componentwise") == 0) {
1036 blend_type = PartBundle::BT_componentwise;
1038 }
else if (cmp_nocase_uh(word,
"componentwise_quat") == 0) {
1039 blend_type = PartBundle::BT_componentwise_quat;
1043 <<
"Invalid BlendType string: " << word <<
"\n";
1044 blend_type = PartBundle::BT_linear;
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
static const LMatrix4f & ident_mat()
Returns an identity matrix.
A basic node of the scene graph or data graph.
void add_uint8(PN_uint8 value)
Adds an unsigned 8-bit integer to the datagram.
This is the base class for all three-component vectors and points.
This table records data about a list of animations for a particular model, such as number of frames a...
bool get_bool()
Extracts a boolean value.
bool freeze_joint(const string &joint_name, const TransformState *transform)
Specifies that the joint with the indicated name should be frozen with the specified transform...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
static BitArray all_on()
Returns a BitArray with an infinite array of bits, all on.
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
virtual bool do_update(PartBundle *root, const CycleData *root_cdata, PartGroup *parent, bool parent_changed, bool anim_changed, Thread *current_thread)
Recursively update this particular part and all of its descendents for the current frame...
Specifies parameters that may be passed to the loader.
This is the root of an AnimChannel hierarchy.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
bool control_joint(const string &joint_name, PandaNode *node)
Specifies that the joint with the indicated name should be animated with the transform on the indicat...
A single page of data maintained by a PipelineCycler.
Base class for objects that can be written to and read from Bam files.
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
virtual void write(ostream &out, int indent_level) const
Writes a brief description of the bundle and all of its descendants.
A convenient class for loading models from disk, in bam or egg format (or any of a number of other fo...
bool release_joint(const string &joint_name)
Releases the named joint from the effects of a previous call to freeze_joint() or control_joint()...
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
void stop()
Stops a currently playing or looping animation right where it is.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
static void register_with_read_factory()
Factory method to generate a PartBundle object.
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
bool has_bits_in_common(const BitArray &other) const
Returns true if this BitArray has any "one" bits in common with the other one, false otherwise...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
bool is_include_empty() const
Returns true if the include list is completely empty, false otherwise.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
bool force_update()
Updates all the parts in the bundle to reflect the data for the current frame, whether we believe it ...
void clear_control_effects()
Sets the control effect of all AnimControls to zero (but does not "stop" the AnimControls).
A dynamic array with an unlimited number of bits.
static bool is_threading_supported()
Returns true if threading support has been compiled in and enabled, or false if no threading is avail...
static AnimBundle * find_anim_bundle(PandaNode *root)
Recursively walks the scene graph beginning at the indicated node (which need not be an AnimBundleNod...
void add_bool(bool value)
Adds a boolean value to the datagram.
The name of a file, such as a texture file or an Egg file.
virtual void determine_effective_channels(const CycleData *root_cdata)
Should be called whenever the ChannelBlend values have changed, this recursively updates the _effecti...
This is a node that contains a pointer to an PartBundle.
double get_frame_time(Thread *current_thread=Thread::get_current_thread()) const
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
virtual bool apply_freeze_matrix(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale)
Freezes this particular joint so that it will always hold the specified transform.
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
bool apply_freeze(const TransformState *transform)
Freezes this particular joint so that it will always hold the specified transform.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
This class specializes ConfigVariable as an enumerated type.
bool do_bind_anim(AnimControl *control, AnimBundle *anim, int hierarchy_match_flags, const PartSubset &subset)
The internal implementation of bind_anim(), this receives a pointer to an uninitialized AnimControl a...
virtual bool apply_control(PandaNode *node)
Specifies a node to influence this particular joint so that it will always hold the node's transform...
virtual void output(ostream &out) const
Writes a one-line description of the bundle.
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
void setup_anim(PartBundle *part, AnimBundle *anim, int channel_index, const BitArray &bound_joints)
This can only be called once for a given AnimControl.
const BitArray & get_bound_joints() const
Returns the subset of joints controlled by this AnimControl.
void wait_pending()
Blocks the current thread until the AnimControl has finished loading and is fully bound...
bool check_hierarchy(const AnimGroup *anim, const PartGroup *parent, int hierarchy_match_flags=0) const
Walks the part hierarchy in tandem with the indicated anim hierarchy, and returns true if the hierarc...
virtual PartGroup * make_copy() const
Allocates and returns a new copy of the node.
This class is used to define a subset of part names to apply to the PartBundle::bind_anim() operation...
This class object manages an asynchronous load-and-bind animation request, as issued through PartBund...
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
virtual void control_activated(AnimControl *control)
Called by the AnimControl whenever it starts an animation.
void set_anim_blend_flag(bool anim_blend_flag)
Defines the way the character responds to multiple calls to set_control_effect()).
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Takes in a vector of pointers to TypedWritable objects that correspond to all the requests for pointe...
A thread; that is, a lightweight process.
This is the root of a MovingPart hierarchy.
virtual bool clear_forced_channel()
Undoes the effect of a previous call to apply_freeze() or apply_control().
bool update()
Updates all the parts in the bundle to reflect the data for the current frame (as set in each of the ...
PartBundle * get_part() const
Returns the PartBundle bound in with this AnimControl.
Controls the timing of a character animation.
void mark_channels(bool frame_blend_flag)
Marks this point as the point of reference for the next call to channel_has_changed().
A class to retrieve the individual data elements previously stored in a Datagram. ...
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
TypeHandle is the identifier used to differentiate C++ class types.
void merge_anim_preloads(const PartBundle *other)
Copies the contents of the other PartBundle's preload table into this one.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Takes in a vector of pointers to TypedWritable objects that correspond to all the requests for pointe...
static int get_current_pipeline_stage()
Returns the integer pipeline stage associated with the current thread.
virtual bool apply_freeze_scalar(PN_stdfloat value)
Freezes this particular joint so that it will always hold the specified transform.
PartGroup * find_child(const string &name) const
Returns the first descendant found with the indicated name, or NULL if no such descendant exists...
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
void read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
This is the base class for PartRoot and MovingPart.