00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "eggXfmSAnim.h"
00020 #include "eggSAnimData.h"
00021 #include "eggXfmAnimData.h"
00022 #include "eggParameters.h"
00023 #include "config_egg.h"
00024
00025 #include <indent.h>
00026 #include <compose_matrix.h>
00027
00028 #include <math.h>
00029
00030 TypeHandle EggXfmSAnim::_type_handle;
00031
00032
00033
00034
00035
00036
00037
00038 string EggXfmSAnim::_standard_order = "sphrt";
00039
00040
00041
00042
00043
00044
00045
00046
00047 EggXfmSAnim::
00048 EggXfmSAnim(const EggXfmAnimData &convert_from)
00049 : EggGroupNode(convert_from.get_name())
00050 {
00051 _has_fps = false;
00052 _coordsys = convert_from.get_coordinate_system();
00053
00054 if (convert_from.has_order()) {
00055 set_order(convert_from.get_order());
00056 }
00057 if (convert_from.has_fps()) {
00058 set_fps(convert_from.get_fps());
00059 }
00060
00061 const string &contents = convert_from.get_contents();
00062 for (int col = 0; col < convert_from.get_num_cols(); col++) {
00063 EggSAnimData *sanim = new EggSAnimData(contents.substr(col, 1));
00064 add_child(sanim);
00065 for (int row = 0; row < convert_from.get_num_rows(); row++) {
00066 sanim->add_data(convert_from.get_value(row, col));
00067 }
00068 }
00069 }
00070
00071
00072
00073
00074
00075
00076
00077 void EggXfmSAnim::
00078 optimize() {
00079 iterator ci = begin();
00080 while (ci != end()) {
00081 iterator ci_next = ci;
00082 ++ci_next;
00083
00084 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00085 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00086 sanim->optimize();
00087
00088 if (sanim->get_num_rows() == 1) {
00089
00090
00091 double value = sanim->get_value(0);
00092 double default_value;
00093 if (sanim->has_name() && strchr("ijk", sanim->get_name()[0]) != NULL) {
00094 default_value = 1.0;
00095 } else {
00096 default_value = 0.0;
00097 }
00098
00099 if (fabs(value - default_value) < egg_parameters->_table_threshold) {
00100
00101
00102 erase(ci);
00103 }
00104 }
00105 }
00106
00107 ci = ci_next;
00108 }
00109 }
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120 void EggXfmSAnim::
00121 optimize_to_standard_order() {
00122 if (get_order() != get_standard_order()) {
00123 normalize_by_rebuilding();
00124 }
00125 optimize();
00126 }
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137 void EggXfmSAnim::
00138 normalize() {
00139 if (get_order() != get_standard_order()) {
00140
00141
00142
00143 normalize_by_rebuilding();
00144
00145 } else {
00146
00147
00148
00149 normalize_by_expanding();
00150 }
00151 }
00152
00153
00154
00155
00156
00157
00158
00159 void EggXfmSAnim::
00160 write(ostream &out, int indent_level) const {
00161 test_under_integrity();
00162
00163 write_header(out, indent_level, "<Xfm$Anim_S$>");
00164
00165 if (has_fps()) {
00166 indent(out, indent_level + 2) << "<Scalar> fps { " << get_fps() << " }\n";
00167 }
00168
00169 if (has_order()) {
00170 indent(out, indent_level + 2)
00171 << "<Char*> order { " << get_order() << " }\n";
00172 }
00173
00174 EggGroupNode::write(out, indent_level + 2);
00175 indent(out, indent_level) << "}\n";
00176 }
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 void EggXfmSAnim::
00188 compose_with_order(LMatrix4d &mat,
00189 const LVecBase3d &scale,
00190 const LVecBase3d &hpr,
00191 const LVecBase3d &trans,
00192 const string &order,
00193 CoordinateSystem cs) {
00194
00195 mat = LMatrix4d::ident_mat();
00196
00197 bool reverse_roll = false;
00198
00199 if (order == "sphrt" && egg_support_old_anims) {
00200
00201
00202
00203 reverse_roll = true;
00204 }
00205
00206 string::const_iterator pi;
00207 for (pi = order.begin(); pi != order.end(); ++pi) {
00208 switch (*pi) {
00209 case 's':
00210 mat = mat * LMatrix4d::scale_mat(scale);
00211 break;
00212
00213 case 'h':
00214 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[0], LVector3d::up(cs), cs);
00215 break;
00216
00217 case 'p':
00218 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[1], LVector3d::right(cs), cs);
00219 break;
00220
00221 case 'r':
00222 if (reverse_roll) {
00223 mat = mat * LMatrix4d::rotate_mat_normaxis(-hpr[2], LVector3d::forward(cs), cs);
00224 } else {
00225 mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[2], LVector3d::forward(cs), cs);
00226 }
00227 break;
00228
00229 case 't':
00230 mat = mat * LMatrix4d::translate_mat(trans);
00231 break;
00232
00233 default:
00234 egg_cat.warning()
00235 << "Invalid letter in order string: " << *pi << "\n";
00236 }
00237 }
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249 int EggXfmSAnim::
00250 get_num_rows() const {
00251 bool found_any = false;
00252 int min_rows = 1;
00253
00254 const_iterator ci;
00255 for (ci = begin(); ci != end(); ++ci) {
00256 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00257 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00258 if (sanim->get_num_rows() > 1) {
00259 if (!found_any) {
00260 min_rows = sanim->get_num_rows();
00261
00262 } else {
00263 min_rows = min(min_rows, sanim->get_num_rows());
00264 }
00265 }
00266 }
00267 }
00268
00269 return min_rows;
00270 }
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283 void EggXfmSAnim::
00284 get_value(int row, LMatrix4d &mat) const {
00285 LVector3d scale(1.0, 1.0, 1.0);
00286 LVector3d hpr(0.0, 0.0, 0.0);
00287 LVector3d translate(0.0, 0.0, 0.0);
00288
00289 const_iterator ci;
00290 for (ci = begin(); ci != end(); ++ci) {
00291 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00292 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00293
00294 if (sanim->get_num_rows() == 0) {
00295
00296
00297 break;
00298 }
00299
00300 double value;
00301 if (sanim->get_num_rows() == 1) {
00302 value = sanim->get_value(0);
00303 } else {
00304 nassertv(row < sanim->get_num_rows());
00305 value = sanim->get_value(row);
00306 }
00307
00308
00309 nassertv(sanim->get_name().length() == 1);
00310
00311 switch (sanim->get_name()[0]) {
00312 case 'i':
00313 scale[0] = value;
00314 break;
00315
00316 case 'j':
00317 scale[1] = value;
00318 break;
00319
00320 case 'k':
00321 scale[2] = value;
00322 break;
00323
00324 case 'h':
00325 hpr[0] = value;
00326 break;
00327
00328 case 'p':
00329 hpr[1] = value;
00330 break;
00331
00332 case 'r':
00333 hpr[2] = value;
00334 break;
00335
00336 case 'x':
00337 translate[0] = value;
00338 break;
00339
00340 case 'y':
00341 translate[1] = value;
00342 break;
00343
00344 case 'z':
00345 translate[2] = value;
00346 break;
00347
00348 default:
00349
00350 nassertv(false);
00351 }
00352 }
00353 }
00354
00355
00356 compose_with_order(mat, scale, hpr, translate, get_order(), _coordsys);
00357 }
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373 bool EggXfmSAnim::
00374 set_value(int row, const LMatrix4d &mat) {
00375 nassertr(get_order() == get_standard_order(), false);
00376
00377 LVector3d scale, hpr, translate;
00378 bool result = decompose_matrix(mat, scale, hpr, translate, _coordsys);
00379 if (!result) {
00380 return false;
00381 }
00382
00383
00384 #ifndef NDEBUG
00385 int table_length = -1;
00386 int num_tables = 0;
00387 #endif
00388
00389 const_iterator ci;
00390 for (ci = begin(); ci != end(); ++ci) {
00391 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00392 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00393
00394 #ifndef NDEBUG
00395 num_tables++;
00396
00397
00398 if (table_length < 0) {
00399 table_length = sanim->get_num_rows();
00400 } else {
00401 nassertr(sanim->get_num_rows() == table_length, false);
00402 }
00403 #endif
00404
00405
00406 nassertr(sanim->get_name().length() == 1, false);
00407
00408 switch (sanim->get_name()[0]) {
00409 case 'i':
00410 sanim->set_value(row, scale[0]);
00411 break;
00412
00413 case 'j':
00414 sanim->set_value(row, scale[1]);
00415 break;
00416
00417 case 'k':
00418 sanim->set_value(row, scale[2]);
00419 break;
00420
00421 case 'h':
00422 sanim->set_value(row, hpr[0]);
00423 break;
00424
00425 case 'p':
00426 sanim->set_value(row, hpr[1]);
00427 break;
00428
00429 case 'r':
00430 sanim->set_value(row, hpr[2]);
00431 break;
00432
00433 case 'x':
00434 sanim->set_value(row, translate[0]);
00435 break;
00436
00437 case 'y':
00438 sanim->set_value(row, translate[1]);
00439 break;
00440
00441 case 'z':
00442 sanim->set_value(row, translate[2]);
00443 break;
00444
00445 default:
00446
00447 nassertr(false, false);
00448 }
00449 }
00450 }
00451
00452 nassertr(num_tables == 9, false);
00453 return true;
00454 }
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488 bool EggXfmSAnim::
00489 add_data(const LMatrix4d &mat) {
00490 LVector3d scale, hpr, translate;
00491 bool result = decompose_matrix(mat, scale, hpr, translate, _coordsys);
00492 if (!result) {
00493 return false;
00494 }
00495
00496 if (empty()) {
00497
00498 const char *table_ids = "ijkhprxyz";
00499 for (const char *p = table_ids; *p; p++) {
00500 EggSAnimData *sanim = new EggSAnimData(string(1, *p));
00501 add_child(sanim);
00502 }
00503
00504
00505 set_order(get_standard_order());
00506 }
00507
00508 nassertr(get_order() == get_standard_order(), false);
00509
00510 #ifndef NDEBUG
00511 int table_length = -1;
00512 int num_tables = 0;
00513 #endif
00514
00515 const_iterator ci;
00516 for (ci = begin(); ci != end(); ++ci) {
00517 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00518 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00519
00520 #ifndef NDEBUG
00521 num_tables++;
00522
00523
00524 if (table_length < 0) {
00525 table_length = sanim->get_num_rows();
00526 } else {
00527 nassertr(sanim->get_num_rows() == table_length, false);
00528 }
00529 #endif
00530
00531
00532 nassertr(sanim->get_name().length() == 1, false);
00533
00534 switch (sanim->get_name()[0]) {
00535 case 'i':
00536 sanim->add_data(scale[0]);
00537 break;
00538
00539 case 'j':
00540 sanim->add_data(scale[1]);
00541 break;
00542
00543 case 'k':
00544 sanim->add_data(scale[2]);
00545 break;
00546
00547 case 'h':
00548 sanim->add_data(hpr[0]);
00549 break;
00550
00551 case 'p':
00552 sanim->add_data(hpr[1]);
00553 break;
00554
00555 case 'r':
00556 sanim->add_data(hpr[2]);
00557 break;
00558
00559 case 'x':
00560 sanim->add_data(translate[0]);
00561 break;
00562
00563 case 'y':
00564 sanim->add_data(translate[1]);
00565 break;
00566
00567 case 'z':
00568 sanim->add_data(translate[2]);
00569 break;
00570
00571 default:
00572
00573 nassertr(false, false);
00574 }
00575 }
00576 }
00577
00578 nassertr(num_tables == 9, false);
00579 return true;
00580 }
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 void EggXfmSAnim::
00591 r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
00592 CoordinateSystem to_cs) {
00593
00594
00595 LMatrix4d inv1 = inv;
00596 inv1.set_row(3, LVector3d(0.0, 0.0, 0.0));
00597
00598
00599 EggXfmSAnim original;
00600 original.steal_children(*this);
00601 original = (*this);
00602
00603
00604 if (to_cs != CS_default) {
00605 _coordsys = to_cs;
00606 }
00607
00608 int num_rows = original.get_num_rows();
00609 LMatrix4d orig_mat;
00610 for (int r = 0; r < num_rows; r++) {
00611 original.get_value(r, orig_mat);
00612 bool result = add_data(inv1 * orig_mat * mat);
00613
00614
00615
00616
00617 nassertv(result);
00618 }
00619
00620
00621 optimize();
00622 }
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633 void EggXfmSAnim::
00634 r_mark_coordsys(CoordinateSystem cs) {
00635 _coordsys = cs;
00636 }
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648 void EggXfmSAnim::
00649 normalize_by_rebuilding() {
00650
00651 EggXfmSAnim original;
00652 original.steal_children(*this);
00653 original = (*this);
00654
00655
00656 int num_rows = original.get_num_rows();
00657 LMatrix4d orig_mat;
00658 for (int r = 0; r < num_rows; r++) {
00659 original.get_value(r, orig_mat);
00660 bool result = add_data(orig_mat);
00661
00662
00663
00664
00665
00666 nassertv(result);
00667 }
00668 }
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679 void EggXfmSAnim::
00680 normalize_by_expanding() {
00681 iterator ci;
00682
00683
00684
00685 int num_tables = 0;
00686 int table_length = 1;
00687 string remaining_tables = "ijkhprxyz";
00688
00689 for (ci = begin(); ci != end(); ++ci) {
00690 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00691 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00692
00693 nassertv(sanim->get_name().length() == 1);
00694 char name = sanim->get_name()[0];
00695 size_t p = remaining_tables.find(name);
00696 nassertv(p != string::npos);
00697 remaining_tables[p] = ' ';
00698
00699 num_tables++;
00700 if (sanim->get_num_rows() > 1) {
00701 if (table_length == 1) {
00702 table_length = sanim->get_num_rows();
00703 } else {
00704 nassertv(sanim->get_num_rows() == table_length);
00705 }
00706 }
00707 }
00708 }
00709
00710 if (num_tables < 9) {
00711
00712 for (size_t p = 0; p < remaining_tables.length(); p++) {
00713 if (remaining_tables[p] != ' ') {
00714 double default_value;
00715 switch (remaining_tables[p]) {
00716 case 'i':
00717 case 'j':
00718 case 'k':
00719 default_value = 1.0;
00720 break;
00721
00722 default:
00723 default_value = 0.0;
00724 }
00725
00726 string name(1, remaining_tables[p]);
00727 EggSAnimData *sanim = new EggSAnimData(name);
00728 add_child(sanim);
00729 sanim->add_data(default_value);
00730 }
00731 }
00732 }
00733
00734
00735 for (ci = begin(); ci != end(); ++ci) {
00736 if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
00737 EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
00738 if (sanim->get_num_rows() == 1) {
00739 double value = sanim->get_value(0);
00740 for (int i = 1; i < table_length; i++) {
00741 sanim->add_data(value);
00742 }
00743 }
00744 nassertv(sanim->get_num_rows() == table_length);
00745 }
00746 }
00747 }