eggRetargetAnim.cxx
00001 // Filename: eggRetargetAnim.cxx
00002 // Created by:  drose (05May05)
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 "eggRetargetAnim.h"
00016 
00017 #include "dcast.h"
00018 #include "eggJointData.h"
00019 #include "eggCharacterCollection.h"
00020 #include "eggCharacterData.h"
00021 #include "eggCharacterDb.h"
00022 #include "eggJointPointer.h"
00023 #include "eggTable.h"
00024 #include "compose_matrix.h"
00025 #include "pystub.h"
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: EggRetargetAnim::Constructor
00029 //       Access: Public
00030 //  Description:
00031 ////////////////////////////////////////////////////////////////////
00032 EggRetargetAnim::
00033 EggRetargetAnim() {
00034   add_path_replace_options();
00035   add_path_store_options();
00036 
00037   set_program_description
00038     ("egg-retarget-anim reads a character model and its associated animation "
00039      "files, and removes the translations and scales from the animation "
00040      "files, replacing them with the translations and scales from the "
00041      "rest position of the character model.\n\n"
00042 
00043      "This allows an animation that was generated for a model with one "
00044      "skeleton to be played successfully on a model with a different "
00045      "skeleton, provided that both skeletons have the same hierarchy and "
00046      "differ only in scales and/or translations of the various joints, "
00047      "and that scales and translations are not part of the per-frame "
00048      "animations.");
00049 
00050   add_option
00051     ("r", "file.egg", 0,
00052      "Read the reference model from the indicated egg file.  All of the "
00053      "animations will be retargeted to match the indicated file.",
00054      &EggRetargetAnim::dispatch_filename, NULL, &_reference_filename);
00055 
00056   add_option
00057     ("keep", "joint[,joint...]", 0,
00058      "Preserve the full animation on the named joint(s).  This is especially "
00059      "appropriate for the root joint.",
00060      &EggRetargetAnim::dispatch_vector_string_comma, NULL, &_keep_joints);
00061 }
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: EggRetargetAnim::run
00065 //       Access: Public
00066 //  Description:
00067 ////////////////////////////////////////////////////////////////////
00068 void EggRetargetAnim::
00069 run() {
00070   nassertv(_collection != (EggCharacterCollection *)NULL);
00071   nassertv(_collection->get_num_eggs() > 0);
00072 
00073   if (_reference_filename.empty()) {
00074     nout << "No reference filename specified.\n";
00075     exit(1);
00076   }
00077 
00078   int num_characters = _collection->get_num_characters();
00079   if (num_characters != 1) {
00080     nout << "All animations must have the same character name.\n";
00081     exit(1);
00082   }
00083 
00084   // Read in the extra egg file that we use for extracting the
00085   // references out.
00086   PT(EggData) reference_egg = read_egg(_reference_filename);
00087   if (reference_egg == (EggData *)NULL) {
00088     nout << "Cannot read " << _reference_filename << "\n";
00089     exit(1);
00090   }
00091 
00092   // First, we add it to a separate EggCharacterCollection, so we can
00093   // figure out its name.
00094   EggCharacterCollection col;
00095   if (col.add_egg(reference_egg) < 0) {
00096     nout << _reference_filename
00097          << " does not contain a character model or animation reference.\n";
00098     exit(1);
00099   }
00100 
00101   if (col.get_num_characters() != 1) {
00102     nout << "Reference model must contain only one character.\n";
00103     exit(1);
00104   }
00105 
00106   string ref_name = col.get_character(0)->get_name();
00107 
00108   // Now rename all of the animations to the same name as the
00109   // reference model, and add the reference animation in to the same
00110   // collection to match it up joint-for-joint.
00111   _collection->rename_char(0, ref_name);
00112   int reference_egg_index = _collection->add_egg(reference_egg);
00113   nassertv(reference_egg_index > 0);
00114   nassertv(_collection->get_num_characters() == 1);
00115   
00116   int reference_model = _collection->get_first_model_index(reference_egg_index);
00117   EggCharacterData *char_data = _collection->get_character(0);
00118   nout << "Processing " << char_data->get_name() << "\n";
00119 
00120   typedef pset<string> Names;
00121   Names keep_names;
00122 
00123   vector_string::const_iterator si;
00124   for (si = _keep_joints.begin(); si != _keep_joints.end(); ++si) {
00125     keep_names.insert(*si);
00126   }
00127 
00128   EggCharacterDb db;
00129   EggJointData *root_joint = char_data->get_root_joint();
00130   retarget_anim(char_data, root_joint, reference_model, keep_names, db);
00131   root_joint->do_rebuild_all(db);
00132 
00133   write_eggs();
00134 }
00135 
00136 ////////////////////////////////////////////////////////////////////
00137 //     Function: EggRetargetAnim::retarget_anim
00138 //       Access: Public
00139 //  Description: Recursively replaces the scale and translate
00140 //               information on all of the joints in the char_data
00141 //               hierarchy wiht this from reference_char.
00142 ////////////////////////////////////////////////////////////////////
00143 void EggRetargetAnim::
00144 retarget_anim(EggCharacterData *char_data, EggJointData *joint_data,
00145               int reference_model, const pset<string> &keep_names,
00146               EggCharacterDb &db) {
00147   if (keep_names.find(joint_data->get_name()) != keep_names.end()) {
00148     // Don't retarget this joint; keep the translation and scale and whatever.
00149 
00150   } else {
00151     // Retarget this joint.
00152     int num_models = joint_data->get_num_models();
00153     for (int i = 0; i < num_models; i++) {
00154       if (joint_data->has_model(i)) {
00155         int num_frames = char_data->get_num_frames(i);
00156         
00157         EggBackPointer *back = joint_data->get_model(i);
00158         nassertv(back != (EggBackPointer *)NULL);
00159         EggJointPointer *joint;
00160         DCAST_INTO_V(joint, back);
00161         
00162         LMatrix4d ref = joint_data->get_frame(reference_model, 0);
00163         LVecBase3d ref_scale, ref_shear, ref_hpr, ref_translate;
00164         if (!decompose_matrix(ref, ref_scale, ref_shear, ref_hpr, ref_translate)) {
00165           nout << "Could not decompose rest frame for " 
00166                << joint_data->get_name() << "\n";
00167         } else {
00168           int f;
00169           for (f = 0; f < num_frames; f++) {
00170             LMatrix4d mat = joint_data->get_frame(i, f);
00171             
00172             LVecBase3d scale, shear, hpr, translate;
00173             if (decompose_matrix(mat, scale, shear, hpr, translate)) {
00174               compose_matrix(mat, ref_scale, ref_shear, hpr, ref_translate);
00175             } else {
00176               nout << "Could not decompose matrix for " << joint_data->get_name()
00177                    << "\n";
00178             }
00179 
00180             db.set_matrix(joint, EggCharacterDb::TT_rebuild_frame,
00181                           f, mat);
00182           }
00183         }
00184       }
00185     }
00186   }
00187 
00188   int num_children = joint_data->get_num_children();
00189   for (int i = 0; i < num_children; i++) {
00190     EggJointData *next_joint_data = joint_data->get_child(i);
00191     retarget_anim(char_data, next_joint_data, reference_model, keep_names, db);
00192   }
00193 }
00194 
00195 
00196 int main(int argc, char *argv[]) {
00197   // A call to pystub() to force libpystub.so to be linked in.
00198   pystub();
00199 
00200   EggRetargetAnim prog;
00201   prog.parse_command_line(argc, argv);
00202   prog.run();
00203   return 0;
00204 }