00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "eggTopstrip.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 EggTopstrip::
00033 EggTopstrip() {
00034 add_path_replace_options();
00035 add_path_store_options();
00036
00037 set_program_description
00038 ("egg-topstrip reads a character model and its associated animation "
00039 "files, and unapplies the animation from one of the top joints. "
00040 "This effectively freezes that particular joint, and makes the rest "
00041 "of the character relative to that joint.\n\n"
00042
00043 "This is a particularly useful thing to do to generate character "
00044 "models that can stack one on top of the other in a sensible way.");
00045
00046 add_option
00047 ("t", "name", 0,
00048 "Specify the name of the 'top' joint, from which to draw the "
00049 "animation channels which will be applied to the entire animation.",
00050 &EggTopstrip::dispatch_string, NULL, &_top_joint_name);
00051
00052 add_option
00053 ("i", "", 0,
00054 "Invert the matrix before applying. This causes a subtractive "
00055 "effect. This is the default unless -r is specified.",
00056 &EggTopstrip::dispatch_true, &_got_invert_transform, &_invert_transform);
00057
00058 add_option
00059 ("n", "", 0,
00060 "Do not invert the matrix before applying. This causes an "
00061 "additive effect.",
00062 &EggTopstrip::dispatch_false, &_got_invert_transform, &_invert_transform);
00063
00064 add_option
00065 ("s", "[ijkphrxyz]", 0,
00066 "Specify the components of the transform that are to be applied. Use "
00067 "any combination of the nine token letters: i, j, k represent the "
00068 "three scale axes; h, p, r represent rotation; and x, y, z represent "
00069 "translation. The default is everything: -s ijkphrxyz.",
00070 &EggTopstrip::dispatch_string, NULL, &_transform_channels);
00071
00072 add_option
00073 ("r", "file.egg", 0,
00074 "Read the animation channel from the indicated egg file. If this "
00075 "is not specified, each egg file will supply its own animation channel.",
00076 &EggTopstrip::dispatch_filename, NULL, &_channel_filename);
00077
00078 _invert_transform = true;
00079 _transform_channels = "ijkphrxyz";
00080 }
00081
00082
00083
00084
00085
00086
00087 void EggTopstrip::
00088 run() {
00089 nassertv(_collection != (EggCharacterCollection *)NULL);
00090 nassertv(_collection->get_num_eggs() > 0);
00091
00092 check_transform_channels();
00093
00094
00095
00096 int num_characters = _collection->get_num_characters();
00097
00098
00099
00100 int from_model = -1;
00101
00102 if (!_channel_filename.empty()) {
00103
00104
00105 PT(EggData) channel_egg = read_egg(_channel_filename);
00106 if (channel_egg == (EggData *)NULL) {
00107 nout << "Cannot read " << _channel_filename << "\n";
00108 exit(1);
00109 }
00110 int channel_egg_index = _collection->add_egg(channel_egg);
00111 if (channel_egg_index < 0) {
00112 nout << _channel_filename
00113 << " does not contain a character model or animation channel.\n";
00114 exit(1);
00115 }
00116
00117 from_model = _collection->get_first_model_index(channel_egg_index);
00118
00119 if (!_got_invert_transform) {
00120
00121 _invert_transform = false;
00122 }
00123 }
00124
00125
00126 EggCharacterDb db;
00127
00128 int ci;
00129 for (ci = 0; ci < num_characters; ci++) {
00130 EggCharacterData *char_data = _collection->get_character(ci);
00131 nout << "Processing " << char_data->get_name() << "\n";
00132
00133 EggJointData *root_joint = char_data->get_root_joint();
00134
00135
00136
00137 EggCharacterData *from_char = char_data;
00138 if (from_model != -1) {
00139 from_char = _collection->get_character_by_model_index(from_model);
00140 }
00141
00142
00143
00144 EggJointData *top_joint = (EggJointData *)NULL;
00145 if (_top_joint_name.empty()) {
00146
00147
00148 if (root_joint->get_num_children() == 0) {
00149 nout << "Character " << from_char->get_name() << " has no joints.\n";
00150 exit(1);
00151 }
00152 top_joint = root_joint->get_child(0);
00153 } else {
00154 top_joint = from_char->find_joint(_top_joint_name);
00155 if (top_joint == (EggJointData *)NULL) {
00156 nout << "Character " << from_char->get_name()
00157 << " has no joint named " << _top_joint_name << "\n";
00158 exit(1);
00159 }
00160 }
00161
00162
00163 int num_children = root_joint->get_num_children();
00164 for (int i = 0; i < num_children; i++) {
00165 EggJointData *joint_data = root_joint->get_child(i);
00166 strip_anim(char_data, joint_data, from_model, from_char, top_joint, db);
00167 }
00168
00169
00170
00171 int num_models = char_data->get_num_models();
00172 for (int m = 0; m < num_models; m++) {
00173 EggNode *node = char_data->get_model_root(m);
00174 if (!node->is_of_type(EggTable::get_class_type())) {
00175 strip_anim_vertices(node, char_data->get_model_index(m),
00176 from_model, top_joint, db);
00177 }
00178 }
00179 }
00180
00181
00182 for (ci = 0; ci < num_characters; ci++) {
00183 EggCharacterData *char_data = _collection->get_character(ci);
00184 char_data->get_root_joint()->do_rebuild_all(db);
00185 }
00186
00187 write_eggs();
00188 }
00189
00190
00191
00192
00193
00194
00195
00196
00197 void EggTopstrip::
00198 check_transform_channels() {
00199 static string expected = "ijkphrxyz";
00200 static const int num_channels = 9;
00201 bool has_each[num_channels];
00202 memset(has_each, 0, num_channels * sizeof(bool));
00203
00204 for (size_t p = 0; p < _transform_channels.size(); p++) {
00205 int i = expected.find(_transform_channels[p]);
00206 if (i == (int)string::npos) {
00207 nout << "Invalid letter for -s: " << _transform_channels[p] << "\n";
00208 exit(1);
00209 }
00210 nassertv(i < num_channels);
00211 has_each[i] = true;
00212 }
00213
00214 _transform_channels = "";
00215 for (int i = 0; i < num_channels; i++) {
00216 if (has_each[i]) {
00217 _transform_channels += expected[i];
00218 }
00219 }
00220
00221 if (_transform_channels.empty()) {
00222 nout << "No transform specified for -s.\n";
00223 exit(1);
00224 }
00225 }
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235 void EggTopstrip::
00236 strip_anim(EggCharacterData *char_data, EggJointData *joint_data,
00237 int from_model, EggCharacterData *from_char,
00238 EggJointData *top_joint, EggCharacterDb &db) {
00239 int num_models = joint_data->get_num_models();
00240 for (int i = 0; i < num_models; i++) {
00241 int model = (from_model < 0) ? i : from_model;
00242 if (joint_data->has_model(i)) {
00243 if (!top_joint->has_model(model)) {
00244 nout << "Warning: Joint " << top_joint->get_name()
00245 << " is not defined in all models.\n";
00246 return;
00247 }
00248
00249 int num_into_frames = char_data->get_num_frames(i);
00250 int num_from_frames = from_char->get_num_frames(model);
00251
00252 int num_frames = max(num_into_frames, num_from_frames);
00253
00254 EggBackPointer *back = joint_data->get_model(i);
00255 nassertv(back != (EggBackPointer *)NULL);
00256 EggJointPointer *joint;
00257 DCAST_INTO_V(joint, back);
00258
00259
00260
00261 int f;
00262 for (f = 0; f < num_frames; f++) {
00263 LMatrix4d into = joint_data->get_frame(i, f % num_into_frames);
00264 LMatrix4d from = top_joint->get_net_frame(model, f % num_from_frames, db);
00265
00266 adjust_transform(from);
00267
00268 db.set_matrix(joint, EggCharacterDb::TT_rebuild_frame,
00269 f, into * from);
00270 }
00271 }
00272 }
00273 }
00274
00275
00276
00277
00278
00279
00280
00281 void EggTopstrip::
00282 strip_anim_vertices(EggNode *egg_node, int into_model, int from_model,
00283 EggJointData *top_joint, EggCharacterDb &db) {
00284 int model = (from_model < 0) ? into_model : from_model;
00285 if (!top_joint->has_model(model)) {
00286 nout << "Warning: Joint " << top_joint->get_name()
00287 << " is not defined in all models.\n";
00288 return;
00289 }
00290
00291 LMatrix4d from = top_joint->get_net_frame(model, 0, db);
00292 adjust_transform(from);
00293
00294 egg_node->transform_vertices_only(from);
00295 }
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305 void EggTopstrip::
00306 adjust_transform(LMatrix4d &mat) const {
00307 if (_transform_channels.length() != 9) {
00308
00309
00310
00311 LVecBase3d scale, hpr, translate;
00312 bool result = decompose_matrix(mat, scale, hpr, translate, _coordinate_system);
00313 if (!result) {
00314 nout << "Warning: skew transform in animation.\n";
00315 } else {
00316 LVecBase3d new_scale(1.0, 1.0, 1.0);
00317 LVecBase3d new_hpr(0.0, 0.0, 0.0);
00318 LVecBase3d new_translate(0.0, 0.0, 0.0);
00319
00320 for (size_t i = 0; i < _transform_channels.size(); i++) {
00321 switch (_transform_channels[i]) {
00322 case 'i':
00323 new_scale[0] = scale[0];
00324 break;
00325 case 'j':
00326 new_scale[1] = scale[1];
00327 break;
00328 case 'k':
00329 new_scale[2] = scale[2];
00330 break;
00331
00332 case 'h':
00333 new_hpr[0] = hpr[0];
00334 break;
00335 case 'p':
00336 new_hpr[1] = hpr[1];
00337 break;
00338 case 'r':
00339 new_hpr[2] = hpr[2];
00340 break;
00341
00342 case 'x':
00343 new_translate[0] = translate[0];
00344 break;
00345 case 'y':
00346 new_translate[1] = translate[1];
00347 break;
00348 case 'z':
00349 new_translate[2] = translate[2];
00350 break;
00351 }
00352 }
00353
00354 compose_matrix(mat, new_scale, new_hpr, new_translate, _coordinate_system);
00355 }
00356 }
00357 if (_invert_transform) {
00358 mat.invert_in_place();
00359 }
00360 }
00361
00362
00363 int main(int argc, char *argv[]) {
00364
00365 pystub();
00366
00367 EggTopstrip prog;
00368 prog.parse_command_line(argc, argv);
00369 prog.run();
00370 return 0;
00371 }