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  */
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  */
110 bool MovingPartBase::
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  */
179 bool MovingPartBase::
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  */
343 void MovingPartBase::
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  */
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 }
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
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
virtual void write(std::ostream &out, int indent_level) const
Writes a brief description of the channel and all of its descendants.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
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 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.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 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_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
void clear_bit(int index)
Sets the nth bit off.
Definition: bitArray.I:133
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:39
Parent class for all animation channels.
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:33
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
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.
This class is used to define a subset of part names to apply to the PartBundle::bind_anim() operation...
Definition: partSubset.h:25
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.
void set_bit(int index)
Sets the nth bit on.
Definition: bitArray.I:115
A thread; that is, a lightweight process.
Definition: thread.h:46
This is the root of a MovingPart hierarchy.
Definition: partBundle.h:46
virtual bool clear_forced_channel()
Undoes the effect of a previous call to apply_freeze() or apply_control().
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
int get_channel_index() const
Returns the particular channel index associated with this AnimControl.
Definition: animControl.I:52
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
Controls the timing of a character animation.
Definition: animControl.h:38
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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 &me)
Function to write the important information in the particular object to a Datagram.
Definition: partGroup.cxx:629
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class for PartRoot and MovingPart.
Definition: partGroup.h:43