Panda3D
eggXfmSAnim.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file eggXfmSAnim.cxx
10  * @author drose
11  * @date 1999-02-19
12  */
13 
14 #include "eggXfmSAnim.h"
15 #include "eggSAnimData.h"
16 #include "eggXfmAnimData.h"
17 #include "eggParameters.h"
18 #include "config_egg.h"
19 
20 #include "indent.h"
21 #include "compose_matrix.h"
22 #include "dcast.h"
23 
24 #include <math.h>
25 
26 using std::string;
27 
28 TypeHandle EggXfmSAnim::_type_handle;
29 
30 const string EggXfmSAnim::_standard_order = "srpht";
31 
32 /**
33  * Converts the older-style XfmAnim table to the newer-style XfmSAnim table.
34  */
35 EggXfmSAnim::
36 EggXfmSAnim(const EggXfmAnimData &convert_from)
37  : EggGroupNode(convert_from.get_name())
38 {
39  _has_fps = false;
40  _coordsys = convert_from.get_coordinate_system();
41 
42  if (convert_from.has_order()) {
43  set_order(convert_from.get_order());
44  }
45  if (convert_from.has_fps()) {
46  set_fps(convert_from.get_fps());
47  }
48 
49  const string &contents = convert_from.get_contents();
50  for (int col = 0; col < convert_from.get_num_cols(); col++) {
51  EggSAnimData *sanim = new EggSAnimData(contents.substr(col, 1));
52  add_child(sanim);
53  for (int row = 0; row < convert_from.get_num_rows(); row++) {
54  sanim->add_data(convert_from.get_value(row, col));
55  }
56  }
57 }
58 
59 /**
60  * Optimizes the table by collapsing redundant sub-tables.
61  */
63 optimize() {
64  iterator ci = begin();
65  while (ci != end()) {
66  iterator ci_next = ci;
67  ++ci_next;
68 
69  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
70  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
71  sanim->optimize();
72 
73  if (sanim->get_num_rows() == 1) {
74  // If we've optimized down to one value, check to see if it is a
75  // default value.
76  double value = sanim->get_value(0);
77  double default_value;
78  if (sanim->has_name() && strchr("ijk", sanim->get_name()[0]) != nullptr) {
79  default_value = 1.0;
80  } else {
81  default_value = 0.0;
82  }
83 
84  if (fabs(value - default_value) < egg_parameters->_table_threshold) {
85  // It's a default-valued table, and therefore redundant: remove it.
86  erase(ci);
87  }
88  }
89  }
90 
91  ci = ci_next;
92  }
93 }
94 
95 /**
96  * Optimizes the table by collapsing redundant sub-tables, and simultaneously
97  * ensures that the order string is the standard order (which is the same as
98  * that supported by compose_matrix() and decompose_matrix()).
99  */
102  if (get_order() != get_standard_order()) {
103  normalize_by_rebuilding();
104  }
105  optimize();
106 }
107 
108 /**
109  * The inverse operation of optimize(), this ensures that all the sub-tables
110  * have the same length by duplicating rows as necessary. This is needed
111  * before doing operations like add_data() or set_value() on an existing
112  * table.
113  */
115 normalize() {
116  if (get_order() != get_standard_order()) {
117  // If our order string is wrong, we must fix it now. This will
118  // incidentally also normalize the table, because we are totally
119  // rebuilding it.
120  normalize_by_rebuilding();
121 
122  } else {
123  // Otherwise, if the order string is already the standard order string, we
124  // can do this the easy way (from a computational standpoint), which is
125  // just to lengthen the tables directly.
126  normalize_by_expanding();
127  }
128 }
129 
130 /**
131  * Returns true if this node represents a table of animation transformation
132  * data, false otherwise.
133  */
135 is_anim_matrix() const {
136  return true;
137 }
138 
139 /**
140  * Writes the data to the indicated output stream in Egg format.
141  */
143 write(std::ostream &out, int indent_level) const {
144  test_under_integrity();
145 
146  write_header(out, indent_level, "<Xfm$Anim_S$>");
147 
148  if (has_fps()) {
149  indent(out, indent_level + 2) << "<Scalar> fps { " << get_fps() << " }\n";
150  }
151 
152  if (has_order()) {
153  indent(out, indent_level + 2)
154  << "<Char*> order { " << get_order() << " }\n";
155  }
156 
157  // Rather than calling EggGroupNode::write() to write out the children, we
158  // do it directly here so we can control the order. We write out all the
159  // non-table children first, then write out the table children in our
160  // expected order. (Normally there are only table children.)
161  EggSAnimData *tables[num_matrix_components];
162  memset(tables, 0, sizeof(EggSAnimData *) * num_matrix_components);
163 
164  const_iterator ci;
165  for (ci = begin(); ci != end(); ++ci) {
166  EggNode *child = (*ci);
167  if (child->is_of_type(EggSAnimData::get_class_type())) {
168  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
169 
170  // Each child SAnimData table should have a one-letter name.
171  nassertv(sanim->get_name().length() == 1);
172  char name = sanim->get_name()[0];
173  char *p = (char *)strchr(matrix_component_letters, name);
174  nassertv(p != nullptr);
175  if (p != nullptr) {
176  int index = p - matrix_component_letters;
177  nassertv(tables[index] == nullptr);
178  tables[index] = sanim;
179  }
180  } else {
181  // Any non-table children are written directly.
182  child->write(out, indent_level + 2);
183  }
184  }
185 
186  // Now write out the table children in our normal order.
187  for (int i = 0; i < num_matrix_components; i++) {
188  if (tables[i] != nullptr) {
189  tables[i]->write(out, indent_level + 2);
190  }
191  }
192 
193  indent(out, indent_level) << "}\n";
194 }
195 
196 
197 /**
198  * Composes a matrix out of the nine individual components, respecting the
199  * order string. The components will be applied in the order indicated by the
200  * string.
201  */
203 compose_with_order(LMatrix4d &mat,
204  const LVecBase3d &scale,
205  const LVecBase3d &shear,
206  const LVecBase3d &hpr,
207  const LVecBase3d &trans,
208  const string &order,
209  CoordinateSystem cs) {
210 
211  mat = LMatrix4d::ident_mat();
212 
213  bool reverse_roll = false;
214 
215  if (order == "sphrt" && egg_support_old_anims) {
216  // As a special case, if the order string is exactly "sphrt" (which is
217  // what all our legacy anim files used), we interpret roll in the opposite
218  // direction (as our legacy anim files did).
219  reverse_roll = true;
220  }
221 
222  string::const_iterator pi;
223  for (pi = order.begin(); pi != order.end(); ++pi) {
224  switch (*pi) {
225  case 's':
226  mat = mat * LMatrix4d::scale_shear_mat(scale, shear, cs);
227  break;
228 
229  case 'h':
230  mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[0], LVector3d::up(cs), cs);
231  break;
232 
233  case 'p':
234  mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[1], LVector3d::right(cs), cs);
235  break;
236 
237  case 'r':
238  if (reverse_roll) {
239  mat = mat * LMatrix4d::rotate_mat_normaxis(-hpr[2], LVector3d::forward(cs), cs);
240  } else {
241  mat = mat * LMatrix4d::rotate_mat_normaxis(hpr[2], LVector3d::forward(cs), cs);
242  }
243  break;
244 
245  case 't':
246  mat = mat * LMatrix4d::translate_mat(trans);
247  break;
248 
249  default:
250  egg_cat.warning()
251  << "Invalid letter in order string: " << *pi << "\n";
252  }
253  }
254 }
255 
256 /**
257  * Returns the effective number of rows in the table. This is actually the
258  * number of rows of the smallest subtable larger than one row. This is a
259  * convenience function that treats the table of tables as if it were a single
260  * table of matrices.
261  */
263 get_num_rows() const {
264  bool found_any = false;
265  int min_rows = 1;
266 
267  const_iterator ci;
268  for (ci = begin(); ci != end(); ++ci) {
269  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
270  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
271  if (sanim->get_num_rows() > 1) {
272  if (!found_any) {
273  min_rows = sanim->get_num_rows();
274 
275  } else {
276  min_rows = std::min(min_rows, sanim->get_num_rows());
277  }
278  }
279  }
280  }
281 
282  return min_rows;
283 }
284 
285 /**
286  * Returns the value of the aggregate row of the table as a matrix. This is a
287  * convenience function that treats the table of tables as if it were a single
288  * table of matrices. It is an error to call this if any SAnimData children
289  * of this node have an improper name (e.g. not a single letter, or not one
290  * of "ijkabchprxyz").
291  */
293 get_value(int row, LMatrix4d &mat) const {
294  LVector3d scale(1.0, 1.0, 1.0);
295  LVector3d shear(0.0, 0.0, 0.0);
296  LVector3d hpr(0.0, 0.0, 0.0);
297  LVector3d translate(0.0, 0.0, 0.0);
298 
299  const_iterator ci;
300  for (ci = begin(); ci != end(); ++ci) {
301  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
302  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
303 
304  if (sanim->get_num_rows() == 0) {
305  // If the table is totally empty, let's keep the default value.
306  break;
307  }
308 
309  double value;
310  if (sanim->get_num_rows() == 1) {
311  value = sanim->get_value(0);
312  } else {
313  nassertv(row < sanim->get_num_rows());
314  value = sanim->get_value(row);
315  }
316 
317  // Each child SAnimData table should have a one-letter name.
318  nassertv(sanim->get_name().length() == 1);
319 
320  switch (sanim->get_name()[0]) {
321  case 'i':
322  scale[0] = value;
323  break;
324 
325  case 'j':
326  scale[1] = value;
327  break;
328 
329  case 'k':
330  scale[2] = value;
331  break;
332 
333  case 'a':
334  shear[0] = value;
335  break;
336 
337  case 'b':
338  shear[1] = value;
339  break;
340 
341  case 'c':
342  shear[2] = value;
343  break;
344 
345  case 'h':
346  hpr[0] = value;
347  break;
348 
349  case 'p':
350  hpr[1] = value;
351  break;
352 
353  case 'r':
354  hpr[2] = value;
355  break;
356 
357  case 'x':
358  translate[0] = value;
359  break;
360 
361  case 'y':
362  translate[1] = value;
363  break;
364 
365  case 'z':
366  translate[2] = value;
367  break;
368 
369  default:
370  // One of the child tables had an invalid name.
371  nassert_raise("invalid name in child table");
372  return;
373  }
374  }
375  }
376 
377  // So now we've got the nine components; build a matrix.
378  compose_with_order(mat, scale, shear, hpr, translate, get_order(), _coordsys);
379 }
380 
381 /**
382  * Replaces the indicated row of the table with the given matrix.
383  *
384  * This function can only be called if all the constraints of add_data(),
385  * below, are met. Call normalize() first if you are not sure.
386  *
387  * The return value is true if the matrix can be decomposed and stored as
388  * scale, shear, rotate, and translate, or false otherwise. The data is set
389  * in either case.
390  */
392 set_value(int row, const LMatrix4d &mat) {
393  nassertr(get_order() == get_standard_order(), false);
394 
395  double components[num_matrix_components];
396  bool add_ok = decompose_matrix(mat, components, _coordsys);
397 
398  // Sanity check our sub-tables.
399 #ifndef NDEBUG
400  int table_length = -1;
401 #endif
402 
403  for (int i = 0; i < num_matrix_components; i++) {
404  string name(1, matrix_component_letters[i]);
405  EggNode *child = find_child(name);
406  nassertr(child != nullptr &&
407  child->is_of_type(EggSAnimData::get_class_type()), false);
408  EggSAnimData *sanim = DCAST(EggSAnimData, child);
409 
410 #ifndef NDEBUG
411  // Each table must have the same length.
412  if (table_length < 0) {
413  table_length = sanim->get_num_rows();
414  } else {
415  nassertr(sanim->get_num_rows() == table_length, false);
416  }
417 #endif
418  sanim->set_value(row, components[i]);
419  }
420 
421 #ifndef NDEBUG
422  // Sanity check the result.
423  LMatrix4d new_mat;
424  get_value(row, new_mat);
425  if (!new_mat.almost_equal(mat, 0.005)) {
426  egg_cat.warning()
427  << "After set_row(" << row << ", ...) to:\n";
428  mat.write(egg_cat.warning(false), 2);
429  egg_cat.warning(false)
430  << "which produces components:\n";
431  for (int i = 0; i < num_matrix_components; i += 3) {
432  egg_cat.warning(false)
433  << " "
434  << matrix_component_letters[i]
435  << matrix_component_letters[i + 1]
436  << matrix_component_letters[i + 2]
437  << ": "
438  << components[i] << " "
439  << components[i + 1] << " "
440  << components[i + 2] << "\n";
441  }
442  egg_cat.warning(false)
443  << "new mat set was:\n";
444  new_mat.write(egg_cat.warning(false), 2);
445  return false;
446  }
447 #endif
448 
449  return add_ok;
450 }
451 
452 /**
453  * Adds a new matrix to the table, by adding a new row to each of the
454  * subtables.
455  *
456  * This is a convenience function that treats the table of tables as if it
457  * were a single table of matrices. It is an error to call this if any
458  * SAnimData children of this node have an improper name (e.g. not a single
459  * letter, or not one of "ijkabchprxyz").
460  *
461  * This function has the further requirement that all nine of the subtables
462  * must exist and be of the same length. Furthermore, the order string must
463  * be the standard order string, which matches the system compose_matrix() and
464  * decompose_matrix() functions.
465  *
466  * Thus, you probably cannot take an existing EggXfmSAnim object and start
467  * adding matrices to the end; you must clear out the original data first.
468  * (As a special exception, if no tables exist, they will be created.) The
469  * method normalize() will do this for you on an existing EggXfmSAnim.
470  *
471  * This function may fail silently if the matrix cannot be decomposed into
472  * scale, shear, rotate, and translate. In this case, the closest
473  * approximation is added to the table, and false is returned.
474  */
476 add_data(const LMatrix4d &mat) {
477  double components[num_matrix_components];
478  bool add_ok = decompose_matrix(mat, components, _coordsys);
479 
480  if (empty()) {
481  // If we have no children, create all twelve tables now.
482  for (int i = 0; i < num_matrix_components; i++) {
483  char name = matrix_component_letters[i];
484  EggSAnimData *sanim = new EggSAnimData(string(1, name));
485  add_child(sanim);
486  }
487 
488  // Also insist on the correct ordering right off the bat.
489  set_order(get_standard_order());
490  }
491 
492  nassertr(get_order() == get_standard_order(), false);
493 
494 #ifndef NDEBUG
495  int table_length = -1;
496 #endif
497 
498  for (int i = 0; i < num_matrix_components; i++) {
499  string name(1, matrix_component_letters[i]);
500  EggNode *child = find_child(name);
501  nassertr(child != nullptr &&
502  child->is_of_type(EggSAnimData::get_class_type()), false);
503  EggSAnimData *sanim = DCAST(EggSAnimData, child);
504 
505 #ifndef NDEBUG
506  // Each table must have the same length.
507  if (table_length < 0) {
508  table_length = sanim->get_num_rows();
509  } else {
510  nassertr(sanim->get_num_rows() == table_length, false);
511  }
512 #endif
513  sanim->add_data(components[i]);
514  }
515 
516 #ifndef NDEBUG
517  // Sanity check the result.
518  LMatrix4d new_mat;
519  if (table_length >= 0) {
520  get_value(table_length, new_mat);
521  } else {
522  get_value(0, new_mat);
523  }
524  if (!new_mat.almost_equal(mat, 0.005)) {
525  egg_cat.warning()
526  << "After add_data():\n";
527  mat.write(egg_cat.warning(false), 2);
528  egg_cat.warning(false)
529  << "which produces components:\n";
530  for (int i = 0; i < num_matrix_components; i += 3) {
531  egg_cat.warning(false)
532  << " "
533  << matrix_component_letters[i]
534  << matrix_component_letters[i + 1]
535  << matrix_component_letters[i + 2]
536  << ": "
537  << components[i] << " "
538  << components[i + 1] << " "
539  << components[i + 2] << "\n";
540  }
541  egg_cat.warning(false)
542  << "new mat set was:\n";
543  new_mat.write(egg_cat.warning(false), 2);
544  return false;
545  }
546 #endif
547 
548  return add_ok;
549 }
550 
551 /**
552  * Adds a new row to the named component (one of matrix_component_letters) of
553  * the table.
554  */
556 add_component_data(const string &component_name, double value) {
557  EggNode *child = find_child(component_name);
558  EggSAnimData *sanim;
559  if (child == nullptr) {
560  // We don't have this component yet; create it.
561  sanim = new EggSAnimData(component_name);
562  add_child(sanim);
563 
564  } else {
565  DCAST_INTO_V(sanim, child);
566  }
567 
568  sanim->add_data(value);
569 }
570 
571 /**
572  * Adds a new row to the indicated component (0-12) of the table.
573  */
575 add_component_data(int component, double value) {
576  nassertv(component >= 0 && component < num_matrix_components);
577 
578  string name(1, matrix_component_letters[component]);
579  add_component_data(name, value);
580 }
581 
582 /**
583  * Applies the indicated transform to all the rows of the table. This
584  * actually forces the generation of a totally new set of rows, and will
585  * quietly change the order to the standard order (if it is different).
586  */
587 void EggXfmSAnim::
588 r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
589  CoordinateSystem to_cs) {
590  // We need to build an inverse matrix that doesn't reflect the translation
591  // component.
592  LMatrix4d inv1 = inv;
593  inv1.set_row(3, LVector3d(0.0, 0.0, 0.0));
594 
595  // Save a temporary copy of the original data.
596  EggXfmSAnim original;
597  original.steal_children(*this);
598  original = (*this);
599 
600  // Now we have no children, so our data is clear. Rebuild it.
601  if (to_cs != CS_default) {
602  _coordsys = to_cs;
603  }
604 
605  int num_rows = original.get_num_rows();
606  LMatrix4d orig_mat;
607  for (int r = 0; r < num_rows; r++) {
608  original.get_value(r, orig_mat);
609  bool result = add_data(inv1 * orig_mat * mat);
610 
611  // If this assertion fails, we attempted to transform by a skew matrix or
612  // some such thing that cannot be represented in an anim file.
613  nassertv(result);
614  }
615 
616  // Now clean out the redundant columns we created.
617  optimize();
618 }
619 
620 /**
621  * This is only called immediately after loading an egg file from disk, to
622  * propagate the value found in the CoordinateSystem entry (or the default
623  * Y-up coordinate system) to all nodes that care about what the coordinate
624  * system is.
625  */
626 void EggXfmSAnim::
627 r_mark_coordsys(CoordinateSystem cs) {
628  _coordsys = cs;
629 }
630 
631 /**
632  * One implementation of normalize() that rebuilds the entire table by
633  * composing and decomposing the rows. This has the advantage that it will
634  * also reset the order string to the standard order string, but it is more
635  * computationally intensive and is subject to roundoff error.
636  */
637 void EggXfmSAnim::
638 normalize_by_rebuilding() {
639  // Save a temporary copy of the original data.
640  EggXfmSAnim original;
641  original.steal_children(*this);
642  original = (*this);
643 
644  // Now we have no children, so our data is clear. Rebuild it.
645  int num_rows = original.get_num_rows();
646  LMatrix4d orig_mat;
647  for (int r = 0; r < num_rows; r++) {
648  original.get_value(r, orig_mat);
649  bool result = add_data(orig_mat);
650 
651  // If this assertion fails, we somehow got a matrix out of the original
652  // table that we could not represent in the new table. That shouldn't be
653  // possible; there's probably something wrong in decompose_matrix().
654  nassertv(result);
655  }
656 }
657 
658 /**
659  * Another implementation of normalize() that simply expands any one-row
660  * tables and creates default-valued tables where none were before. This will
661  * not change the order string, but is much faster and does not introduce
662  * roundoff error.
663  */
664 void EggXfmSAnim::
665 normalize_by_expanding() {
666  iterator ci;
667 
668  // First, determine which tables we already have, and how long they are.
669  int num_tables = 0;
670  int table_length = 1;
671  string remaining_tables = matrix_component_letters;
672 
673  for (ci = begin(); ci != end(); ++ci) {
674  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
675  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
676 
677  nassertv(sanim->get_name().length() == 1);
678  char name = sanim->get_name()[0];
679  size_t p = remaining_tables.find(name);
680  nassertv(p != string::npos);
681  remaining_tables[p] = ' ';
682 
683  num_tables++;
684  if (sanim->get_num_rows() > 1) {
685  if (table_length == 1) {
686  table_length = sanim->get_num_rows();
687  } else {
688  nassertv(sanim->get_num_rows() == table_length);
689  }
690  }
691  }
692  }
693 
694  if (num_tables < num_matrix_components) {
695  // Create new, default, children for each table we lack.
696  for (size_t p = 0; p < remaining_tables.length(); p++) {
697  if (remaining_tables[p] != ' ') {
698  double default_value;
699  switch (remaining_tables[p]) {
700  case 'i':
701  case 'j':
702  case 'k':
703  default_value = 1.0;
704  break;
705 
706  default:
707  default_value = 0.0;
708  }
709 
710  string name(1, remaining_tables[p]);
711  EggSAnimData *sanim = new EggSAnimData(name);
712  add_child(sanim);
713  sanim->add_data(default_value);
714  }
715  }
716  }
717 
718  // Now expand any one-row tables as needed.
719  for (ci = begin(); ci != end(); ++ci) {
720  if ((*ci)->is_of_type(EggSAnimData::get_class_type())) {
721  EggSAnimData *sanim = DCAST(EggSAnimData, *ci);
722  if (sanim->get_num_rows() == 1) {
723  double value = sanim->get_value(0);
724  for (int i = 1; i < table_length; i++) {
725  sanim->add_data(value);
726  }
727  }
728  nassertv(sanim->get_num_rows() == table_length);
729  }
730  }
731 }
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggAnimData.I:79
void add_data(double value)
Adds a single element to the table.
Definition: eggAnimData.I:97
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggNode * find_child(const std::string &name) const
Returns the child of this node whose name is the indicated string, or NULL if there is no child of th...
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
void write_header(std::ostream &out, int indent_level, const char *egg_keyword) const
Writes the first line of the egg object, e.g.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
Corresponding to an <S$Anim> entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:25
double get_value(int row) const
Returns the value at the indicated row.
Definition: eggSAnimData.I:56
void optimize()
Optimizes the data by collapsing a long table of duplicate values into a single value.
int get_num_rows() const
Returns the number of rows in the table.
Definition: eggSAnimData.I:46
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
void set_value(int row, double value)
Changes the value at the indicated row.
Definition: eggSAnimData.I:67
Corresponding to an <Xfm$Anim> entry, this stores a two-dimensional table with up to nine columns,...
int get_num_cols() const
Returns the number of columns in the table.
int get_num_rows() const
Returns the number of rows in the table.
double get_value(int row, int col) const
Returns the value at the indicated row.
CoordinateSystem get_coordinate_system() const
Returns the coordinate system this table believes it is defined within.
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:28
void optimize_to_standard_order()
Optimizes the table by collapsing redundant sub-tables, and simultaneously ensures that the order str...
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:63
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
int get_num_rows() const
Returns the effective number of rows in the table.
static void compose_with_order(LMatrix4d &mat, const LVecBase3d &scale, const LVecBase3d &shear, const LVecBase3d &hpr, const LVecBase3d &trans, const std::string &order, CoordinateSystem cs)
Composes a matrix out of the nine individual components, respecting the order string.
virtual bool is_anim_matrix() const
Returns true if this node represents a table of animation transformation data, false otherwise.
bool add_data(const LMatrix4d &mat)
Adds a new matrix to the table, by adding a new row to each of the subtables.
void get_value(int row, LMatrix4d &mat) const
Returns the value of the aggregate row of the table as a matrix.
bool set_value(int row, const LMatrix4d &mat)
Replaces the indicated row of the table with the given matrix.
void add_component_data(const std::string &component_name, double value)
Adds a new row to the named component (one of matrix_component_letters) of the table.
void normalize()
The inverse operation of optimize(), this ensures that all the sub-tables have the same length by dup...
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggXfmSAnim.I:82
static const std::string & get_standard_order()
Returns the standard order of matrix component composition.
Definition: eggXfmSAnim.I:129
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.