animBundleMaker.cxx
00001 // Filename: animBundleMaker.cxx
00002 // Created by:  drose (22Feb99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "animBundleMaker.h"
00016 #include "config_egg2pg.h"
00017 
00018 #include "eggTable.h"
00019 #include "eggAnimData.h"
00020 #include "eggSAnimData.h"
00021 #include "eggXfmAnimData.h"
00022 #include "eggXfmSAnim.h"
00023 #include "eggGroupNode.h"
00024 #include "animBundle.h"
00025 #include "animBundleNode.h"
00026 #include "animChannelMatrixXfmTable.h"
00027 #include "animChannelScalarTable.h"
00028 
00029 ////////////////////////////////////////////////////////////////////
00030 //     Function: AnimBundleMaker::Construtor
00031 //       Access: Public
00032 //  Description:
00033 ////////////////////////////////////////////////////////////////////
00034 AnimBundleMaker::
00035 AnimBundleMaker(EggTable *root) : _root(root) {
00036   _fps = 0.0f;
00037   _num_frames = 1;
00038 
00039   _ok_fps = true;
00040   _ok_num_frames = true;
00041 
00042   inspect_tree(root);
00043 
00044   if (!_ok_fps) {
00045     egg2pg_cat.warning()
00046       << "AnimBundle " << _root->get_name()
00047       << " specifies contradictory frame rates.\n";
00048   } else if (_fps == 0.0f) {
00049     egg2pg_cat.warning()
00050       << "AnimBundle " << _root->get_name()
00051       << " does not specify a frame rate.\n";
00052     _fps = 24.0f;
00053   }
00054 
00055   if (!_ok_num_frames) {
00056     egg2pg_cat.warning()
00057       << "AnimBundle " << _root->get_name()
00058       << " specifies contradictory number of frames.\n";
00059   }
00060 }
00061 
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: AnimBundleMaker::make_node
00065 //       Access: Public
00066 //  Description:
00067 ////////////////////////////////////////////////////////////////////
00068 AnimBundleNode *AnimBundleMaker::
00069 make_node() {
00070   return new AnimBundleNode(_root->get_name(), make_bundle());
00071 }
00072 
00073 ////////////////////////////////////////////////////////////////////
00074 //     Function: AnimBundleMaker::make_bundle
00075 //       Access: Private
00076 //  Description:
00077 ////////////////////////////////////////////////////////////////////
00078 AnimBundle *AnimBundleMaker::
00079 make_bundle() {
00080   AnimBundle *bundle = new AnimBundle(_root->get_name(), _fps, _num_frames);
00081 
00082   EggTable::const_iterator ci;
00083   for (ci = _root->begin(); ci != _root->end(); ++ci) {
00084     if ((*ci)->is_of_type(EggTable::get_class_type())) {
00085       EggTable *child = DCAST(EggTable, *ci);
00086       build_hierarchy(child, bundle);
00087     }
00088   }
00089 
00090   bundle->sort_descendants();
00091 
00092   return bundle;
00093 }
00094 
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: AnimBundleMaker::inspect_tree
00098 //       Access: Private
00099 //  Description: Walks the egg tree, getting out the fps and the
00100 //               number of frames.
00101 ////////////////////////////////////////////////////////////////////
00102 void AnimBundleMaker::
00103 inspect_tree(EggNode *egg_node) {
00104   if (egg_node->is_of_type(EggAnimData::get_class_type())) {
00105     // Check frame rate.
00106     EggAnimData *egg_anim = DCAST(EggAnimData, egg_node);
00107     if (egg_anim->has_fps()) {
00108       if (_fps == 0.0f) {
00109         _fps = egg_anim->get_fps();
00110       } else if (_fps != egg_anim->get_fps()) {
00111         // Whoops!  This table differs in opinion from the other tables.
00112         _fps = min(_fps, (PN_stdfloat)egg_anim->get_fps());
00113         _ok_fps = false;
00114       }
00115     }
00116   }
00117 
00118   if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) {
00119     // Check frame rate.
00120     EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node);
00121     if (egg_anim->has_fps()) {
00122       if (_fps == 0.0f) {
00123         _fps = egg_anim->get_fps();
00124       } else if (_fps != egg_anim->get_fps()) {
00125         // Whoops!  This table differs in opinion from the other tables.
00126         _fps = min(_fps, (PN_stdfloat)egg_anim->get_fps());
00127         _ok_fps = false;
00128       }
00129     }
00130   }
00131 
00132   if (egg_node->is_of_type(EggSAnimData::get_class_type())) {
00133     // Check number of frames.
00134     EggSAnimData *egg_anim = DCAST(EggSAnimData, egg_node);
00135     int num_frames = egg_anim->get_num_rows();
00136 
00137     if (num_frames > 1) {
00138       if (_num_frames == 1) {
00139         _num_frames = num_frames;
00140       } else if (_num_frames != num_frames) {
00141         // Whoops!  Another disagreement.
00142         _num_frames = min(_num_frames, num_frames);
00143         _ok_num_frames = false;
00144       }
00145     }
00146   }
00147 
00148   if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) {
00149     // Check number of frames.
00150     EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node);
00151     int num_frames = egg_anim->get_num_rows();
00152 
00153     if (num_frames > 1) {
00154       if (_num_frames == 1) {
00155         _num_frames = num_frames;
00156       } else if (_num_frames != num_frames) {
00157         // Whoops!  Another disagreement.
00158         _num_frames = min(_num_frames, num_frames);
00159         _ok_num_frames = false;
00160       }
00161     }
00162   }
00163 
00164   if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
00165     // Now recurse.
00166     EggGroupNode *group = DCAST(EggGroupNode, egg_node);
00167     EggGroupNode::const_iterator ci;
00168     for (ci = group->begin(); ci != group->end(); ++ci) {
00169       inspect_tree(*ci);
00170     }
00171   }
00172 }
00173 
00174 
00175 ////////////////////////////////////////////////////////////////////
00176 //     Function: AnimBundleMaker::build_hierarchy
00177 //       Access: Private
00178 //  Description: Walks the egg tree again, creating the AnimChannels
00179 //               as appropriate.
00180 ////////////////////////////////////////////////////////////////////
00181 void AnimBundleMaker::
00182 build_hierarchy(EggTable *egg_table, AnimGroup *parent) {
00183   AnimGroup *this_node = NULL;
00184 
00185   // First, scan the children of egg_table for anim data tables.  If
00186   // any of them is named "xform", it's a special case--this one
00187   // stands for the egg_table node itself.  Don't ask me why.
00188 
00189   EggTable::const_iterator ci;
00190   for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) {
00191     if ((*ci)->get_name() == "xform") {
00192       if (this_node == NULL) {
00193         this_node = create_xfm_channel((*ci), egg_table->get_name(), parent);
00194       } else {
00195         egg2pg_cat.warning()
00196           << "Duplicate xform table under node "
00197           << egg_table->get_name() << "\n";
00198       }
00199     }
00200   }
00201 
00202   // If none of them were named "xform", just create a plain old
00203   // AnimGroup.
00204   if (this_node == NULL) {
00205     this_node = new AnimGroup(parent, egg_table->get_name());
00206   }
00207 
00208   // Now walk the children again, creating any leftover tables, and
00209   // recursing.
00210   for (ci = egg_table->begin(); ci != egg_table->end(); ++ci) {
00211     if ((*ci)->get_name() == "xform") {
00212       // Skip this one.  We already got it.
00213     } else if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00214       EggSAnimData *egg_anim = DCAST(EggSAnimData, *ci);
00215       create_s_channel(egg_anim, egg_anim->get_name(), this_node);
00216 
00217     } else if ((*ci)->is_of_type(EggTable::get_class_type())) {
00218       EggTable *child = DCAST(EggTable, *ci);
00219       build_hierarchy(child, this_node);
00220     }
00221   }
00222 }
00223 
00224 
00225 ////////////////////////////////////////////////////////////////////
00226 //     Function: AnimBundleMaker::create_s_channel
00227 //       Access: Private
00228 //  Description: Creates an AnimChannelScalarTable corresponding to
00229 //               the given EggSAnimData structure.
00230 ////////////////////////////////////////////////////////////////////
00231 AnimChannelScalarTable *AnimBundleMaker::
00232 create_s_channel(EggSAnimData *egg_anim, const string &name,
00233                  AnimGroup *parent) {
00234   AnimChannelScalarTable *table
00235     = new AnimChannelScalarTable(parent, name);
00236 
00237   // First we have to copy the table data from PTA_double to
00238   // PTA_stdfloat.
00239   PTA_stdfloat new_data = PTA_stdfloat::empty_array(egg_anim->get_num_rows(),
00240                                                     table->get_class_type());
00241   for (int i = 0; i < egg_anim->get_num_rows(); i++) {
00242     new_data[i] = (PN_stdfloat)egg_anim->get_value(i);
00243   }
00244 
00245   // Now we can assign the table.
00246   table->set_table(new_data);
00247 
00248   return table;
00249 }
00250 
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: AnimBundleMaker::create_xfm_channel (EggNode)
00254 //       Access: Private
00255 //  Description: Creates an AnimChannelMatrixXfmTable corresponding to
00256 //               the given EggNode structure, if possible.
00257 ////////////////////////////////////////////////////////////////////
00258 AnimChannelMatrixXfmTable *AnimBundleMaker::
00259 create_xfm_channel(EggNode *egg_node, const string &name,
00260                    AnimGroup *parent) {
00261   if (egg_node->is_of_type(EggXfmAnimData::get_class_type())) {
00262     EggXfmAnimData *egg_anim = DCAST(EggXfmAnimData, egg_node);
00263     EggXfmSAnim new_anim(*egg_anim);
00264     return create_xfm_channel(&new_anim, name, parent);
00265 
00266   } else if (egg_node->is_of_type(EggXfmSAnim::get_class_type())) {
00267     EggXfmSAnim *egg_anim = DCAST(EggXfmSAnim, egg_node);
00268     return create_xfm_channel(egg_anim, name, parent);
00269   }
00270 
00271   egg2pg_cat.warning()
00272     << "Inappropriate node named xform under node "
00273     << name << "\n";
00274   return NULL;
00275 }
00276 
00277 
00278 ////////////////////////////////////////////////////////////////////
00279 //     Function: AnimBundleMaker::create_xfm_channel (EggXfmSAnim)
00280 //       Access: Private
00281 //  Description: Creates an AnimChannelMatrixXfmTable corresponding to
00282 //               the given EggXfmSAnim structure.
00283 ////////////////////////////////////////////////////////////////////
00284 AnimChannelMatrixXfmTable *AnimBundleMaker::
00285 create_xfm_channel(EggXfmSAnim *egg_anim, const string &name,
00286                    AnimGroup *parent) {
00287   // Ensure that the anim table is optimal and that it is standard
00288   // order.
00289   egg_anim->optimize_to_standard_order();
00290 
00291   AnimChannelMatrixXfmTable *table
00292     = new AnimChannelMatrixXfmTable(parent, name);
00293 
00294   // The EggXfmSAnim structure has a number of children which are
00295   // EggSAnimData tables.  Each of these represents a separate
00296   // component of the transform data, and will be added to the table.
00297 
00298   EggXfmSAnim::const_iterator ci;
00299   for (ci = egg_anim->begin(); ci != egg_anim->end(); ++ci) {
00300     if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00301       EggSAnimData *child = DCAST(EggSAnimData, *ci);
00302 
00303       if (child->get_name().empty()) {
00304         egg2pg_cat.warning()
00305           << "Unnamed subtable of <Xfm$Anim_S$> " << name
00306           << "\n";
00307       } else {
00308         char table_id = child->get_name()[0];
00309 
00310         if (child->get_name().length() > 1 ||
00311             !table->is_valid_id(table_id)) {
00312           egg2pg_cat.warning()
00313             << "Unexpected table name " << child->get_name()
00314             << ", child of " << name << "\n";
00315 
00316         } else if (table->has_table(table_id)) {
00317           egg2pg_cat.warning()
00318             << "Duplicate table definition for " << table_id
00319             << " under " << name << "\n";
00320 
00321         } else {
00322 
00323           // Now we have to copy the table data from PTA_double to
00324           // PTA_stdfloat.
00325           PTA_stdfloat new_data=PTA_stdfloat::empty_array(child->get_num_rows(),
00326                                                     table->get_class_type());
00327           for (int i = 0; i < child->get_num_rows(); i++) {
00328             new_data[i] = (PN_stdfloat)child->get_value(i);
00329           }
00330 
00331           // Now we can assign the table.
00332           table->set_table(table_id, new_data);
00333         }
00334       }
00335     }
00336   }
00337 
00338   return table;
00339 }