Panda3D
movingPartBase.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file movingPartBase.cxx
10  * @author drose
11  * @date 1999-02-22
12  */
13 
14 #include "movingPartBase.h"
15 #include "animControl.h"
16 #include "animChannelBase.h"
17 #include "bitArray.h"
18 #include "config_chan.h"
19 #include "dcast.h"
20 #include "indent.h"
21 
22 TypeHandle MovingPartBase::_type_handle;
23 
24 
25 /**
26  *
27  */
28 MovingPartBase::
29 MovingPartBase(PartGroup *parent, const std::string &name) :
30  PartGroup(parent, name),
31  _num_effective_channels(0),
32  _effective_control(nullptr)
33 {
34 }
35 
36 /**
37  *
38  */
39 MovingPartBase::
40 MovingPartBase() :
41  _num_effective_channels(0),
42  _effective_control(nullptr)
43 {
44 }
45 
46 /**
47  * Undoes the effect of a previous call to apply_freeze() or apply_control().
48  * Returns true if the joint was modified, false otherwise.
49  */
52  if (_forced_channel != nullptr) {
53  _forced_channel.clear();
54  return true;
55  }
56  return false;
57 }
58 
59 /**
60  * Returns the AnimChannelBase that has been forced to this joint by a
61  * previous call to apply_freeze() or apply_control(), or NULL if no such
62  * channel has been applied.
63  */
65 get_forced_channel() const {
66  return _forced_channel;
67 }
68 
69 /**
70  * Writes a brief description of the channel and all of its descendants.
71  */
73 write(std::ostream &out, int indent_level) const {
74  indent(out, indent_level) << get_value_type() << " " << get_name();
75  if (_children.empty()) {
76  out << "\n";
77  } else {
78  out << " {\n";
79  write_descendants(out, indent_level + 2);
80  indent(out, indent_level) << "}\n";
81  }
82 }
83 
84 /**
85  * Writes a brief description of the channel and all of its descendants, along
86  * with their values.
87  */
89 write_with_value(std::ostream &out, int indent_level) const {
90  indent(out, indent_level) << get_value_type() << " " << get_name() << "\n";
91  indent(out, indent_level);
92  output_value(out);
93 
94  if (_children.empty()) {
95  out << "\n";
96  } else {
97  out << " {\n";
98  write_descendants_with_value(out, indent_level + 2);
99  indent(out, indent_level) << "}\n";
100  }
101 }
102 
103 /**
104  * Recursively update this particular part and all of its descendents for the
105  * current frame. This is not really public and is not intended to be called
106  * directly; it is called from the top of the tree by PartBundle::update().
107  *
108  * The return value is true if any part has changed, false otherwise.
109  */
111 do_update(PartBundle *root, const CycleData *root_cdata, PartGroup *parent,
112  bool parent_changed, bool anim_changed,
113  Thread *current_thread) {
114  bool any_changed = false;
115  bool needs_update = anim_changed;
116 
117  // See if any of the channel values have changed since last time.
118 
119  if (!needs_update) {
120  if (_forced_channel != nullptr) {
121  needs_update = _forced_channel->has_changed(0, 0.0, 0, 0.0);
122 
123  } else if (_effective_control != nullptr) {
124  const PartBundle::CData *cdata = (const PartBundle::CData *)root_cdata;
125  needs_update = _effective_control->channel_has_changed(_effective_channel, cdata->_frame_blend_flag);
126 
127  } else {
128  const PartBundle::CData *cdata = (const PartBundle::CData *)root_cdata;
129  PartBundle::ChannelBlend::const_iterator bci;
130  for (bci = cdata->_blend.begin();
131  !needs_update && bci != cdata->_blend.end();
132  ++bci) {
133  AnimControl *control = (*bci).first;
134 
135  AnimChannelBase *channel = nullptr;
136  int channel_index = control->get_channel_index();
137  if (channel_index >= 0 && channel_index < (int)_channels.size()) {
138  channel = _channels[channel_index];
139  }
140  if (channel != nullptr) {
141  needs_update = control->channel_has_changed(channel, cdata->_frame_blend_flag);
142  }
143  }
144  }
145  }
146 
147  if (needs_update) {
148  // Ok, get the latest value.
149  get_blend_value(root);
150  }
151 
152  if (parent_changed || needs_update) {
153  any_changed = update_internals(root, parent, needs_update, parent_changed,
154  current_thread);
155  }
156 
157  // Now recurse.
158  Children::iterator ci;
159  for (ci = _children.begin(); ci != _children.end(); ++ci) {
160  if ((*ci)->do_update(root, root_cdata, this,
161  parent_changed || needs_update,
162  anim_changed, current_thread)) {
163  any_changed = true;
164  }
165  }
166 
167  return any_changed;
168 }
169 
170 
171 /**
172  * This is called by do_update() whenever the part or some ancestor has
173  * changed values. It is a hook for derived classes to update whatever cache
174  * they may have that depends on these.
175  *
176  * The return value is true if the part has changed as a result of the update,
177  * or false otherwise.
178  */
180 update_internals(PartBundle *, PartGroup *, bool, bool, Thread *) {
181  return true;
182 }
183 
184 /**
185  * Walks the part hierarchy, looking for a suitable channel index number to
186  * use. Available index numbers are the elements of the holes set, as well as
187  * next to infinity.
188  */
189 void MovingPartBase::
190 pick_channel_index(plist<int> &holes, int &next) const {
191  // Verify each of the holes.
192 
193  plist<int>::iterator ii, ii_next;
194  ii = holes.begin();
195  while (ii != holes.end()) {
196  ii_next = ii;
197  ++ii_next;
198 
199  int hole = (*ii);
200  nassertv(hole >= 0 && hole < next);
201  if (hole < (int)_channels.size() ||
202  _channels[hole] != nullptr) {
203  // We can't accept this hole; we're using it!
204  holes.erase(ii);
205  }
206  ii = ii_next;
207  }
208 
209  // Now do we have any more to restrict?
210  if (next < (int)_channels.size()) {
211  int i;
212  for (i = next; i < (int)_channels.size(); i++) {
213  if (_channels[i] == nullptr) {
214  // Here's a hole we do have.
215  holes.push_back(i);
216  }
217  }
218  next = _channels.size();
219  }
220 
221  PartGroup::pick_channel_index(holes, next);
222 }
223 
224 
225 
226 /**
227  * Binds the indicated anim hierarchy to the part hierarchy, at the given
228  * channel index number.
229  */
230 void MovingPartBase::
231 bind_hierarchy(AnimGroup *anim, int channel_index, int &joint_index,
232  bool is_included, BitArray &bound_joints,
233  const PartSubset &subset) {
234  if (subset.matches_include(get_name())) {
235  is_included = true;
236  } else if (subset.matches_exclude(get_name())) {
237  is_included = false;
238  }
239 
240  if (chan_cat.is_debug()) {
241  if (anim == nullptr) {
242  chan_cat.debug()
243  << "binding " << *this << " to NULL, is_included = "
244  << is_included << "\n";
245  } else {
246  chan_cat.debug()
247  << "binding " << *this << " to " << *anim << ", is_included = "
248  << is_included << "\n";
249  }
250  }
251  while ((int)_channels.size() <= channel_index) {
252  _channels.push_back(nullptr);
253  }
254 
255  nassertv(_channels[channel_index] == nullptr);
256 
257  if (is_included) {
258  if (anim == nullptr) {
259  // If we're binding to the NULL anim, it means actually to create a
260  // default AnimChannel that just returns the part's initial value.
261  _channels[channel_index] = make_default_channel();
262  } else {
263  _channels[channel_index] = DCAST(AnimChannelBase, anim);
264  }
265 
266  // Record that we have bound this joint in the bound_joints BitArray.
267  bound_joints.set_bit(joint_index);
268  } else {
269  // Record that we have *not* bound this particular joint.
270  bound_joints.clear_bit(joint_index);
271  }
272  ++joint_index;
273 
274  PartGroup::bind_hierarchy(anim, channel_index, joint_index,
275  is_included, bound_joints, subset);
276 }
277 
278 /**
279  * Similar to bind_hierarchy, but does not actually perform any binding. All
280  * it does is compute the BitArray bount_joints according to the specified
281  * subset. This is useful in preparation for asynchronous binding--in this
282  * case, we may need to know bound_joints immediately, without having to wait
283  * for the animation itself to load and bind.
284  */
285 void MovingPartBase::
286 find_bound_joints(int &joint_index, bool is_included, BitArray &bound_joints,
287  const PartSubset &subset) {
288  if (subset.matches_include(get_name())) {
289  is_included = true;
290  } else if (subset.matches_exclude(get_name())) {
291  is_included = false;
292  }
293 
294  bound_joints.set_bit_to(joint_index, is_included);
295  ++joint_index;
296 
297  PartGroup::find_bound_joints(joint_index, is_included, bound_joints, subset);
298 }
299 
300 /**
301  * Should be called whenever the ChannelBlend values have changed, this
302  * recursively updates the _effective_channel member in each part.
303  */
304 void MovingPartBase::
305 determine_effective_channels(const CycleData *root_cdata) {
306  _effective_control = nullptr;
307  _effective_channel = nullptr;
308  _num_effective_channels = 0;
309 
310  AnimControl *effective_control = nullptr;
311  AnimChannelBase *effective_channel = nullptr;
312  int num_effective_channels = 0;
313 
314  const PartBundle::CData *cdata = (const PartBundle::CData *)root_cdata;
315  PartBundle::ChannelBlend::const_iterator cbi;
316  for (cbi = cdata->_blend.begin();
317  cbi != cdata->_blend.end();
318  ++cbi) {
319  AnimControl *control = (*cbi).first;
320  int channel_index = control->get_channel_index();
321  if (channel_index >= 0 && channel_index < (int)_channels.size()) {
322  if (_channels[channel_index] != nullptr) {
323  effective_control = control;
324  effective_channel = _channels[channel_index];
325  ++num_effective_channels;
326  }
327  }
328  }
329 
330  _num_effective_channels = num_effective_channels;
331  if (num_effective_channels == 1) {
332  _effective_control = effective_control;
333  _effective_channel = effective_channel;
334  }
335 
337 }
338 
339 /**
340  * Writes the contents of this object to the datagram for shipping out to a
341  * Bam file.
342  */
344 write_datagram(BamWriter *manager, Datagram &dg) {
345  PartGroup::write_datagram(manager, dg);
346 
347  manager->write_pointer(dg, _forced_channel);
348 }
349 
350 /**
351  * Receives an array of pointers, one for each time manager->read_pointer()
352  * was called in fillin(). Returns the number of pointers processed.
353  *
354  * This is the callback function that is made by the BamReader at some later
355  * point, after all of the required pointers have been filled in. It is
356  * necessary because there might be forward references in a bam file; when we
357  * call read_pointer() in fillin(), the object may not have been read from the
358  * file yet, so we do not have a pointer available at that time. Thus,
359  * instead of returning a pointer, read_pointer() simply reserves a later
360  * callback. This function provides that callback. The calling object is
361  * responsible for keeping track of the number of times it called
362  * read_pointer() and extracting the same number of pointers out of the
363  * supplied vector, and storing them appropriately within the object.
364  */
366 complete_pointers(TypedWritable **p_list, BamReader *manager) {
367  int pi = PartGroup::complete_pointers(p_list, manager);
368 
369  if (manager->get_file_minor_ver() >= 20) {
370  _forced_channel = DCAST(AnimChannelBase, p_list[pi++]);
371  }
372 
373  return pi;
374 }
375 
376 /**
377  * This internal function is called by make_from_bam to read in all of the
378  * relevant data from the BamFile for the new MovingPartBase.
379  */
380 void MovingPartBase::
381 fillin(DatagramIterator &scan, BamReader *manager) {
382  PartGroup::fillin(scan, manager);
383 
384  if (manager->get_file_minor_ver() >= 20) {
385  manager->read_pointer(scan);
386  }
387 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Parent class for all animation channels.
Controls the timing of a character animation.
Definition: animControl.h:38
bool channel_has_changed(AnimChannelBase *channel, bool frame_blend_flag) const
Returns true if the indicated channel value has changed since the last call to mark_channels().
int get_channel_index() const
Returns the particular channel index associated with this AnimControl.
Definition: animControl.I:52
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:33
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:40
void clear_bit(int index)
Sets the nth bit off.
Definition: bitArray.I:133
void set_bit_to(int index, bool value)
Sets the nth bit either on or off, according to the indicated bool value.
Definition: bitArray.I:150
void set_bit(int index)
Sets the nth bit on.
Definition: bitArray.I:115
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
virtual TypeHandle get_value_type() const =0
Returns the TypeHandle associated with the ValueType we are concerned with.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
virtual bool clear_forced_channel()
Undoes the effect of a previous call to apply_freeze() or apply_control().
virtual bool update_internals(PartBundle *root, PartGroup *parent, bool self_changed, bool parent_changed, Thread *current_thread)
This is called by do_update() whenever the part or some ancestor has changed values.
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
virtual void write(std::ostream &out, int indent_level) const
Writes a brief description of the channel and all of its descendants.
virtual void write_with_value(std::ostream &out, int indent_level) const
Writes a brief description of the channel and all of its descendants, along with their values.
virtual AnimChannelBase * get_forced_channel() const
Returns the AnimChannelBase that has been forced to this joint by a previous call to apply_freeze() o...
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.
This is the root of a MovingPart hierarchy.
Definition: partBundle.h:46
This is the base class for PartRoot and MovingPart.
Definition: partGroup.h:43
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
Definition: partGroup.cxx:629
virtual void determine_effective_channels(const CycleData *root_cdata)
Should be called whenever the ChannelBlend values have changed, this recursively updates the _effecti...
Definition: partGroup.cxx:503
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...
Definition: partGroup.cxx:666
This class is used to define a subset of part names to apply to the PartBundle::bind_anim() operation...
Definition: partSubset.h:25
bool matches_exclude(const std::string &joint_name) const
Returns true if the indicated name matches a name on the exclude list, false otherwise.
Definition: partSubset.cxx:140
bool matches_include(const std::string &joint_name) const
Returns true if the indicated name matches a name on the include list, false otherwise.
Definition: partSubset.cxx:123
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.