00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00029
00030
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
00065
00066
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
00085
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
00093
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
00109
00110
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
00138
00139
00140
00141
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
00149
00150 } else {
00151
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
00198 pystub();
00199
00200 EggRetargetAnim prog;
00201 prog.parse_command_line(argc, argv);
00202 prog.run();
00203 return 0;
00204 }