Panda3D
 All Classes Functions Variables Enumerations
eggTopstrip.cxx
00001 // Filename: eggTopstrip.cxx
00002 // Created by:  drose (23Feb01)
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 "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 //     Function: EggTopstrip::Constructor
00029 //       Access: Public
00030 //  Description:
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 //     Function: EggTopstrip::run
00084 //       Access: Public
00085 //  Description:
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   // Get the number of characters first, in case adding the
00095   // _channel_egg changes this.
00096   int num_characters = _collection->get_num_characters();
00097 
00098   // Determine which model and character we'll be pulling the
00099   // animation channels from.
00100   int from_model = -1;
00101 
00102   if (!_channel_filename.empty()) {
00103     // Read in the extra egg file that we use for extracting the
00104     // channels out.
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       // With -r, the default is not to invert the transform.
00121       _invert_transform = false;
00122     }
00123   }
00124 
00125   // Now process each character.
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     // We'll read the transform to apply from this character, which
00136     // will be the same character unless -r was specified.
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     // Determine which joint we'll use to extract the transform to
00143     // apply.
00144     EggJointData *top_joint = (EggJointData *)NULL;
00145     if (_top_joint_name.empty()) {
00146       // The default top joint name is the alphabetically first joint
00147       // in the top level.
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     // First, transform all the joints.
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     // We also need to transform the vertices for any models involved
00170     // here.
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   // Now, trigger the actual rebuilding of all the joint data.
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 //     Function: EggTopstrip::check_transform_channels
00192 //       Access: Public
00193 //  Description: Checks the _transform_channels string to ensure that
00194 //               it contains only the expected nine letters, or a
00195 //               subset.
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 //     Function: EggTopstrip::strip_anim
00230 //       Access: Public
00231 //  Description: Applies the channels from joint _top_joint
00232 //               in model from_model to the joint referenced by
00233 //               joint_data.
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       // Compute and apply the new transforms.
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 //     Function: EggTopstrip::strip_anim_vertices
00277 //       Access: Public
00278 //  Description: Applies the channels from joint _top_joint
00279 //               in model from_model to the vertices at egg_node.
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 //     Function: EggTopstrip::adjust_transform
00300 //       Access: Public
00301 //  Description: Adjust the transform extracted from the "top" joint
00302 //               according to the -s and -i/-n options, prior to
00303 //               applying it to the skeleton.
00304 ////////////////////////////////////////////////////////////////////
00305 void EggTopstrip::
00306 adjust_transform(LMatrix4d &mat) const {
00307   if (_transform_channels.length() != 9) {
00308     // Decompose and recompose the matrix, so we can eliminate the
00309     // parts the user doesn't want.
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   // A call to pystub() to force libpystub.so to be linked in.
00365   pystub();
00366 
00367   EggTopstrip prog;
00368   prog.parse_command_line(argc, argv);
00369   prog.run();
00370   return 0;
00371 }
 All Classes Functions Variables Enumerations