Panda3D
|
00001 // Filename: eggXfmSAnim.cxx 00002 // Created by: drose (19Feb99) 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 "eggXfmSAnim.h" 00016 #include "eggSAnimData.h" 00017 #include "eggXfmAnimData.h" 00018 #include "eggParameters.h" 00019 #include "config_egg.h" 00020 00021 #include "indent.h" 00022 #include "compose_matrix.h" 00023 #include "dcast.h" 00024 00025 #include <math.h> 00026 00027 TypeHandle EggXfmSAnim::_type_handle; 00028 00029 00030 // For now, the standard order is sphrt. This matches the old, 00031 // incorrect behavior of decompose_matrix(). When we have a new 00032 // egg-optchar, we can safely remove the old decompose_matrix() and 00033 // restore the correct standard order. 00034 const string EggXfmSAnim::_standard_order_legacy = "sphrt"; 00035 const string EggXfmSAnim::_standard_order_hpr_fix = "srpht"; 00036 00037 00038 //////////////////////////////////////////////////////////////////// 00039 // Function: EggXfmSAnim::Conversion constructor 00040 // Access: Public 00041 // Description: Converts the older-style XfmAnim table to the 00042 // newer-style XfmSAnim table. 00043 //////////////////////////////////////////////////////////////////// 00044 EggXfmSAnim:: 00045 EggXfmSAnim(const EggXfmAnimData &convert_from) 00046 : EggGroupNode(convert_from.get_name()) 00047 { 00048 _has_fps = false; 00049 _coordsys = convert_from.get_coordinate_system(); 00050 00051 if (convert_from.has_order()) { 00052 set_order(convert_from.get_order()); 00053 } 00054 if (convert_from.has_fps()) { 00055 set_fps(convert_from.get_fps()); 00056 } 00057 00058 const string &contents = convert_from.get_contents(); 00059 for (int col = 0; col < convert_from.get_num_cols(); col++) { 00060 EggSAnimData *sanim = new EggSAnimData(contents.substr(col, 1)); 00061 add_child(sanim); 00062 for (int row = 0; row < convert_from.get_num_rows(); row++) { 00063 sanim->add_data(convert_from.get_value(row, col)); 00064 } 00065 } 00066 } 00067 00068 //////////////////////////////////////////////////////////////////// 00069 // Function: EggXfmSAnim::optimize 00070 // Access: Public 00071 // Description: Optimizes the table by collapsing redundant 00072 // sub-tables. 00073 //////////////////////////////////////////////////////////////////// 00074 void EggXfmSAnim:: 00075 optimize() { 00076 iterator ci = begin(); 00077 while (ci != end()) { 00078 iterator ci_next = ci; 00079 ++ci_next; 00080 00081 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { 00082 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00083 sanim->optimize(); 00084 00085 if (sanim->get_num_rows() == 1) { 00086 // If we've optimized down to one value, check to see if it is 00087 // a default value. 00088 double value = sanim->get_value(0); 00089 double default_value; 00090 if (sanim->has_name() && strchr("ijk", sanim->get_name()[0]) != NULL) { 00091 default_value = 1.0; 00092 } else { 00093 default_value = 0.0; 00094 } 00095 00096 if (fabs(value - default_value) < egg_parameters->_table_threshold) { 00097 // It's a default-valued table, and therefore redundant: 00098 // remove it. 00099 erase(ci); 00100 } 00101 } 00102 } 00103 00104 ci = ci_next; 00105 } 00106 } 00107 00108 //////////////////////////////////////////////////////////////////// 00109 // Function: EggXfmSAnim::optimize_to_standard_order 00110 // Access: Public 00111 // Description: Optimizes the table by collapsing redundant 00112 // sub-tables, and simultaneously ensures that the order 00113 // string is the standard order (which is the same as 00114 // that supported by compose_matrix() and 00115 // decompose_matrix()). 00116 //////////////////////////////////////////////////////////////////// 00117 void EggXfmSAnim:: 00118 optimize_to_standard_order() { 00119 if (get_order() != get_standard_order()) { 00120 normalize_by_rebuilding(); 00121 } 00122 optimize(); 00123 } 00124 00125 //////////////////////////////////////////////////////////////////// 00126 // Function: EggXfmSAnim::normalize 00127 // Access: Public 00128 // Description: The inverse operation of optimize(), this ensures 00129 // that all the sub-tables have the same length by 00130 // duplicating rows as necessary. This is needed before 00131 // doing operations like add_data() or set_value() on an 00132 // existing table. 00133 //////////////////////////////////////////////////////////////////// 00134 void EggXfmSAnim:: 00135 normalize() { 00136 if (get_order() != get_standard_order()) { 00137 // If our order string is wrong, we must fix it now. This will 00138 // incidentally also normalize the table, because we are totally 00139 // rebuilding it. 00140 normalize_by_rebuilding(); 00141 00142 } else { 00143 // Otherwise, if the order string is already the standard order 00144 // string, we can do this the easy way (from a computational 00145 // standpoint), which is just to lengthen the tables directly. 00146 normalize_by_expanding(); 00147 } 00148 } 00149 00150 //////////////////////////////////////////////////////////////////// 00151 // Function: EggXfmSAnim::is_anim_matrix 00152 // Access: Public, Virtual 00153 // Description: Returns true if this node represents a table of 00154 // animation transformation data, false otherwise. 00155 //////////////////////////////////////////////////////////////////// 00156 bool EggXfmSAnim:: 00157 is_anim_matrix() const { 00158 return true; 00159 } 00160 00161 //////////////////////////////////////////////////////////////////// 00162 // Function: EggXfmSAnim::write 00163 // Access: Public, Virtual 00164 // Description: Writes the data to the indicated output stream in Egg 00165 // format. 00166 //////////////////////////////////////////////////////////////////// 00167 void EggXfmSAnim:: 00168 write(ostream &out, int indent_level) const { 00169 test_under_integrity(); 00170 00171 write_header(out, indent_level, "<Xfm$Anim_S$>"); 00172 00173 if (has_fps()) { 00174 indent(out, indent_level + 2) << "<Scalar> fps { " << get_fps() << " }\n"; 00175 } 00176 00177 if (has_order()) { 00178 indent(out, indent_level + 2) 00179 << "<Char*> order { " << get_order() << " }\n"; 00180 } 00181 00182 // Rather than calling EggGroupNode::write() to write out the 00183 // children, we do it directly here so we can control the order. We 00184 // write out all the non-table children first, then write out the 00185 // table children in our expected order. (Normally there are only 00186 // table children.) 00187 EggSAnimData *tables[num_matrix_components]; 00188 memset(tables, 0, sizeof(EggSAnimData *) * num_matrix_components); 00189 00190 const_iterator ci; 00191 for (ci = begin(); ci != end(); ++ci) { 00192 EggNode *child = (*ci); 00193 if (child->is_of_type(EggSAnimData::get_class_type())) { 00194 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00195 00196 // Each child SAnimData table should have a one-letter name. 00197 nassertv(sanim->get_name().length() == 1); 00198 char name = sanim->get_name()[0]; 00199 char *p = (char *)strchr(matrix_component_letters, name); 00200 nassertv(p != (char *)NULL); 00201 if (p != (char *)NULL) { 00202 int index = p - matrix_component_letters; 00203 nassertv(tables[index] == (EggSAnimData *)NULL); 00204 tables[index] = sanim; 00205 } 00206 } else { 00207 // Any non-table children are written directly. 00208 child->write(out, indent_level + 2); 00209 } 00210 } 00211 00212 // Now write out the table children in our normal order. 00213 for (int i = 0; i < num_matrix_components; i++) { 00214 if (tables[i] != (EggSAnimData *)NULL) { 00215 tables[i]->write(out, indent_level + 2); 00216 } 00217 } 00218 00219 indent(out, indent_level) << "}\n"; 00220 } 00221 00222 00223 //////////////////////////////////////////////////////////////////// 00224 // Function: EggXfmSAnim::compose_with_order 00225 // Access: Public, Static 00226 // Description: Composes a matrix out of the nine individual 00227 // components, respecting the order string. The 00228 // components will be applied in the order indicated by 00229 // the string. 00230 //////////////////////////////////////////////////////////////////// 00231 void EggXfmSAnim:: 00232 compose_with_order(LMatrix4d &mat, 00233 const LVecBase3d &scale, 00234 const LVecBase3d &shear, 00235 const LVecBase3d &hpr, 00236 const LVecBase3d &trans, 00237 const string &order, 00238 CoordinateSystem cs) { 00239 00240 mat = LMatrix4d::ident_mat(); 00241 00242 bool reverse_roll = false; 00243 00244 if (order == "sphrt" && egg_support_old_anims) { 00245 // As a special case, if the order string is exactly "sphrt" 00246 // (which is what all our legacy anim files used), we interpret 00247 // roll in the opposite direction (as our legacy anim files did). 00248 reverse_roll = true; 00249 } 00250 00251 string::const_iterator pi; 00252 for (pi = order.begin(); pi != order.end(); ++pi) { 00253 switch (*pi) { 00254 case 's': 00255 mat = mat * LMatrix4d::scale_shear_mat(scale, shear, cs); 00256 break; 00257 00258 case 'h': 00259 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[0], LVector3d::up(cs), cs); 00260 break; 00261 00262 case 'p': 00263 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[1], LVector3d::right(cs), cs); 00264 break; 00265 00266 case 'r': 00267 if (reverse_roll) { 00268 mat = mat * LMatrix4d::rotate_mat_normaxis(-hpr[2], LVector3d::forward(cs), cs); 00269 } else { 00270 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[2], LVector3d::forward(cs), cs); 00271 } 00272 break; 00273 00274 case 't': 00275 mat = mat * LMatrix4d::translate_mat(trans); 00276 break; 00277 00278 default: 00279 egg_cat.warning() 00280 << "Invalid letter in order string: " << *pi << "\n"; 00281 } 00282 } 00283 } 00284 00285 //////////////////////////////////////////////////////////////////// 00286 // Function: EggXfmSAnim::get_num_rows 00287 // Access: Public 00288 // Description: Returns the effective number of rows in the table. 00289 // This is actually the number of rows of the smallest 00290 // subtable larger than one row. This is a convenience 00291 // function that treats the table of tables as if it 00292 // were a single table of matrices. 00293 //////////////////////////////////////////////////////////////////// 00294 int EggXfmSAnim:: 00295 get_num_rows() const { 00296 bool found_any = false; 00297 int min_rows = 1; 00298 00299 const_iterator ci; 00300 for (ci = begin(); ci != end(); ++ci) { 00301 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { 00302 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00303 if (sanim->get_num_rows() > 1) { 00304 if (!found_any) { 00305 min_rows = sanim->get_num_rows(); 00306 00307 } else { 00308 min_rows = min(min_rows, sanim->get_num_rows()); 00309 } 00310 } 00311 } 00312 } 00313 00314 return min_rows; 00315 } 00316 00317 //////////////////////////////////////////////////////////////////// 00318 // Function: EggXfmSAnim::get_value 00319 // Access: Public 00320 // Description: Returns the value of the aggregate row of the table 00321 // as a matrix. This is a convenience function that 00322 // treats the table of tables as if it were a single 00323 // table of matrices. It is an error to call this if 00324 // any SAnimData children of this node have an improper 00325 // name (e.g. not a single letter, or not one of 00326 // "ijkabchprxyz"). 00327 //////////////////////////////////////////////////////////////////// 00328 void EggXfmSAnim:: 00329 get_value(int row, LMatrix4d &mat) const { 00330 LVector3d scale(1.0, 1.0, 1.0); 00331 LVector3d shear(0.0, 0.0, 0.0); 00332 LVector3d hpr(0.0, 0.0, 0.0); 00333 LVector3d translate(0.0, 0.0, 0.0); 00334 00335 const_iterator ci; 00336 for (ci = begin(); ci != end(); ++ci) { 00337 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { 00338 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00339 00340 if (sanim->get_num_rows() == 0) { 00341 // If the table is totally empty, let's keep the default 00342 // value. 00343 break; 00344 } 00345 00346 double value; 00347 if (sanim->get_num_rows() == 1) { 00348 value = sanim->get_value(0); 00349 } else { 00350 nassertv(row < sanim->get_num_rows()); 00351 value = sanim->get_value(row); 00352 } 00353 00354 // Each child SAnimData table should have a one-letter name. 00355 nassertv(sanim->get_name().length() == 1); 00356 00357 switch (sanim->get_name()[0]) { 00358 case 'i': 00359 scale[0] = value; 00360 break; 00361 00362 case 'j': 00363 scale[1] = value; 00364 break; 00365 00366 case 'k': 00367 scale[2] = value; 00368 break; 00369 00370 case 'a': 00371 shear[0] = value; 00372 break; 00373 00374 case 'b': 00375 shear[1] = value; 00376 break; 00377 00378 case 'c': 00379 shear[2] = value; 00380 break; 00381 00382 case 'h': 00383 hpr[0] = value; 00384 break; 00385 00386 case 'p': 00387 hpr[1] = value; 00388 break; 00389 00390 case 'r': 00391 hpr[2] = value; 00392 break; 00393 00394 case 'x': 00395 translate[0] = value; 00396 break; 00397 00398 case 'y': 00399 translate[1] = value; 00400 break; 00401 00402 case 'z': 00403 translate[2] = value; 00404 break; 00405 00406 default: 00407 // One of the child tables had an invalid name. 00408 nassertv(false); 00409 } 00410 } 00411 } 00412 00413 // So now we've got the nine components; build a matrix. 00414 compose_with_order(mat, scale, shear, hpr, translate, get_order(), _coordsys); 00415 } 00416 00417 //////////////////////////////////////////////////////////////////// 00418 // Function: EggXfmSAnim::set_value 00419 // Access: Public 00420 // Description: Replaces the indicated row of the table with the 00421 // given matrix. 00422 // 00423 // This function can only be called if all the 00424 // constraints of add_data(), below, are met. Call 00425 // normalize() first if you are not sure. 00426 // 00427 // The return value is true if the matrix can be 00428 // decomposed and stored as scale, shear, rotate, and 00429 // translate, or false otherwise. The data is set in 00430 // either case. 00431 //////////////////////////////////////////////////////////////////// 00432 bool EggXfmSAnim:: 00433 set_value(int row, const LMatrix4d &mat) { 00434 nassertr(get_order() == get_standard_order(), false); 00435 00436 double components[num_matrix_components]; 00437 bool add_ok = decompose_matrix(mat, components, _coordsys); 00438 00439 // Sanity check our sub-tables. 00440 #ifndef NDEBUG 00441 int table_length = -1; 00442 #endif 00443 00444 for (int i = 0; i < num_matrix_components; i++) { 00445 string name(1, matrix_component_letters[i]); 00446 EggNode *child = find_child(name); 00447 nassertr(child != (EggNode *)NULL && 00448 child->is_of_type(EggSAnimData::get_class_type()), false); 00449 EggSAnimData *sanim = DCAST(EggSAnimData, child); 00450 00451 #ifndef NDEBUG 00452 // Each table must have the same length. 00453 if (table_length < 0) { 00454 table_length = sanim->get_num_rows(); 00455 } else { 00456 nassertr(sanim->get_num_rows() == table_length, false); 00457 } 00458 #endif 00459 sanim->set_value(row, components[i]); 00460 } 00461 00462 #ifndef NDEBUG 00463 // Sanity check the result. 00464 LMatrix4d new_mat; 00465 get_value(row, new_mat); 00466 if (!new_mat.almost_equal(mat, 0.005)) { 00467 egg_cat.warning() 00468 << "After set_row(" << row << ", ...) to:\n"; 00469 mat.write(egg_cat.warning(false), 2); 00470 egg_cat.warning(false) 00471 << "which produces components:\n"; 00472 for (int i = 0; i < num_matrix_components; i += 3) { 00473 egg_cat.warning(false) 00474 << " " 00475 << matrix_component_letters[i] 00476 << matrix_component_letters[i + 1] 00477 << matrix_component_letters[i + 2] 00478 << ": " 00479 << components[i] << " " 00480 << components[i + 1] << " " 00481 << components[i + 2] << "\n"; 00482 } 00483 egg_cat.warning(false) 00484 << "new mat set was:\n"; 00485 new_mat.write(egg_cat.warning(false), 2); 00486 return false; 00487 } 00488 #endif 00489 00490 return add_ok; 00491 } 00492 00493 //////////////////////////////////////////////////////////////////// 00494 // Function: EggXfmSAnim::add_data 00495 // Access: Public 00496 // Description: Adds a new matrix to the table, by adding a new row 00497 // to each of the subtables. 00498 // 00499 // This is a convenience function that 00500 // treats the table of tables as if it were a single 00501 // table of matrices. It is an error to call this if 00502 // any SAnimData children of this node have an improper 00503 // name (e.g. not a single letter, or not one of 00504 // "ijkabchprxyz"). 00505 // 00506 // This function has the further requirement that all 00507 // nine of the subtables must exist and be of the same 00508 // length. Furthermore, the order string must be the 00509 // standard order string, which matches the system 00510 // compose_matrix() and decompose_matrix() functions. 00511 // 00512 // Thus, you probably cannot take an existing 00513 // EggXfmSAnim object and start adding matrices to the 00514 // end; you must clear out the original data first. (As 00515 // a special exception, if no tables exist, they will be 00516 // created.) The method normalize() will do this for 00517 // you on an existing EggXfmSAnim. 00518 // 00519 // This function may fail silently if the matrix cannot 00520 // be decomposed into scale, shear, rotate, and 00521 // translate. In this case, the closest approximation 00522 // is added to the table, and false is returned. 00523 //////////////////////////////////////////////////////////////////// 00524 bool EggXfmSAnim:: 00525 add_data(const LMatrix4d &mat) { 00526 double components[num_matrix_components]; 00527 bool add_ok = decompose_matrix(mat, components, _coordsys); 00528 00529 if (empty()) { 00530 // If we have no children, create all twelve tables now. 00531 for (int i = 0; i < num_matrix_components; i++) { 00532 char name = matrix_component_letters[i]; 00533 EggSAnimData *sanim = new EggSAnimData(string(1, name)); 00534 add_child(sanim); 00535 } 00536 00537 // Also insist on the correct ordering right off the bat. 00538 set_order(get_standard_order()); 00539 } 00540 00541 nassertr(get_order() == get_standard_order(), false); 00542 00543 #ifndef NDEBUG 00544 int table_length = -1; 00545 #endif 00546 00547 for (int i = 0; i < num_matrix_components; i++) { 00548 string name(1, matrix_component_letters[i]); 00549 EggNode *child = find_child(name); 00550 nassertr(child != (EggNode *)NULL && 00551 child->is_of_type(EggSAnimData::get_class_type()), false); 00552 EggSAnimData *sanim = DCAST(EggSAnimData, child); 00553 00554 #ifndef NDEBUG 00555 // Each table must have the same length. 00556 if (table_length < 0) { 00557 table_length = sanim->get_num_rows(); 00558 } else { 00559 nassertr(sanim->get_num_rows() == table_length, false); 00560 } 00561 #endif 00562 sanim->add_data(components[i]); 00563 } 00564 00565 #ifndef NDEBUG 00566 // Sanity check the result. 00567 LMatrix4d new_mat; 00568 if (table_length >= 0) { 00569 get_value(table_length, new_mat); 00570 } else { 00571 get_value(0, new_mat); 00572 } 00573 if (!new_mat.almost_equal(mat, 0.005)) { 00574 egg_cat.warning() 00575 << "After add_data():\n"; 00576 mat.write(egg_cat.warning(false), 2); 00577 egg_cat.warning(false) 00578 << "which produces components:\n"; 00579 for (int i = 0; i < num_matrix_components; i += 3) { 00580 egg_cat.warning(false) 00581 << " " 00582 << matrix_component_letters[i] 00583 << matrix_component_letters[i + 1] 00584 << matrix_component_letters[i + 2] 00585 << ": " 00586 << components[i] << " " 00587 << components[i + 1] << " " 00588 << components[i + 2] << "\n"; 00589 } 00590 egg_cat.warning(false) 00591 << "new mat set was:\n"; 00592 new_mat.write(egg_cat.warning(false), 2); 00593 return false; 00594 } 00595 #endif 00596 00597 return add_ok; 00598 } 00599 00600 //////////////////////////////////////////////////////////////////// 00601 // Function: EggXfmSAnim::add_component_data 00602 // Access: Public 00603 // Description: Adds a new row to the named component (one of 00604 // matrix_component_letters) of the table. 00605 //////////////////////////////////////////////////////////////////// 00606 void EggXfmSAnim:: 00607 add_component_data(const string &component_name, double value) { 00608 EggNode *child = find_child(component_name); 00609 EggSAnimData *sanim; 00610 if (child == (EggNode *)NULL) { 00611 // We don't have this component yet; create it. 00612 sanim = new EggSAnimData(component_name); 00613 add_child(sanim); 00614 00615 } else { 00616 DCAST_INTO_V(sanim, child); 00617 } 00618 00619 sanim->add_data(value); 00620 } 00621 00622 //////////////////////////////////////////////////////////////////// 00623 // Function: EggXfmSAnim::add_component_data 00624 // Access: Public 00625 // Description: Adds a new row to the indicated component (0-12) of 00626 // the table. 00627 //////////////////////////////////////////////////////////////////// 00628 void EggXfmSAnim:: 00629 add_component_data(int component, double value) { 00630 nassertv(component >= 0 && component < num_matrix_components); 00631 00632 string name(1, matrix_component_letters[component]); 00633 add_component_data(name, value); 00634 } 00635 00636 //////////////////////////////////////////////////////////////////// 00637 // Function: EggXfmSAnim::r_transform 00638 // Access: Protected, Virtual 00639 // Description: Applies the indicated transform to all the rows of 00640 // the table. This actually forces the generation of a 00641 // totally new set of rows, and will quietly change the 00642 // order to the standard order (if it is different). 00643 //////////////////////////////////////////////////////////////////// 00644 void EggXfmSAnim:: 00645 r_transform(const LMatrix4d &mat, const LMatrix4d &inv, 00646 CoordinateSystem to_cs) { 00647 // We need to build an inverse matrix that doesn't reflect the 00648 // translation component. 00649 LMatrix4d inv1 = inv; 00650 inv1.set_row(3, LVector3d(0.0, 0.0, 0.0)); 00651 00652 // Save a temporary copy of the original data. 00653 EggXfmSAnim original; 00654 original.steal_children(*this); 00655 original = (*this); 00656 00657 // Now we have no children, so our data is clear. Rebuild it. 00658 if (to_cs != CS_default) { 00659 _coordsys = to_cs; 00660 } 00661 00662 int num_rows = original.get_num_rows(); 00663 LMatrix4d orig_mat; 00664 for (int r = 0; r < num_rows; r++) { 00665 original.get_value(r, orig_mat); 00666 bool result = add_data(inv1 * orig_mat * mat); 00667 00668 // If this assertion fails, we attempted to transform by a skew 00669 // matrix or some such thing that cannot be represented in an anim 00670 // file. 00671 nassertv(result); 00672 } 00673 00674 // Now clean out the redundant columns we created. 00675 optimize(); 00676 } 00677 00678 //////////////////////////////////////////////////////////////////// 00679 // Function: EggXfmSAnim::r_mark_coordsys 00680 // Access: Protected, Virtual 00681 // Description: This is only called immediately after loading an egg 00682 // file from disk, to propagate the value found in the 00683 // CoordinateSystem entry (or the default Y-up 00684 // coordinate system) to all nodes that care about what 00685 // the coordinate system is. 00686 //////////////////////////////////////////////////////////////////// 00687 void EggXfmSAnim:: 00688 r_mark_coordsys(CoordinateSystem cs) { 00689 _coordsys = cs; 00690 } 00691 00692 //////////////////////////////////////////////////////////////////// 00693 // Function: EggXfmSAnim::normalize_by_rebuilding 00694 // Access: Private 00695 // Description: One implementation of normalize() that rebuilds the 00696 // entire table by composing and decomposing the rows. 00697 // This has the advantage that it will also reset the 00698 // order string to the standard order string, but it is 00699 // more computationally intensive and is subject to 00700 // roundoff error. 00701 //////////////////////////////////////////////////////////////////// 00702 void EggXfmSAnim:: 00703 normalize_by_rebuilding() { 00704 // Save a temporary copy of the original data. 00705 EggXfmSAnim original; 00706 original.steal_children(*this); 00707 original = (*this); 00708 00709 // Now we have no children, so our data is clear. Rebuild it. 00710 int num_rows = original.get_num_rows(); 00711 LMatrix4d orig_mat; 00712 for (int r = 0; r < num_rows; r++) { 00713 original.get_value(r, orig_mat); 00714 bool result = add_data(orig_mat); 00715 00716 // If this assertion fails, we somehow got a matrix out of the 00717 // original table that we could not represent in the new table. 00718 // That shouldn't be possible; there's probably something wrong 00719 // in decompose_matrix(). 00720 nassertv(result); 00721 } 00722 } 00723 00724 //////////////////////////////////////////////////////////////////// 00725 // Function: EggXfmSAnim::normalize_by_expanding 00726 // Access: Private 00727 // Description: Another implementation of normalize() that simply 00728 // expands any one-row tables and creates default-valued 00729 // tables where none were before. This will not change 00730 // the order string, but is much faster and does not 00731 // introduce roundoff error. 00732 //////////////////////////////////////////////////////////////////// 00733 void EggXfmSAnim:: 00734 normalize_by_expanding() { 00735 iterator ci; 00736 00737 // First, determine which tables we already have, and how long they 00738 // are. 00739 int num_tables = 0; 00740 int table_length = 1; 00741 string remaining_tables = matrix_component_letters; 00742 00743 for (ci = begin(); ci != end(); ++ci) { 00744 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { 00745 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00746 00747 nassertv(sanim->get_name().length() == 1); 00748 char name = sanim->get_name()[0]; 00749 size_t p = remaining_tables.find(name); 00750 nassertv(p != string::npos); 00751 remaining_tables[p] = ' '; 00752 00753 num_tables++; 00754 if (sanim->get_num_rows() > 1) { 00755 if (table_length == 1) { 00756 table_length = sanim->get_num_rows(); 00757 } else { 00758 nassertv(sanim->get_num_rows() == table_length); 00759 } 00760 } 00761 } 00762 } 00763 00764 if (num_tables < num_matrix_components) { 00765 // Create new, default, children for each table we lack. 00766 for (size_t p = 0; p < remaining_tables.length(); p++) { 00767 if (remaining_tables[p] != ' ') { 00768 double default_value; 00769 switch (remaining_tables[p]) { 00770 case 'i': 00771 case 'j': 00772 case 'k': 00773 default_value = 1.0; 00774 break; 00775 00776 default: 00777 default_value = 0.0; 00778 } 00779 00780 string name(1, remaining_tables[p]); 00781 EggSAnimData *sanim = new EggSAnimData(name); 00782 add_child(sanim); 00783 sanim->add_data(default_value); 00784 } 00785 } 00786 } 00787 00788 // Now expand any one-row tables as needed. 00789 for (ci = begin(); ci != end(); ++ci) { 00790 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) { 00791 EggSAnimData *sanim = DCAST(EggSAnimData, *ci); 00792 if (sanim->get_num_rows() == 1) { 00793 double value = sanim->get_value(0); 00794 for (int i = 1; i < table_length; i++) { 00795 sanim->add_data(value); 00796 } 00797 } 00798 nassertv(sanim->get_num_rows() == table_length); 00799 } 00800 } 00801 }