Panda3D
|
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 }