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