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  */
62 void EggXfmSAnim::
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  */
100 void EggXfmSAnim::
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  */
114 void EggXfmSAnim::
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  */
134 bool EggXfmSAnim::
135 is_anim_matrix() const {
136  return true;
137 }
138 
139 /**
140  * Writes the data to the indicated output stream in Egg format.
141  */
142 void EggXfmSAnim::
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  */
202 void EggXfmSAnim::
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  */
262 int EggXfmSAnim::
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  */
292 void EggXfmSAnim::
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  */
391 bool EggXfmSAnim::
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  */
475 bool EggXfmSAnim::
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  */
555 void EggXfmSAnim::
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  */
574 void EggXfmSAnim::
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 }
void optimize()
Optimizes the data by collapsing a long table of duplicate values into a single value.
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.
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.
void write_header(std::ostream &out, int indent_level, const char *egg_keyword) const
Writes the first line of the egg object, e.g.
bool add_data(const LMatrix4d &mat)
Adds a new matrix to the table, by adding a new row to each of the subtables.
void add_data(double value)
Adds a single element to the table.
Definition: eggAnimData.I:97
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
void normalize()
The inverse operation of optimize(), this ensures that all the sub-tables have the same length by dup...
Corresponding to an <S$Anim> entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:25
CoordinateSystem get_coordinate_system() const
Returns the coordinate system this table believes it is defined within.
double get_value(int row) const
Returns the value at the indicated row.
Definition: eggSAnimData.I:56
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
double get_value(int row, int col) const
Returns the value at the indicated row.
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:28
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggAnimData.I:79
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
Corresponding to an <Xfm$Anim> entry, this stores a two-dimensional table with up to nine columns,...
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
void get_value(int row, LMatrix4d &mat) const
Returns the value of the aggregate row of the table as a matrix.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
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.
int get_num_rows() const
Returns the effective number of rows in the table.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
virtual bool is_anim_matrix() const
Returns true if this node represents a table of animation transformation data, false otherwise.
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggXfmSAnim.I:82
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.
int get_num_rows() const
Returns the number of rows in the table.
Definition: eggSAnimData.I:46
bool set_value(int row, const LMatrix4d &mat)
Replaces the indicated row of the table with the given matrix.
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:44
static const std::string & get_standard_order()
Returns the standard order of matrix component composition.
Definition: eggXfmSAnim.I:129
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.