Panda3D
|
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 }