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::
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 */
100 void EggXfmSAnim::
101 optimize_to_standard_order() {
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::
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 */
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 }
EggAnimData::get_fps
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggAnimData.I:79
EggAnimData::add_data
void add_data(double value)
Adds a single element to the table.
Definition: eggAnimData.I:97
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggGroupNode::find_child
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...
Definition: eggGroupNode.cxx:296
EggGroupNode::add_child
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
Definition: eggGroupNode.cxx:243
EggGroupNode::steal_children
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
Definition: eggGroupNode.cxx:278
EggNamedObject::write_header
void write_header(std::ostream &out, int indent_level, const char *egg_keyword) const
Writes the first line of the egg object, e.g.
Definition: eggNamedObject.cxx:39
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
EggSAnimData
Corresponding to an <S$Anim> entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:25
EggSAnimData::get_value
double get_value(int row) const
Returns the value at the indicated row.
Definition: eggSAnimData.I:56
EggSAnimData::optimize
void optimize()
Optimizes the data by collapsing a long table of duplicate values into a single value.
Definition: eggSAnimData.cxx:29
EggSAnimData::get_num_rows
int get_num_rows() const
Returns the number of rows in the table.
Definition: eggSAnimData.I:46
EggSAnimData::write
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
Definition: eggSAnimData.cxx:51
EggSAnimData::set_value
void set_value(int row, double value)
Changes the value at the indicated row.
Definition: eggSAnimData.I:67
EggXfmAnimData
Corresponding to an <Xfm$Anim> entry, this stores a two-dimensional table with up to nine columns,...
Definition: eggXfmAnimData.h:29
EggXfmAnimData::get_num_cols
int get_num_cols() const
Returns the number of columns in the table.
Definition: eggXfmAnimData.I:156
EggXfmAnimData::get_num_rows
int get_num_rows() const
Returns the number of rows in the table.
Definition: eggXfmAnimData.I:144
EggXfmAnimData::get_value
double get_value(int row, int col) const
Returns the value at the indicated row.
Definition: eggXfmAnimData.I:166
EggXfmAnimData::get_coordinate_system
CoordinateSystem get_coordinate_system() const
Returns the coordinate system this table believes it is defined within.
Definition: eggXfmAnimData.I:135
EggXfmSAnim
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:28
EggXfmSAnim::optimize_to_standard_order
void optimize_to_standard_order()
Optimizes the table by collapsing redundant sub-tables, and simultaneously ensures that the order str...
Definition: eggXfmSAnim.cxx:101
EggXfmSAnim::optimize
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:63
EggXfmSAnim::write
virtual void write(std::ostream &out, int indent_level) const
Writes the data to the indicated output stream in Egg format.
Definition: eggXfmSAnim.cxx:143
EggXfmSAnim::get_num_rows
int get_num_rows() const
Returns the effective number of rows in the table.
Definition: eggXfmSAnim.cxx:263
EggXfmSAnim::compose_with_order
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.
Definition: eggXfmSAnim.cxx:203
EggXfmSAnim::is_anim_matrix
virtual bool is_anim_matrix() const
Returns true if this node represents a table of animation transformation data, false otherwise.
Definition: eggXfmSAnim.cxx:135
EggXfmSAnim::add_data
bool add_data(const LMatrix4d &mat)
Adds a new matrix to the table, by adding a new row to each of the subtables.
Definition: eggXfmSAnim.cxx:476
EggXfmSAnim::get_value
void get_value(int row, LMatrix4d &mat) const
Returns the value of the aggregate row of the table as a matrix.
Definition: eggXfmSAnim.cxx:293
EggXfmSAnim::set_value
bool set_value(int row, const LMatrix4d &mat)
Replaces the indicated row of the table with the given matrix.
Definition: eggXfmSAnim.cxx:392
EggXfmSAnim::add_component_data
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.
Definition: eggXfmSAnim.cxx:556
EggXfmSAnim::normalize
void normalize()
The inverse operation of optimize(), this ensures that all the sub-tables have the same length by dup...
Definition: eggXfmSAnim.cxx:115
EggXfmSAnim::get_fps
double get_fps() const
This is only valid if has_fps() returns true.
Definition: eggXfmSAnim.I:82
EggXfmSAnim::get_standard_order
static const std::string & get_standard_order()
Returns the standard order of matrix component composition.
Definition: eggXfmSAnim.I:129
Namable::has_name
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
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
compose_matrix.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_egg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggParameters.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggSAnimData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggXfmAnimData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggXfmSAnim.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
indent
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
indent.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.