Panda3D
 All Classes Functions Variables Enumerations
animBundleMaker.cxx
1 // Filename: animBundleMaker.cxx
2 // Created by: drose (22Feb99)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "animBundleMaker.h"
16 #include "config_egg2pg.h"
17 
18 #include "eggTable.h"
19 #include "eggAnimData.h"
20 #include "eggSAnimData.h"
21 #include "eggXfmAnimData.h"
22 #include "eggXfmSAnim.h"
23 #include "eggGroupNode.h"
24 #include "animBundle.h"
25 #include "animBundleNode.h"
26 #include "animChannelMatrixXfmTable.h"
27 #include "animChannelScalarTable.h"
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: AnimBundleMaker::Construtor
31 // Access: Public
32 // Description:
33 ////////////////////////////////////////////////////////////////////
34 AnimBundleMaker::
35 AnimBundleMaker(EggTable *root) : _root(root) {
36  _fps = 0.0f;
37  _num_frames = 1;
38 
39  _ok_fps = true;
40  _ok_num_frames = true;
41 
42  inspect_tree(root);
43 
44  if (!_ok_fps) {
45  egg2pg_cat.warning()
46  << "AnimBundle " << _root->get_name()
47  << " specifies contradictory frame rates.\n";
48  } else if (_fps == 0.0f) {
49  egg2pg_cat.warning()
50  << "AnimBundle " << _root->get_name()
51  << " does not specify a frame rate.\n";
52  _fps = 24.0f;
53  }
54 
55  if (!_ok_num_frames) {
56  egg2pg_cat.warning()
57  << "AnimBundle " << _root->get_name()
58  << " specifies contradictory number of frames.\n";
59  }
60 }
61 
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: AnimBundleMaker::make_node
65 // Access: Public
66 // Description:
67 ////////////////////////////////////////////////////////////////////
68 AnimBundleNode *AnimBundleMaker::
69 make_node() {
70  return new AnimBundleNode(_root->get_name(), make_bundle());
71 }
72 
73 ////////////////////////////////////////////////////////////////////
74 // Function: AnimBundleMaker::make_bundle
75 // Access: Private
76 // Description:
77 ////////////////////////////////////////////////////////////////////
78 AnimBundle *AnimBundleMaker::
79 make_bundle() {
80  AnimBundle *bundle = new AnimBundle(_root->get_name(), _fps, _num_frames);
81 
82  EggTable::const_iterator ci;
83  for (ci = _root->begin(); ci != _root->end(); ++ci) {
84  if ((*ci)->is_of_type(EggTable::get_class_type())) {
85  EggTable *child = DCAST(EggTable, *ci);
86  build_hierarchy(child, bundle);
87  }
88  }
89 
90  bundle->sort_descendants();
91 
92  return bundle;
93 }
94 
95 
96 ////////////////////////////////////////////////////////////////////
97 // Function: AnimBundleMaker::inspect_tree
98 // Access: Private
99 // Description: Walks the egg tree, getting out the fps and the
100 // number of frames.
101 ////////////////////////////////////////////////////////////////////
102 void AnimBundleMaker::
103 inspect_tree(EggNode *egg_node) {
104  if (egg_node->is_of_type(EggAnimData::get_class_type())) {
105  // Check frame rate.
106  EggAnimData *egg_anim = DCAST(EggAnimData, egg_node);
107  if (egg_anim->has_fps()) {
108  if (_fps == 0.0f) {
109  _fps = egg_anim->get_fps();
110  } else if (_fps != egg_anim->get_fps()) {
111  // Whoops! This table differs in opinion from the other tables.
112  _fps = min(_fps, (PN_stdfloat)egg_anim->get_fps());
113  _ok_fps = false;
114  }
115  }
116  }
117 
118  if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) {
119  // Check frame rate.
120  EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node);
121  if (egg_anim->has_fps()) {
122  if (_fps == 0.0f) {
123  _fps = egg_anim->get_fps();
124  } else if (_fps != egg_anim->get_fps()) {
125  // Whoops! This table differs in opinion from the other tables.
126  _fps = min(_fps, (PN_stdfloat)egg_anim->get_fps());
127  _ok_fps = false;
128  }
129  }
130  }
131 
132  if (egg_node->is_of_type(EggSAnimData::get_class_type())) {
133  // Check number of frames.
134  EggSAnimData *egg_anim = DCAST(EggSAnimData, egg_node);
135  int num_frames = egg_anim->get_num_rows();
136 
137  if (num_frames > 1) {
138  if (_num_frames == 1) {
139  _num_frames = num_frames;
140  } else if (_num_frames != num_frames) {
141  // Whoops! Another disagreement.
142  _num_frames = min(_num_frames, num_frames);
143  _ok_num_frames = false;
144  }
145  }
146  }
147 
148  if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) {
149  // Check number of frames.
150  EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node);
151  int num_frames = egg_anim->get_num_rows();
152 
153  if (num_frames > 1) {
154  if (_num_frames == 1) {
155  _num_frames = num_frames;
156  } else if (_num_frames != num_frames) {
157  // Whoops! Another disagreement.
158  _num_frames = min(_num_frames, num_frames);
159  _ok_num_frames = false;
160  }
161  }
162  }
163 
164  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
165  // Now recurse.
166  EggGroupNode *group = DCAST(EggGroupNode, egg_node);
167  EggGroupNode::const_iterator ci;
168  for (ci = group->begin(); ci != group->end(); ++ci) {
169  inspect_tree(*ci);
170  }
171  }
172 }
173 
174 
175 ////////////////////////////////////////////////////////////////////
176 // Function: AnimBundleMaker::build_hierarchy
177 // Access: Private
178 // Description: Walks the egg tree again, creating the AnimChannels
179 // as appropriate.
180 ////////////////////////////////////////////////////////////////////
181 void AnimBundleMaker::
182 build_hierarchy(EggTable *egg_table, AnimGroup *parent) {
183  AnimGroup *this_node = NULL;
184 
185  // First, scan the children of egg_table for anim data tables. If
186  // any of them is named "xform", it's a special case--this one
187  // stands for the egg_table node itself. Don't ask me why.
188 
189  EggTable::const_iterator ci;
190  for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) {
191  if ((*ci)->get_name() == "xform") {
192  if (this_node == NULL) {
193  this_node = create_xfm_channel((*ci), egg_table->get_name(), parent);
194  } else {
195  egg2pg_cat.warning()
196  << "Duplicate xform table under node "
197  << egg_table->get_name() << "\n";
198  }
199  }
200  }
201 
202  // If none of them were named "xform", just create a plain old
203  // AnimGroup.
204  if (this_node == NULL) {
205  this_node = new AnimGroup(parent, egg_table->get_name());
206  }
207 
208  // Now walk the children again, creating any leftover tables, and
209  // recursing.
210  for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) {
211  if ((*ci)->get_name() == "xform") {
212  // Skip this one. We already got it.
213  } else if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
214  EggSAnimData *egg_anim = DCAST(EggSAnimData, *ci);
215  create_s_channel(egg_anim, egg_anim->get_name(), this_node);
216 
217  } else if ((*ci)->is_of_type(EggTable::get_class_type())) {
218  EggTable *child = DCAST(EggTable, *ci);
219  build_hierarchy(child, this_node);
220  }
221  }
222 }
223 
224 
225 ////////////////////////////////////////////////////////////////////
226 // Function: AnimBundleMaker::create_s_channel
227 // Access: Private
228 // Description: Creates an AnimChannelScalarTable corresponding to
229 // the given EggSAnimData structure.
230 ////////////////////////////////////////////////////////////////////
231 AnimChannelScalarTable *AnimBundleMaker::
232 create_s_channel(EggSAnimData *egg_anim, const string &name,
233  AnimGroup *parent) {
235  = new AnimChannelScalarTable(parent, name);
236 
237  // First we have to copy the table data from PTA_double to
238  // PTA_stdfloat.
239  PTA_stdfloat new_data = PTA_stdfloat::empty_array(egg_anim->get_num_rows(),
240  table->get_class_type());
241  for (int i = 0; i < egg_anim->get_num_rows(); i++) {
242  new_data[i] = (PN_stdfloat)egg_anim->get_value(i);
243  }
244 
245  // Now we can assign the table.
246  table->set_table(new_data);
247 
248  return table;
249 }
250 
251 
252 ////////////////////////////////////////////////////////////////////
253 // Function: AnimBundleMaker::create_xfm_channel (EggNode)
254 // Access: Private
255 // Description: Creates an AnimChannelMatrixXfmTable corresponding to
256 // the given EggNode structure, if possible.
257 ////////////////////////////////////////////////////////////////////
258 AnimChannelMatrixXfmTable *AnimBundleMaker::
259 create_xfm_channel(EggNode *egg_node, const string &name,
260  AnimGroup *parent) {
261  if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) {
262  EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node);
263  EggXfmSAnim new_anim(*egg_anim);
264  return create_xfm_channel(&new_anim, name, parent);
265 
266  } else if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) {
267  EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node);
268  return create_xfm_channel(egg_anim, name, parent);
269  }
270 
271  egg2pg_cat.warning()
272  << "Inappropriate node named xform under node "
273  << name << "\n";
274  return NULL;
275 }
276 
277 
278 ////////////////////////////////////////////////////////////////////
279 // Function: AnimBundleMaker::create_xfm_channel (EggXfmSAnim)
280 // Access: Private
281 // Description: Creates an AnimChannelMatrixXfmTable corresponding to
282 // the given EggXfmSAnim structure.
283 ////////////////////////////////////////////////////////////////////
284 AnimChannelMatrixXfmTable *AnimBundleMaker::
285 create_xfm_channel(EggXfmSAnim *egg_anim, const string &name,
286  AnimGroup *parent) {
287  // Ensure that the anim table is optimal and that it is standard
288  // order.
289  egg_anim->optimize_to_standard_order();
290 
292  = new AnimChannelMatrixXfmTable(parent, name);
293 
294  // The EggXfmSAnim structure has a number of children which are
295  // EggSAnimData tables. Each of these represents a separate
296  // component of the transform data, and will be added to the table.
297 
298  EggXfmSAnim::const_iterator ci;
299  for (ci = egg_anim->begin(); ci != egg_anim->end(); ++ci) {
300  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
301  EggSAnimData *child = DCAST(EggSAnimData, *ci);
302 
303  if (child->get_name().empty()) {
304  egg2pg_cat.warning()
305  << "Unnamed subtable of <Xfm$Anim_S$> " << name
306  << "\n";
307  } else {
308  char table_id = child->get_name()[0];
309 
310  if (child->get_name().length() > 1 ||
311  !table->is_valid_id(table_id)) {
312  egg2pg_cat.warning()
313  << "Unexpected table name " << child->get_name()
314  << ", child of " << name << "\n";
315 
316  } else if (table->has_table(table_id)) {
317  egg2pg_cat.warning()
318  << "Duplicate table definition for " << table_id
319  << " under " << name << "\n";
320 
321  } else {
322 
323  // Now we have to copy the table data from PTA_double to
324  // PTA_stdfloat.
325  PTA_stdfloat new_data=PTA_stdfloat::empty_array(child->get_num_rows(),
326  table->get_class_type());
327  for (int i = 0; i < child->get_num_rows(); i++) {
328  new_data[i] = (PN_stdfloat)child->get_value(i);
329  }
330 
331  // Now we can assign the table.
332  table->set_table(table_id, new_data);
333  }
334  }
335  }
336  }
337 
338  return table;
339 }
bool has_table(char table_id) const
Returns true if the indicated subtable has been assigned.
This is the root of an AnimChannel hierarchy.
Definition: animBundle.h:31
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
An animation channel that issues a scalar each frame, read from a table such as might have been read ...
static bool is_valid_id(char table_id)
Returns true if the given letter is one of the nine valid table id&#39;s.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
Corresponding to an &lt;S$Anim&gt; entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:28
void sort_descendants()
Sorts the children nodes at each level of the hierarchy into alphabetical order.
Definition: animGroup.cxx:185
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggAnimData.I:94
void set_table(const CPTA_stdfloat &table)
Assigns the data table.
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:36
This corresponds to an &lt;Xfm$Anim_S$&gt; entry, which is a collection of up to nine &lt;S$Anim&gt; entries that...
Definition: eggXfmSAnim.h:33
A base class for EggSAnimData and EggXfmAnimData, which contain rows and columns of numbers...
Definition: eggAnimData.h:32
Corresponding to an &lt;Xfm$Anim&gt; entry, this stores a two-dimensional table with up to nine columns...
void optimize_to_standard_order()
Optimizes the table by collapsing redundant sub-tables, and simultaneously ensures that the order str...
void set_table(char table_id, const CPTA_stdfloat &table)
Assigns the indicated table.
int get_num_rows() const
Returns the number of rows in the table.
Definition: eggSAnimData.I:56
This corresponds to a.
Definition: eggTable.h:31
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggXfmSAnim.I:98
An animation channel that issues a matrix each frame, read from a table such as might have been read ...
This is a node that contains a pointer to an AnimBundle.
int get_num_rows() const
Returns the number of rows in the table.
double get_value(int row) const
Returns the value at the indicated row.
Definition: eggSAnimData.I:68