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