Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/eggprogs/eggTopstrip.cxx

Go to the documentation of this file.
00001 // Filename: eggTopstrip.cxx
00002 // Created by:  drose (23Feb01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
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 //     Function: EggTopstrip::Constructor
00031 //       Access: Public
00032 //  Description:
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 //     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   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     // We'll read the transform to apply from this character, which
00134     // will be the same character unless -r was specified.
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     // Determine which joint we'll use to extract the transform to
00141     // apply.
00142     EggJointData *top_joint = (EggJointData *)NULL;
00143     if (_top_joint_name.empty()) {
00144       // The default top joint name is the alphabetically first joint
00145       // in the top level.
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     // First, transform all the joints.
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     // We also need to transform the vertices for any models involved
00168     // here.
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   // Now, trigger the actual rebuilding of all the joint data.
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 //     Function: EggTopstrip::check_transform_channels
00190 //       Access: Public
00191 //  Description: Checks the _transform_channels string to ensure that
00192 //               it contains only the expected nine letters, or a
00193 //               subset.
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 //     Function: EggTopstrip::strip_anim
00228 //       Access: Public
00229 //  Description: Applies the channels from joint _top_joint
00230 //               in model from_model to the joint referenced by
00231 //               joint_data.
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       // Compute and apply the new transforms.
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 //     Function: EggTopstrip::strip_anim_vertices
00281 //       Access: Public
00282 //  Description: Applies the channels from joint _top_joint
00283 //               in model from_model to the vertices at egg_node.
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 //     Function: EggTopstrip::adjust_transform
00304 //       Access: Public
00305 //  Description: Adjust the transform extracted from the "top" joint
00306 //               according to the -s and -i/-n options, prior to
00307 //               applying it to the skeleton.
00308 ////////////////////////////////////////////////////////////////////
00309 void EggTopstrip::
00310 adjust_transform(LMatrix4d &mat) const {
00311   if (_transform_channels.length() != 9) {
00312     // Decompose and recompose the matrix, so we can eliminate the
00313     // parts the user doesn't want.
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 }

Generated on Fri May 2 03:18:54 2003 for Panda-Tool by doxygen1.3